]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #13455 from sri-mohan1/srib-ldpd
authorDonatas Abraitis <donatas@opensourcerouting.org>
Sat, 6 May 2023 19:47:27 +0000 (22:47 +0300)
committerGitHub <noreply@github.com>
Sat, 6 May 2023 19:47:27 +0000 (22:47 +0300)
ldpd: changes for code maintainability

729 files changed:
.dir-locals.el [deleted file]
.flake8 [new file with mode: 0644]
.github/workflows/conflicts.yml
.gitignore
.isort.cfg [new file with mode: 0644]
.pylintrc
alpine/APKBUILD.in
babeld/babeld.c
babeld/babeld.h
babeld/message.c
babeld/message.h
babeld/util.h
bgpd/bgp_attr.c
bgpd/bgp_bfd.c
bgpd/bgp_bmp.c
bgpd/bgp_dump.c
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_evpn.c
bgpd/bgp_evpn.h
bgpd/bgp_evpn_mh.c
bgpd/bgp_evpn_vty.c
bgpd/bgp_flowspec.c
bgpd/bgp_flowspec.h
bgpd/bgp_flowspec_util.c
bgpd/bgp_lcommunity.c
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn_snmp.c
bgpd/bgp_nexthop.c
bgpd/bgp_packet.c
bgpd/bgp_packet.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_routemap_nb.c
bgpd/bgp_routemap_nb.h
bgpd/bgp_routemap_nb_config.c
bgpd/bgp_updgrp.c
bgpd/bgp_vty.c
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.h
bgpd/rfapi/rfapi.c
bgpd/rfapi/rfapi_import.c
bgpd/rfapi/rfapi_rib.c
bgpd/rfapi/rfapi_vty.c
bgpd/rfapi/vnc_import_bgp.c
configure.ac
doc/developer/logging.rst
doc/developer/topotests.rst
doc/developer/zebra.rst
doc/manpages/vtysh.rst
doc/user/basic.rst
doc/user/bfd.rst
doc/user/bgp.rst
doc/user/index.rst
doc/user/isisd.rst
doc/user/mgmtd.rst
doc/user/ospfd.rst
doc/user/overview.rst
doc/user/pim.rst
doc/user/routemap.rst
docker/ubi-8/Dockerfile [deleted file]
docker/ubi-8/build.sh [deleted file]
docker/ubi-8/docker-start [deleted file]
docker/ubi8-minimal/Dockerfile [new file with mode: 0644]
docker/ubi8-minimal/almalinux.repo [new file with mode: 0644]
docker/ubi8-minimal/build.sh [new file with mode: 0755]
docker/ubi8-minimal/docker-start [new file with mode: 0755]
isisd/fabricd.c
isisd/isis_adjacency.c
isisd/isis_adjacency.h
isisd/isis_affinitymap.c [new file with mode: 0644]
isisd/isis_affinitymap.h [new file with mode: 0644]
isisd/isis_circuit.c
isisd/isis_cli.c
isisd/isis_common.h
isisd/isis_dynhn.c
isisd/isis_events.c
isisd/isis_flex_algo.c [new file with mode: 0644]
isisd/isis_flex_algo.h [new file with mode: 0644]
isisd/isis_lfa.c
isisd/isis_lsp.c
isisd/isis_main.c
isisd/isis_misc.c
isisd/isis_misc.h
isisd/isis_mt.c
isisd/isis_nb.c
isisd/isis_nb.h
isisd/isis_nb_config.c
isisd/isis_nb_notifications.c
isisd/isis_nb_state.c
isisd/isis_pdu.c
isisd/isis_pdu.h
isisd/isis_pdu_counter.c
isisd/isis_pdu_counter.h
isisd/isis_route.c
isisd/isis_route.h
isisd/isis_snmp.c
isisd/isis_spf.c
isisd/isis_spf.h
isisd/isis_spf_private.h
isisd/isis_sr.c
isisd/isis_sr.h
isisd/isis_te.c
isisd/isis_tlvs.c
isisd/isis_tlvs.h
isisd/isis_tx_queue.c
isisd/isis_zebra.c
isisd/isis_zebra.h
isisd/isisd.c
isisd/isisd.h
isisd/subdir.am
lib/command.c
lib/command.h
lib/compiler.h
lib/cspf.c
lib/cspf.h
lib/elf_py.c
lib/flex_algo.c [new file with mode: 0644]
lib/flex_algo.h [new file with mode: 0644]
lib/getopt.c [deleted file]
lib/getopt.h [deleted file]
lib/getopt1.c [deleted file]
lib/if_rmap.c
lib/if_rmap.h
lib/iso.c [new file with mode: 0644]
lib/iso.h [new file with mode: 0644]
lib/link_state.c
lib/link_state.h
lib/log_vty.c
lib/mgmt_be_client.c
lib/mgmt_be_client.h
lib/mgmt_fe_client.c
lib/mgmt_fe_client.h
lib/northbound.c
lib/northbound_cli.c
lib/northbound_sysrepo.c
lib/prefix.c
lib/prefix.h
lib/routemap.c
lib/routemap.h
lib/routemap_cli.c
lib/routemap_northbound.c
lib/segment_routing.c [new file with mode: 0644]
lib/segment_routing.h [new file with mode: 0644]
lib/subdir.am
lib/termtable.c
lib/termtable.h
lib/typesafe.c
lib/typesafe.h
lib/vty.c
lib/vty.h
lib/yang.c
lib/yang.h
lib/yang_translator.c
lib/yang_wrappers.c
lib/zclient.c
lib/zclient.h
mgmtd/mgmt.c
mgmtd/mgmt.h
mgmtd/mgmt_be_adapter.c
mgmtd/mgmt_be_server.c
mgmtd/mgmt_ds.c
mgmtd/mgmt_ds.h
mgmtd/mgmt_fe_adapter.c
mgmtd/mgmt_fe_server.c
mgmtd/mgmt_history.c
mgmtd/mgmt_history.h
mgmtd/mgmt_main.c
mgmtd/mgmt_txn.c
mgmtd/mgmt_vty.c
mgmtd/mgmt_vty.c.safe [deleted file]
nhrpd/debug.h
nhrpd/nhrp_nhs.c
ospf6d/ospf6_gr_helper.c
ospf6d/ospf6_spf.c
ospf6d/ospf6_spf.h
ospfd/ospf_abr.c
ospfd/ospf_abr.h
ospfd/ospf_asbr.c
ospfd/ospf_asbr.h
ospfd/ospf_ase.c
ospfd/ospf_interface.c
ospfd/ospf_interface.h
ospfd/ospf_lsa.c
ospfd/ospf_lsa.h
ospfd/ospf_network.c
ospfd/ospf_network.h
ospfd/ospf_opaque.c
ospfd/ospf_packet.c
ospfd/ospf_ri.c
ospfd/ospf_route.c
ospfd/ospf_route.h
ospfd/ospf_routemap.c
ospfd/ospf_snmp.c
ospfd/ospf_te.c
ospfd/ospf_vty.c
ospfd/ospf_zebra.c
ospfd/ospf_zebra.h
ospfd/ospfd.c
ospfd/ospfd.h
pathd/path_ted.c
pimd/pim6_cmd.c
pimd/pim6_mld.c
pimd/pim6_mld.h
pimd/pim_addr.h
pimd/pim_bsm.c
pimd/pim_cmd.c
pimd/pim_cmd.h
pimd/pim_iface.c
pimd/pim_iface.h
pimd/pim_igmp_join.h
pimd/pim_mroute.c
pimd/pim_nb_config.c
pimd/pim_neighbor.c
pimd/pim_nht.c
pimd/pim_oil.c
pimd/pim_oil.h
pimd/pim_vty.c
pimd/pim_zebra.c
pimd/subdir.am
pimd/test_igmpv3_join.c
python/xref2vtysh.py
ripd/rip_bfd.c [new file with mode: 0644]
ripd/rip_bfd.h [new file with mode: 0644]
ripd/rip_cli.c
ripd/rip_interface.c
ripd/rip_main.c
ripd/rip_nb.c
ripd/rip_nb.h
ripd/rip_nb_config.c
ripd/rip_nb_state.c
ripd/rip_peer.c
ripd/rip_zebra.c
ripd/ripd.c
ripd/ripd.h
ripd/subdir.am
ripngd/ripng_nb.c
ripngd/ripng_nb.h
ripngd/ripng_nb_config.c
ripngd/ripngd.c
sharpd/sharp_vty.c
sharpd/sharp_zebra.c
staticd/static_main.c
staticd/static_vty.c
tests/bgpd/test_mp_attr.c
tests/isisd/test_common.c
tests/isisd/test_isis_spf.c
tests/lib/cxxcompat.c
tests/lib/subdir.am
tests/lib/test_typelist.h
tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4
tests/topotests/all_protocol_startup/test_all_protocol_startup.py
tests/topotests/bfd_topo3/r5/bfdd.conf
tests/topotests/bfd_topo3/r6/bfdd.conf
tests/topotests/bgp_accept_own/ce1/bgpd.conf
tests/topotests/bgp_accept_own/ce2/bgpd.conf
tests/topotests/bgp_accept_own/pe1/bgpd.conf
tests/topotests/bgp_accept_own/rr1/bgpd.conf
tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf
tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf
tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py
tests/topotests/bgp_comm_list_match/r2/bgpd.conf
tests/topotests/bgp_confed1/r1/bgpd.conf
tests/topotests/bgp_confed1/r2/bgpd.conf
tests/topotests/bgp_confed1/r3/bgpd.conf
tests/topotests/bgp_confed1/r4/bgpd.conf
tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf
tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py
tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
tests/topotests/bgp_features/r1/ospf_neighbor.json
tests/topotests/bgp_features/r2/ospf_neighbor.json
tests/topotests/bgp_features/r3/ospf_neighbor.json
tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py
tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py
tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py
tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_lu_explicitnull/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_lu_explicitnull/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/__init__.py [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/r1/frr.conf [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/r2/frr.conf [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/r3/frr.conf [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/r4/frr.conf [new file with mode: 0644]
tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py [new file with mode: 0644]
tests/topotests/bgp_prefix_list_any/r2/bgpd.conf
tests/topotests/bgp_route_map_delay_timer/__init__.py [new file with mode: 0644]
tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py [new file with mode: 0644]
tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf
tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py [new file with mode: 0644]
tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf
tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py [new file with mode: 0755]
tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf
tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf
tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf
tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf
tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf
tests/topotests/bgp_suppress_fib/r2/bgpd.conf
tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py
tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf
tests/topotests/config_timing/test_config_timing.py
tests/topotests/conftest.py
tests/topotests/cspf_topo1/reference/sharp-ted.json
tests/topotests/example_munet/munet.yaml [new file with mode: 0644]
tests/topotests/example_munet/r1/daemons [new file with mode: 0644]
tests/topotests/example_munet/r1/frr.conf [new file with mode: 0644]
tests/topotests/example_munet/r1/vtysh.conf [new file with mode: 0644]
tests/topotests/example_munet/r2/daemons [new file with mode: 0644]
tests/topotests/example_munet/r2/frr.conf [new file with mode: 0644]
tests/topotests/example_munet/r2/vtysh.conf [new file with mode: 0644]
tests/topotests/example_munet/r3/daemons [new file with mode: 0644]
tests/topotests/example_munet/r3/frr.conf [new file with mode: 0644]
tests/topotests/example_munet/r3/vtysh.conf [new file with mode: 0644]
tests/topotests/example_munet/test_munet.py [new file with mode: 0644]
tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py
tests/topotests/isis_snmp/r5/ldpdconf
tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py [new file with mode: 0755]
tests/topotests/isis_sr_flex_algo_topo2/README.md [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh [new file with mode: 0755]
tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf [new file with mode: 0644]
tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py [new file with mode: 0755]
tests/topotests/isis_te_topo1/reference/ted_step1.json
tests/topotests/isis_te_topo1/reference/ted_step10.json
tests/topotests/isis_te_topo1/reference/ted_step2.json
tests/topotests/isis_te_topo1/reference/ted_step3.json
tests/topotests/isis_te_topo1/reference/ted_step4.json
tests/topotests/isis_te_topo1/reference/ted_step5.json
tests/topotests/isis_te_topo1/reference/ted_step6.json
tests/topotests/isis_te_topo1/reference/ted_step7.json
tests/topotests/isis_te_topo1/reference/ted_step8.json
tests/topotests/kinds.yaml [new file with mode: 0644]
tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json
tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json
tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json
tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json
tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json
tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json
tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json
tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json
tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json
tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json
tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json
tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json
tests/topotests/lib/common_config.py
tests/topotests/lib/grpc-query.py
tests/topotests/lib/micronet.py
tests/topotests/lib/micronet_cli.py [deleted file]
tests/topotests/lib/micronet_compat.py
tests/topotests/lib/ospf.py
tests/topotests/lib/pim.py
tests/topotests/lib/topogen.py
tests/topotests/lib/topotest.py
tests/topotests/mgmt_tests/test_yang_mgmt.py
tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json [new file with mode: 0644]
tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py [new file with mode: 0644]
tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py
tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py
tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py
tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py
tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json [new file with mode: 0644]
tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
tests/topotests/munet/__init__.py [new file with mode: 0644]
tests/topotests/munet/__main__.py [new file with mode: 0644]
tests/topotests/munet/base.py [new file with mode: 0644]
tests/topotests/munet/cleanup.py [new file with mode: 0644]
tests/topotests/munet/cli.py [new file with mode: 0644]
tests/topotests/munet/compat.py [new file with mode: 0644]
tests/topotests/munet/config.py [new file with mode: 0644]
tests/topotests/munet/kinds.yaml [new file with mode: 0644]
tests/topotests/munet/linux.py [new file with mode: 0644]
tests/topotests/munet/logconf-mutest.yaml [new file with mode: 0644]
tests/topotests/munet/logconf.yaml [new file with mode: 0644]
tests/topotests/munet/mucmd.py [new file with mode: 0644]
tests/topotests/munet/mulog.py [new file with mode: 0644]
tests/topotests/munet/munet-schema.json [new file with mode: 0644]
tests/topotests/munet/mutest/__main__.py [new file with mode: 0644]
tests/topotests/munet/mutest/userapi.py [new file with mode: 0644]
tests/topotests/munet/mutestshare.py [new file with mode: 0644]
tests/topotests/munet/mutini.py [new file with mode: 0755]
tests/topotests/munet/native.py [new file with mode: 0644]
tests/topotests/munet/parser.py [new file with mode: 0644]
tests/topotests/munet/testing/__init__.py [new file with mode: 0644]
tests/topotests/munet/testing/fixtures.py [new file with mode: 0644]
tests/topotests/munet/testing/hooks.py [new file with mode: 0644]
tests/topotests/munet/testing/util.py [new file with mode: 0644]
tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf
tests/topotests/ospf_basic_functionality/ospf_lan.json
tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
tests/topotests/ospf_basic_functionality/test_ospf_lan.py
tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
tests/topotests/ospf_metric_propagation/__init__.py [new file with mode: 0755]
tests/topotests/ospf_metric_propagation/h1/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/h2/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r2/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r3/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/r4/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/ra/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/rb/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/rc/frr.conf [new file with mode: 0644]
tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/__init__.py [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/staticd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt1/zebra.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/staticd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt2/zebra.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/staticd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt3/zebra.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/staticd.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/rt4/zebra.conf [new file with mode: 0644]
tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py [new file with mode: 0644]
tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
tests/topotests/ospf_te_topo1/reference/ted_step1.json
tests/topotests/ospf_te_topo1/reference/ted_step2.json
tests/topotests/ospf_te_topo1/reference/ted_step3.json
tests/topotests/ospf_te_topo1/reference/ted_step4.json
tests/topotests/ospf_te_topo1/reference/ted_step5.json
tests/topotests/ospf_te_topo1/reference/ted_step6.json
tests/topotests/ospf_te_topo1/reference/ted_step7.json
tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
tests/topotests/pim_acl/r1/ospf_neighbor.json
tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
tests/topotests/pytest.ini
tests/topotests/rip_allow_ecmp/__init__.py [new file with mode: 0644]
tests/topotests/rip_allow_ecmp/r1/frr.conf [new file with mode: 0644]
tests/topotests/rip_allow_ecmp/r2/frr.conf [new file with mode: 0644]
tests/topotests/rip_allow_ecmp/r3/frr.conf [new file with mode: 0644]
tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/__init__.py [new file with mode: 0755]
tests/topotests/rip_bfd_topo1/r1/bfdd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r1/ripd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r1/zebra.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r2/bfdd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r2/ripd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r2/staticd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r2/zebra.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r3/bfdd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r3/ripd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r3/staticd.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/r3/zebra.conf [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png [new file with mode: 0644]
tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py [new file with mode: 0644]
tests/topotests/rip_passive_interface/__init__.py [new file with mode: 0644]
tests/topotests/rip_passive_interface/r1/frr.conf [new file with mode: 0644]
tests/topotests/rip_passive_interface/r2/frr.conf [new file with mode: 0644]
tests/topotests/rip_passive_interface/r3/frr.conf [new file with mode: 0644]
tests/topotests/rip_passive_interface/test_rip_passive_interface.py [new file with mode: 0644]
tests/topotests/rip_topo1/r1/rip_status.ref
tests/topotests/rip_topo1/r2/rip_status.ref
tests/topotests/rip_topo1/r3/rip_status.ref
tests/topotests/zebra_rib/test_zebra_rib.py
tools/emacs.dir-locals.el [new file with mode: 0644]
tools/frr-reload.py
vrrpd/vrrp_main.c
vtysh/vtysh.c
vtysh/vtysh.h
vtysh/vtysh_config.c
vtysh/vtysh_main.c
yang/frr-bgp-route-map.yang
yang/frr-if-rmap.yang [new file with mode: 0644]
yang/frr-isisd.yang
yang/frr-ripd.yang
yang/frr-ripngd.yang
yang/frr-route-map.yang
yang/subdir.am
zebra/dpdk/zebra_dplane_dpdk.c
zebra/if_netlink.c
zebra/interface.c
zebra/interface.h
zebra/main.c
zebra/rib.h
zebra/zebra_dplane.c
zebra/zebra_evpn_mac.c
zebra/zebra_evpn_mh.c
zebra/zebra_gr.c
zebra/zebra_ptm.h
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_routemap.c
zebra/zebra_vxlan.c
zebra/zserv.c
zebra/zserv.h

diff --git a/.dir-locals.el b/.dir-locals.el
deleted file mode 100644 (file)
index b2d7cf3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-;;; Directory Local Variables
-;;; For more information see (info "(emacs) Directory Variables")
-;;; Match project coding conventions
-
-((c-mode . ((indent-tabs-mode . t)
-            (show-trailing-whitespace . t)
-            (c-basic-offset . 8)))
- (json-mode . ((js-indent-level 4))))
diff --git a/.flake8 b/.flake8
new file mode 100644 (file)
index 0000000..e0ea542
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 88
+extend-ignore = E203
\ No newline at end of file
index 3f51e7f4de30c40ee3bc8c6a66e3536df0d1d5aa..100f557e02e0dfe0dfc7ac758c26b7da44eaa9af 100644 (file)
@@ -1,4 +1,4 @@
-name: Add a conflict label is PR needs to rebase
+name: Add a conflict label if PR needs to rebase
 
 on:
   push:
index fb40ee52fed9e6aea49dbfc872caa614a7564406..3dd6a44409e3d948a729c3abafc5b07d23a56d58 100644 (file)
@@ -87,6 +87,7 @@
 {arch}
 build
 .cache
+.dir-locals.el
 .msg
 .rebase-*
 *~
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644 (file)
index 0000000..f238bf7
--- /dev/null
@@ -0,0 +1,2 @@
+[settings]
+profile = black
index 83a71974813137b456a3fd3a287461bfa0ab63e4..ba9430ba6b37a105169fdfa33df4eb0f5b70dc4f 100644 (file)
--- a/.pylintrc
+++ b/.pylintrc
@@ -2,5 +2,8 @@
 init-hook="import sys; sys.path.insert(0, '..')"
 signature-mutators=common_config.retry,retry
 
+[FORMAT]
+max-line-length = 88
+
 [MESSAGES CONTROL]
 disable=I,C,R,W
index fef7a61ccc8947bdce13e7d39c57e0ef46713d2f..fd3c02f47e69867cd4e08a50b129c9da93ed6832 100644 (file)
@@ -18,7 +18,8 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
     ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2
     perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev
     squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev
-    py3-sphinx elfutils elfutils-dev libyang-dev"
+    py3-sphinx elfutils elfutils-dev libyang-dev protobuf-c-compiler protobuf-c-dev
+    lua5.3-dev lua5.3"
 checkdepends="pytest py-setuptools"
 install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
 subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
@@ -47,7 +48,8 @@ build() {
                --enable-vty-group=frrvty \
                --enable-user=$_user \
                --enable-group=$_user \
-               --enable-pcre2posix
+               --enable-pcre2posix \
+               --enable-scripting
        make -j $(nproc)
 }
 
index 4ce92c520472603d1013ea470f852b9ba57985a7..f4c932971eb0e010cd0fcd1131e133c0a9bc61b6 100644 (file)
@@ -444,37 +444,39 @@ babel_fill_with_next_timeout(struct timeval *tv)
 #if (defined NO_DEBUG)
 #define printIfMin(a,b,c,d)
 #else
-#define printIfMin(a,b,c,d) \
-  if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);}
-
-    struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
-    struct interface *ifp = NULL;
+#define printIfMin(a, b, c, d)                                                 \
+       if (unlikely(debug & BABEL_DEBUG_TIMEOUT)) {                           \
+               printIfMin(a, b, c, d);                                        \
+       }
 
-    *tv = check_neighbours_timeout;
-    printIfMin(tv, 0, "check_neighbours_timeout", NULL);
-    timeval_min_sec(tv, expiry_time);
-    printIfMin(tv, 1, "expiry_time", NULL);
-    timeval_min_sec(tv, source_expiry_time);
-    printIfMin(tv, 1, "source_expiry_time", NULL);
-    timeval_min(tv, &resend_time);
-    printIfMin(tv, 1, "resend_time", NULL);
-    FOR_ALL_INTERFACES(vrf, ifp) {
-        babel_interface_nfo *babel_ifp = NULL;
-        if(!if_up(ifp))
-            continue;
-        babel_ifp = babel_get_if_nfo(ifp);
-        timeval_min(tv, &babel_ifp->flush_timeout);
-        printIfMin(tv, 1, "flush_timeout", ifp->name);
-        timeval_min(tv, &babel_ifp->hello_timeout);
-        printIfMin(tv, 1, "hello_timeout", ifp->name);
-        timeval_min(tv, &babel_ifp->update_timeout);
-        printIfMin(tv, 1, "update_timeout", ifp->name);
-        timeval_min(tv, &babel_ifp->update_flush_timeout);
-        printIfMin(tv, 1, "update_flush_timeout",ifp->name);
-    }
-    timeval_min(tv, &unicast_flush_timeout);
-    printIfMin(tv, 1, "unicast_flush_timeout", NULL);
-    printIfMin(tv, 2, NULL, NULL);
+       struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+       struct interface *ifp = NULL;
+
+       *tv = check_neighbours_timeout;
+       printIfMin(tv, 0, "check_neighbours_timeout", NULL);
+       timeval_min_sec(tv, expiry_time);
+       printIfMin(tv, 1, "expiry_time", NULL);
+       timeval_min_sec(tv, source_expiry_time);
+       printIfMin(tv, 1, "source_expiry_time", NULL);
+       timeval_min(tv, &resend_time);
+       printIfMin(tv, 1, "resend_time", NULL);
+       FOR_ALL_INTERFACES (vrf, ifp) {
+               babel_interface_nfo *babel_ifp = NULL;
+               if (!if_up(ifp))
+                       continue;
+               babel_ifp = babel_get_if_nfo(ifp);
+               timeval_min(tv, &babel_ifp->flush_timeout);
+               printIfMin(tv, 1, "flush_timeout", ifp->name);
+               timeval_min(tv, &babel_ifp->hello_timeout);
+               printIfMin(tv, 1, "hello_timeout", ifp->name);
+               timeval_min(tv, &babel_ifp->update_timeout);
+               printIfMin(tv, 1, "update_timeout", ifp->name);
+               timeval_min(tv, &babel_ifp->update_flush_timeout);
+               printIfMin(tv, 1, "update_flush_timeout", ifp->name);
+       }
+       timeval_min(tv, &unicast_flush_timeout);
+       printIfMin(tv, 1, "unicast_flush_timeout", NULL);
+       printIfMin(tv, 2, NULL, NULL);
 #undef printIfMin
 #endif
 }
index 6c51af48a89572c97f56a29c90c62ad87555afe0..619550f651f7579669240e85f8fb324ccb206866 100644 (file)
@@ -26,12 +26,8 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
 
 #if defined(__GNUC__) && (__GNUC__ >= 3)
 #define ATTRIBUTE(x) __attribute__ (x)
-#define LIKELY(_x) __builtin_expect(!!(_x), 1)
-#define UNLIKELY(_x) __builtin_expect(!!(_x), 0)
 #else
 #define ATTRIBUTE(x) /**/
-#define LIKELY(_x) !!(_x)
-#define UNLIKELY(_x) !!(_x)
 #endif
 
 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
index b5c2a589848f97eab66d45686d6d50fc668f9062..d4ddebff081f1311e43a37e33c1f009f41562ce8 100644 (file)
@@ -48,6 +48,13 @@ static const unsigned char tlv_min_length[MESSAGE_MAX + 1] =
     [ MESSAGE_MH_REQUEST ] = 14,
 };
 
+/* Checks whether an AE exists or must be silently ignored */
+static bool
+known_ae(int ae)
+{
+    return ae <= 4;
+}
+
 /* Parse a network prefix, encoded in the somewhat baroque compressed
    representation used by Babel.  Return the number of bytes parsed. */
 static int
@@ -276,6 +283,62 @@ parse_ihu_subtlv(const unsigned char *a, int alen,
     return ret;
 }
 
+static int
+parse_request_subtlv(int ae, const unsigned char *a, int alen,
+                     unsigned char *src_prefix, unsigned char *src_plen)
+{
+    int type, len, i = 0;
+    int have_src_prefix = 0;
+
+    while(i < alen) {
+        type = a[0];
+        if(type == SUBTLV_PAD1) {
+            i++;
+            continue;
+        }
+
+        if(i + 2 > alen)
+            goto fail;
+
+        len = a[i + 1];
+        if(i + 2 + len > alen)
+            goto fail;
+
+        if(type == SUBTLV_PADN) {
+            /* Nothing to do. */
+        } else if(type == SUBTLV_SOURCE_PREFIX) {
+            int rc;
+            if(len < 1)
+                goto fail;
+            if(a[i + 2] == 0)
+                goto fail;
+            if(have_src_prefix != 0)
+                goto fail;
+            rc = network_prefix(ae, a[i + 2], 0, a + i + 3, NULL,
+                                len - 1, src_prefix);
+            if(rc < 0)
+                goto fail;
+            if(ae==1)
+                *src_plen = a[i + 2] + 96;
+            else
+                *src_plen = a[i + 2];
+            have_src_prefix = 1;
+        } else {
+            debugf(BABEL_DEBUG_COMMON,"Received unknown%s Route Request sub-TLV %d.",
+                   ((type & 0x80) != 0) ? " mandatory" : "", type);
+            if((type & 0x80) != 0)
+                return -1;
+        }
+
+        i += len + 2;
+    }
+    return 1;
+
+ fail:
+    flog_err(EC_BABEL_PACKET, "Received truncated sub-TLV on Route Request.");
+    return -1;
+}
+
 static int
 network_address(int ae, const unsigned char *a, unsigned int len,
                 unsigned char *a_r)
@@ -413,18 +476,6 @@ parse_packet(const unsigned char *from, struct interface *ifp,
 #define BABEL_UNICAST_HELLO 0x8000
                DO_NTOHS(flags, message + 2);
 
-               /*
-                * RFC 8966 4.6.5
-                * All other bits MUST be sent as a 0 and silently
-                * ignored on reception
-                */
-               if (CHECK_FLAG(flags, ~BABEL_UNICAST_HELLO)) {
-                       debugf(BABEL_DEBUG_COMMON,
-                              "Received Hello from %s on %s that does not have all 0's in the unused section of flags, ignoring",
-                              format_address(from), ifp->name);
-                       goto done;
-               }
-
                /*
                 * RFC 8966 Appendix F
                 * TL;DR -> Please ignore Unicast hellos until FRR's
@@ -624,8 +675,14 @@ parse_packet(const unsigned char *from, struct interface *ifp,
                                 interval, neigh, nh, channels,
                                 channels_len(channels));
        } else if(type == MESSAGE_REQUEST) {
-            unsigned char prefix[16], plen;
-            int rc;
+            unsigned char prefix[16], src_prefix[16], plen, src_plen;
+            int rc, is_ss;
+            if(len < 2) goto fail;
+            if(!known_ae(message[2])) {
+                debugf(BABEL_DEBUG_COMMON,"Received request with unknown AE %d. Ignoring.",
+                       message[2]);
+                goto done;
+            }
             rc = network_prefix(message[2], message[3], 0,
                                 message + 4, NULL, len - 2, prefix);
             if(rc < 0) goto fail;
@@ -633,8 +690,26 @@ parse_packet(const unsigned char *from, struct interface *ifp,
             debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.",
                    message[2] == 0 ? "any" : format_prefix(prefix, plen),
                    format_address(from), ifp->name);
+            if(message[2] == 1) {
+                v4tov6(src_prefix, zeroes);
+                src_plen = 96;
+            } else {
+                memcpy(src_prefix, zeroes, 16);
+                src_plen = 0;
+            }
+            rc = parse_request_subtlv(message[2], message + 4 + rc,
+                                      len - 2 - rc, src_prefix, &src_plen);
+            if(rc < 0)
+                goto done;
+            is_ss = !is_default(src_prefix, src_plen);
             if(message[2] == 0) {
                 struct babel_interface *neigh_ifp =babel_get_if_nfo(neigh->ifp);
+                if(is_ss) {
+                    /* Wildcard requests don't carry a source prefix. */
+                    flog_err(EC_BABEL_PACKET,
+                             "Received source-specific wildcard request.");
+                    goto done;
+                }
                 /* If a neighbour is requesting a full route dump from us,
                    we might as well send it an IHU. */
                 send_ihu(neigh, NULL);
index 0797a5a77de465c91456249555603f9973f87e0e..7cf062a88824c533aae6bb66918d7ba8c9fa9eac 100644 (file)
@@ -34,6 +34,7 @@ Copyright (c) 2007, 2008 by Juliusz Chroboczek
 #define SUBTLV_PADN 1
 #define SUBTLV_DIVERSITY 2 /* Also known as babelz. */
 #define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */
+#define SUBTLV_SOURCE_PREFIX 128 /* Source-specific routing. */
 #define SUBTLV_MANDATORY 0x80
 
 extern unsigned short myseqno;
index 5e5843543a22cba1e47e7438caf370c3dc155b7e..ddc6a70d43b5de8bb38bc49c2d1627006c800e41 100644 (file)
@@ -104,6 +104,12 @@ void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src);
 int daemonise(void);
 extern const unsigned char v4prefix[16];
 
+static inline bool
+is_default(const unsigned char *prefix, int plen)
+{
+    return plen == 0 || (plen == 96 && v4mapped(prefix));
+}
+
 /* If debugging is disabled, we want to avoid calling format_address
    for every omitted debugging message.  So debug is a macro.  But
    vararg macros are not portable. */
@@ -122,10 +128,11 @@ extern const unsigned char v4prefix[16];
 #define BABEL_DEBUG_ROUTE       (1 << 5)
 #define BABEL_DEBUG_ALL         (0xFFFF)
 
-#define debugf(level, ...) \
-do { \
-if(UNLIKELY(debug & level)) zlog_debug(__VA_ARGS__);     \
-} while(0)
+#define debugf(level, ...)                                                     \
+       do {                                                                   \
+               if (unlikely(debug & level))                                   \
+                       zlog_debug(__VA_ARGS__);                               \
+       } while (0)
 
 #endif /* NO_DEBUG */
 
index c35e45275c9bab538dcb23e5143127b08080287e..5b06bc3913759f0483a8620ce73a35421e029d07 100644 (file)
@@ -2927,9 +2927,21 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
        int srgb_count;
        uint8_t sid_type, sid_flags;
 
+       /*
+        * Check that we actually have at least as much data as
+        * specified by the length field
+        */
+       if (STREAM_READABLE(peer->curr) < length) {
+               flog_err(
+                       EC_BGP_ATTR_LEN,
+                       "Prefix SID specifies length %hu, but only %zu bytes remain",
+                       length, STREAM_READABLE(peer->curr));
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                         args->total);
+       }
+
        if (type == BGP_PREFIX_SID_LABEL_INDEX) {
-               if (STREAM_READABLE(peer->curr) < length
-                   || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
+               if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
                        flog_err(EC_BGP_ATTR_LEN,
                                 "Prefix SID label index length is %hu instead of %u",
                                 length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
@@ -2951,12 +2963,8 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
                /* Store label index; subsequently, we'll check on
                 * address-family */
                attr->label_index = label_index;
-       }
-
-       /* Placeholder code for the IPv6 SID type */
-       else if (type == BGP_PREFIX_SID_IPV6) {
-               if (STREAM_READABLE(peer->curr) < length
-                   || length != BGP_PREFIX_SID_IPV6_LENGTH) {
+       } else if (type == BGP_PREFIX_SID_IPV6) {
+               if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
                        flog_err(EC_BGP_ATTR_LEN,
                                 "Prefix SID IPv6 length is %hu instead of %u",
                                 length, BGP_PREFIX_SID_IPV6_LENGTH);
@@ -2970,10 +2978,7 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
                stream_getw(peer->curr);
 
                stream_get(&ipv6_sid, peer->curr, 16);
-       }
-
-       /* Placeholder code for the Originator SRGB type */
-       else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
+       } else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
                /*
                 * ietf-idr-bgp-prefix-sid-05:
                 *     Length is the total length of the value portion of the
@@ -2998,19 +3003,6 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
                                args->total);
                }
 
-               /*
-                * Check that we actually have at least as much data as
-                * specified by the length field
-                */
-               if (STREAM_READABLE(peer->curr) < length) {
-                       flog_err(EC_BGP_ATTR_LEN,
-                                "Prefix SID Originator SRGB specifies length %hu, but only %zu bytes remain",
-                                length, STREAM_READABLE(peer->curr));
-                       return bgp_attr_malformed(
-                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                               args->total);
-               }
-
                /*
                 * Check that the portion of the TLV containing the sequence of
                 * SRGBs corresponds to a multiple of the SRGB size; to get
@@ -3034,12 +3026,8 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
                        stream_get(&srgb_base, peer->curr, 3);
                        stream_get(&srgb_range, peer->curr, 3);
                }
-       }
-
-       /* Placeholder code for the VPN-SID Service type */
-       else if (type == BGP_PREFIX_SID_VPN_SID) {
-               if (STREAM_READABLE(peer->curr) < length
-                   || length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
+       } else if (type == BGP_PREFIX_SID_VPN_SID) {
+               if (length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
                        flog_err(EC_BGP_ATTR_LEN,
                                 "Prefix SID VPN SID length is %hu instead of %u",
                                 length, BGP_PREFIX_SID_VPN_SID_LENGTH);
@@ -3073,39 +3061,22 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length,
                attr->srv6_vpn->sid_flags = sid_flags;
                sid_copy(&attr->srv6_vpn->sid, &ipv6_sid);
                attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn);
-       }
-
-       /* Placeholder code for the SRv6 L3 Service type */
-       else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
-               if (STREAM_READABLE(peer->curr) < length) {
+       } else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
+               if (STREAM_READABLE(peer->curr) < 1) {
                        flog_err(
                                EC_BGP_ATTR_LEN,
-                               "Prefix SID SRv6 L3-Service length is %hu, but only %zu bytes remain",
-                               length, STREAM_READABLE(peer->curr));
-                       return bgp_attr_malformed(args,
-                                BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                                args->total);
+                               "Prefix SID SRV6 L3 Service not enough data left, it must be at least 1 byte");
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
                }
-
                /* ignore reserved */
                stream_getc(peer->curr);
 
                return bgp_attr_srv6_service(args);
        }
-
        /* Placeholder code for Unsupported TLV */
        else {
-
-               if (STREAM_READABLE(peer->curr) < length) {
-                       flog_err(
-                               EC_BGP_ATTR_LEN,
-                               "Prefix SID SRv6 length is %hu - too long, only %zu remaining in this UPDATE",
-                               length, STREAM_READABLE(peer->curr));
-                       return bgp_attr_malformed(
-                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
-                               args->total);
-               }
-
                if (bgp_debug_update(peer, NULL, NULL, 1))
                        zlog_debug(
                                "%s attr Prefix-SID sub-type=%u is not supported, skipped",
index a51bcb1a1a6fae5012ceccf2cb88c50d0fefacaf..d1ddfd046025e1be25da3b3d25d4078725d86fda 100644 (file)
@@ -55,7 +55,7 @@ static void bfd_session_status_update(struct bfd_session_params *bsp,
                }
                peer->last_reset = PEER_DOWN_BFD_DOWN;
 
-               /* draft-ietf-idr-bfd-subcode */
+               /* rfc9384 */
                if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
                        bgp_notify_send(peer, BGP_NOTIFY_CEASE,
                                        BGP_NOTIFY_CEASE_BFD_DOWN);
index 73dbf4ab2b4ec8f0446b37c5bc78007154cd39a0..baf164679c725586c871eaaa0c7df7e46d2e4037 100644 (file)
@@ -389,13 +389,13 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
 
                /* Local Port, Remote Port */
                if (peer->su_local->sa.sa_family == AF_INET6)
-                       stream_putw(s, peer->su_local->sin6.sin6_port);
+                       stream_putw(s, htons(peer->su_local->sin6.sin6_port));
                else if (peer->su_local->sa.sa_family == AF_INET)
-                       stream_putw(s, peer->su_local->sin.sin_port);
+                       stream_putw(s, htons(peer->su_local->sin.sin_port));
                if (peer->su_remote->sa.sa_family == AF_INET6)
-                       stream_putw(s, peer->su_remote->sin6.sin6_port);
+                       stream_putw(s, htons(peer->su_remote->sin6.sin6_port));
                else if (peer->su_remote->sa.sa_family == AF_INET)
-                       stream_putw(s, peer->su_remote->sin.sin_port);
+                       stream_putw(s, htons(peer->su_remote->sin.sin_port));
 
                static const uint8_t dummy_open[] = {
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
index c78e740ec9671a56aea4f68ecc23d6591d8b2730..fe77e7e2500630d3ad0a3ba821cf3434ed541dc7 100644 (file)
@@ -845,8 +845,7 @@ void bgp_dump_init(void)
        memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes));
 
        bgp_dump_obuf =
-               stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE * 2)
-                          + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
+               stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
 
        install_node(&bgp_dump_node);
 
index 541da9ecd6886edd97270962401fe5c525f2069b..7bf2609019dc2064b88b85e32deb7dbc30ce48cb 100644 (file)
@@ -371,6 +371,7 @@ void ecommunity_finish(void)
 enum ecommunity_token {
        ecommunity_token_unknown = 0,
        ecommunity_token_rt,
+       ecommunity_token_nt,
        ecommunity_token_soo,
        ecommunity_token_val,
        ecommunity_token_rt6,
@@ -415,6 +416,53 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz,
        (void)ptr; /* consume value */
 }
 
+bool ecommunity_node_target_match(struct ecommunity *ecom,
+                                 struct in_addr *local_id)
+{
+       uint32_t i;
+       bool match = false;
+
+       if (!ecom || !ecom->size)
+               return NULL;
+
+       for (i = 0; i < ecom->size; i++) {
+               const uint8_t *pnt;
+               uint8_t type, sub_type;
+
+               pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+               type = *pnt++;
+               sub_type = *pnt++;
+
+               if (type == ECOMMUNITY_ENCODE_IP &&
+                   sub_type == ECOMMUNITY_NODE_TARGET) {
+                       /* Node Target ID is encoded as A.B.C.D:0 */
+                       if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id))
+                               match = true;
+                       (void)pnt;
+               }
+       }
+
+       return match;
+}
+
+static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
+{
+       /*
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *  | 0x01 or 0x41 | Sub-Type(0x09) |    Target BGP Identifier      |
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *  | Target BGP Identifier (cont.) |           Reserved            |
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       struct in_addr node_id = {};
+
+       IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
+
+       snprintfrr(buf, bufsz, "NT:%pI4", &node_id);
+
+       (void)ptr; /* consume value */
+}
+
 static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
                                      int trans, as_t as,
                                      struct in_addr *ip,
@@ -446,28 +494,19 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
                eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
        eval->val[1] = sub_type;
        if (type == ECOMMUNITY_ENCODE_AS) {
-               eval->val[2] = (as >> 8) & 0xff;
-               eval->val[3] = as & 0xff;
-               eval->val[4] = (val >> 24) & 0xff;
-               eval->val[5] = (val >> 16) & 0xff;
-               eval->val[6] = (val >> 8) & 0xff;
-               eval->val[7] = val & 0xff;
+               encode_route_target_as(as, val, eval, trans);
        } else if (type == ECOMMUNITY_ENCODE_IP) {
-               memcpy(&eval->val[2], ip, sizeof(struct in_addr));
-               eval->val[6] = (val >> 8) & 0xff;
-               eval->val[7] = val & 0xff;
+               if (sub_type == ECOMMUNITY_NODE_TARGET)
+                       encode_node_target(ip, eval, trans);
+               else
+                       encode_route_target_ip(ip, val, eval, trans);
        } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
                   sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
                memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
                eval6->val[18] = (val >> 8) & 0xff;
                eval6->val[19] = val & 0xff;
        } else {
-               eval->val[2] = (as >> 24) & 0xff;
-               eval->val[3] = (as >> 16) & 0xff;
-               eval->val[4] = (as >> 8) & 0xff;
-               eval->val[5] = as & 0xff;
-               eval->val[6] = (val >> 8) & 0xff;
-               eval->val[7] = val & 0xff;
+               encode_route_target_as4(as, val, eval, trans);
        }
 
        return 0;
@@ -486,9 +525,8 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
 }
 
 /* Get next Extended Communities token from the string. */
-static const char *ecommunity_gettoken(const char *str,
-                                      void *eval_ptr,
-                                      enum ecommunity_token *token)
+static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
+                                      enum ecommunity_token *token, int type)
 {
        int ret;
        int dot = 0;
@@ -503,6 +541,8 @@ static const char *ecommunity_gettoken(const char *str,
        uint8_t ecomm_type;
        char buf[INET_ADDRSTRLEN + 1];
        struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
+       uint64_t tmp_as = 0;
+
        /* Skip white space. */
        while (isspace((unsigned char)*p)) {
                p++;
@@ -513,7 +553,7 @@ static const char *ecommunity_gettoken(const char *str,
        if (*p == '\0')
                return NULL;
 
-       /* "rt" and "soo" keyword parse. */
+       /* "rt", "nt", and "soo" keyword parse. */
        if (!isdigit((unsigned char)*p)) {
                /* "rt" match check.  */
                if (tolower((unsigned char)*p) == 'r') {
@@ -532,6 +572,20 @@ static const char *ecommunity_gettoken(const char *str,
                        }
                        goto error;
                }
+               /* "nt" match check. */
+               if (tolower((unsigned char)*p) == 'n') {
+                       p++;
+                       if (tolower((unsigned char)*p) == 't') {
+                               p++;
+                               *token = ecommunity_token_nt;
+                               return p;
+                       }
+                       if (isspace((unsigned char)*p) || *p == '\0') {
+                               *token = ecommunity_token_nt;
+                               return p;
+                       }
+                       goto error;
+               }
                /* "soo" match check.  */
                else if (tolower((unsigned char)*p) == 's') {
                        p++;
@@ -581,9 +635,18 @@ static const char *ecommunity_gettoken(const char *str,
                        goto error;
 
                endptr++;
-               as = strtoul(endptr, &endptr, 10);
-               if (*endptr != '\0' || as == BGP_AS4_MAX)
+               errno = 0;
+               tmp_as = strtoul(endptr, &endptr, 10);
+               /* 'unsigned long' is a uint64 on 64-bit
+                * systems, and uint32 on 32-bit systems. So for
+                * 64-bit we can just directly check the value
+                * against BGP_AS4_MAX/UINT32_MAX, and for
+                * 32-bit we can check for errno (set to ERANGE
+                * upon overflow).
+                */
+               if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno)
                        goto error;
+               as = (as_t)tmp_as;
 
                memcpy(buf, p, (limit - p));
                buf[limit - p] = '\0';
@@ -625,9 +688,19 @@ static const char *ecommunity_gettoken(const char *str,
                                        goto error;
                        } else {
                                /* ASN */
-                               as = strtoul(buf, &endptr, 10);
-                               if (*endptr != '\0' || as == BGP_AS4_MAX)
+                               errno = 0;
+                               tmp_as = strtoul(buf, &endptr, 10);
+                               /* 'unsigned long' is a uint64 on 64-bit
+                                * systems, and uint32 on 32-bit systems. So for
+                                * 64-bit we can just directly check the value
+                                * against BGP_AS4_MAX/UINT32_MAX, and for
+                                * 32-bit we can check for errno (set to ERANGE
+                                * upon overflow).
+                                */
+                               if (*endptr != '\0' || tmp_as > BGP_AS4_MAX ||
+                                   errno)
                                        goto error;
+                               as = (as_t)tmp_as;
                        }
                } else if (*p == '.') {
                        if (separator)
@@ -658,7 +731,7 @@ static const char *ecommunity_gettoken(const char *str,
                ecomm_type = ECOMMUNITY_ENCODE_AS4;
        else
                ecomm_type = ECOMMUNITY_ENCODE_AS;
-       if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
+       if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval))
                goto error;
        *token = ecommunity_token_val;
        return p;
@@ -679,9 +752,10 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
 
        if (is_ipv6_extcomm)
                token = ecommunity_token_rt6;
-       while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
+       while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) {
                switch (token) {
                case ecommunity_token_rt:
+               case ecommunity_token_nt:
                case ecommunity_token_rt6:
                case ecommunity_token_soo:
                        if (!keyword_included || keyword) {
@@ -698,6 +772,9 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
                        if (token == ecommunity_token_soo) {
                                type = ECOMMUNITY_SITE_ORIGIN;
                        }
+                       if (token == ecommunity_token_nt) {
+                               type = ECOMMUNITY_NODE_TARGET;
+                       }
                        break;
                case ecommunity_token_val:
                        if (keyword_included) {
@@ -979,6 +1056,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
                                        ecommunity_lb_str(
                                                encbuf, sizeof(encbuf), pnt,
                                                ecom->disable_ieee_floating);
+                               } else if (sub_type == ECOMMUNITY_NODE_TARGET &&
+                                          type == ECOMMUNITY_ENCODE_IP) {
+                                       ecommunity_node_target_str(
+                                               encbuf, sizeof(encbuf), pnt);
                                } else
                                        unk_ecom = 1;
                        } else {
@@ -1188,6 +1269,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
                                                  ecom->disable_ieee_floating);
                        else
                                unk_ecom = 1;
+               } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
+                       sub_type = *pnt++;
+                       if (sub_type == ECOMMUNITY_NODE_TARGET)
+                               ecommunity_node_target_str(encbuf,
+                                                          sizeof(encbuf), pnt);
+                       else
+                               unk_ecom = 1;
                } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
                        sub_type = *pnt++;
                        if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
index 83a1584489a92ea45e250c64491cca3fb3070919..94a178bbb61a60b9adad38b292b8e208646990de 100644 (file)
@@ -101,6 +101,10 @@ enum ecommunity_origin_validation_states {
 /* Extended Community readable string length */
 #define ECOMMUNITY_STRLEN 64
 
+/* Node Target Extended Communities */
+#define ECOMMUNITY_NODE_TARGET 0x09
+#define ECOMMUNITY_NODE_TARGET_RESERVED 0
+
 /* Extended Communities attribute.  */
 struct ecommunity {
        /* Reference counter.  */
@@ -155,9 +159,12 @@ struct ecommunity_val_ipv6 {
  * Encode BGP Route Target AS:nn.
  */
 static inline void encode_route_target_as(as_t as, uint32_t val,
-                                         struct ecommunity_val *eval)
+                                         struct ecommunity_val *eval,
+                                         bool trans)
 {
        eval->val[0] = ECOMMUNITY_ENCODE_AS;
+       if (!trans)
+               eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
        eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
        eval->val[2] = (as >> 8) & 0xff;
        eval->val[3] = as & 0xff;
@@ -170,12 +177,15 @@ static inline void encode_route_target_as(as_t as, uint32_t val,
 /*
  * Encode BGP Route Target IP:nn.
  */
-static inline void encode_route_target_ip(struct in_addr ip, uint16_t val,
-                                         struct ecommunity_val *eval)
+static inline void encode_route_target_ip(struct in_addr *ip, uint16_t val,
+                                         struct ecommunity_val *eval,
+                                         bool trans)
 {
        eval->val[0] = ECOMMUNITY_ENCODE_IP;
+       if (!trans)
+               eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
        eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
-       memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
+       memcpy(&eval->val[2], ip, sizeof(struct in_addr));
        eval->val[6] = (val >> 8) & 0xff;
        eval->val[7] = val & 0xff;
 }
@@ -184,9 +194,12 @@ static inline void encode_route_target_ip(struct in_addr ip, uint16_t val,
  * Encode BGP Route Target AS4:nn.
  */
 static inline void encode_route_target_as4(as_t as, uint16_t val,
-                                          struct ecommunity_val *eval)
+                                          struct ecommunity_val *eval,
+                                          bool trans)
 {
        eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+       if (!trans)
+               eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
        eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
        eval->val[2] = (as >> 24) & 0xff;
        eval->val[3] = (as >> 16) & 0xff;
@@ -257,6 +270,26 @@ static inline void encode_origin_validation_state(enum rpki_states state,
        eval->val[7] = ovs_state;
 }
 
+static inline void encode_node_target(struct in_addr *node_id,
+                                     struct ecommunity_val *eval, bool trans)
+{
+       /*
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *  | 0x01 or 0x41 | Sub-Type(0x09) |    Target BGP Identifier      |
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        *  | Target BGP Identifier (cont.) |           Reserved            |
+        *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+        */
+       memset(eval, 0, sizeof(*eval));
+       eval->val[0] = ECOMMUNITY_ENCODE_IP;
+       if (!trans)
+               eval->val[0] |= ECOMMUNITY_ENCODE_IP_NON_TRANS;
+       eval->val[1] = ECOMMUNITY_NODE_TARGET;
+       memcpy(&eval->val[2], node_id, sizeof(*node_id));
+       eval->val[6] = ECOMMUNITY_NODE_TARGET_RESERVED;
+       eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED;
+}
+
 extern void ecommunity_init(void);
 extern void ecommunity_finish(void);
 extern void ecommunity_free(struct ecommunity **);
@@ -338,4 +371,9 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom)
 extern struct ecommunity *
 ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
                                       struct ecommunity *ecom);
+extern struct ecommunity *ecommunity_add_node_target(struct in_addr *node_id,
+                                                    struct ecommunity *old,
+                                                    bool non_trans);
+extern bool ecommunity_node_target_match(struct ecommunity *ecomm,
+                                        struct in_addr *local_id);
 #endif /* _QUAGGA_BGP_ECOMMUNITY_H */
index 78e96d130c06a28d92b4ddd5ca1d04ed33216ed7..fc8889c17502ced6328882a7b2dd4827ce7256cf 100644 (file)
@@ -577,7 +577,7 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl,
 
        if (bgp->advertise_autort_rfc8365)
                vni |= EVPN_AUTORT_VXLAN;
-       encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+       encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
 
        ecomadd = ecommunity_new();
        ecommunity_add_val(ecomadd, &eval, false, false);
@@ -1513,14 +1513,9 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
        struct bgp_path_info *tmp_pi = NULL;
 
        *route_changed = 0;
-       /* locate the local route entry if any */
-       for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
-            tmp_pi = tmp_pi->next) {
-               if (tmp_pi->peer == bgp_evpn->peer_self
-                   && tmp_pi->type == ZEBRA_ROUTE_BGP
-                   && tmp_pi->sub_type == BGP_ROUTE_STATIC)
-                       local_pi = tmp_pi;
-       }
+
+       /* See if this is an update of an existing route, or a new add. */
+       local_pi = bgp_evpn_route_get_local_path(bgp_evpn, dest);
 
        /*
         * create a new route entry if one doesn't exist.
@@ -5168,7 +5163,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
        if (bgp->advertise_autort_rfc8365)
                vni |= EVPN_AUTORT_VXLAN;
 
-       encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+       encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true);
 
        ecom_auto = ecommunity_new();
        ecommunity_add_val(ecom_auto, &eval, false, false);
@@ -5679,7 +5674,7 @@ void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
 }
 
 int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
-                       struct bgp_nlri *packet, int withdraw)
+                       struct bgp_nlri *packet, bool withdraw)
 {
        uint8_t *pnt;
        uint8_t *lim;
@@ -6235,6 +6230,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,
                        l3vni);
                return -1;
        }
+
+       if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+               flog_err(EC_BGP_NO_DFLT,
+                         "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down",
+                         l3vni);
+               return -1;
+       }
+
        as = bgp_evpn->as;
 
        /* if the BGP vrf instance doesn't exist - create one */
@@ -6377,6 +6380,13 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id)
                return -1;
        }
 
+       if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+               flog_err(EC_BGP_NO_DFLT,
+                         "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down",
+                         l3vni);
+               return -1;
+       }
+
        /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured,
         * bgp_delete would not remove/decrement bgp_path_info of the ip_prefix
         * routes. This will uninstalling the routes from zebra and decremnt the
index 1dce4820d680a05a67bbe8f202c1aa14cad388d6..a034bfbd7ec8006001ab638e6ed2afe1035fc1fe 100644 (file)
@@ -150,7 +150,7 @@ extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
                                   struct attr *attr, bool addpath_capable,
                                   uint32_t addpath_tx_id);
 extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
-                              struct bgp_nlri *packet, int withdraw);
+                              struct bgp_nlri *packet, bool withdraw);
 extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
                                 const struct prefix *p,
                                 struct bgp_path_info *ri);
index d821d4d5829d7e4f263a670f7a1f2ee6a4224c18..efadda17b86d2446a8cafa3c5f5b6b777121dd63 100644 (file)
@@ -1071,7 +1071,8 @@ void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn)
                        continue;
 
                /* Update EAD-ES */
-               bgp_evpn_ead_es_route_update(bgp, es);
+               if (bgp_evpn_local_es_is_active(es))
+                       bgp_evpn_ead_es_route_update(bgp, es);
 
                /* Update EAD-EVI */
                if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
@@ -2457,6 +2458,7 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps,
 {
        json_object *json_vtep_entry;
        json_object *json_flags;
+       char alg_buf[EVPN_DF_ALG_STR_LEN];
 
        json_vtep_entry = json_object_new_object();
 
@@ -2473,8 +2475,10 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps,
                if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) {
                        json_object_int_add(json_vtep_entry, "dfPreference",
                                            es_vtep->df_pref);
-                       json_object_int_add(json_vtep_entry, "dfAlgorithm",
-                                           es_vtep->df_pref);
+                       json_object_string_add(
+                               json_vtep_entry, "dfAlgorithm",
+                               evpn_es_df_alg2str(es_vtep->df_alg, alg_buf,
+                                                  sizeof(alg_buf)));
                }
        }
 
@@ -2608,6 +2612,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty,
                                    listcount(es->macip_global_path_list));
                json_object_int_add(json, "inconsistentVniVtepCount",
                                es->incons_evi_vtep_cnt);
+               if (es->flags & BGP_EVPNES_LOCAL)
+                       json_object_int_add(json, "localEsDfPreference",
+                                           es->df_pref);
                if (listcount(es->es_vtep_list)) {
                        json_vteps = json_object_new_array();
                        for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node,
index c1bcbf77ce7554257118d3c46345b7ef77fe5ab6..811856bfed6a08a9a5599588d75ffcbaed194bb5 100644 (file)
@@ -4305,7 +4305,7 @@ DEFPY (bgp_evpn_advertise_pip_ip_mac,
        struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
        struct bgp *bgp_evpn = NULL;
 
-       if (EVPN_ENABLED(bgp_vrf)) {
+       if (!bgp_vrf || EVPN_ENABLED(bgp_vrf)) {
                vty_out(vty,
                        "This command is supported under L3VNI BGP EVPN VRF\n");
                return CMD_WARNING_CONFIG_FAILED;
index f9debe43cd45b350defdc326defe5f9c7bad42df..70bdbaf0356f94cdc24a687c0eff71d5de7b59e6 100644 (file)
@@ -82,7 +82,7 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len,
 }
 
 int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
-                           struct bgp_nlri *packet, int withdraw)
+                           struct bgp_nlri *packet, bool withdraw)
 {
        uint8_t *pnt;
        uint8_t *lim;
@@ -98,6 +98,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
        afi = packet->afi;
        safi = packet->safi;
 
+       /*
+        * All other AFI/SAFI's treat no attribute as a implicit
+        * withdraw.  Flowspec should as well.
+        */
+       if (!attr)
+               withdraw = true;
+
        if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) {
                flog_err(EC_BGP_FLOWSPEC_PACKET,
                         "BGP flowspec nlri length maximum reached (%u)",
index 58fc1775ab2be6e479b7c85fd5a491629cc38c0b..bc2f00511e0de103c745d7a4bb06a0dacdfdf504 100644 (file)
@@ -15,7 +15,7 @@
 #define BGP_FLOWSPEC_NLRI_STRING_MAX 512
 
 extern int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
-                                  struct bgp_nlri *packet, int withdraw);
+                                  struct bgp_nlri *packet, bool withdraw);
 
 extern void bgp_flowspec_vty_init(void);
 
index 326d7f2efb2b18b5f71df090e7fa36cc46cfad83..66426ab32f02cb8109ac29ab32028994a9cb3f88 100644 (file)
@@ -185,16 +185,23 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
                offset++;
        }
        /* Prefix length check. */
-       if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
+       if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) {
                *error = -1;
+               return offset;
+       }
        /* When packet overflow occur return immediately. */
-       if (psize + offset > max_len)
+       if (psize + offset > max_len) {
                *error = -1;
+               return offset;
+       }
        /* Defensive coding, double-check
         * the psize fits in a struct prefix
         */
-       if (psize > (ssize_t)sizeof(prefix_local.u))
+       if (psize > (ssize_t)sizeof(prefix_local.u)) {
                *error = -1;
+               return offset;
+       }
+
        memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
        offset += psize;
        switch (type) {
@@ -352,8 +359,10 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
 
        *error = 0;
        do {
-               if (loop > BGP_PBR_MATCH_VAL_MAX)
+               if (loop > BGP_PBR_MATCH_VAL_MAX) {
                        *error = -2;
+                       return offset;
+               }
                hex2bin(&nlri_ptr[offset], op);
                /* if first element, AND bit can not be set */
                if (op[1] == 1 && loop == 0)
index 15bf4198681813ae12708201d11daa305fe51f05..1b8c22a5038c7458cfbf44ea16053ccde5b1315c 100644 (file)
@@ -197,12 +197,13 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
        }
 
        /* 1 space + lcom->size lcom strings + null terminator */
-       size_t str_buf_sz = BUFSIZ;
+       size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
        str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
 
+       len = 0;
        for (i = 0; i < lcom->size; i++) {
                if (i > 0)
-                       strlcat(str_buf, " ", str_buf_sz);
+                       len = strlcat(str_buf, " ", str_buf_sz);
 
                pnt = lcom->val + (i * LCOMMUNITY_SIZE);
                pnt = ptr_get_be32(pnt, &global);
@@ -215,11 +216,22 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
                snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
                         local2);
 
+               /*
+                * Aliases can cause havoc, if the alias length is greater
+                * than the LCOMMUNITY_STRLEN for a particular item
+                * then we need to realloc the memory associated
+                * with the string so that it can fit
+                */
                const char *com2alias =
                        translate_alias ? bgp_community2alias(lcsb) : lcsb;
+               size_t individual_len = strlen(com2alias);
+               if (individual_len + len > str_buf_sz) {
+                       str_buf_sz = individual_len + len + 1;
+                       str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
+                                          str_buf_sz);
+               }
 
                len = strlcat(str_buf, com2alias, str_buf_sz);
-               assert((unsigned int)len < str_buf_sz);
 
                if (make_json) {
                        json_string = json_object_new_string(com2alias);
index c08f95cdf43b11b088f5778550acac61f11b5f01..63168f1e7a826ccbbbf38a5b340bb5418d3c3474 100644 (file)
@@ -1103,7 +1103,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
        struct bgp_path_info *new;
        struct bgp_path_info_extra *extra;
        uint32_t num_sids = 0;
-       void *parent = source_bpi;
+       struct bgp_path_info *parent = source_bpi;
 
        if (new_attr->srv6_l3vpn || new_attr->srv6_vpn)
                num_sids = 1;
@@ -1311,7 +1311,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
 
        new->extra->parent = bgp_path_info_lock(parent);
        bgp_dest_lock_node(
-               (struct bgp_dest *)((struct bgp_path_info *)parent)->net);
+               (struct bgp_dest *)parent->net);
        if (bgp_orig)
                new->extra->bgp_orig = bgp_lock(bgp_orig);
        if (nexthop_orig)
index 20fec6d77be648f0ae6835d03e83a2f8955d4661..0208a6f5a5493bef6254067f68c3f7174932d466 100644 (file)
@@ -1627,6 +1627,7 @@ static uint8_t *mplsL3vpnRteTable(struct variable *v, oid name[],
                        }
                } else
                        return SNMP_INTEGER(MPLSL3VPNVRFRTECIDRTYPEOTHER);
+               break;
        case MPLSL3VPNVRFRTEINETCIDRPROTO:
                switch (pi->type) {
                case ZEBRA_ROUTE_CONNECT:
index c4b832ee595eff58da20f20c75310bd1ce52ffc9..1c79d7d03be34ce8bea84cd64517500c71ec3c60 100644 (file)
@@ -908,17 +908,34 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
                }
                switch (nexthop->type) {
                case NEXTHOP_TYPE_IPV6:
-                       vty_out(vty, "  gate %pI6\n", &nexthop->gate.ipv6);
-                       break;
                case NEXTHOP_TYPE_IPV6_IFINDEX:
-                       vty_out(vty, "  gate %pI6, if %s\n",
-                               &nexthop->gate.ipv6,
-                               ifindex2ifname(bnc->ifindex ? bnc->ifindex
-                                                           : nexthop->ifindex,
-                                              bgp->vrf_id));
+                       vty_out(vty, "  gate %pI6", &nexthop->gate.ipv6);
+                       if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX &&
+                           bnc->ifindex)
+                               vty_out(vty, ", if %s\n",
+                                       ifindex2ifname(bnc->ifindex,
+                                                      bgp->vrf_id));
+                       else if (nexthop->ifindex)
+                               vty_out(vty, ", if %s\n",
+                                       ifindex2ifname(nexthop->ifindex,
+                                                      bgp->vrf_id));
+                       else
+                               vty_out(vty, "\n");
                        break;
                case NEXTHOP_TYPE_IPV4:
-                       vty_out(vty, "  gate %pI4\n", &nexthop->gate.ipv4);
+               case NEXTHOP_TYPE_IPV4_IFINDEX:
+                       vty_out(vty, "  gate %pI4", &nexthop->gate.ipv4);
+                       if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX &&
+                           bnc->ifindex)
+                               vty_out(vty, ", if %s\n",
+                                       ifindex2ifname(bnc->ifindex,
+                                                      bgp->vrf_id));
+                       else if (nexthop->ifindex)
+                               vty_out(vty, ", if %s\n",
+                                       ifindex2ifname(nexthop->ifindex,
+                                                      bgp->vrf_id));
+                       else
+                               vty_out(vty, "\n");
                        break;
                case NEXTHOP_TYPE_IFINDEX:
                        vty_out(vty, "  if %s\n",
@@ -926,13 +943,6 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
                                                            : nexthop->ifindex,
                                               bgp->vrf_id));
                        break;
-               case NEXTHOP_TYPE_IPV4_IFINDEX:
-                       vty_out(vty, "  gate %pI4, if %s\n",
-                               &nexthop->gate.ipv4,
-                               ifindex2ifname(bnc->ifindex ? bnc->ifindex
-                                                           : nexthop->ifindex,
-                                              bgp->vrf_id));
-                       break;
                case NEXTHOP_TYPE_BLACKHOLE:
                        vty_out(vty, "  blackhole\n");
                        break;
index 1c76828a2f2063c085a7c5f590bc52048f57afae..9469a0778f9722be2737910baa05a1687e2d8e95 100644 (file)
@@ -331,7 +331,7 @@ static void bgp_update_explicit_eors(struct peer *peer)
  * calling safi function and for evpn, passed as parameter
  */
 int bgp_nlri_parse(struct peer *peer, struct attr *attr,
-                  struct bgp_nlri *packet, int mp_withdraw)
+                  struct bgp_nlri *packet, bool mp_withdraw)
 {
        switch (packet->safi) {
        case SAFI_UNICAST:
index c072bbc235160a4e8b6c90ea6ff6616f0168a2b2..04bdb81849bd806822166016380c9b71776e81dd 100644 (file)
@@ -42,26 +42,28 @@ DECLARE_HOOK(bgp_packet_send,
        } while (0)
 
 /* Packet send and receive function prototypes. */
-extern void bgp_keepalive_send(struct peer *);
-extern void bgp_open_send(struct peer *);
-extern void bgp_notify_send(struct peer *, uint8_t, uint8_t);
-extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t,
-                                     uint8_t *, size_t);
+extern void bgp_keepalive_send(struct peer *peer);
+extern void bgp_open_send(struct peer *peer);
+extern void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code);
+extern void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
+                                     uint8_t sub_code, uint8_t *data,
+                                     size_t datalen);
 void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code,
                           uint8_t *data, size_t datalen);
 extern void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
                                   uint8_t orf_type, uint8_t when_to_refresh,
                                   int remove, uint8_t subtype);
-extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int);
+extern void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
+                               int capabilty_code, int action);
 
-extern int bgp_capability_receive(struct peer *, bgp_size_t);
+extern int bgp_capability_receive(struct peer *peer, bgp_size_t length);
 
-extern int bgp_nlri_parse(struct peer *, struct attr *, struct bgp_nlri *,
-                         int mp_withdraw);
+extern int bgp_nlri_parse(struct peer *peer, struct attr *attr,
+                         struct bgp_nlri *nlri, bool mp_withdraw);
 
-extern void bgp_update_restarted_peers(struct peer *);
-extern void bgp_update_implicit_eors(struct peer *);
-extern void bgp_check_update_delay(struct bgp *);
+extern void bgp_update_restarted_peers(struct peer *peer);
+extern void bgp_update_implicit_eors(struct peer *peer);
+extern void bgp_check_update_delay(struct bgp *peer);
 
 extern int bgp_packet_set_marker(struct stream *s, uint8_t type);
 extern void bgp_packet_set_size(struct stream *s);
index 4441e86fbbf8fbdc3f4f4f4eb6b1de930081a5e7..b51396c8d1b66566a4f2fad80f228016a57d8409 100644 (file)
@@ -3070,7 +3070,9 @@ static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi,
  * the IMPLICIT_NULL label. This is pretty specialized: it's only called
  * in a path where we basically _know_ this is a BGP-LU route.
  */
-static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select)
+static bool bgp_lu_need_null_label(struct bgp *bgp,
+                                  const struct bgp_path_info *new_select,
+                                  afi_t afi, mpls_label_t *label)
 {
        /* Certain types get imp null; so do paths where the nexthop is
         * not labeled.
@@ -3078,13 +3080,25 @@ static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select)
        if (new_select->sub_type == BGP_ROUTE_STATIC
            || new_select->sub_type == BGP_ROUTE_AGGREGATE
            || new_select->sub_type == BGP_ROUTE_REDISTRIBUTE)
+               goto need_null_label;
+       else if (new_select->extra &&
+                bgp_is_valid_label(&new_select->extra->label[0]))
+               return false;
+need_null_label:
+       if (label == NULL)
                return true;
-       else if (new_select->extra == NULL ||
-                !bgp_is_valid_label(&new_select->extra->label[0]))
-               /* TODO -- should be configurable? */
-               return true;
+       /* Disable PHP : explicit-null */
+       if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) &&
+           afi == AFI_IP)
+               *label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
+       else if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL) &&
+                afi == AFI_IP6)
+               *label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
        else
-               return false;
+               /* Enforced PHP popping: implicit-null */
+               *label = MPLS_LABEL_IMPLICIT_NULL;
+
+       return true;
 }
 
 /*
@@ -3113,6 +3127,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
        struct bgp_path_info *old_select;
        struct bgp_path_info_pair old_and_new;
        int debug = 0;
+       mpls_label_t mpls_label_null;
 
        if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
                if (dest)
@@ -3167,7 +3182,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
         * Right now, since we only deal with per-prefix labels, it is not
         * necessary to do this upon changes to best path. Exceptions:
         * - label index has changed -> recalculate resulting label
-        * - path_info sub_type changed -> switch to/from implicit-null
+        * - path_info sub_type changed -> switch to/from null label value
         * - no valid label (due to removed static label binding) -> get new one
         */
        if (bgp->allocate_mpls_labels[afi][safi]) {
@@ -3176,11 +3191,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
                            || bgp_label_index_differs(new_select, old_select)
                            || new_select->sub_type != old_select->sub_type
                            || !bgp_is_valid_label(&dest->local_label)) {
-                               /* Enforced penultimate hop popping:
-                                * implicit-null for local routes, aggregate
-                                * and redistributed routes
+                               /* control label imposition for local routes,
+                                * aggregate and redistributed routes
                                 */
-                               if (bgp_lu_need_imp_null(new_select)) {
+                               mpls_label_null = MPLS_LABEL_IMPLICIT_NULL;
+                               if (bgp_lu_need_null_label(bgp, new_select, afi,
+                                                          &mpls_label_null)) {
                                        if (CHECK_FLAG(
                                                    dest->flags,
                                                    BGP_NODE_REGISTERED_FOR_LABEL)
@@ -3189,8 +3205,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
                                                    BGP_NODE_LABEL_REQUESTED))
                                                bgp_unregister_for_label(dest);
                                        dest->local_label = mpls_lse_encode(
-                                               MPLS_LABEL_IMPLICIT_NULL, 0, 0,
-                                               1);
+                                               mpls_label_null, 0, 0, 1);
                                        bgp_set_valid_label(&dest->local_label);
                                } else
                                        bgp_register_for_label(dest,
@@ -3838,6 +3853,12 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
                (type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true
                                                                       : false;
 
+       /* If `bgp allow-martian-nexthop` is turned on, return next-hop
+        * as good.
+        */
+       if (bgp->allow_martian)
+               return false;
+
        /*
         * Only validated for unicast and multicast currently.
         * Also valid for EVPN where the nexthop is an IP address.
@@ -4047,17 +4068,24 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
        if (has_valid_label)
                assert(label != NULL);
 
-       /* Update overlay index of the attribute */
-       if (afi == AFI_L2VPN && evpn)
-               memcpy(&attr->evpn_overlay, evpn,
-                      sizeof(struct bgp_route_evpn));
 
        /* When peer's soft reconfiguration enabled.  Record input packet in
           Adj-RIBs-In.  */
-       if (!soft_reconfig
-           && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)
-           && peer != bgp->peer_self)
+       if (!soft_reconfig &&
+           CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) &&
+           peer != bgp->peer_self) {
+               /*
+                * If the trigger is not from soft_reconfig and if
+                * PEER_FLAG_SOFT_RECONFIG is enabled for the peer, then attr
+                * will not be interned. In which case, it is ok to update the
+                * attr->evpn_overlay, so that, this can be stored in adj_in.
+                */
+               if ((afi == AFI_L2VPN) && evpn) {
+                       memcpy(&attr->evpn_overlay, evpn,
+                              sizeof(struct bgp_route_evpn));
+               }
                bgp_adj_in_set(dest, peer, attr, addpath_id);
+       }
 
        /* Update permitted loop count */
        if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
@@ -4148,6 +4176,21 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
                goto filtered;
        }
 
+       /* If the route has Node Target Extended Communities, check
+        * if it's allowed to be installed locally.
+        */
+       if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+               struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+               if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
+                                     ECOMMUNITY_NODE_TARGET) &&
+                   !ecommunity_node_target_match(ecomm, &peer->local_id)) {
+                       reason =
+                               "Node-Target Extended Communities do not contain own BGP Identifier;";
+                       goto filtered;
+               }
+       }
+
        /* RFC 8212 to prevent route leaks.
         * This specification intends to improve this situation by requiring the
         * explicit configuration of both BGP Import and Export Policies for any
@@ -4185,6 +4228,15 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
                }
 
        new_attr = *attr;
+       /*
+        * If bgp_update is called with soft_reconfig set then
+        * attr is interned. In this case, do not overwrite the
+        * attr->evpn_overlay with evpn directly. Instead memcpy
+        * evpn to new_atr.evpn_overlay before it is interned.
+        */
+       if (soft_reconfig && (afi == AFI_L2VPN) && evpn)
+               memcpy(&new_attr.evpn_overlay, evpn,
+                      sizeof(struct bgp_route_evpn));
 
        /* Apply incoming route-map.
         * NB: new_attr may now contain newly allocated values from route-map
@@ -7004,8 +7056,8 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
                bgp_static->label = label;
                bgp_static->prd = prd;
 
-               if (rd_str)
-                       bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str);
+               bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str);
+
                if (rmap_str) {
                        XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name);
                        route_map_counter_decrement(bgp_static->rmap.map);
@@ -8338,30 +8390,7 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str,
        /* Unlock aggregate address configuration. */
        bgp_dest_set_bgp_aggregate_info(dest, NULL);
 
-       if (aggregate->community)
-               community_free(&aggregate->community);
-
-       hash_clean_and_free(&aggregate->community_hash,
-                           bgp_aggr_community_remove);
-
-       if (aggregate->ecommunity)
-               ecommunity_free(&aggregate->ecommunity);
-
-       hash_clean_and_free(&aggregate->ecommunity_hash,
-                           bgp_aggr_ecommunity_remove);
-
-       if (aggregate->lcommunity)
-               lcommunity_free(&aggregate->lcommunity);
-
-       hash_clean_and_free(&aggregate->lcommunity_hash,
-                           bgp_aggr_lcommunity_remove);
-
-       if (aggregate->aspath)
-               aspath_free(aggregate->aspath);
-
-       hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove);
-
-       bgp_aggregate_free(aggregate);
+       bgp_free_aggregate_info(aggregate);
        bgp_dest_unlock_node(dest);
        bgp_dest_unlock_node(dest);
 
@@ -8545,6 +8574,34 @@ DEFPY(aggregate_addressv4, aggregate_addressv4_cmd,
                                 match_med != NULL, suppress_map);
 }
 
+void bgp_free_aggregate_info(struct bgp_aggregate *aggregate)
+{
+       if (aggregate->community)
+               community_free(&aggregate->community);
+
+       hash_clean_and_free(&aggregate->community_hash,
+                           bgp_aggr_community_remove);
+
+       if (aggregate->ecommunity)
+               ecommunity_free(&aggregate->ecommunity);
+
+       hash_clean_and_free(&aggregate->ecommunity_hash,
+                           bgp_aggr_ecommunity_remove);
+
+       if (aggregate->lcommunity)
+               lcommunity_free(&aggregate->lcommunity);
+
+       hash_clean_and_free(&aggregate->lcommunity_hash,
+                           bgp_aggr_lcommunity_remove);
+
+       if (aggregate->aspath)
+               aspath_free(aggregate->aspath);
+
+       hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove);
+
+       bgp_aggregate_free(aggregate);
+}
+
 DEFPY(aggregate_addressv6, aggregate_addressv6_cmd,
       "[no] aggregate-address X:X::X:X/M$prefix [{"
       "as-set$as_set_s"
@@ -11592,9 +11649,28 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                        vty_out(vty, ",\"%pFX\": ", dest_p);
                        }
 
+                       /* This is used for 'json detail' vty keywords.
+                        *
+                        * In plain 'json' the per-prefix header is encoded
+                        * as a standalone dictionary in the first json_paths
+                        * array element:
+                        * "<prefix>": [{header}, {path-1}, {path-N}]
+                        * (which is confusing and borderline broken)
+                        *
+                        * For 'json detail' this changes the value
+                        * of each prefix-key to be a dictionary where each
+                        * header item has its own key, and json_paths is
+                        * tucked under the "paths" key:
+                        * "<prefix>": {
+                        *   "<header-key-1>": <header-val-1>,
+                        *   "<header-key-N>": <header-val-N>,
+                        *   "paths": [{path-1}, {path-N}]
+                        * }
+                        */
                        if (json_detail_header && json_paths != NULL) {
                                const struct prefix_rd *prd;
 
+                               /* Start per-prefix dictionary */
                                vty_out(vty, "{\n");
 
                                prd = bgp_rd_from_dest(dest, safi);
@@ -11619,6 +11695,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                         */
                        vty_json_no_pretty(vty, json_paths);
 
+                       /* End per-prefix dictionary */
                        if (json_detail_header_used)
                                vty_out(vty, "} ");
 
@@ -11861,12 +11938,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
                        vty_out(vty,
                                "BGP routing table entry for %s%s%pFX, version %" PRIu64
                                "\n",
-                               ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
+                               (((safi == SAFI_MPLS_VPN ||
+                                  safi == SAFI_ENCAP) &&
+                                 prd)
                                         ? prefix_rd2str(prd, buf1,
                                                         sizeof(buf1),
                                                         bgp->asnotation)
                                         : ""),
-                               safi == SAFI_MPLS_VPN ? ":" : "", p,
+                               safi == SAFI_MPLS_VPN && prd ? ":" : "", p,
                                dest->version);
 
                } else {
@@ -14201,7 +14280,6 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
                        for (ain = dest->adj_in; ain; ain = ain->next) {
                                if (ain->peer != peer)
                                        continue;
-
                                show_adj_route_header(vty, peer, table, header1,
                                                      header2, json, json_scode,
                                                      json_ocode, wide, detail);
@@ -14252,9 +14330,23 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
                                        if (use_json)
                                                json_net =
                                                        json_object_new_object();
+
+                                       struct bgp_path_info bpi;
+                                       struct bgp_dest buildit = *dest;
+                                       struct bgp_dest *pass_in;
+
+                                       if (route_filtered ||
+                                           ret == RMAP_DENY) {
+                                               bpi.attr = &attr;
+                                               bpi.peer = peer;
+                                               buildit.info = &bpi;
+
+                                               pass_in = &buildit;
+                                       } else
+                                               pass_in = dest;
                                        bgp_show_path_info(
-                                               NULL /* prefix_rd */, dest, vty,
-                                               bgp, afi, safi, json_net,
+                                               NULL, pass_in, vty, bgp, afi,
+                                               safi, json_net,
                                                BGP_PATH_SHOW_ALL, &display,
                                                RPKI_NOT_BEING_USED);
                                        if (use_json)
index b48e8eda1125a5bd4437461fd1b10c5fd3a895c3..a64144b62557fcb1197bd42814ad1ca6194e834e 100644 (file)
@@ -661,6 +661,7 @@ extern void bgp_process_queue_init(struct bgp *bgp);
 extern void bgp_route_init(void);
 extern void bgp_route_finish(void);
 extern void bgp_cleanup_routes(struct bgp *);
+extern void bgp_free_aggregate_info(struct bgp_aggregate *aggregate);
 extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi,
                               bool force);
 extern void bgp_stop_announce_route_timer(struct peer_af *paf);
index 4faf4a9f15ce290dc18e0921a6f3dbb8d68f64d3..10fc3ecda4455abb5e9ed8f4f3566dd950baf4de 100644 (file)
@@ -461,7 +461,8 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object)
        if (prefix->family == AF_INET) {
                alist = access_list_lookup(AFI_IP, (char *)rule);
                if (alist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -521,7 +522,8 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object)
 
                alist = access_list_lookup(AFI_IP, (char *)rule);
                if (alist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -581,7 +583,8 @@ route_match_ip_route_source(void *rule, const struct prefix *pfx, void *object)
 
                alist = access_list_lookup(AFI_IP, (char *)rule);
                if (alist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -676,7 +679,7 @@ route_match_address_prefix_list(void *rule, afi_t afi,
 
        plist = prefix_list_lookup(afi, (char *)rule);
        if (plist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -737,7 +740,8 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
 
                plist = prefix_list_lookup(AFI_IP, (char *)rule);
                if (plist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -786,7 +790,8 @@ route_match_ipv6_next_hop_prefix_list(void *rule, const struct prefix *prefix,
 
                plist = prefix_list_lookup(AFI_IP6, (char *)rule);
                if (!plist) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -891,7 +896,8 @@ route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix,
 
                plist = prefix_list_lookup(AFI_IP, (char *)rule);
                if (plist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -956,7 +962,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object)
 
        alist = access_list_lookup(AFI_L2VPN, (char *)rule);
        if (alist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -964,7 +970,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object)
                return RMAP_NOMATCH;
        }
        if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix %pFX is not a EVPN MAC IP ROUTE defaulting to NO_MATCH",
                                __func__, prefix);
@@ -2861,6 +2867,29 @@ static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = {
        route_set_ecommunity_free,
 };
 
+static void *route_set_ecommunity_nt_compile(const char *arg)
+{
+       struct rmap_ecom_set *rcs;
+       struct ecommunity *ecom;
+
+       ecom = ecommunity_str2com(arg, ECOMMUNITY_NODE_TARGET, 0);
+       if (!ecom)
+               return NULL;
+
+       rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
+       rcs->ecom = ecommunity_intern(ecom);
+       rcs->none = false;
+
+       return rcs;
+}
+
+static const struct route_map_rule_cmd route_set_ecommunity_nt_cmd = {
+       "extcommunity nt",
+       route_set_ecommunity,
+       route_set_ecommunity_nt_compile,
+       route_set_ecommunity_free,
+};
+
 /* `set extcommunity bandwidth' */
 
 struct rmap_ecomm_lb_set {
@@ -3242,7 +3271,8 @@ route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object)
        if (prefix->family == AF_INET6) {
                alist = access_list_lookup(AFI_IP6, (char *)rule);
                if (alist == NULL) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -3299,7 +3329,8 @@ route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, void *object)
 
                alist = access_list_lookup(AFI_IP6, (char *)rule);
                if (!alist) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                        __func__, (char *)rule);
@@ -6410,6 +6441,55 @@ ALIAS_YANG (no_set_ecommunity_lb,
             "BGP extended community attribute\n"
             "Link bandwidth extended community\n")
 
+DEFPY_YANG (set_ecommunity_nt,
+           set_ecommunity_nt_cmd,
+           "set extcommunity nt RTLIST...",
+           SET_STR
+           "BGP extended community attribute\n"
+           "Node Target extended community\n"
+           "Node Target ID\n")
+{
+       int idx_nt = 3;
+       char *str;
+       int ret;
+       const char *xpath =
+               "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
+       char xpath_value[XPATH_MAXLEN];
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-set-action/frr-bgp-route-map:extcommunity-nt", xpath);
+       str = argv_concat(argv, argc, idx_nt);
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+       ret = nb_cli_apply_changes(vty, NULL);
+       XFREE(MTYPE_TMP, str);
+       return ret;
+}
+
+DEFPY_YANG (no_set_ecommunity_nt,
+           no_set_ecommunity_nt_cmd,
+           "no set extcommunity nt RTLIST...",
+           NO_STR
+           SET_STR
+           "BGP extended community attribute\n"
+           "Node Target extended community\n"
+           "Node Target ID\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']";
+       nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_ecommunity_nt,
+            no_set_ecommunity_nt_short_cmd,
+            "no set extcommunity nt",
+            NO_STR
+            SET_STR
+            "BGP extended community attribute\n"
+            "Node Target extended community\n")
+
 DEFUN_YANG (set_origin,
            set_origin_cmd,
            "set origin <egp|igp|incomplete>",
@@ -7215,6 +7295,7 @@ void bgp_route_map_init(void)
        route_map_install_set(&route_set_vpnv6_nexthop_cmd);
        route_map_install_set(&route_set_originator_id_cmd);
        route_map_install_set(&route_set_ecommunity_rt_cmd);
+       route_map_install_set(&route_set_ecommunity_nt_cmd);
        route_map_install_set(&route_set_ecommunity_soo_cmd);
        route_map_install_set(&route_set_ecommunity_lb_cmd);
        route_map_install_set(&route_set_ecommunity_none_cmd);
@@ -7317,6 +7398,9 @@ void bgp_route_map_init(void)
        install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd);
        install_element(RMAP_NODE, &set_ecommunity_none_cmd);
        install_element(RMAP_NODE, &no_set_ecommunity_none_cmd);
+       install_element(RMAP_NODE, &set_ecommunity_nt_cmd);
+       install_element(RMAP_NODE, &no_set_ecommunity_nt_cmd);
+       install_element(RMAP_NODE, &no_set_ecommunity_nt_short_cmd);
 #ifdef KEEP_OLD_VPN_COMMANDS
        install_element(RMAP_NODE, &set_vpn_nexthop_cmd);
        install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd);
index 9188a40cc94abd795c3d648b34a297ceaf375247..6e8439cc26cfe991649d83292aa613db9fbaafc5 100644 (file)
@@ -185,6 +185,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
                                .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy,
                        }
                },
+               {
+                       .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt",
+                       .cbs = {
+                               .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify,
+                               .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
                        .cbs = {
index 07a1a6eb57fb3e7f88965844329af2c548e300a0..bcd1e837e852c3cb3fa759ffcf63b807fd86c563 100644 (file)
@@ -69,6 +69,10 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_
 int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args);
 int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args);
 int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
+       struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
+       struct nb_cb_destroy_args *args);
 int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args);
 int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args);
 int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args);
index 4db8dba2ccf346ec665cf400c824d0d435fa5104..938a5ec31b7c662fea9466b15aa9abdb6ec6b78b 100644 (file)
@@ -1413,6 +1413,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(
        return NB_OK;
 }
 
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt
+ */
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct routemap_hook_context *rhc;
+       const char *str;
+       int rv;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               /* Add configuration. */
+               rhc = nb_running_get_entry(args->dnode, NULL, true);
+               str = yang_dnode_get_string(args->dnode, NULL);
+
+               /* Set destroy information. */
+               rhc->rhc_shook = generic_set_delete;
+               rhc->rhc_rule = "extcommunity nt";
+               rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+               rv = generic_set_add(rhc->rhc_rmi, "extcommunity nt", str,
+                                    args->errmsg, args->errmsg_len);
+               if (rv != CMD_SUCCESS) {
+                       rhc->rhc_shook = NULL;
+                       return NB_ERR_INCONSISTENCY;
+               }
+       }
+
+       return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               return lib_route_map_entry_match_destroy(args);
+       }
+
+       return NB_OK;
+}
+
 /*
  * XPath:
  * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo
index 72dca9f702925c57022fe41a0d9786ac806a86fc..0b1e54916a7892e204010f878725f47a025cbbc6 100644 (file)
@@ -1961,15 +1961,8 @@ void update_group_adjust_peer(struct peer_af *paf)
        }
 
        updgrp = update_group_find(paf);
-       if (!updgrp) {
+       if (!updgrp)
                updgrp = update_group_create(paf);
-               if (!updgrp) {
-                       flog_err(EC_BGP_UPDGRP_CREATE,
-                                "couldn't create update group for peer %s",
-                                paf->peer->host);
-                       return;
-               }
-       }
 
        old_subgrp = paf->subgroup;
 
@@ -1992,11 +1985,8 @@ void update_group_adjust_peer(struct peer_af *paf)
        }
 
        subgrp = update_subgroup_find(updgrp, paf);
-       if (!subgrp) {
+       if (!subgrp)
                subgrp = update_subgroup_create(updgrp);
-               if (!subgrp)
-                       return;
-       }
 
        update_subgroup_add_peer(subgrp, paf, 1);
        if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
index 4bbbced806822bb52f19c211683ab5d1c3e0ea24..ccf198c3927260913a02ecda5d8e19185196c5cf 100644 (file)
@@ -1675,28 +1675,46 @@ DEFUN (no_router_bgp,
                        for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
                                if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
                                        continue;
-                               if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
-                                              BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
-                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
-                                              BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
-                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
-                                              BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
-                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
-                                              BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
-                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
+                               if (CHECK_FLAG(
+                                           tmp_bgp->af_flags[AFI_IP]
+                                                            [SAFI_UNICAST],
+                                           BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
+                                   CHECK_FLAG(
+                                           tmp_bgp->af_flags[AFI_IP6]
+                                                            [SAFI_UNICAST],
+                                           BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
+                                   CHECK_FLAG(
+                                           tmp_bgp->af_flags[AFI_IP]
+                                                            [SAFI_UNICAST],
+                                           BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
+                                   CHECK_FLAG(
+                                           tmp_bgp->af_flags[AFI_IP6]
+                                                            [SAFI_UNICAST],
+                                           BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
+                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP]
+                                                               [SAFI_UNICAST],
                                               BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
-                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
+                                   CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6]
+                                                               [SAFI_UNICAST],
                                               BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
                                    (bgp == bgp_get_evpn() &&
-                                   (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
-                                               BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) ||
-                                    CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
-                                               BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) ||
-                                    CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
-                                               BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) ||
-                                    CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
-                                               BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) ||
-                                   (hashcount(tmp_bgp->vnihash))) {
+                                    (CHECK_FLAG(
+                                             tmp_bgp->af_flags[AFI_L2VPN]
+                                                              [SAFI_EVPN],
+                                             BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) ||
+                                     CHECK_FLAG(
+                                             tmp_bgp->af_flags[AFI_L2VPN]
+                                                              [SAFI_EVPN],
+                                             BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) ||
+                                     CHECK_FLAG(
+                                             tmp_bgp->af_flags[AFI_L2VPN]
+                                                              [SAFI_EVPN],
+                                             BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) ||
+                                     CHECK_FLAG(
+                                             tmp_bgp->af_flags[AFI_L2VPN]
+                                                              [SAFI_EVPN],
+                                             BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) ||
+                                   (tmp_bgp->l3vni)) {
                                        vty_out(vty,
                                                "%% Cannot delete default BGP instance. Dependent VRF instances exist\n");
                                        return CMD_WARNING_CONFIG_FAILED;
@@ -2802,6 +2820,31 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd,
        return CMD_SUCCESS;
 }
 
+DEFPY(bgp_lu_uses_explicit_null, bgp_lu_uses_explicit_null_cmd,
+      "[no] bgp labeled-unicast <explicit-null|ipv4-explicit-null|ipv6-explicit-null>$value",
+      NO_STR BGP_STR
+      "BGP Labeled-unicast options\n"
+      "Use explicit-null label values for all local prefixes\n"
+      "Use the IPv4 explicit-null label value for IPv4 local prefixes\n"
+      "Use the IPv6 explicit-null label value for IPv6 local prefixes\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       uint64_t label_mode;
+
+       if (strmatch(value, "ipv4-explicit-null"))
+               label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL;
+       else if (strmatch(value, "ipv6-explicit-null"))
+               label_mode = BGP_FLAG_LU_IPV6_EXPLICIT_NULL;
+       else
+               label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL |
+                            BGP_FLAG_LU_IPV6_EXPLICIT_NULL;
+       if (no)
+               UNSET_FLAG(bgp->flags, label_mode);
+       else
+               SET_FLAG(bgp->flags, label_mode);
+       return CMD_SUCCESS;
+}
+
 DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd,
       "bgp suppress-duplicates",
       BGP_STR
@@ -9249,9 +9292,24 @@ DEFPY (af_sid_vpn_export,
                return CMD_WARNING_CONFIG_FAILED;
 
        if (!yes) {
-               /* implement me */
-               vty_out(vty, "It's not implemented\n");
-               return CMD_WARNING_CONFIG_FAILED;
+               /* when SID is not set, do nothing */
+               if ((bgp->vpn_policy[afi].tovpn_sid_index == 0) &&
+                   !CHECK_FLAG(bgp->vpn_policy[afi].flags,
+                               BGP_VPN_POLICY_TOVPN_SID_AUTO))
+                       return CMD_SUCCESS;
+
+               /* pre-change */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+               bgp->vpn_policy[afi].tovpn_sid_index = 0;
+               UNSET_FLAG(bgp->vpn_policy[afi].flags,
+                          BGP_VPN_POLICY_TOVPN_SID_AUTO);
+
+               /* post-change */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+
+               return CMD_SUCCESS;
        }
 
        if (bgp->tovpn_sid_index != 0 ||
@@ -9290,7 +9348,7 @@ DEFPY (af_sid_vpn_export,
                        zlog_debug("%s: auto sid alloc.", __func__);
                SET_FLAG(bgp->vpn_policy[afi].flags,
                         BGP_VPN_POLICY_TOVPN_SID_AUTO);
-       } else {
+       } else if (sid_idx != 0) {
                /* SID allocation index-mode */
                if (debug)
                        zlog_debug("%s: idx %ld sid alloc.", __func__, sid_idx);
@@ -11036,10 +11094,10 @@ static char *bgp_peer_description_stripped(char *desc, uint32_t size)
 {
        static char stripped[BUFSIZ];
        uint32_t i = 0;
-       uint32_t last_space = 0;
+       uint32_t last_space = size;
 
        while (i < size) {
-               if (*(desc + i) == 0) {
+               if (*(desc + i) == '\0') {
                        stripped[i] = '\0';
                        return stripped;
                }
@@ -11049,10 +11107,7 @@ static char *bgp_peer_description_stripped(char *desc, uint32_t size)
                i++;
        }
 
-       if (last_space > size)
-               stripped[size + 1] = '\0';
-       else
-               stripped[last_space] = '\0';
+       stripped[last_space] = '\0';
 
        return stripped;
 }
@@ -18220,6 +18275,18 @@ int bgp_config_write(struct vty *vty)
                                        ? ""
                                        : "no ");
 
+               if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) &&
+                   !!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL))
+                       vty_out(vty, " bgp labeled-unicast explicit-null\n");
+               else if (!!CHECK_FLAG(bgp->flags,
+                                     BGP_FLAG_LU_IPV4_EXPLICIT_NULL))
+                       vty_out(vty,
+                               " bgp labeled-unicast ipv4-explicit-null\n");
+               else if (!!CHECK_FLAG(bgp->flags,
+                                     BGP_FLAG_LU_IPV6_EXPLICIT_NULL))
+                       vty_out(vty,
+                               " bgp labeled-unicast ipv6-explicit-null\n");
+
                /* draft-ietf-idr-deprecate-as-set-confed-set */
                if (bgp->reject_as_sets)
                        vty_out(vty, " bgp reject-as-sets\n");
@@ -19139,6 +19206,9 @@ void bgp_vty_init(void)
        install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd);
        install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd);
 
+       /* bgp labeled-unicast explicit-null */
+       install_element(BGP_NODE, &bgp_lu_uses_explicit_null_cmd);
+
        /* bgp suppress-duplicates */
        install_element(BGP_NODE, &bgp_suppress_duplicates_cmd);
        install_element(BGP_NODE, &no_bgp_suppress_duplicates_cmd);
index 3d659d48d44d177a292147a285157bda5e1e5d74..96b1f3e00f4c1776e9f9028b240869b7643a2838 100644 (file)
@@ -834,6 +834,13 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote,
                                                      peer->bgp->vrf_id);
        }
 
+       /* Handle peerings via loopbacks. For instance, peer between
+        * 127.0.0.1 and 127.0.0.2. In short, allow peering with self
+        * via 127.0.0.0/8.
+        */
+       if (!ifp && cmd_allow_reserved_ranges_get())
+               ifp = if_get_vrf_loopback(peer->bgp->vrf_id);
+
        if (!ifp) {
                /*
                 * BGP views do not currently get proper data
index 0f0ba4d6ade7d7ca72a1191710d343b1c733b32c..9d7a1f967e5f5aee336d23d7bcf61344f76d5762 100644 (file)
@@ -3844,6 +3844,23 @@ int bgp_delete(struct bgp *bgp)
 #ifdef ENABLE_BGP_VNC
        rfapi_delete(bgp);
 #endif
+
+       /* Free memory allocated with aggregate address configuration. */
+       FOREACH_AFI_SAFI (afi, safi) {
+               struct bgp_aggregate *aggregate = NULL;
+
+               for (struct bgp_dest *dest =
+                            bgp_table_top(bgp->aggregate[afi][safi]);
+                    dest; dest = bgp_route_next(dest)) {
+                       aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+                       if (aggregate == NULL)
+                               continue;
+
+                       bgp_dest_set_bgp_aggregate_info(dest, NULL);
+                       bgp_free_aggregate_info(aggregate);
+               }
+       }
+
        bgp_cleanup_routes(bgp);
 
        for (afi = 0; afi < AFI_MAX; ++afi) {
@@ -5678,20 +5695,9 @@ void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
                        return;
 
                if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) ||
-                   CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) {
-                       if (CHECK_FLAG(peer->af_cap[afi][safi],
-                                      PEER_CAP_ORF_PREFIX_SM_ADV) &&
-                           (CHECK_FLAG(peer->af_cap[afi][safi],
-                                       PEER_CAP_ORF_PREFIX_RM_RCV) ||
-                            CHECK_FLAG(peer->af_cap[afi][safi],
-                                       PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
-                               peer_clear_soft(peer, afi, safi,
-                                               BGP_CLEAR_SOFT_IN_ORF_PREFIX);
-                       else
-                               bgp_route_refresh_send(
-                                       peer, afi, safi, 0, 0, 0,
-                                       BGP_ROUTE_REFRESH_NORMAL);
-               }
+                   CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+                       bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+                                              BGP_ROUTE_REFRESH_NORMAL);
        }
 }
 
@@ -6932,11 +6938,18 @@ static void peer_prefix_list_update(struct prefix_list *plist)
 
                                /* If we touch prefix-list, we need to process
                                 * new updates. This is important for ORF to
-                                * work correctly as well.
+                                * work correctly.
                                 */
-                               if (peer->afc_nego[afi][safi])
-                                       peer_on_policy_change(peer, afi, safi,
-                                                             0);
+                               if (CHECK_FLAG(peer->af_cap[afi][safi],
+                                              PEER_CAP_ORF_PREFIX_SM_ADV) &&
+                                   (CHECK_FLAG(peer->af_cap[afi][safi],
+                                               PEER_CAP_ORF_PREFIX_RM_RCV) ||
+                                    CHECK_FLAG(
+                                            peer->af_cap[afi][safi],
+                                            PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
+                                       peer_clear_soft(
+                                               peer, afi, safi,
+                                               BGP_CLEAR_SOFT_IN_ORF_PREFIX);
                        }
                }
                for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
index a08a2870ee7ce54fa1342e57ceb75a8c24c04715..c3cb6ba91e69fc1969c8bc805579417595610400 100644 (file)
@@ -500,6 +500,10 @@ struct bgp {
 #define BGP_FLAG_HARD_ADMIN_RESET (1ULL << 31)
 /* Evaluate the AIGP attribute during the best path selection process */
 #define BGP_FLAG_COMPARE_AIGP (1ULL << 32)
+/* For BGP-LU, force IPv4 local prefixes to use ipv4-explicit-null label */
+#define BGP_FLAG_LU_IPV4_EXPLICIT_NULL (1ULL << 33)
+/* For BGP-LU, force IPv6 local prefixes to use ipv6-explicit-null label */
+#define BGP_FLAG_LU_IPV6_EXPLICIT_NULL (1ULL << 34)
 
        /* BGP default address-families.
         * New peers inherit enabled afi/safis from bgp instance.
index 8d6db9d7759d709763f53ec4a0d4b7c504d02b74..67c70431bd19537f1b0ded50c0ee73b23755e7e4 100644 (file)
@@ -51,6 +51,8 @@
 #include <execinfo.h>
 #endif /* HAVE_GLIBC_BACKTRACE */
 
+#define DEBUG_CLEANUP 0
+
 struct ethaddr rfapi_ethaddr0 = {{0}};
 
 #define DEBUG_RFAPI_STR "RF API debugging/testing command\n"
@@ -3677,11 +3679,36 @@ void rfapi_delete(struct bgp *bgp)
 {
        extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */
 
+#if DEBUG_CLEANUP
+       zlog_debug("%s: bgp %p", __func__, bgp);
+#endif
+
        /*
         * This clears queries and registered routes, and closes nves
         */
        if (bgp->rfapi)
                rfp_clear_vnc_nve_all();
+
+       /*
+        * close any remaining descriptors
+        */
+       struct rfapi *h = bgp->rfapi;
+
+       if (h && h->descriptors.count) {
+               struct listnode *node, *nnode;
+               struct rfapi_descriptor *rfd;
+#if DEBUG_CLEANUP
+               zlog_debug("%s: descriptor count %u", __func__,
+                          h->descriptors.count);
+#endif
+               for (ALL_LIST_ELEMENTS(&h->descriptors, node, nnode, rfd)) {
+#if DEBUG_CLEANUP
+                       zlog_debug("%s: closing rfd %p", __func__, rfd);
+#endif
+                       (void)rfapi_close(rfd);
+               }
+       }
+
        bgp_rfapi_cfg_destroy(bgp, bgp->rfapi_cfg);
        bgp->rfapi_cfg = NULL;
        bgp_rfapi_destroy(bgp, bgp->rfapi);
index e6da79fafb86f000052e6afb2a98f9f7a5294a2b..4b8e07a9c4c787b9c2daea8b752d533a3dae9f97 100644 (file)
@@ -127,7 +127,6 @@ void rfapiCheckRouteCount(void)
                        struct agg_node *rn;
 
                        int holddown_count = 0;
-                       int local_count = 0;
                        int imported_count = 0;
                        int remote_count = 0;
 
@@ -146,9 +145,7 @@ void rfapiCheckRouteCount(void)
                                                ++holddown_count;
 
                                        } else {
-                                               if (RFAPI_LOCAL_BI(bpi)) {
-                                                       ++local_count;
-                                               } else {
+                                               if (!RFAPI_LOCAL_BI(bpi)) {
                                                        if (RFAPI_DIRECT_IMPORT_BI(
                                                                    bpi)) {
                                                                ++imported_count;
index 4dc3139ed1f28b9b53833ee2da83a850e7594cc5..5784f95b278691c735dbcef925a51364e7d71168 100644 (file)
@@ -40,6 +40,7 @@
 #define DEBUG_PENDING_DELETE_ROUTE     0
 #define DEBUG_NHL                      0
 #define DEBUG_RIB_SL_RD                 0
+#define DEBUG_CLEANUP 0
 
 /* forward decl */
 #if DEBUG_NHL
@@ -116,7 +117,6 @@ void rfapiRibCheckCounts(
        struct bgp *bgp = bgp_get_default();
 
        uint32_t t_pfx_active = 0;
-       uint32_t t_pfx_deleted = 0;
 
        uint32_t t_ri_active = 0;
        uint32_t t_ri_deleted = 0;
@@ -131,7 +131,6 @@ void rfapiRibCheckCounts(
 
                afi_t afi;
                uint32_t pfx_active = 0;
-               uint32_t pfx_deleted = 0;
 
                for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
 
@@ -156,8 +155,6 @@ void rfapiRibCheckCounts(
                                if (dsl) {
                                        ri_deleted = skiplist_count(dsl);
                                        t_ri_deleted += ri_deleted;
-                                       ++pfx_deleted;
-                                       ++t_pfx_deleted;
                                }
                        }
                        for (rn = agg_route_top(rfd->rib_pending[afi]); rn;
@@ -331,6 +328,11 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd,
                tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE,
                              sizeof(struct rfapi_rib_tcb));
        }
+#if DEBUG_CLEANUP
+       zlog_debug("%s: rfd %p, rn %p, ri %p, tcb %p", __func__, rfd, rn, ri,
+                  tcb);
+#endif
+
        tcb->rfd = rfd;
        tcb->ri = ri;
        tcb->rn = rn;
@@ -510,6 +512,16 @@ void rfapiRibClear(struct rfapi_descriptor *rfd)
                                                            NULL,
                                                            (void **)&ri)) {
 
+                                               if (ri->timer) {
+                                                       struct rfapi_rib_tcb
+                                                               *tcb;
+
+                                                       tcb = EVENT_ARG(
+                                                               ri->timer);
+                                                       EVENT_OFF(ri->timer);
+                                                       XFREE(MTYPE_RFAPI_RECENT_DELETE,
+                                                             tcb);
+                                               }
                                                rfapi_info_free(ri);
                                                skiplist_delete_first(
                                                        (struct skiplist *)
@@ -559,6 +571,9 @@ void rfapiRibFree(struct rfapi_descriptor *rfd)
 {
        afi_t afi;
 
+#if DEBUG_CLEANUP
+       zlog_debug("%s: rfd %p", __func__, rfd);
+#endif
 
        /*
         * NB rfd is typically detached from master list, so is not included
@@ -2303,10 +2318,6 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
        int printedheader = 0;
        int routes_total = 0;
        int nhs_total = 0;
-       int prefixes_total = 0;
-       int prefixes_displayed = 0;
-       int nves_total = 0;
-       int nves_with_routes = 0;
        int nves_displayed = 0;
        int routes_displayed = 0;
        int nhs_displayed = 0;
@@ -2326,10 +2337,6 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
                int printednve = 0;
                afi_t afi;
 
-               ++nves_total;
-               if (rfd->rib_prefix_count)
-                       ++nves_with_routes;
-
                for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
 
                        struct agg_node *rn;
@@ -2355,14 +2362,11 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
 
                                routes_total++;
                                nhs_total += skiplist_count(sl);
-                               ++prefixes_total;
 
                                if (pfx_match && !prefix_match(pfx_match, p)
                                    && !prefix_match(p, pfx_match))
                                        continue;
 
-                               ++prefixes_displayed;
-
                                if (!printedheader) {
                                        ++printedheader;
 
index 94c77d826ebf520407fc86d52965f32c48b88499..29698846c37973f0504618f328b087440565e3ae 100644 (file)
@@ -822,11 +822,6 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
        const char *vty_newline;
 
        int printedheader = 0;
-
-       int nves_total = 0;
-       int nves_with_queries = 0;
-       int nves_displayed = 0;
-
        int queries_total = 0;
        int queries_displayed = 0;
 
@@ -850,15 +845,9 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
                struct agg_node *rn;
                int printedquerier = 0;
 
-
-               ++nves_total;
-
-               if (rfd->mon
-                   || (rfd->mon_eth && skiplist_count(rfd->mon_eth))) {
-                       ++nves_with_queries;
-               } else {
+               if (!rfd->mon &&
+                   !(rfd->mon_eth && skiplist_count(rfd->mon_eth)))
                        continue;
-               }
 
                /*
                 * IP Queries
@@ -904,8 +893,6 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
 
                                        fp(out, "%-15s %-15s", buf_vn, buf_un);
                                        printedquerier = 1;
-
-                                       ++nves_displayed;
                                } else
                                        fp(out, "%-15s %-15s", "", "");
                                buf_remain[0] = 0;
@@ -978,8 +965,6 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
 
                                        fp(out, "%-15s %-15s", buf_vn, buf_un);
                                        printedquerier = 1;
-
-                                       ++nves_displayed;
                                } else
                                        fp(out, "%-15s %-15s", "", "");
                                buf_remain[0] = 0;
@@ -4903,6 +4888,7 @@ static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf,
        clear_vnc_prefix(&cda);
        vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count,
                start_count);
+       print_cleared_stats(&cda); /* frees lists in cda */
        return CMD_SUCCESS;
 }
 
index 19ac6f353d89af66a2159d8c74b696f0730a187f..3fcc0e6f5f4b7bae5ee919af927dbc705727d5a9 100644 (file)
@@ -1986,8 +1986,6 @@ void vnc_import_bgp_exterior_add_route_interior(
 
        if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) {
 
-               int count = 0; /* debugging */
-
                vnc_zlog_debug_verbose(
                        "%s: has exterior monitor; ext src: %p", __func__,
                        RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
@@ -2011,9 +2009,6 @@ void vnc_import_bgp_exterior_add_route_interior(
                        struct attr new_attr;
                        uint32_t label = 0;
 
-
-                       ++count; /* debugging */
-
                        assert(bpi_exterior);
                        assert(pfx_exterior);
 
index f3968df2aad1849ffa7885819b74ef05e1eaf11b..b9af7686419325d5076039abda5cda5d16ef1299 100644 (file)
@@ -299,14 +299,20 @@ if test "$enable_scripting" = "yes"; then
    AX_LUA_HEADERS([], [
      AC_MSG_ERROR([Lua 5.3 headers are required to build with Lua support. No other version is supported.])
    ])
-   AX_LUA_LIBS([
+   PKG_CHECK_MODULES([LUA], [lua5.3], [
      AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting])
-     LIBS="$LIBS $LUA_LIB"
+     LIBS="$LIBS $LUA_LIBS"
      SCRIPTING=true
    ], [
-     SCRIPTING=false
-     AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.])
-   ])
+      AX_LUA_LIBS([
+        AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting])
+        LIBS="$LIBS $LUA_LIB"
+        SCRIPTING=true
+      ], [
+        SCRIPTING=false
+        AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.])
+      ])
+    ])
 fi
 
 dnl the following flags go in CFLAGS rather than AC_CFLAGS since they make
@@ -685,6 +691,8 @@ AC_ARG_ENABLE([ospfapi],
 AC_ARG_ENABLE([ospfclient],
   AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI,
                           (this is the default if --disable-ospfapi is set)]))
+AC_ARG_WITH([log_timestamp_precision],
+  AS_HELP_STRING([--with-log-timestamp-precision=ARG], [set startup log timestamp precision, ARG must be 0-12]))
 AC_ARG_ENABLE([multipath],
   AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit]))
 AC_ARG_WITH([service_timeout],
@@ -956,8 +964,19 @@ esac
 
 AC_DEFINE_UNQUOTED([MULTIPATH_NUM], [$MPATH_NUM], [Maximum number of paths for a route])
 
-AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use])
+case "${with_log_timestamp_precision}" in
+[[0-9]|1[012]])
+;;
+"")
+;;
+*)
+AC_MSG_FAILURE([Please specify a number from 0-12 for log precision ARG])
+;;
+esac
+with_log_timestamp_precision=${with_log_timestamp_precision:-0}
+AC_DEFINE_UNQUOTED([LOG_TIMESTAMP_PRECISION], [${with_log_timestamp_precision}], [Startup zlog timestamp precision])
 
+AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use])
 
 TIMEOUT_MIN=2
 case "${with_service_timeout}" in
index b7021b69a12acd8f63294daffa9ca0d1545ca5a8..52653d37686cc1b7c4b41f99432dde2f1b123544 100644 (file)
@@ -502,6 +502,51 @@ General utility formats
    representation for a hexdump.  Non-printable characters are replaced with
    a dot.
 
+.. frrfmt:: %pIS (struct iso_address *)
+
+   ([IS]o Network address) - Format ISO Network Address
+
+   ``%pIS``: :frrfmtout:`01.0203.04O5`
+   ISO Network address is printed as separated byte. The number of byte of the
+   address is embeded in the `iso_net` structure.
+
+   ``%pISl``: :frrfmtout:`01.0203.04O5.0607.0809.1011.1213.14` - long format to
+   print the long version of the ISO Network address which include the System
+   ID and the PSEUDO-ID of the IS-IS system
+
+   Note that the `ISO_ADDR_STRLEN` define gives the total size of the string
+   that could be used in conjunction to snprintfrr. Use like::
+
+     char buf[ISO_ADDR_STRLEN];
+     struct iso_address addr = {.addr_len = 4, .area_addr = {1, 2, 3, 4}};
+     snprintfrr(buf, ISO_ADDR_STRLEN, "%pIS", &addr);
+
+.. frrfmt:: %pSY (uint8_t *)
+
+   (IS-IS [SY]stem ID) - Format IS-IS System ID
+
+   ``%pSY``: :frrfmtout:`0102.0304.0506`
+
+.. frrfmt:: %pPN (uint8_t *)
+
+   (IS-IS [P]seudo [N]ode System ID) - Format IS-IS Pseudo Node System ID
+
+   ``%pPN``: :frrfmtout:`0102.0304.0506.07`
+
+.. frrfmt:: %pLS (uint8_t *)
+
+   (IS-IS [L]sp fragment [S]ystem ID) - Format IS-IS Pseudo System ID
+
+   ``%pLS``: :frrfmtout:`0102.0304.0506.07-08`
+
+   Note that the `ISO_SYSID_STRLEN` define gives the total size of the string
+   that could be used in conjunction to snprintfrr. Use like::
+
+     char buf[ISO_SYSID_STRLEN];
+     uint8_t id[8] = {1, 2, 3, 4 , 5 , 6 , 7, 8};
+     snprintfrr(buf, SYS_ID_SIZE, "%pSY", id);
+
+
 Integer formats
 ^^^^^^^^^^^^^^^
 
index 5f90b8c5a4fd4ae7b80f98b3fd8ac355d8f32476..13936e18ed846c0fc2b6f6e6984fe8948c098f75 100644 (file)
@@ -296,14 +296,14 @@ Execute single test
 .. code:: shell
 
    cd test_to_be_run
-   ./test_to_be_run.py
+   sudo -E pytest ./test_to_be_run.py
 
 For example, and assuming you are inside the frr directory:
 
 .. code:: shell
 
    cd tests/topotests/bgp_l3vpn_to_bgp_vrf
-   ./test_bgp_l3vpn_to_bgp_vrf.py
+   sudo -E pytest ./test_bgp_l3vpn_to_bgp_vrf.py
 
 For further options, refer to pytest documentation.
 
@@ -402,6 +402,63 @@ environment.
 .. _screen: https://www.gnu.org/software/screen/
 .. _tmux: https://github.com/tmux/tmux/wiki
 
+Capturing Packets
+"""""""""""""""""
+
+One can view and capture packets on any of the networks or interfaces defined by
+the topotest by specifying the ``--pcap=NET|INTF|all[,NET|INTF,...]`` CLI option
+as shown in the examples below.
+
+.. code:: shell
+
+   # Capture on all networks in isis_topo1 test
+   sudo -E pytest isis_topo1 --pcap=all
+
+   # Capture on `sw1` network
+   sudo -E pytest isis_topo1 --pcap=sw1
+
+   # Capture on `sw1` network and on interface `eth0` on router `r2`
+   sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
+
+For each capture a window is opened displaying a live summary of the captured
+packets. Additionally, the entire packet stream is captured in a pcap file in
+the tests log directory e.g.,::
+
+.. code:: console
+
+   $ sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
+   ...
+   $ ls -l /tmp/topotests/isis_topo1.test_isis_topo1/
+   -rw------- 1 root root 45172 Apr 19 05:30 capture-r2-r2-eth0.pcap
+   -rw------- 1 root root 48412 Apr 19 05:30 capture-sw1.pcap
+   ...
+-
+Viewing Live Daemon Logs
+""""""""""""""""""""""""
+
+One can live view daemon or the frr logs in separate windows using the
+``--logd`` CLI option as shown below.
+
+.. code:: shell
+
+   # View `ripd` logs on all routers in test
+   sudo -E pytest rip_allow_ecmp --logd=ripd
+
+   # View `ripd` logs on all routers and `mgmtd` log on `r1`
+   sudo -E pytest rip_allow_ecmp --logd=ripd --logd=mgmtd,r1
+
+For each capture a window is opened displaying a live summary of the captured
+packets. Additionally, the entire packet stream is captured in a pcap file in
+the tests log directory e.g.,::
+
+When using a unified log file `frr.log` one substitutes `frr` for the daemon
+name in the ``--logd`` CLI option, e.g.,
+
+.. code:: shell
+
+   # View `frr` log on all routers in test
+   sudo -E pytest some_test_suite --logd=frr
+
 Spawning Debugging CLI, ``vtysh`` or Shells on Routers on Test Failure
 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
@@ -421,12 +478,30 @@ the help command from within a CLI launched on error:
 
     test_bgp_multiview_topo1/test_bgp_routingTable> help
 
-    Commands:
-    help                       :: this help
-    sh [hosts] <shell-command> :: execute <shell-command> on <host>
-    term [hosts]               :: open shell terminals for hosts
-    vtysh [hosts]              :: open vtysh terminals for hosts
-    [hosts] <vtysh-command>    :: execute vtysh-command on hosts
+    Basic Commands:
+      cli   :: open a secondary CLI window
+      help  :: this help
+      hosts :: list hosts
+      quit  :: quit the cli
+
+      HOST can be a host or one of the following:
+        - '*' for all hosts
+        - '.' for the parent munet
+        - a regex specified between '/' (e.g., '/rtr.*/')
+
+    New Window Commands:
+      logd HOST [HOST ...] DAEMON   :: tail -f on the logfile of the given DAEMON for the given HOST[S]
+      pcap NETWORK  :: capture packets from NETWORK into file capture-NETWORK.pcap the command is run within a new window which also shows packet summaries. NETWORK can also be an interface specified as HOST:INTF. To capture inside the host namespace.
+      stderr HOST [HOST ...] DAEMON :: tail -f on the stderr of the given DAEMON for the given HOST[S]
+      stdlog HOST [HOST ...]        :: tail -f on the `frr.log` for the given HOST[S]
+      stdout HOST [HOST ...] DAEMON :: tail -f on the stdout of the given DAEMON for the given HOST[S]
+      term HOST [HOST ...]  :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all
+      vtysh ROUTER [ROUTER ...]     ::
+      xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all
+    Inline Commands:
+      [ROUTER ...] COMMAND  :: execute vtysh COMMAND on the router[s]
+      [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts
+      [HOST ...] shi <INTERACTIVE-COMMAND>  :: execute <INTERACTIVE-COMMAND> on HOST[s]
 
     test_bgp_multiview_topo1/test_bgp_routingTable> r1 show int br
     ------ Host: r1 ------
@@ -501,6 +576,27 @@ memleak detection is enabled.
 
    sudo -E pytest --valgrind-memleaks all-protocol-startup
 
+Collecting Performance Data using perf(1)
+"""""""""""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch any daemon under ``perf(1)`` to collect
+performance data. The daemon is run in non-daemon mode with ``perf record -g``.
+The ``perf.data`` file will be saved in the router specific directory under the
+tests run directoy.
+
+Here's an example of collecting performance data from ``mgmtd`` on router ``r1``
+during the config_timing test.
+
+.. code:: console
+
+   $ sudo -E pytest --perf=mgmtd,r1 config_timing
+   ...
+   $ find /tmp/topotests/ -name '*perf.data*'
+   /tmp/topotests/config_timing.test_config_timing/r1/perf.data
+
+To specify different arguments for ``perf record``, one can use the
+``--perf-options`` this will replace the ``-g`` used by default.
+
 .. _topotests_docker:
 
 Running Tests with Docker
index cef53f1cbeeccd729454090d08514d648fece374..5f039758a5eeafbe6be79415b1ce55271d0de2d9 100644 (file)
@@ -218,8 +218,6 @@ Zebra Protocol Commands
 +------------------------------------+-------+
 | ZEBRA_IMPORT_ROUTE_UNREGISTER      | 27    |
 +------------------------------------+-------+
-| ZEBRA_IMPORT_CHECK_UPDATE          | 28    |
-+------------------------------------+-------+
 | ZEBRA_BFD_DEST_REGISTER            | 29    |
 +------------------------------------+-------+
 | ZEBRA_BFD_DEST_DEREGISTER          | 30    |
index af527bea407ccb19bf40c5245abb5a43670634d5..396fcfc66e708cb55ac4e555f2d53e050b90dfd1 100644 (file)
@@ -67,6 +67,15 @@ OPTIONS available for the vtysh command:
 
    Display a usage message on standard output and exit.
 
+.. option:: -t, --timestamp
+
+   Print a timestamp before going to shell or reading the configuration file.
+
+.. option:: --no-fork
+
+   When used in conjunction with ``-b``, prevents vtysh from forking children to handle configuring each target daemon.
+
+
 ENVIRONMENT VARIABLES
 =====================
 VTYSH_PAGER
index 254dad8303b17dd620096a28b63539930688b6f2..337cfff9378ff1b969cf63696f840f5e26997916 100644 (file)
@@ -349,6 +349,10 @@ Basic Config Commands
    Allow using IPv4 reserved (Class E) IP ranges for daemons. E.g.: setting
    IPv4 addresses for interfaces or allowing reserved ranges in BGP next-hops.
 
+   If you need multiple FRR instances (or FRR + any other daemon) running in a
+   single router and peering via 127.0.0.0/8, it's also possible to use this
+   knob if turned on.
+
    Default: off.
 
 .. _sample-config-file:
index 1a42996771de6027c48c442b44f539f66fc67117..776726f193a6f7b2adeb3d33c932ce323960d352 100644 (file)
@@ -10,6 +10,7 @@ the following RFCs:
 
 * :rfc:`5880`
 * :rfc:`5881`
+* :rfc:`5882`
 * :rfc:`5883`
 
 Currently, there are two implementations of the BFD commands in FRR:
@@ -353,6 +354,33 @@ The following commands are available inside the interface configuration node.
    that interface.
 
 
+.. _bfd-rip-peer-config:
+
+RIP BFD configuration
+---------------------
+
+The following commands are available inside the interface configuration node:
+
+.. clicmd:: ip rip bfd
+
+   Automatically create BFD session for each RIP peer discovered in this
+   interface. When the BFD session monitor signalize that the link is down
+   the RIP peer is removed and all the learned routes associated with that
+   peer are removed.
+
+
+.. clicmd:: ip rip bfd profile BFD_PROFILE_NAME
+
+   Selects a BFD profile for the BFD sessions created in this interface.
+
+
+The following command is available in the RIP router configuration node:
+
+.. clicmd:: bfd default-profile BFD_PROFILE_NAME
+
+   Selects a default BFD profile for all sessions without a profile specified.
+
+
 .. _bfd-static-peer-config:
 
 BFD Static Route Monitoring Configuration
index 946f0699f271cfcb55e585d067dcc94d68537048..e2cc121d95f8d241bfafef94f53a40f7d3d2c878 100644 (file)
@@ -2584,11 +2584,19 @@ BGP Extended Communities in Route Map
 
 .. clicmd:: set extcommunity rt EXTCOMMUNITY
 
-   This command set Route Target value.
+   This command sets Route Target value.
+
+.. clicmd:: set extcommunity nt EXTCOMMUNITY
+
+   This command sets Node Target value.
+
+   If the receiving BGP router supports Node Target Extended Communities,
+   it will install the route with the community that contains it's own
+   local BGP Identifier. Otherwise, it's not installed.
 
 .. clicmd:: set extcommunity soo EXTCOMMUNITY
 
-   This command set Site of Origin value.
+   This command sets Site of Origin value.
 
 .. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive]
 
@@ -2767,6 +2775,17 @@ happened automatically if local-role is set.
    value of his role (by setting local-role on his side). Otherwise, a Role
    Mismatch Notification will be sent.
 
+Labeled unicast
+---------------
+
+*bgpd* supports labeled information, as per :rfc:`3107`.
+
+.. clicmd:: bgp labeled-unicast <explicit-null|ipv4-explicit-null|ipv6-explicit-null>
+
+By default, locally advertised prefixes use the `implicit-null` label to
+encode in the outgoing NLRI. The following command uses the `explicit-null`
+label value for all the BGP instances.
+
 .. _bgp-l3vpn-vrfs:
 
 L3VPN VRFs
index c02d10deda7b07b5d86ed87a14525aa5d1d6c779..4789677a9a0492fc459e58e701cfb76bcb04562a 100644 (file)
@@ -27,6 +27,7 @@ Basics
    grpc
    filter
    routemap
+   affinitymap
    ipv6
    kernel
    snmp
index 90c13d4f932878c031864aa894a61f0c6bc324fc..570b8bd182d4cd0df236bd9ab3b4529cf3a59c16 100644 (file)
@@ -68,6 +68,10 @@ writing, *isisd* does not support multiple ISIS processes.
 
    Log changes in adjacency state.
 
+.. clicmd:: log-pdu-drops
+
+   Log any dropped PDUs.
+
 .. clicmd:: metric-style [narrow | transition | wide]
 
    Set old-style (ISO 10589) or new-style packet formats:
@@ -312,12 +316,12 @@ Showing ISIS information
    Show the ISIS database globally, for a specific LSP id without or with
    details.
 
-.. clicmd:: show isis topology [level-1|level-2]
+.. clicmd:: show isis topology [level-1|level-2] [algorithm (128-255)]
 
    Show topology IS-IS paths to Intermediate Systems, globally, in area
    (level-1) or domain (level-2).
 
-.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup]
+.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] [algorithm (128-255)]
 
    Show the ISIS routing table, as determined by the most recent SPF
    calculation.
@@ -385,8 +389,7 @@ Traffic Engineering
 
    :ref:`ospf-traffic-engineering`
 
-
-.. _debugging-isis:
+.. _isis-segment-routing:
 
 Segment Routing
 ===============
@@ -419,7 +422,7 @@ Known limitations:
    MPLS dataplane. E.g. for Linux kernel, since version 4.13 the maximum value
    is 32.
 
-.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear]
+.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> [algorithm (128-255)] <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear]
 
    prefix. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR
    node to request to its neighbor to not pop the label. The 'explicit-null'
@@ -428,10 +431,164 @@ Known limitations:
    clear the Node flag that is set by default for Prefix-SIDs associated to
    loopback addresses. This option is necessary to configure Anycast-SIDs.
 
-.. clicmd:: show isis segment-routing node
+.. clicmd:: show isis segment-routing node [algorithm (128-255)]
 
    Show detailed information about all learned Segment Routing Nodes.
 
+.. _isis-flex-algo:
+
+Flex-Algos (Flex-Algo)
+======================
+
+*isisd* supports some features of
+`RFC 9350 <https://tools.ietf.org/html/rfc9350>`_ on an MPLS Segment-Routing
+dataplane. The compatibility has been tested against Cisco.
+
+IS-IS uses by default the `Shortest-Path-First` algorithm that basically
+calculates paths based on the shortest total metric to the destinations.
+Flex-Algo allows new algorithms to run in parallel to compute paths in different
+manners, based on metrics (IGP metric or a new type of metrics such as Traffic
+Engineering (TE) metric and minimum delay...) and constraints. New metric types
+are not yet implemented but constraints are already operational. Constraints can
+restrict paths to links with specific affinities or avoid links with specific
+affinities. Combinations of these are also possible.
+
+The administrator can configure up to 128 Flex-Algos in an IS-IS area.
+To do so, it defines a set of Flex-Algo Definitions (FAD) which
+have the following characteristics:
+
+- a numeric identifier (ID) between 128 and 255 inclusive
+- a set of constraints (basically, include or exclude a certain given set of
+       links, designated by a admin-group)
+- the calculation type (only the `Shortest-Path-First` is currently supported)
+- the metric type (only the IGP inherited metric type is currently supported)
+- some additional flags (not supported for the moment).
+
+A subset of routers advertises the Flex-Algo Definitions (FAD) to the other
+routers within an area. In order to use a common set of FADs, each router runs a
+FAD election process for each locally configured algorithm, using the following
+rules:
+
+- If a locally configured FAD is not advertised to the area, the router does not
+       participate in the particular flex algorithm.
+- If a given flex algorithm is running, the participation in this particular
+       flex algorithm stops when its advertisements are over.
+- A router includes its own FAD in the election process if and only if it is
+       advertised to the other routers.
+- If only one router advertises the FAD, the FAD is elected.
+- If several FADs are advertised with different priorities, the one with the
+       highest priority value is selected.
+- If there are multiple advertisements of the FAD with the same highest
+       priority, the FAD of the router with the highest IS-IS system-ID is
+       selected.
+
+Routers only use the specifications of the elected FAD regardless of the locally
+configured definitions. If a router does not support one of the FAD
+characteristics, it stops participating in the Flex-Algo.
+
+For each running Flex-Algo, the Segment-Routing SIDs must be
+configured with values unique to the algorithm. It allows routers to identify
+which flex algorithm they must use for a given packet.
+
+The following commands configure Flex-Algo at the 'router isis' configuration
+level. Segment-Routing prefixes must be configured for the Flex-Algo.
+
+.. clicmd:: flexible-algorithm (128-255)
+
+   Add a Flex-Algo Definition (FAD) and enter the FAD configuration
+   level. The algorithm ID value is in the range of 128 to 255 inclusive.
+
+.. clicmd:: no flexible-algorithm (128-255)
+
+   Unconfigure a Flex-Algo Definition.
+
+.. clicmd:: affinity-map NAME bit-position (0-255)
+
+   Add the specified 'affinity-map'. Affinity-map definitions are used in
+   FADs and in interfaces admin-group definition.
+
+   Affinity-maps format in advertisement TLVs use the extended admin-group
+   format defined in the RFC7308 section 2.2. The extended admin-group uses a
+   256 bits field. If an affinity-map is set, the bit at the extended
+   admin-group 'bit-position' is set 1, else it is set to 0.
+
+The following commands configure Flex-Algo at the 'router isis' and
+'flexible-algorithm (128-255)' configuration level.
+
+.. clicmd:: advertise-definition
+
+   Advertise the current FAD to other IS-IS routers by using specific IS-IS
+   TLVs. By default, the definition is is not shared with other routers.
+
+  Â A router can advertise a FAD without participating in the Flex-Algo.
+
+.. clicmd:: priority (0-255)
+
+   Set the specified 'priority' in the current FAD advertisements .
+
+.. clicmd:: metric-type [igp|te|delay]
+
+   Set the 'metric-type' for the current FAD. 'igp' is
+   the default value and refers to the classic 'Shortest-Path-First' algorithm.
+   If the 'te' or the 'delay' metric is selected, the value is advertised but
+   the flex algorithm is disabled locally because these types are not currently
+   supported.
+
+.. clicmd:: no metric-type
+
+   Reset the 'metric-type' to the default 'igp' metric.
+
+.. clicmd:: affinity exclude-any NAME
+
+   Add the specified affinity to the list of exclude-any affinities. The
+   Flex-Algo will compute paths that exclude the segments with any of
+   the specified affinities.
+
+.. clicmd:: no affinity exclude-any NAME
+
+   Remove the specified affinity to the list of exclude-any affinities.
+
+.. clicmd:: affinity include-all NAME
+
+   Add the specified affinity to the list of include-all affinities. The
+   Flex-Algo will compute paths that include the segments with all
+   the specified affinities.
+
+.. clicmd:: no affinity include-all NAME
+
+   Remove the specified affinity to the list of include-all affinities.
+
+.. clicmd:: affinity include-any NAME
+
+   Add the specified affinity to the list of include-any affinities. The
+   Flex-Algo will compute paths that include the segments with any of
+   the specified affinities.
+
+.. clicmd:: no affinity include-any NAME
+
+   Remove the specified affinity to the list of include-any affinities.
+
+The following commands configure Flex-Algo at the 'interface' configuration
+level.
+
+.. clicmd:: isis affinity flex-algo NAME
+
+       Add the specified affinity to the interface.
+
+.. clicmd:: no isis affinity flex-algo NAME
+
+       Remove the specified affinity from the interface.
+
+The following command show Flex-Algo information:
+
+.. clicmd:: show isis flex-algo [(128-255)]
+
+       Show information about the elected FADs
+
+'show isis route', 'show isis topology' and 'show isis segment-routing node'
+includes an 'algorithm (128-255)' optional argument. See
+:ref:`showing-isis-information` and :ref:`isis-segment-routing`.
+
 Debugging ISIS
 ==============
 
index 6ea7e7891a0067f28775c3a0aa75d348114095d6..737eb57c85bcf276fad4c51d1299087ec2321457 100644 (file)
@@ -70,14 +70,6 @@ Frontend Interface and MGMTd:
     database.
   - Data can be retrieved anytime using GET_CONFIG/GET_DATA API.
 
- - Startup Database:
-
-  - Consists of configuration data items only.
-  - This is a copy of Running database that is stored in persistent
-    storage and is used to load configurations on Running database during
-    MGMT daemon startup.
-  - Data cannot be edited/retrieved directly via Frontend interface.
-
  - Operational Database:
 
   - Consists of non-configurational data items.
@@ -313,8 +305,7 @@ MGMT Configuration commands
 .. clicmd:: mgmt commit apply
 
     This command commits any uncommited changes in the Candidate DB to the
-    Running DB. It also dumps a copy of the tree in JSON format into
-    frr_startup.json.
+    Running DB.
 
 .. clicmd:: mgmt commit check
 
@@ -373,3 +364,46 @@ MGMT Show commands
 .. clicmd:: show mgmt commit-history
 
     This command dumps details of upto last 10 commits handled by MGMTd.
+
+
+MGMT Daemon debug commands
+==========================
+
+The following debug commands enable debugging within the management daemon:
+
+.. clicmd:: [no] debug mgmt backend
+
+   Enable[/Disable] debugging messages related to backend operations within the
+   management daemon.
+
+.. clicmd:: [no] debug mgmt datastore
+
+   Enable[/Disable] debugging messages related to YANG datastore operations
+   within the management daemon.
+
+.. clicmd:: [no] debug mgmt frontend
+
+   Enable[/Disable] debugging messages related to frontend operations within the
+   management daemon.
+
+.. clicmd:: [no] debug mgmt transaction
+
+   Enable[/Disable] debugging messages related to transactions within the
+   management daemon.
+
+
+MGMT Client debug commands
+==========================
+
+The following debug commands enable debugging within the management front and
+backend clients:
+
+.. clicmd:: [no] debug mgmt client backend
+
+   Enable[/Disable] debugging messages related to backend operations inside the
+   backend mgmtd clients.
+
+.. clicmd:: [no] debug mgmt client frontend
+
+   Enable[/Disable] debugging messages related to frontend operations inside the
+   frontend mgmtd clients.
index 30d55f34a722ed4a69d05755414cda3a04da5379..5171832604e5aefb377693d0d519b28d66dcbc0c 100644 (file)
@@ -310,6 +310,18 @@ To start OSPF process you have to specify the OSPF router.
    of packets to process before returning. The defult value of this parameter
    is 20.
 
+.. clicmd:: socket buffer <send | recv | all> (1-4000000000)
+
+   This command controls the ospf instance's socket buffer sizes. The
+   'no' form resets one or both values to the default.
+   
+.. clicmd:: no socket-per-interface
+
+   Ordinarily, ospfd uses a socket per interface for sending
+   packets. This command disables those per-interface sockets, and
+   causes ospfd to use a single socket per ospf instance for sending
+   and receiving packets.
+
 .. _ospf-area:
 
 Areas
@@ -325,7 +337,6 @@ Areas
    announced to other areas. This command can be used only in ABR and ONLY
    router-LSAs (Type-1) and network-LSAs (Type-2) (i.e. LSAs with scope area) can
    be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
-   Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR.
 
    .. code-block:: frr
 
@@ -431,6 +442,31 @@ Areas
     configured not to advertise forwarding addresses into the backbone to direct
     forwarded traffic to the NSSA ABR translator.
 
+.. clicmd:: area A.B.C.D nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)]
+
+.. clicmd:: area (0-4294967295) nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)]
+
+   NSSA ABRs and ASBRs can be configured with the `default-information-originate`
+   option to originate a Type-7 default route into the NSSA area. In the case
+   of NSSA ASBRs, the origination of the default route is conditioned to the
+   existence of a default route in the RIB that wasn't learned via the OSPF
+   protocol.
+
+.. clicmd:: area A.B.C.D nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>]
+
+.. clicmd:: area (0-4294967295) nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>]
+
+    Summarize a group of external subnets into a single Type-7 LSA, which is
+    then translated to a Type-5 LSA and avertised to the backbone.
+    This command can only be used at the area boundary (NSSA ABR router).
+
+    By default, the metric of the summary route is calculated as the highest
+    metric among the summarized routes. The `cost` option, however, can be used
+    to set an explicit metric.
+
+    The `not-advertise` option, when present, prevents the summary route from
+    being advertised, effectively filtering the summarized routes.
+
 .. clicmd:: area A.B.C.D default-cost (0-16777215)
 
 
index 4a24fa52be565e59a15252c344582ac5108fab94..33a19346280bd928c8b53e711994e9e832ea7081 100644 (file)
@@ -424,6 +424,8 @@ BGP
   :t:`Extended Optional Parameters Length for BGP OPEN Message. E. Chen, J. Scudder. July 2021.`
 - :rfc:`9234`
   :t:`Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages. A. Azimov, E. Bogomazov, R. Bush, K. Patel, K. Sriram. May 2022.`
+- :rfc:`9384`
+  :t:`A BGP Cease NOTIFICATION Subcode for Bidirectional Forwarding Detection (BFD). J. Haas. March 2023.`
 
 OSPF
 ----
@@ -463,6 +465,8 @@ BFD
   :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
 - :rfc:`5881`
   :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010`
+- :rfc:`5882`
+  :t:`Generic Application of Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
 - :rfc:`5883`
   :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010`
 
index 72471d7af0e23ac11270ebf1e29a3258c03f37ca..d70c3c0e64e9bc8c6c19235e5d10a0a893d4bb74 100644 (file)
@@ -387,9 +387,14 @@ cause great confusion.
 .. clicmd:: show ip igmp [vrf NAME] join [json]
 
    Display IGMP static join information for a specific vrf.
-   If "vrf all" is provided, it displays information for all the vrfs present.
+   
+.. index:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json]
+.. clicmd:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json]
 
-.. clicmd:: show ip igmp groups
+   Display IGMP static join information for all the vrfs present.
+
+.. index:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]
+.. clicmd:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]
 
    Display IGMP groups information.
 
index 9e4d7a611aced3268fad9dff21a664ca47889a02..b7f53365649d120616b84a89caf986eaf759fd23 100644 (file)
@@ -305,10 +305,18 @@ Route Map Set Command
 
 .. clicmd:: set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt>
 
-   Set the BGP attribute MED to a specific value. Use `+`/`-` to add or subtract
-   the specified value to/from the MED. Use `rtt` to set the MED to the round
-   trip time or `+rtt`/`-rtt` to add/subtract the round trip time to/from the
-   MED.
+   Set the route metric. When used with BGP, set the BGP attribute MED to a
+   specific value. Use `+`/`-` to add or subtract the specified value to/from
+   the existing/MED. Use `rtt` to set the MED to the round trip time or
+   `+rtt`/`-rtt` to add/subtract the round trip time to/from the MED.
+
+.. clicmd:: set min-metric <(0-4294967295)>
+
+   Set the minimum meric for the route.
+
+.. clicmd:: set max-metric <(0-4294967295)>
+
+   Set the maximum meric for the route.
 
 .. clicmd:: set aigp-metric <igp-metric|(1-4294967295)>
 
diff --git a/docker/ubi-8/Dockerfile b/docker/ubi-8/Dockerfile
deleted file mode 100644 (file)
index 1d1e8bd..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-# This stage builds an rpm from the source
-FROM registry.access.redhat.com/ubi8/ubi:8.5 as ubi-8-builder
-
-RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical
-
-RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \
-    && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \
-    && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os \
-    && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/PowerTools/x86_64/os
-
-RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
-    && dnf install --enablerepo=* -qy rpm-build git autoconf pcre-devel \
-    systemd-devel automake libtool make  readline-devel  texinfo  \
-    net-snmp-devel  pkgconfig  groff pkgconfig  json-c-devel pam-devel  \
-    bison  flex  python3-pytest  c-ares-devel python3-devel python3-sphinx \
-    libcap-devel  platform-python-devel \
-    https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
-    https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
-    https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \
-    https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.x86_64.rpm
-
-
-COPY . /src
-
-ARG PKGVER
-
-RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
-    && cd /src \
-    && ./bootstrap.sh \
-    && ./configure \
-        --enable-rpki \
-        --enable-snmp=agentx \
-        --enable-numeric-version \
-        --with-pkg-extra-version="_palmetto_git$PKGVER" \
-    && make dist \
-    && cd / \
-    && mkdir -p /rpmbuild/{SOURCES,SPECS} \
-    && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
-    && cp /src/redhat/frr.spec /rpmbuild/SPECS \
-    && rpmbuild \
-        --define "_topdir /rpmbuild" \
-        -ba /rpmbuild/SPECS/frr.spec
-
-# This stage installs frr from the rpm
-FROM registry.access.redhat.com/ubi8/ubi:8.5
-RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical
-ARG FRR_IMAGE_TAG
-ARG FRR_RELEASE
-ARG FRR_NAME
-ARG FRR_VENDOR
-LABEL name=$FRR_NAME \
-      vendor=$FRR_VENDOR \
-      version=$FRR_IMAGE_TAG \
-      release=$FRR_RELEASE
-
-RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \
-    && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \
-    && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os
-
-RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \
-    && mkdir -p /pkgs/rpm \
-    && dnf install --enablerepo=* -qy https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \
-    https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm
-
-COPY --from=ubi-8-builder /rpmbuild/RPMS/ /pkgs/rpm/
-
-RUN dnf install -qy /pkgs/rpm/*/*.rpm \
-    && rm -rf /pkgs \
-# Own the config / PID files
-    && mkdir -p /var/run/frr \
-    && chown -R frr:frr /etc/frr /var/run/frr
-
-# Add tini because no CentOS8 package
-ENV TINI_VERSION v0.19.0
-ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini
-RUN chmod +x /sbin/tini
-
-# Simple init manager for reaping processes and forwarding signals
-ENTRYPOINT ["/sbin/tini", "--"]
-
-# Default CMD starts watchfrr
-COPY docker/ubi-8/docker-start /usr/lib/frr/docker-start
-CMD ["/usr/lib/frr/docker-start"]
diff --git a/docker/ubi-8/build.sh b/docker/ubi-8/build.sh
deleted file mode 100755 (executable)
index 0216636..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-
-set -e
-
-##
-# Package version needs to be decimal
-##
-DISTRO=ubi-8
-
-GITREV="$2"
-if [ -z "$GITREV" ];then
-       GITREV="$(git rev-parse --short=10 HEAD)"
-fi
-
-FRR_IMAGE_TAG="$1"
-if [ -z $FRR_IMAGE_TAG ];then
-       FRR_IMAGE_TAG="frr:ubi-8-$GITREV"
-fi
-PKGVER="$(printf '%u\n' 0x$GITREV)"
-
-FRR_RELEASE="$3"
-if [ -z $FRR_RELEASE ];then
-       FRR_RELEASE=$(git describe --tags --abbrev=0)
-fi
-
-FRR_NAME=$4
-if [ -z $FRR_NAME ];then
-       FRR_NAME=frr
-fi
-
-FRR_VENDOR=$5
-if [ -z $FRR_VENDOR ];then
-       FRR_VENDOR=frr
-fi
-
-docker build \
-       --cache-from="frr:$DISTRO-builder-$GITREV" \
-       --file=docker/$DISTRO/Dockerfile \
-       --build-arg="PKGVER=$PKGVER" \
-       --build-arg="FRR_IMAGE_TAG=$FRR_IMAGE_TAG" \
-       --build-arg="FRR_RELEASE=$FRR_RELEASE" \
-       --build-arg="FRR_NAME=$FRR_NAME" \
-       --build-arg="FRR_VENDOR=$FRR_VENDOR" \
-       --tag="$FRR_IMAGE_TAG" \
-       .
-
diff --git a/docker/ubi-8/docker-start b/docker/ubi-8/docker-start
deleted file mode 100755 (executable)
index d954142..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-source /usr/lib/frr/frrcommon.sh
-/usr/lib/frr/watchfrr $(daemon_list)
diff --git a/docker/ubi8-minimal/Dockerfile b/docker/ubi8-minimal/Dockerfile
new file mode 100644 (file)
index 0000000..adb0421
--- /dev/null
@@ -0,0 +1,132 @@
+# This stage builds an rpm from the source
+ARG UBI8_MINIMAL_VERSION
+FROM registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION} as ubi8-minimal-builder
+
+RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8
+
+ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo
+
+# ubi8-minimal comes with broken tzdata package installed, so we need to remove them
+# and later reinstall it again: https://bugzilla.redhat.com/show_bug.cgi?id=1668185
+RUN rpm --quiet -e --nodeps tzdata >/dev/null 2>&1
+
+RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \
+    autoconf \
+    automake \
+    bison \
+    c-ares-devel \
+    flex \
+    git \
+    groff \
+    json-c-devel \
+    libcap-devel \
+    libssh-devel \
+    libtool \
+    make \
+    net-snmp-devel \
+    openssl \
+    pam-devel  \
+    pcre-devel \
+    pkgconfig \
+    platform-python-devel \
+    python3-devel \
+    python3-pytest \
+    python3-sphinx \
+    readline-devel \
+    rpm-build \
+    systemd-devel \
+    texinfo \
+    tzdata \
+    && microdnf --disableplugin=subscription-manager clean all
+
+RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \
+    && rpm -i /tmp/libyang2.rpm \
+    && rm -f /tmp/libyang2.rpm
+
+RUN curl -sSL -o /tmp/libyang2-devel.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-devel-2.0.7-1.el8.x86_64.rpm \
+    && rpm -i /tmp/libyang2-devel.rpm \
+    && rm -f /tmp/libyang2-devel.rpm
+
+RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \
+    && rpm -i /tmp/librtr.rpm \
+    && rm -f /tmp/librtr.rpm
+
+RUN curl -sSL -o /tmp/librtr-devel.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-devel-0.8.0-1.el8.x86_64.rpm \
+    && rpm -i /tmp/librtr-devel.rpm \
+    && rm -f /tmp/librtr-devel.rpm
+
+COPY . /src
+
+ARG PKGVER
+
+RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
+    && cd /src \
+    && ./bootstrap.sh \
+    && ./configure \
+        --enable-rpki \
+        --enable-snmp=agentx \
+        --enable-numeric-version \
+        --with-pkg-extra-version="_git$PKGVER" \
+    && make dist \
+    && cd / \
+    && mkdir -p /rpmbuild/{SOURCES,SPECS} \
+    && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
+    && cp /src/redhat/frr.spec /rpmbuild/SPECS \
+    && rpmbuild \
+        --define "_topdir /rpmbuild" \
+        -ba /rpmbuild/SPECS/frr.spec
+
+# This stage installs frr from the rpm
+FROM registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION}
+ARG FRR_IMAGE_TAG
+ARG FRR_RELEASE
+ARG FRR_NAME
+ARG FRR_VENDOR
+LABEL name=$FRR_NAME \
+      vendor=$FRR_VENDOR \
+      version=$FRR_IMAGE_TAG \
+      release=$FRR_RELEASE
+
+ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo
+
+RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8
+
+RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \
+    c-ares \
+    initscripts \
+    net-snmp-agent-libs \
+    net-snmp-libs \
+    openssl \
+    python3 \
+    shadow-utils \
+    systemd \
+    && microdnf --disableplugin=subscription-manager clean all
+
+RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \
+    && rpm -i /tmp/libyang2.rpm \
+    && rm -f /tmp/libyang2.rpm
+
+RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \
+    && rpm -i /tmp/librtr.rpm \
+    && rm -f /tmp/librtr.rpm
+
+COPY --from=ubi8-minimal-builder /rpmbuild/RPMS/ /pkgs/rpm/
+
+# Install packages and create FRR files and folders. Be sure to own the config / PID files
+RUN rpm -i /pkgs/rpm/x86_64/*.rpm \
+    && rm -rf /pkgs \
+    && rm -rf /mnt/rootfs/var/cache/* /mnt/rootfs/var/log/dnf* /mnt/rootfs/var/log/yum.* \
+    && mkdir -p /var/run/frr \
+    && chown -R frr:frr /etc/frr /var/run/frr
+
+# There is no package for tini, add it manually
+ENV TINI_VERSION v0.19.0
+ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini
+RUN chmod +x /sbin/tini
+
+# Simple init manager for reaping processes and forwarding signals
+ENTRYPOINT ["/sbin/tini", "--"]
+
+# Default CMD starts watchfrr
+COPY docker/ubi8-minimal/docker-start /usr/lib/frr/docker-start
+CMD ["/usr/lib/frr/docker-start"]
diff --git a/docker/ubi8-minimal/almalinux.repo b/docker/ubi8-minimal/almalinux.repo
new file mode 100644 (file)
index 0000000..9b9877b
--- /dev/null
@@ -0,0 +1,23 @@
+[AlmaLinux - baseos]
+name=AlmaLinux $releasever - BaseOS
+mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos
+# baseurl=https://repo.almalinux.org/almalinux/$releasever/BaseOS/$basearch/os/
+enabled=1
+gpgcheck=1
+countme=1
+
+[AlmaLinux - appstream]
+name=AlmaLinux $releasever - AppStream
+mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/appstream
+# baseurl=https://repo.almalinux.org/almalinux/$releasever/AppStream/$basearch/os/
+enabled=1
+gpgcheck=1
+countme=1
+
+[AlmaLinux - powertools]
+name=AlmaLinux $releasever - PowerTools
+mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/powertools
+# baseurl=https://repo.almalinux.org/almalinux/$releasever/PowerTools/$basearch/os/
+enabled=1
+gpgcheck=1
+countme=1
diff --git a/docker/ubi8-minimal/build.sh b/docker/ubi8-minimal/build.sh
new file mode 100755 (executable)
index 0000000..2aa45c9
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+set -e
+
+##
+# Package version needs to be decimal
+##
+DISTRO=ubi8-minimal
+
+UBI8_MINIMAL_VERSION=$1
+if [ -z "$UBI8_MINIMAL_VERSION" ]; then
+       UBI8_MINIMAL_VERSION="latest"
+fi
+
+GITREV="$2"
+if [ -z "$GITREV" ];then
+       GITREV="$(git rev-parse --short=10 HEAD)"
+fi
+
+FRR_IMAGE_TAG="$3"
+if [ -z $FRR_IMAGE_TAG ];then
+       FRR_IMAGE_TAG="frr:ubi8-minimal-$GITREV"
+fi
+PKGVER="$(printf '%u\n' 0x$GITREV)"
+
+FRR_RELEASE="$4"
+if [ -z $FRR_RELEASE ];then
+       FRR_RELEASE=$(git describe --tags --abbrev=0)
+fi
+
+FRR_NAME=$5
+if [ -z $FRR_NAME ];then
+       FRR_NAME=frr
+fi
+
+FRR_VENDOR=$6
+if [ -z $FRR_VENDOR ];then
+       FRR_VENDOR=frr
+fi
+
+DOCKERFILE_PATH="$(dirname $(realpath $0))/Dockerfile"
+
+docker build \
+       --cache-from="frr:$DISTRO-builder-$GITREV" \
+       --file="$DOCKERFILE_PATH" \
+       --build-arg="UBI8_MINIMAL_VERSION=$UBI8_MINIMAL_VERSION" \
+       --build-arg="PKGVER=$PKGVER" \
+       --build-arg="FRR_IMAGE_TAG=$FRR_IMAGE_TAG" \
+       --build-arg="FRR_RELEASE=$FRR_RELEASE" \
+       --build-arg="FRR_NAME=$FRR_NAME" \
+       --build-arg="FRR_VENDOR=$FRR_VENDOR" \
+       --tag="$FRR_IMAGE_TAG" \
+       .
+
diff --git a/docker/ubi8-minimal/docker-start b/docker/ubi8-minimal/docker-start
new file mode 100755 (executable)
index 0000000..d954142
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
index 4fd39498a0bd57024c794b48bf6804addad2f782..b229aa6717bdadbae5e3799f82dbd1783bc3a30c 100644 (file)
@@ -207,10 +207,10 @@ struct fabricd *fabricd_new(struct isis_area *area)
        rv->area = area;
        rv->initial_sync_state = FABRICD_SYNC_PENDING;
 
-       rv->spftree =
-               isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1],
-                                area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4,
-                                SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC);
+       rv->spftree = isis_spftree_new(
+               area, &area->lspdb[IS_LEVEL_2 - 1], area->isis->sysid,
+               ISIS_LEVEL2, SPFTREE_IPV4, SPF_TYPE_FORWARD,
+               F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF);
        rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp,
                                     neighbor_entry_del_void);
        rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key,
@@ -273,8 +273,8 @@ void fabricd_initial_sync_hello(struct isis_circuit *circuit)
 
        if (IS_DEBUG_ADJ_PACKETS)
                zlog_debug(
-                       "OpenFabric: Started initial synchronization with %s on %s",
-                       sysid_print(circuit->u.p2p.neighbor->sysid),
+                       "OpenFabric: Started initial synchronization with %pSY on %s",
+                       circuit->u.p2p.neighbor->sysid,
                        circuit->interface->name);
 }
 
@@ -359,7 +359,9 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
                return ISIS_TIER_UNDEFINED;
        }
 
-       zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N);
+       zlog_info(
+               "OpenFabric: Found %pLS as furthest t0 from local system, dist == %u",
+               furthest_t0->N.id, furthest_t0->d_N);
 
        struct isis_spftree *remote_tree =
                isis_run_hopcount_spf(area, furthest_t0->N.id, NULL);
@@ -372,8 +374,9 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area)
                isis_spftree_del(remote_tree);
                return ISIS_TIER_UNDEFINED;
        } else {
-               zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id),
-                         furthest_from_remote->d_N);
+               zlog_info(
+                       "OpenFabric: Found %pLS as furthest from remote dist == %u",
+                       furthest_from_remote->N.id, furthest_from_remote->d_N);
        }
 
        int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N;
index 1871078cc165734824784f9b484099a0e73796da..30b71537e0e5ea2062e426062c77f9bf3b718abc 100644 (file)
@@ -283,6 +283,8 @@ void isis_adj_process_threeway(struct isis_adjacency *adj,
 }
 const char *isis_adj_name(const struct isis_adjacency *adj)
 {
+       static char buf[ISO_SYSID_STRLEN];
+
        if (!adj)
                return "NONE";
 
@@ -291,8 +293,9 @@ const char *isis_adj_name(const struct isis_adjacency *adj)
        dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid);
        if (dyn)
                return dyn->hostname;
-       else
-               return sysid_print(adj->sysid);
+
+       snprintfrr(buf, sizeof(buf), "%pSY", adj->sysid);
+       return buf;
 }
 void isis_log_adj_change(struct isis_adjacency *adj,
                         enum isis_adj_state old_state,
@@ -439,9 +442,8 @@ void isis_adj_print(struct isis_adjacency *adj)
        if (dyn)
                zlog_debug("%s", dyn->hostname);
 
-       zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d",
-                  sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level,
-                  adj->hold_time);
+       zlog_debug("SystemId %20pSY SNPA %pSY, level %d; Holding Time %d",
+                  adj->sysid, adj->snpa, adj->level, adj->hold_time);
        if (adj->ipv4_address_count) {
                zlog_debug("IPv4 Address(es):");
                for (unsigned int i = 0; i < adj->ipv4_address_count; i++)
@@ -530,7 +532,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json,
                                        time2string(adj->last_upd +
                                                    adj->hold_time - now));
                }
-               json_object_string_add(json, "snpa", snpa_print(adj->snpa));
+               json_object_string_addf(json, "snpa", "%pSY", adj->snpa);
        }
 
        if (detail == ISIS_UI_LEVEL_DETAIL) {
@@ -581,8 +583,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json,
                                        isis_mtid2str(adj->mt_set[i]));
                        }
                }
-               json_object_string_add(iface_json, "snpa",
-                                      snpa_print(adj->snpa));
+               json_object_string_addf(iface_json, "snpa", "%pSY", adj->snpa);
                if (adj->circuit &&
                    (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) {
                        dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid);
@@ -593,11 +594,8 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json,
                                json_object_string_add(iface_json, "lan-id",
                                                       buf);
                        } else {
-                               snprintfrr(buf, sizeof(buf), "%s-%02x",
-                                          sysid_print(adj->lanid),
-                                          adj->lanid[ISIS_SYS_ID_LEN]);
-                               json_object_string_add(iface_json, "lan-id",
-                                                      buf);
+                               json_object_string_addf(iface_json, "lan-id",
+                                                       "%pSY", adj->lanid);
                        }
 
                        json_object_int_add(iface_json, "lan-prio",
@@ -626,12 +624,9 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json,
                                               area_addr_json);
                        for (unsigned int i = 0; i < adj->area_address_count;
                             i++) {
-                               json_object_string_add(
-                                       area_addr_json, "isonet",
-                                       isonet_print(adj->area_addresses[i]
-                                                            .area_addr,
-                                                    adj->area_addresses[i]
-                                                            .addr_len));
+                               json_object_string_addf(
+                                       area_addr_json, "isonet", "%pIS",
+                                       &adj->area_addresses[i]);
                        }
                }
                if (adj->ipv4_address_count) {
@@ -736,7 +731,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
                                                + adj->hold_time - now);
                } else
                        vty_out(vty, "-        ");
-               vty_out(vty, "%-10s", snpa_print(adj->snpa));
+               vty_out(vty, "%-10pSY", adj->snpa);
                vty_out(vty, "\n");
        }
 
@@ -780,7 +775,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
                                vty_out(vty, "      %s\n",
                                        isis_mtid2str(adj->mt_set[i]));
                }
-               vty_out(vty, "    SNPA: %s", snpa_print(adj->snpa));
+               vty_out(vty, "    SNPA: %pSY", adj->snpa);
                if (adj->circuit
                    && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) {
                        dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid);
@@ -788,9 +783,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
                                vty_out(vty, ", LAN id: %s.%02x", dyn->hostname,
                                        adj->lanid[ISIS_SYS_ID_LEN]);
                        else
-                               vty_out(vty, ", LAN id: %s.%02x",
-                                       sysid_print(adj->lanid),
-                                       adj->lanid[ISIS_SYS_ID_LEN]);
+                               vty_out(vty, ", LAN id: %pPN", adj->lanid);
 
                        vty_out(vty, "\n");
                        vty_out(vty, "    LAN Priority: %u",
@@ -811,11 +804,8 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
                        vty_out(vty, "    Area Address(es):\n");
                        for (unsigned int i = 0; i < adj->area_address_count;
                             i++) {
-                               vty_out(vty, "      %s\n",
-                                       isonet_print(adj->area_addresses[i]
-                                                            .area_addr,
-                                                    adj->area_addresses[i]
-                                                            .addr_len));
+                               vty_out(vty, "      %pIS\n",
+                                       &adj->area_addresses[i]);
                        }
                }
                if (adj->ipv4_address_count) {
index f02f7a68eac6a9c6a3e793adf7fe974dce2c6052..c0c8e68145d2633946169be5c949e3b5ecf07612 100644 (file)
@@ -69,7 +69,7 @@ struct isis_adjacency {
        struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS];
        enum isis_adj_state adj_state;    /* adjacencyState */
        enum isis_adj_usage adj_usage;    /* adjacencyUsage */
-       struct area_addr *area_addresses; /* areaAdressesOfNeighbour */
+       struct iso_address *area_addresses; /* areaAdressesOfNeighbour */
        unsigned int area_address_count;
        struct nlpids nlpids; /* protocols spoken ... */
        struct in_addr *ipv4_addresses;
diff --git a/isisd/isis_affinitymap.c b/isisd/isis_affinitymap.c
new file mode 100644 (file)
index 0000000..41bad0a
--- /dev/null
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* IS-IS  affinity-map
+ * Copyright 2023 6WIND S.A.
+ */
+
+#include <zebra.h>
+#include "lib/if.h"
+#include "lib/vrf.h"
+#include "isisd/isisd.h"
+#include "isisd/isis_affinitymap.h"
+
+#ifndef FABRICD
+
+static bool isis_affinity_map_check_use(const char *affmap_name)
+{
+       struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+       struct isis_area *area;
+       struct listnode *area_node, *fa_node;
+       struct flex_algo *fa;
+       struct affinity_map *map;
+       uint16_t pos;
+
+       if (!isis)
+               return false;
+
+       map = affinity_map_get(affmap_name);
+       pos = map->bit_position;
+
+       for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) {
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node,
+                                         fa)) {
+                       if (admin_group_get(&fa->admin_group_exclude_any,
+                                           pos) ||
+                           admin_group_get(&fa->admin_group_include_any,
+                                           pos) ||
+                           admin_group_get(&fa->admin_group_include_all, pos))
+                               return true;
+               }
+       }
+       return false;
+}
+
+static void isis_affinity_map_update(const char *affmap_name, uint16_t old_pos,
+                                    uint16_t new_pos)
+{
+       struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+       struct listnode *area_node, *fa_node;
+       struct isis_area *area;
+       struct flex_algo *fa;
+       bool changed;
+
+       if (!isis)
+               return;
+
+       for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) {
+               changed = false;
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node,
+                                         fa)) {
+                       if (admin_group_get(&fa->admin_group_exclude_any,
+                                           old_pos)) {
+                               admin_group_unset(&fa->admin_group_exclude_any,
+                                                 old_pos);
+                               admin_group_set(&fa->admin_group_exclude_any,
+                                               new_pos);
+                               changed = true;
+                       }
+                       if (admin_group_get(&fa->admin_group_include_any,
+                                           old_pos)) {
+                               admin_group_unset(&fa->admin_group_include_any,
+                                                 old_pos);
+                               admin_group_set(&fa->admin_group_include_any,
+                                               new_pos);
+                               changed = true;
+                       }
+                       if (admin_group_get(&fa->admin_group_include_all,
+                                           old_pos)) {
+                               admin_group_unset(&fa->admin_group_include_all,
+                                                 old_pos);
+                               admin_group_set(&fa->admin_group_include_all,
+                                               new_pos);
+                               changed = true;
+                       }
+               }
+               if (changed)
+                       lsp_regenerate_schedule(area, area->is_type, 0);
+       }
+}
+
+void isis_affinity_map_init(void)
+{
+       affinity_map_init();
+
+       affinity_map_set_check_use_hook(isis_affinity_map_check_use);
+       affinity_map_set_update_hook(isis_affinity_map_update);
+}
+
+#endif /* ifndef FABRICD */
diff --git a/isisd/isis_affinitymap.h b/isisd/isis_affinitymap.h
new file mode 100644 (file)
index 0000000..c432e99
--- /dev/null
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* IS-IS  affinity-map header
+ * Copyright 2023 6WIND S.A.
+ */
+
+#ifndef __ISIS_AFFINITYMAP_H__
+#define __ISIS_AFFINITYMAP_H__
+
+#include "lib/affinitymap.h"
+
+#ifndef FABRICD
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void isis_affinity_map_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef FABRICD */
+
+#endif /* __ISIS_AFFINITYMAP_H__ */
index 124dcdd86d5b2abf068e01aa1b15b0058f331962..feab45123383e3027376080b57b446bb43f042d0 100644 (file)
@@ -286,8 +286,7 @@ void isis_circuit_add_addr(struct isis_circuit *circuit,
        if (connected->address->family == AF_INET) {
                uint32_t addr = connected->address->u.prefix4.s_addr;
                addr = ntohl(addr);
-               if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr)
-                   || IPV4_LINKLOCAL(addr))
+               if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr))
                        return;
 
                for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4))
@@ -696,10 +695,9 @@ int isis_circuit_up(struct isis_circuit *circuit)
                }
 #ifdef EXTREME_DEGUG
                if (IS_DEBUG_EVENTS)
-                       zlog_debug("%s: if_id %d, isomtu %d snpa %s", __func__,
-                                  circuit->interface->ifindex,
-                                  ISO_MTU(circuit),
-                                  snpa_print(circuit->u.bc.snpa));
+                       zlog_debug("%s: if_id %d, isomtu %d snpa %pSY",
+                                  __func__, circuit->interface->ifindex,
+                                  ISO_MTU(circuit), circuit->u.bc.snpa);
 #endif /* EXTREME_DEBUG */
 
                circuit->u.bc.adjdb[0] = list_new();
@@ -996,8 +994,8 @@ void isis_circuit_print_json(struct isis_circuit *circuit,
                json_object_string_add(iface_json, "level",
                                       circuit_t2string(circuit->is_type));
                if (circuit->circ_type == CIRCUIT_T_BROADCAST)
-                       json_object_string_add(iface_json, "snpa",
-                                              snpa_print(circuit->u.bc.snpa));
+                       json_object_string_addf(iface_json, "snpa", "%pSY",
+                                               circuit->u.bc.snpa);
 
 
                levels_json = json_object_new_array();
@@ -1123,8 +1121,7 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty,
                        circuit_type2string(circuit->circ_type));
                vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type));
                if (circuit->circ_type == CIRCUIT_T_BROADCAST)
-                       vty_out(vty, ", SNPA: %-10s",
-                               snpa_print(circuit->u.bc.snpa));
+                       vty_out(vty, ", SNPA: %-10pSY", circuit->u.bc.snpa);
                vty_out(vty, "\n");
                if (circuit->is_type & IS_LEVEL_1) {
                        vty_out(vty, "    Level-1 Information:\n");
index 7e1bb9255c491b984c43f6321e7a1c3bce737c2d..7c7a8d238941a2d1c88aa680801e3d1275c6116b 100644 (file)
@@ -23,6 +23,7 @@
 #include "isisd/isis_misc.h"
 #include "isisd/isis_circuit.h"
 #include "isisd/isis_csm.h"
+#include "isisd/isis_flex_algo.h"
 
 #include "isisd/isis_cli_clippy.c"
 
@@ -1126,6 +1127,53 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode,
        vty_out(vty, " purge-originator\n");
 }
 
+
+/*
+ * XPath: /frr-isisd:isis/instance/admin-group-send-zero
+ */
+DEFPY_YANG(isis_admin_group_send_zero, isis_admin_group_send_zero_cmd,
+          "[no] admin-group-send-zero",
+          NO_STR
+          "Allow sending the default admin-group value of 0x00000000.\n")
+{
+       nb_cli_enqueue_change(vty, "./admin-group-send-zero", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_admin_group_send_zero(struct vty *vty,
+                                        const struct lyd_node *dnode,
+                                        bool show_defaults)
+{
+       if (!yang_dnode_get_bool(dnode, NULL))
+               vty_out(vty, " no");
+       vty_out(vty, " admin-group-send-zero\n");
+}
+
+
+/*
+ * XPath: /frr-isisd:isis/instance/asla-legacy-flag
+ */
+DEFPY_HIDDEN(isis_asla_legacy_flag, isis_asla_legacy_flag_cmd,
+            "[no] asla-legacy-flag",
+            NO_STR "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV.\n")
+{
+       nb_cli_enqueue_change(vty, "./asla-legacy-flag", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_asla_legacy_flag(struct vty *vty,
+                                   const struct lyd_node *dnode,
+                                   bool show_defaults)
+{
+       if (!yang_dnode_get_bool(dnode, NULL))
+               vty_out(vty, " no");
+       vty_out(vty, " asla-legacy-flag\n");
+}
+
 /*
  * XPath: /frr-isisd:isis/instance/mpls-te
  */
@@ -1769,6 +1817,122 @@ void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode,
        vty_out(vty, "\n");
 }
 
+#ifndef FABRICD
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid
+ */
+DEFPY_YANG(
+       isis_sr_prefix_sid_algorithm, isis_sr_prefix_sid_algorithm_cmd,
+       "segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\
+              algorithm (128-255)$algorithm\
+              <absolute$sid_type (16-1048575)$sid_value|index$sid_type (0-65535)$sid_value>\
+              [<no-php-flag|explicit-null>$lh_behavior] [n-flag-clear$n_flag_clear]",
+       SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix\n"
+       "IPv6 Prefix\n"
+       "Algorithm Specific Prefix SID Configuration\n"
+       "Algorithm number\n"
+       "Specify the absolute value of Prefix Segment ID\n"
+       "The Prefix Segment ID value\n"
+       "Specify the index of Prefix Segment ID\n"
+       "The Prefix Segment ID index\n"
+       "Don't request Penultimate Hop Popping (PHP)\n"
+       "Upstream neighbor must replace prefix-sid with explicit null label\n"
+       "Not a node SID\n")
+{
+       nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+       nb_cli_enqueue_change(vty, "./sid-value-type", NB_OP_MODIFY, sid_type);
+       nb_cli_enqueue_change(vty, "./sid-value", NB_OP_MODIFY, sid_value_str);
+       if (lh_behavior) {
+               const char *value;
+
+               if (strmatch(lh_behavior, "no-php-flag"))
+                       value = "no-php";
+               else if (strmatch(lh_behavior, "explicit-null"))
+                       value = "explicit-null";
+               else
+                       value = "php";
+
+               nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY,
+                                     value);
+       } else
+               nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY,
+                                     NULL);
+       nb_cli_enqueue_change(vty, "./n-flag-clear", NB_OP_MODIFY,
+                             n_flag_clear ? "true" : "false");
+
+       return nb_cli_apply_changes(
+               vty,
+               "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']",
+               prefix_str, algorithm_str);
+}
+
+DEFPY_YANG(
+       no_isis_sr_prefix_algorithm_sid, no_isis_sr_prefix_sid_algorithm_cmd,
+       "no segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\
+              algorithm (128-255)$algorithm\
+              [<absolute$sid_type (16-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]\
+              [n-flag-clear]",
+       NO_STR SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix\n"
+       "IPv6 Prefix\n"
+       "Algorithm Specific Prefix SID Configuration\n"
+       "Algorithm number\n"
+       "Specify the absolute value of Prefix Segment ID\n"
+       "The Prefix Segment ID value\n"
+       "Specify the index of Prefix Segment ID\n"
+       "The Prefix Segment ID index\n"
+       "Don't request Penultimate Hop Popping (PHP)\n"
+       "Upstream neighbor must replace prefix-sid with explicit null label\n"
+       "Not a node SID\n")
+{
+       nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+       return nb_cli_apply_changes(
+               vty,
+               "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']",
+               prefix_str, algorithm_str);
+       return CMD_SUCCESS;
+}
+#endif /* ifndef FABRICD */
+
+void cli_show_isis_prefix_sid_algorithm(struct vty *vty,
+                                       const struct lyd_node *dnode,
+                                       bool show_defaults)
+{
+       const char *prefix;
+       const char *lh_behavior;
+       const char *sid_value_type;
+       const char *sid_value;
+       bool n_flag_clear;
+       uint32_t algorithm;
+
+       prefix = yang_dnode_get_string(dnode, "./prefix");
+       sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type");
+       sid_value = yang_dnode_get_string(dnode, "./sid-value");
+       algorithm = yang_dnode_get_uint32(dnode, "./algo");
+       lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior");
+       n_flag_clear = yang_dnode_get_bool(dnode, "./n-flag-clear");
+
+       vty_out(vty, " segment-routing prefix %s", prefix);
+       vty_out(vty, " algorithm %u", algorithm);
+       if (strmatch(sid_value_type, "absolute"))
+               vty_out(vty, " absolute");
+       else
+               vty_out(vty, " index");
+       vty_out(vty, " %s", sid_value);
+
+       if (strmatch(lh_behavior, "no-php"))
+               vty_out(vty, " no-php-flag");
+       else if (strmatch(lh_behavior, "explicit-null"))
+               vty_out(vty, " explicit-null");
+       if (n_flag_clear)
+               vty_out(vty, " n-flag-clear");
+       vty_out(vty, "\n");
+}
 
 /*
  * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/priority-limit
@@ -2558,6 +2722,24 @@ void cli_show_ip_isis_circ_type(struct vty *vty, const struct lyd_node *dnode,
        }
 }
 
+static int ag_change(struct vty *vty, int argc, struct cmd_token **argv,
+                    const char *xpath, bool no, int start_idx)
+{
+       for (int i = start_idx; i < argc; i++)
+               nb_cli_enqueue_change(vty, xpath,
+                                     no ? NB_OP_DESTROY : NB_OP_CREATE,
+                                     argv[i]->arg);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+static int ag_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+       struct vty *vty = (struct vty *)arg;
+
+       vty_out(vty, " %s", yang_dnode_get_string(dnode, "."));
+       return YANG_ITER_CONTINUE;
+}
+
 /*
  * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type
  */
@@ -3025,6 +3207,26 @@ void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode,
        vty_out(vty, " log-adjacency-changes\n");
 }
 
+/*
+ * XPath: /frr-isisd:isis/instance/log-pdu-drops
+ */
+DEFPY_YANG(log_pdu_drops, log_pdu_drops_cmd, "[no] log-pdu-drops",
+          NO_STR "Log any dropped PDUs\n")
+{
+       nb_cli_enqueue_change(vty, "./log-pdu-drops", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode,
+                                bool show_defaults)
+{
+       if (!yang_dnode_get_bool(dnode, NULL))
+               vty_out(vty, " no");
+       vty_out(vty, " log-pdu-drops\n");
+}
+
 /*
  * XPath: /frr-isisd:isis/instance/mpls/ldp-sync
  */
@@ -3162,6 +3364,250 @@ void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty,
                yang_dnode_get_string(dnode, NULL));
 }
 
+DEFPY_YANG_NOSH(flex_algo, flex_algo_cmd, "flex-algo (128-255)$algorithm",
+               "Flexible Algorithm\n"
+               "Flexible Algorithm Number\n")
+{
+       int ret;
+       char xpath[XPATH_MAXLEN + 37];
+
+       snprintf(xpath, sizeof(xpath),
+                "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH,
+                algorithm);
+
+       nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+
+       ret = nb_cli_apply_changes(
+               vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm);
+       if (ret == CMD_SUCCESS)
+               VTY_PUSH_XPATH(ISIS_FLEX_ALGO_NODE, xpath);
+
+       return ret;
+}
+
+DEFPY_YANG(no_flex_algo, no_flex_algo_cmd, "no flex-algo (128-255)$algorithm",
+          NO_STR
+          "Flexible Algorithm\n"
+          "Flexible Algorithm Number\n")
+{
+       char xpath[XPATH_MAXLEN + 37];
+
+       snprintf(xpath, sizeof(xpath),
+                "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH,
+                algorithm);
+
+       if (!yang_dnode_exists(vty->candidate_config->dnode, xpath)) {
+               vty_out(vty, "ISIS flex-algo %ld isn't exist.\n", algorithm);
+               return CMD_ERR_NO_MATCH;
+       }
+
+       nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+       return nb_cli_apply_changes_clear_pending(
+               vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm);
+}
+
+DEFPY_YANG(advertise_definition, advertise_definition_cmd,
+          "[no] advertise-definition",
+          NO_STR "Advertise Local Flexible Algorithm\n")
+{
+       nb_cli_enqueue_change(vty, "./advertise-definition",
+                             no ? NB_OP_DESTROY : NB_OP_CREATE,
+                             no ? NULL : "true");
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(affinity_include_any, affinity_include_any_cmd,
+          "[no] affinity include-any NAME...",
+          NO_STR
+          "Affinity configuration\n"
+          "Any Include with\n"
+          "Include NAME list\n")
+{
+       const char *xpath = "./affinity-include-anies/affinity-include-any";
+
+       return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2);
+}
+
+DEFPY_YANG(affinity_include_all, affinity_include_all_cmd,
+          "[no] affinity include-all NAME...",
+          NO_STR
+          "Affinity configuration\n"
+          "All Include with\n"
+          "Include NAME list\n")
+{
+       const char *xpath = "./affinity-include-alls/affinity-include-all";
+
+       return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2);
+}
+
+DEFPY_YANG(affinity_exclude_any, affinity_exclude_any_cmd,
+          "[no] affinity exclude-any NAME...",
+          NO_STR
+          "Affinity configuration\n"
+          "Any Exclude with\n"
+          "Exclude NAME list\n")
+{
+       const char *xpath = "./affinity-exclude-anies/affinity-exclude-any";
+
+       return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2);
+}
+
+DEFPY_YANG(prefix_metric, prefix_metric_cmd, "[no] prefix-metric",
+          NO_STR "Use Flex-Algo Prefix Metric\n")
+{
+       nb_cli_enqueue_change(vty, "./prefix-metric",
+                             no ? NB_OP_DESTROY : NB_OP_CREATE, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(dplane_sr_mpls, dplane_sr_mpls_cmd, "[no] dataplane sr-mpls",
+          NO_STR
+          "Advertise and participate in the specified Data-Planes\n"
+          "Advertise and participate in SR-MPLS data-plane\n")
+{
+       nb_cli_enqueue_change(vty, "./dplane-sr-mpls",
+                             no ? NB_OP_DESTROY : NB_OP_CREATE, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_HIDDEN(dplane_srv6, dplane_srv6_cmd, "[no] dataplane srv6",
+            NO_STR
+            "Advertise and participate in the specified Data-Planes\n"
+            "Advertise and participate in SRv6 data-plane\n")
+{
+
+       nb_cli_enqueue_change(vty, "./dplane-srv6",
+                             no ? NB_OP_DESTROY : NB_OP_CREATE, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_HIDDEN(dplane_ip, dplane_ip_cmd, "[no] dataplane ip",
+            NO_STR
+            "Advertise and participate in the specified Data-Planes\n"
+            "Advertise and participate in IP data-plane\n")
+{
+       nb_cli_enqueue_change(vty, "./dplane-ip",
+                             no ? NB_OP_DESTROY : NB_OP_CREATE, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(metric_type, metric_type_cmd,
+          "[no] metric-type [igp$igp|te$te|delay$delay]",
+          NO_STR
+          "Metric-type used by flex-algo calculation\n"
+          "Use IGP metric (default)\n"
+          "Use Delay as metric\n"
+          "Use Traffic Engineering metric\n")
+{
+       const char *type = NULL;
+
+       if (igp) {
+               type = "igp";
+       } else if (te) {
+               type = "te-default";
+       } else if (delay) {
+               type = "min-uni-link-delay";
+       } else {
+               vty_out(vty, "Error: unknown metric type\n");
+               return CMD_SUCCESS;
+       }
+
+       if (!igp)
+               vty_out(vty,
+                       "Warning: this version can advertise a Flex-Algorithm Definition (FAD) with the %s metric.\n"
+                       "However, participation in a Flex-Algorithm with such a metric is not yet supported.\n",
+                       type);
+
+       nb_cli_enqueue_change(vty, "./metric-type",
+                             no ? NB_OP_DESTROY : NB_OP_MODIFY,
+                             no ? NULL : type);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(priority, priority_cmd, "[no] priority (0-255)$priority",
+          NO_STR
+          "Flex-Algo definition priority\n"
+          "Priority value\n")
+{
+       nb_cli_enqueue_change(vty, "./priority",
+                             no ? NB_OP_DESTROY : NB_OP_MODIFY,
+                             no ? NULL : priority_str);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode,
+                            bool show_defaults)
+{
+       uint32_t algorithm;
+       enum flex_algo_metric_type metric_type;
+       uint32_t priority;
+       char type_str[10];
+
+       algorithm = yang_dnode_get_uint32(dnode, "./flex-algo");
+       vty_out(vty, " flex-algo %u\n", algorithm);
+
+       if (yang_dnode_exists(dnode, "./advertise-definition"))
+               vty_out(vty, "  advertise-definition\n");
+
+       if (yang_dnode_exists(dnode, "./dplane-sr-mpls"))
+               vty_out(vty, "  dataplane sr-mpls\n");
+       if (yang_dnode_exists(dnode, "./dplane-srv6"))
+               vty_out(vty, "  dataplane srv6\n");
+       if (yang_dnode_exists(dnode, "./dplane-ip"))
+               vty_out(vty, "  dataplane ip\n");
+
+       if (yang_dnode_exists(dnode, "./prefix-metric"))
+               vty_out(vty, "  prefix-metric\n");
+
+       if (yang_dnode_exists(dnode, "./metric-type")) {
+               metric_type = yang_dnode_get_enum(dnode, "./metric-type");
+               if (metric_type != MT_IGP) {
+                       flex_algo_metric_type_print(type_str, sizeof(type_str),
+                                                   metric_type);
+                       vty_out(vty, "  metric-type %s\n", type_str);
+               }
+       }
+
+       if (yang_dnode_exists(dnode, "./priority")) {
+               priority = yang_dnode_get_uint32(dnode, "./priority");
+               if (priority != FLEX_ALGO_PRIO_DEFAULT)
+                       vty_out(vty, "  priority %u\n", priority);
+       }
+
+       if (yang_dnode_exists(dnode,
+                             "./affinity-include-alls/affinity-include-all")) {
+               vty_out(vty, "  affinity include-all");
+               yang_dnode_iterate(
+                       ag_iter_cb, vty, dnode,
+                       "./affinity-include-alls/affinity-include-all");
+               vty_out(vty, "\n");
+       }
+
+       if (yang_dnode_exists(
+                   dnode, "./affinity-include-anies/affinity-include-any")) {
+               vty_out(vty, "  affinity include-any");
+               yang_dnode_iterate(
+                       ag_iter_cb, vty, dnode,
+                       "./affinity-include-anies/affinity-include-any");
+               vty_out(vty, "\n");
+       }
+
+       if (yang_dnode_exists(
+                   dnode, "./affinity-exclude-anies/affinity-exclude-any")) {
+               vty_out(vty, "  affinity exclude-any");
+               yang_dnode_iterate(
+                       ag_iter_cb, vty, dnode,
+                       "./affinity-exclude-anies/affinity-exclude-any");
+               vty_out(vty, "\n");
+       }
+}
+
+void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode)
+{
+       vty_out(vty, " !\n");
+}
+
+
 void isis_cli_init(void)
 {
        install_element(CONFIG_NODE, &router_isis_cmd);
@@ -3220,6 +3666,9 @@ void isis_cli_init(void)
 
        install_element(ISIS_NODE, &area_purge_originator_cmd);
 
+       install_element(ISIS_NODE, &isis_admin_group_send_zero_cmd);
+       install_element(ISIS_NODE, &isis_asla_legacy_flag_cmd);
+
        install_element(ISIS_NODE, &isis_mpls_te_on_cmd);
        install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd);
        install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd);
@@ -3243,6 +3692,10 @@ void isis_cli_init(void)
        install_element(ISIS_NODE, &no_isis_sr_node_msd_cmd);
        install_element(ISIS_NODE, &isis_sr_prefix_sid_cmd);
        install_element(ISIS_NODE, &no_isis_sr_prefix_sid_cmd);
+#ifndef FABRICD
+       install_element(ISIS_NODE, &isis_sr_prefix_sid_algorithm_cmd);
+       install_element(ISIS_NODE, &no_isis_sr_prefix_sid_algorithm_cmd);
+#endif /* ifndef FABRICD */
        install_element(ISIS_NODE, &isis_frr_lfa_priority_limit_cmd);
        install_element(ISIS_NODE, &isis_frr_lfa_tiebreaker_cmd);
        install_element(ISIS_NODE, &isis_frr_lfa_load_sharing_cmd);
@@ -3290,6 +3743,7 @@ void isis_cli_init(void)
        install_element(INTERFACE_NODE, &isis_ti_lfa_cmd);
 
        install_element(ISIS_NODE, &log_adj_changes_cmd);
+       install_element(ISIS_NODE, &log_pdu_drops_cmd);
 
        install_element(ISIS_NODE, &isis_mpls_ldp_sync_cmd);
        install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_cmd);
@@ -3298,6 +3752,19 @@ void isis_cli_init(void)
        install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_cmd);
        install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_holddown_cmd);
        install_element(INTERFACE_NODE, &no_isis_mpls_if_ldp_sync_holddown_cmd);
+
+       install_element(ISIS_NODE, &flex_algo_cmd);
+       install_element(ISIS_NODE, &no_flex_algo_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &advertise_definition_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_any_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_all_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &affinity_exclude_any_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &dplane_sr_mpls_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &dplane_srv6_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &dplane_ip_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &prefix_metric_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &metric_type_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &priority_cmd);
 }
 
 #endif /* ifndef FABRICD */
index c908dc2e24632c3d0af7d05c48ed709c8115b966..2ded68f84d5b5ef24c0488f9cf38755d886cdb6b 100644 (file)
 #ifndef ISIS_COMMON_H
 #define ISIS_COMMON_H
 
-/*
- * Area Address
- */
-struct area_addr {
-       uint8_t addr_len;
-       uint8_t area_addr[20];
-};
-
 struct isis_passwd {
        uint8_t len;
 #define ISIS_PASSWD_TYPE_UNUSED   0
index 446e52201998c3068aae4bce3c2078c4c225b728..61c49d08a742af88b924940594941561ffba9bd8 100644 (file)
@@ -145,12 +145,10 @@ void dynhn_print_all(struct vty *vty, struct isis *isis)
        vty_out(vty, "Level  System ID      Dynamic Hostname\n");
        for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) {
                vty_out(vty, "%-7d", dyn->level);
-               vty_out(vty, "%-15s%-15s\n", sysid_print(dyn->id),
-                       dyn->hostname);
+               vty_out(vty, "%pSY %-15s\n", dyn->id, dyn->hostname);
        }
 
-       vty_out(vty, "     * %s %s\n", sysid_print(isis->sysid),
-               cmd_hostname_get());
+       vty_out(vty, "     * %pSY %s\n", isis->sysid, cmd_hostname_get());
        return;
 }
 
index 8a2a2ab971edd084dd1bf4f36f03d67a478bb727..32231a079f1af104800e085d00a65d180eb2e74a 100644 (file)
@@ -217,8 +217,8 @@ void isis_event_auth_failure(char *area_tag, const char *error_string,
                             uint8_t *sysid)
 {
        if (IS_DEBUG_EVENTS)
-               zlog_debug("ISIS-Evt (%s) Authentication failure %s from %s",
-                          area_tag, error_string, sysid_print(sysid));
+               zlog_debug("ISIS-Evt (%s) Authentication failure %s from %pSY",
+                          area_tag, error_string, sysid);
 
        return;
 }
diff --git a/isisd/isis_flex_algo.c b/isisd/isis_flex_algo.c
new file mode 100644 (file)
index 0000000..ef30987
--- /dev/null
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * isis_flex_algo.c: IS-IS Flexible Algorithm
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "stream.h"
+#include "sbuf.h"
+#include "network.h"
+#include "command.h"
+#include "bitfield.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_tlvs.h"
+#include "isisd/isis_common.h"
+#include "isisd/isis_mt.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_adjacency.h"
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_pdu.h"
+#include "isisd/isis_lsp.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_te.h"
+#include "isisd/isis_sr.h"
+#include "isisd/isis_spf_private.h"
+#include "isisd/isis_flex_algo.h"
+
+#ifndef FABRICD
+DEFINE_MTYPE_STATIC(ISISD, FLEX_ALGO, "ISIS Flex Algo");
+
+void *isis_flex_algo_data_alloc(void *voidarg)
+{
+       struct isis_flex_algo_alloc_arg *arg = voidarg;
+       struct isis_flex_algo_data *data;
+
+       data = XCALLOC(MTYPE_FLEX_ALGO, sizeof(struct isis_flex_algo_data));
+
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+                       if (!(arg->area->is_type & level))
+                               continue;
+                       data->spftree[tree][level - 1] = isis_spftree_new(
+                               arg->area, &arg->area->lspdb[level - 1],
+                               arg->area->isis->sysid, level, tree,
+                               SPF_TYPE_FORWARD, 0, arg->algorithm);
+               }
+       }
+
+       return data;
+}
+
+void isis_flex_algo_data_free(void *voiddata)
+{
+       struct isis_flex_algo_data *data = voiddata;
+
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++)
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++)
+                       if (data->spftree[tree][level - 1])
+                               isis_spftree_del(
+                                       data->spftree[tree][level - 1]);
+       XFREE(MTYPE_FLEX_ALGO, data);
+}
+
+static struct isis_router_cap_fad *
+isis_flex_algo_definition_cmp(struct isis_router_cap_fad *elected,
+                             struct isis_router_cap_fad *fa)
+{
+       if (!elected || fa->fad.priority > elected->fad.priority ||
+           (fa->fad.priority == elected->fad.priority &&
+            lsp_id_cmp(fa->sysid, elected->sysid) > 0))
+               return fa;
+
+       return elected;
+}
+
+/**
+ * @brief Look up the flex-algo definition with the highest priority in the LSP
+ * Database (LSDB). If the value of priority is the same, the flex-algo
+ * definition with the highest sysid will be selected.
+ * @param algorithm flex-algo algorithm number
+ * @param area pointer
+ * @param local router capability Flex-Algo Definition (FAD) double pointer.
+ *    - fad is NULL: use the local router capability FAD from LSDB for the
+ *      election.
+ *    - fad is not NULL and *fad is NULL: use no local router capability FAD for
+ *      the election.
+ *    - fad and *fad are not NULL: uses the *fad local definition instead of the
+ *      local definition from LSDB for the election.
+ * @return elected flex-algo-definition object if exist, else NULL
+ */
+static struct isis_router_cap_fad *
+_isis_flex_algo_elected(int algorithm, const struct isis_area *area,
+                       struct isis_router_cap_fad **fad)
+{
+       struct flex_algo *flex_ago;
+       const struct isis_lsp *lsp;
+       struct isis_router_cap_fad *fa, *elected = NULL;
+
+       if (!flex_algo_id_valid(algorithm))
+               return NULL;
+
+       /* No elected FAD if the algorithm is not locally configured */
+       flex_ago = flex_algo_lookup(area->flex_algos, algorithm);
+       if (!flex_ago)
+               return NULL;
+
+       /* No elected FAD if no data-plane is enabled
+        * Currently, only Segment-Routing MPLS is supported.
+        * Segment-Routing SRv6 and IP will be configured in the future.
+        */
+       if (!CHECK_FLAG(flex_ago->dataplanes, FLEX_ALGO_SR_MPLS))
+               return NULL;
+
+       /*
+        * Perform FAD comparison. First, compare the priority, and if they are
+        * the same, compare the sys-id.
+        */
+       /* clang-format off */
+       frr_each_const(lspdb, &area->lspdb[ISIS_LEVEL1 - 1], lsp) {
+               /* clang-format on */
+
+               if (!lsp->tlvs || !lsp->tlvs->router_cap)
+                       continue;
+
+               if (lsp->own_lsp && fad)
+                       continue;
+
+               fa = lsp->tlvs->router_cap->fads[algorithm];
+
+               if (!fa)
+                       continue;
+
+               assert(algorithm == fa->fad.algorithm);
+
+               memcpy(fa->sysid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
+
+               elected = isis_flex_algo_definition_cmp(elected, fa);
+       }
+
+       if (fad && *fad)
+               elected = isis_flex_algo_definition_cmp(elected, *fad);
+
+       return elected;
+}
+
+struct isis_router_cap_fad *isis_flex_algo_elected(int algorithm,
+                                                  const struct isis_area *area)
+{
+       return _isis_flex_algo_elected(algorithm, area, NULL);
+}
+
+/**
+ * @brief Check the Flex-Algo Definition is supported by the current FRR version
+ * @param flex-algo
+ * @return true if supported else false
+ */
+bool isis_flex_algo_supported(struct flex_algo *fad)
+{
+       if (fad->calc_type != CALC_TYPE_SPF)
+               return false;
+       if (fad->metric_type != MT_IGP)
+               return false;
+       if (fad->flags != 0)
+               return false;
+       if (fad->exclude_srlg)
+               return false;
+       if (fad->unsupported_subtlv)
+               return false;
+
+       return true;
+}
+
+/**
+ * @brief Look for the elected Flex-Algo Definition and check that it is
+ * supported by the current FRR version
+ * @param algorithm flex-algo algorithm number
+ * @param area pointer
+ * @param local router capability Flex-Algo Definition (FAD) double pointer.
+ * @return elected flex-algo-definition object if exist and supported, else NULL
+ */
+static struct isis_router_cap_fad *
+_isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area,
+                                 struct isis_router_cap_fad **fad)
+{
+       struct isis_router_cap_fad *elected_fad;
+
+       elected_fad = _isis_flex_algo_elected(algorithm, area, fad);
+       if (!elected_fad)
+               return NULL;
+
+       if (isis_flex_algo_supported(&elected_fad->fad))
+               return elected_fad;
+
+       return NULL;
+}
+
+struct isis_router_cap_fad *
+isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area)
+{
+       return _isis_flex_algo_elected_supported(algorithm, area, NULL);
+}
+
+struct isis_router_cap_fad *
+isis_flex_algo_elected_supported_local_fad(int algorithm,
+                                          const struct isis_area *area,
+                                          struct isis_router_cap_fad **fad)
+{
+       return _isis_flex_algo_elected_supported(algorithm, area, fad);
+}
+
+/**
+ * Check LSP is participating specified SR Algorithm
+ *
+ * @param lsp        IS-IS lsp
+ * @param algorithm  SR Algorithm
+ * @return           Return true if sr-algorithm tlv includes specified
+ *                   algorithm in router capability tlv
+ */
+bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm)
+{
+       if (!lsp || !lsp->tlvs || !lsp->tlvs->router_cap)
+               return false;
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+               if (lsp->tlvs->router_cap->algo[i] == algorithm)
+                       return true;
+       return false;
+}
+
+bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree,
+                                   struct isis_lsp *lsp,
+                                   struct isis_extended_reach *reach)
+{
+       bool ret;
+       struct isis_ext_subtlvs *subtlvs = reach->subtlvs;
+       struct isis_router_cap_fad *fad;
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node;
+       uint32_t *link_admin_group = NULL;
+       uint32_t link_ext_admin_group_bitmap0;
+       struct admin_group *link_ext_admin_group = NULL;
+
+       fad = isis_flex_algo_elected_supported(spftree->algorithm,
+                                              spftree->area);
+       if (!fad)
+               return true;
+
+       for (ALL_LIST_ELEMENTS_RO(subtlvs->aslas, node, asla)) {
+               if (!CHECK_FLAG(asla->standard_apps, ISIS_SABM_FLAG_X))
+                       continue;
+               if (asla->legacy) {
+                       if (IS_SUBTLV(subtlvs, EXT_ADM_GRP))
+                               link_admin_group = &subtlvs->adm_group;
+
+                       if (IS_SUBTLV(subtlvs, EXT_EXTEND_ADM_GRP) &&
+                           admin_group_nb_words(&subtlvs->ext_admin_group) !=
+                                   0)
+                               link_ext_admin_group =
+                                       &subtlvs->ext_admin_group;
+               } else {
+                       if (IS_SUBTLV(asla, EXT_ADM_GRP))
+                               link_admin_group = &asla->admin_group;
+                       if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) &&
+                           admin_group_nb_words(&asla->ext_admin_group) != 0)
+                               link_ext_admin_group = &asla->ext_admin_group;
+               }
+               break;
+       }
+
+       /* RFC7308 section 2.3.1
+        * A receiving node that notices that the AG differs from the first 32
+        * bits of the EAG SHOULD report this mismatch to the operator.
+        */
+       if (link_admin_group && link_ext_admin_group) {
+               link_ext_admin_group_bitmap0 =
+                       admin_group_get_offset(link_ext_admin_group, 0);
+               if (*link_admin_group != link_ext_admin_group_bitmap0)
+                       zlog_warn(
+                               "ISIS-SPF: LSP from %pPN neighbor %pPN. Admin-group 0x%08x differs from ext admin-group 0x%08x.",
+                               lsp->hdr.lsp_id, reach->id, *link_admin_group,
+                               link_ext_admin_group_bitmap0);
+       }
+
+       /*
+        * Exclude Any
+        */
+       if (!admin_group_zero(&fad->fad.admin_group_exclude_any)) {
+               ret = admin_group_match_any(&fad->fad.admin_group_exclude_any,
+                                           link_admin_group,
+                                           link_ext_admin_group);
+               if (ret)
+                       return true;
+       }
+
+       /*
+        * Include Any
+        */
+       if (!admin_group_zero(&fad->fad.admin_group_include_any)) {
+               ret = admin_group_match_any(&fad->fad.admin_group_include_any,
+                                           link_admin_group,
+                                           link_ext_admin_group);
+               if (!ret)
+                       return true;
+       }
+
+       /*
+        * Include All
+        */
+       if (!admin_group_zero(&fad->fad.admin_group_include_all)) {
+               ret = admin_group_match_all(&fad->fad.admin_group_include_all,
+                                           link_admin_group,
+                                           link_ext_admin_group);
+               if (!ret)
+                       return true;
+       }
+
+       return false;
+}
+
+#endif /* ifndef FABRICD */
diff --git a/isisd/isis_flex_algo.h b/isisd/isis_flex_algo.h
new file mode 100644 (file)
index 0000000..c475838
--- /dev/null
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * isis_flex_algo.h: IS-IS Flexible Algorithm
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#ifndef ISIS_FLEX_ALGO_H
+#define ISIS_FLEX_ALGO_H
+
+#include "flex_algo.h"
+#include "isisd/isis_constants.h"
+
+#ifndef FABRICD
+
+struct isis_flex_algo_data {
+       struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS];
+       struct isis_area *area;
+};
+
+struct isis_flex_algo_alloc_arg {
+       uint8_t algorithm;
+       struct isis_area *area;
+};
+
+void *isis_flex_algo_data_alloc(void *arg);
+void isis_flex_algo_data_free(void *data);
+
+struct isis_router_cap_fad *
+isis_flex_algo_elected(int algorithm, const struct isis_area *area);
+bool isis_flex_algo_supported(struct flex_algo *fad);
+struct isis_router_cap_fad *
+isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area);
+struct isis_router_cap_fad *
+isis_flex_algo_elected_supported_local_fad(int algorithm,
+                                          const struct isis_area *area,
+                                          struct isis_router_cap_fad **fad);
+struct isis_lsp;
+bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm);
+
+bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree,
+                                   struct isis_lsp *lsp,
+                                   struct isis_extended_reach *reach);
+
+#endif /* ifndef FABRICD */
+
+#endif /* ISIS_FLEX_ALGO_H */
index 7a25a9253553385e304d10a1a92580b8b99382a5..6f21f4cea2ccd536422cc1e43c941f05299353ae 100644 (file)
@@ -355,6 +355,7 @@ bool isis_lfa_excise_node_check(const struct isis_spftree *spftree,
 
 struct tilfa_find_pnode_prefix_sid_args {
        uint32_t sid_index;
+       int algorithm;
 };
 
 static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix,
@@ -368,15 +369,17 @@ static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix,
        if (!subtlvs || subtlvs->prefix_sids.count == 0)
                return LSP_ITER_CONTINUE;
 
-       psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head;
-
-       /* Require the node flag to be set. */
-       if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE))
-               return LSP_ITER_CONTINUE;
-
-       args->sid_index = psid->value;
-
-       return LSP_ITER_STOP;
+       for (psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; psid;
+            psid = psid->next) {
+               /* Require the node flag to be set. */
+               if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE))
+                       continue;
+               if (psid->algorithm != args->algorithm)
+                       continue;
+               args->sid_index = psid->value;
+               return LSP_ITER_STOP;
+       }
+       return LSP_ITER_CONTINUE;
 }
 
 /* Find Prefix-SID associated to a System ID. */
@@ -390,6 +393,8 @@ static uint32_t tilfa_find_pnode_prefix_sid(struct isis_spftree *spftree,
        if (!lsp)
                return UINT32_MAX;
 
+       args.algorithm = spftree->algorithm;
+
        args.sid_index = UINT32_MAX;
        isis_lsp_iterate_ip_reach(lsp, spftree->family, spftree->mtid,
                                  tilfa_find_pnode_prefix_sid_cb, &args);
@@ -1098,7 +1103,8 @@ struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree)
        spftree_reverse = isis_spftree_new(
                spftree->area, spftree->lspdb, spftree->sysid, spftree->level,
                spftree->tree_id, SPF_TYPE_REVERSE,
-               F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES);
+               F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES,
+               spftree->algorithm);
        isis_run_spf(spftree_reverse);
 
        return spftree_reverse;
@@ -1194,7 +1200,8 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area,
        /* Create post-convergence SPF tree. */
        spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid,
                                      spftree->level, spftree->tree_id,
-                                     SPF_TYPE_TI_LFA, spftree->flags);
+                                     SPF_TYPE_TI_LFA, spftree->flags,
+                                     spftree->algorithm);
        spftree_pc->lfa.old.spftree = spftree;
        spftree_pc->lfa.old.spftree_reverse = spftree_reverse;
        spftree_pc->lfa.protected_resource = *resource;
@@ -1242,7 +1249,8 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
                adj_node->lfa.spftree = isis_spftree_new(
                        spftree->area, spftree->lspdb, adj_node->sysid,
                        spftree->level, spftree->tree_id, SPF_TYPE_FORWARD,
-                       F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES);
+                       F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES,
+                       spftree->algorithm);
                isis_run_spf(adj_node->lfa.spftree);
        }
 
@@ -1466,8 +1474,8 @@ int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
                if (ldp_label == MPLS_INVALID_LABEL) {
                        if (IS_DEBUG_LFA)
                                zlog_debug(
-                                       "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s",
-                                       sysid_print(vadj->sadj->id));
+                                       "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %pSY",
+                                       vadj->sadj->id);
                        return -1;
                }
 
@@ -1722,7 +1730,8 @@ struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
        /* Create post-convergence SPF tree. */
        spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid,
                                      spftree->level, spftree->tree_id,
-                                     SPF_TYPE_RLFA, spftree->flags);
+                                     SPF_TYPE_RLFA, spftree->flags,
+                                     spftree->algorithm);
        spftree_pc->lfa.old.spftree = spftree;
        spftree_pc->lfa.old.spftree_reverse = spftree_reverse;
        spftree_pc->lfa.remote.max_metric = max_metric;
index d569f6bd5be66e67685b5d5bcb03ee4954884863..950d5f359c08ec60bcda27a3ad59d057a1cda5d9 100644 (file)
@@ -46,6 +46,7 @@
 #include "isisd/fabricd.h"
 #include "isisd/isis_tx_queue.h"
 #include "isisd/isis_nb.h"
+#include "isisd/isis_flex_algo.h"
 
 DEFINE_MTYPE_STATIC(ISISD, ISIS_LSP, "ISIS LSP");
 
@@ -193,10 +194,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
                || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) {
                if (IS_DEBUG_SNP_PACKETS) {
                        zlog_debug(
-                               "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
-                               areatag, rawlspid_print(lsp->hdr.lsp_id),
-                               lsp->hdr.seqno, lsp->hdr.checksum,
-                               lsp->hdr.rem_lifetime);
+                               "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus",
+                               areatag, lsp->hdr.lsp_id, lsp->hdr.seqno,
+                               lsp->hdr.checksum, lsp->hdr.rem_lifetime);
                        zlog_debug(
                                "ISIS-Snp (%s):         is equal to ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
                                areatag, seqno, checksum, rem_lifetime);
@@ -223,9 +223,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
                        && lsp->hdr.rem_lifetime)))) {
                if (IS_DEBUG_SNP_PACKETS) {
                        zlog_debug(
-                               "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
-                               areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
-                               checksum, rem_lifetime);
+                               "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus",
+                               areatag, lsp->hdr.lsp_id, seqno, checksum,
+                               rem_lifetime);
                        zlog_debug(
                                "ISIS-Snp (%s):       is newer than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
                                areatag, lsp->hdr.seqno, lsp->hdr.checksum,
@@ -234,9 +234,10 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
                return LSP_NEWER;
        }
        if (IS_DEBUG_SNP_PACKETS) {
-               zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus",
-                          areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
-                          checksum, rem_lifetime);
+               zlog_debug(
+                       "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus",
+                       areatag, lsp->hdr.lsp_id, seqno, checksum,
+                       rem_lifetime);
                zlog_debug(
                        "ISIS-Snp (%s):       is older than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus",
                        areatag, lsp->hdr.seqno, lsp->hdr.checksum,
@@ -554,8 +555,8 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
        if (lsp->own_lsp) {
                flog_err(
                        EC_LIB_DEVELOPMENT,
-                       "ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP",
-                       area->area_tag, rawlspid_print(lsp->hdr.lsp_id));
+                       "ISIS-Upd (%s): BUG updating LSP %pLS still marked as own LSP",
+                       area->area_tag, lsp->hdr.lsp_id);
                lsp_clear_data(lsp);
                lsp->own_lsp = 0;
        }
@@ -634,10 +635,8 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
        put_lsp_hdr(lsp, NULL, false);
 
        if (IS_DEBUG_EVENTS)
-               zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x",
-                          sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id),
-                          LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
-                          lsp->hdr.seqno);
+               zlog_debug("New LSP with ID %pLS len %d seqnum %08x", lsp_id,
+                          lsp->hdr.pdu_len, lsp->hdr.seqno);
 
        return lsp;
 }
@@ -704,7 +703,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost,
        else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost)
                snprintf(id, sizeof(id), "%.14s", cmd_hostname_get());
        else
-               memcpy(id, sysid_print(lsp_id), 15);
+               snprintf(id, sizeof(id), "%pSY", lsp_id);
 
        if (frag)
                snprintf(dest, dest_len, "%s.%02x-%02x", id,
@@ -881,6 +880,65 @@ static uint16_t lsp_refresh_time(struct isis_lsp *lsp, uint16_t rem_lifetime)
        return refresh_time;
 }
 
+static void lsp_build_internal_reach_ipv4(struct isis_lsp *lsp,
+                                         struct isis_area *area,
+                                         struct prefix_ipv4 *ipv4,
+                                         uint32_t metric)
+{
+       struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL};
+
+       if (area->oldmetric) {
+               lsp_debug(
+                       "ISIS (%s): Adding old-style IP reachability for %pFX",
+                       area->area_tag, ipv4);
+               isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric);
+       }
+
+       if (area->newmetric) {
+               lsp_debug("ISIS (%s): Adding te-style IP reachability for %pFX",
+                         area->area_tag, ipv4);
+
+               if (area->srdb.enabled)
+                       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+#ifndef FABRICD
+                               if (flex_algo_id_valid(i) &&
+                                   !isis_flex_algo_elected_supported(i, area))
+                                       continue;
+#endif /* ifndef FABRICD */
+                               pcfgs[i] =
+                                       isis_sr_cfg_prefix_find(area, ipv4, i);
+                       }
+
+               isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, false,
+                                               pcfgs);
+       }
+}
+
+static void lsp_build_internal_reach_ipv6(struct isis_lsp *lsp,
+                                         struct isis_area *area,
+                                         struct prefix_ipv6 *ipv6,
+                                         uint32_t metric)
+{
+       struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL};
+
+       lsp_debug("ISIS (%s): Adding IPv6 reachability for %pFX",
+                 area->area_tag, ipv6);
+
+       if (area->srdb.enabled)
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+#ifndef FABRICD
+                       if (flex_algo_id_valid(i) &&
+                           !isis_flex_algo_elected_supported(i, area))
+                               continue;
+#endif /* ifndef FABRICD */
+                       pcfgs[i] = isis_sr_cfg_prefix_find(area, ipv6, i);
+               }
+
+       isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), ipv6,
+                                metric, false, pcfgs);
+}
+
+
 static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp,
                                     struct isis_area *area)
 {
@@ -906,13 +964,23 @@ static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp,
                        isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4,
                                                        metric);
                if (area->newmetric) {
-                       struct sr_prefix_cfg *pcfg = NULL;
+                       struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {
+                               NULL};
 
                        if (area->srdb.enabled)
-                               pcfg = isis_sr_cfg_prefix_find(area, ipv4);
+                               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+#ifndef FABRICD
+                                       if (flex_algo_id_valid(i) &&
+                                           !isis_flex_algo_elected_supported(
+                                                   i, area))
+                                               continue;
+#endif /* ifndef FABRICD */
+                                       pcfgs[i] = isis_sr_cfg_prefix_find(
+                                               area, ipv4, i);
+                               }
 
                        isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric,
-                                                       true, pcfg);
+                                                       true, pcfgs);
                }
        }
 }
@@ -940,14 +1008,24 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
                        metric = MAX_WIDE_PATH_METRIC;
 
                if (!src_p || !src_p->prefixlen) {
-                       struct sr_prefix_cfg *pcfg = NULL;
+                       struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {
+                               NULL};
 
                        if (area->srdb.enabled)
-                               pcfg = isis_sr_cfg_prefix_find(area, p);
+                               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+#ifndef FABRICD
+                                       if (flex_algo_id_valid(i) &&
+                                           !isis_flex_algo_elected_supported(
+                                                   i, area))
+                                               continue;
+#endif /* ifndef FABRICD */
+                                       pcfgs[i] = isis_sr_cfg_prefix_find(
+                                               area, p, i);
+                               }
 
                        isis_tlvs_add_ipv6_reach(lsp->tlvs,
                                                 isis_area_ipv6_topology(area),
-                                                p, metric, true, pcfg);
+                                                p, metric, true, pcfgs);
                } else if (isis_area_ipv6_dstsrc_enabled(area)) {
                        isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
                                                        ISIS_MT_IPV6_DSTSRC,
@@ -1064,9 +1142,30 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 
        /* Add Router Capability TLV. */
        if (area->isis->router_id != 0) {
-               struct isis_router_cap cap = {};
+               struct isis_router_cap *rcap;
+#ifndef FABRICD
+               struct isis_router_cap_fad *rcap_fad;
+               struct listnode *node;
+               struct flex_algo *fa;
+#endif /* ifndef FABRICD */
+
+               rcap = isis_tlvs_init_router_capability(lsp->tlvs);
 
-               cap.router_id.s_addr = area->isis->router_id;
+               rcap->router_id.s_addr = area->isis->router_id;
+
+#ifndef FABRICD
+               /* Set flex-algo definitions */
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       if (!fa->advertise_definition)
+                               continue;
+                       lsp_debug("ISIS (%s):   Flex-Algo Definition %u",
+                                 area->area_tag, fa->algorithm);
+                       isis_tlvs_set_router_capability_fad(lsp->tlvs, fa,
+                                                           fa->algorithm,
+                                                           area->isis->sysid);
+               }
+#endif /* ifndef FABRICD */
 
                /* Add SR Sub-TLVs if SR is enabled. */
                if (area->srdb.enabled) {
@@ -1076,30 +1175,41 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
                        /* SRGB first */
                        range_size = srdb->config.srgb_upper_bound
                                     - srdb->config.srgb_lower_bound + 1;
-                       cap.srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I
-                                        | ISIS_SUBTLV_SRGB_FLAG_V;
-                       cap.srgb.range_size = range_size;
-                       cap.srgb.lower_bound = srdb->config.srgb_lower_bound;
+                       rcap->srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I |
+                                          ISIS_SUBTLV_SRGB_FLAG_V;
+                       rcap->srgb.range_size = range_size;
+                       rcap->srgb.lower_bound = srdb->config.srgb_lower_bound;
                        /* Then Algorithm */
-                       cap.algo[0] = SR_ALGORITHM_SPF;
-                       cap.algo[1] = SR_ALGORITHM_UNSET;
+                       rcap->algo[0] = SR_ALGORITHM_SPF;
+                       rcap->algo[1] = SR_ALGORITHM_UNSET;
+#ifndef FABRICD
+                       for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos,
+                                                 node, fa)) {
+                               if (fa->advertise_definition)
+                                       rcap_fad = rcap->fads[fa->algorithm];
+                               else
+                                       rcap_fad = NULL;
+
+                               if (!isis_flex_algo_elected_supported_local_fad(
+                                           fa->algorithm, area, &rcap_fad)) {
+                                       fa->state = false;
+                                       continue;
+                               }
+                               fa->state = true;
+                               lsp_debug("ISIS (%s):   SR Algorithm %u",
+                                         area->area_tag, fa->algorithm);
+                               rcap->algo[fa->algorithm] = fa->algorithm;
+                       }
+#endif /* ifndef FABRICD */
                        /* SRLB */
-                       cap.srlb.flags = 0;
+                       rcap->srlb.flags = 0;
                        range_size = srdb->config.srlb_upper_bound
                                     - srdb->config.srlb_lower_bound + 1;
-                       cap.srlb.range_size = range_size;
-                       cap.srlb.lower_bound = srdb->config.srlb_lower_bound;
+                       rcap->srlb.range_size = range_size;
+                       rcap->srlb.lower_bound = srdb->config.srlb_lower_bound;
                        /* And finally MSD */
-                       cap.msd = srdb->config.msd;
-               } else {
-                       /* Disable SR Algorithm */
-                       cap.algo[0] = SR_ALGORITHM_UNSET;
-                       cap.algo[1] = SR_ALGORITHM_UNSET;
+                       rcap->msd = srdb->config.msd;
                }
-
-               isis_tlvs_set_router_capability(lsp->tlvs, &cap);
-               lsp_debug("ISIS (%s): Adding Router Capabilities information",
-                         area->area_tag);
        }
 
        /* IPv4 address and TE router ID TLVs.
@@ -1189,31 +1299,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
                        struct listnode *ipnode;
                        struct prefix_ipv4 *ipv4;
                        for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
-                                                 ipv4)) {
-                               if (area->oldmetric) {
-                                       lsp_debug(
-                                               "ISIS (%s): Adding old-style IP reachability for %pFX",
-                                               area->area_tag, ipv4);
-                                       isis_tlvs_add_oldstyle_ip_reach(
-                                               lsp->tlvs, ipv4, metric);
-                               }
-
-                               if (area->newmetric) {
-                                       struct sr_prefix_cfg *pcfg = NULL;
-
-                                       lsp_debug(
-                                               "ISIS (%s): Adding te-style IP reachability for %pFX",
-                                               area->area_tag, ipv4);
-
-                                       if (area->srdb.enabled)
-                                               pcfg = isis_sr_cfg_prefix_find(
-                                                       area, ipv4);
-
-                                       isis_tlvs_add_extended_ip_reach(
-                                               lsp->tlvs, ipv4, metric, false,
-                                               pcfg);
-                               }
-                       }
+                                                 ipv4))
+                               lsp_build_internal_reach_ipv4(lsp, area, ipv4,
+                                                             metric);
                }
 
                if (circuit->ipv6_router && circuit->ipv6_non_link->count > 0) {
@@ -1221,22 +1309,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
                        struct prefix_ipv6 *ipv6;
 
                        for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
-                                                 ipnode, ipv6)) {
-                               struct sr_prefix_cfg *pcfg = NULL;
-
-                               lsp_debug(
-                                       "ISIS (%s): Adding IPv6 reachability for %pFX",
-                                       area->area_tag, ipv6);
-
-                               if (area->srdb.enabled)
-                                       pcfg = isis_sr_cfg_prefix_find(area,
-                                                                      ipv6);
-
-                               isis_tlvs_add_ipv6_reach(
-                                       lsp->tlvs,
-                                       isis_area_ipv6_topology(area), ipv6,
-                                       metric, false, pcfg);
-                       }
+                                                 ipnode, ipv6))
+                               lsp_build_internal_reach_ipv6(lsp, area, ipv6,
+                                                             metric);
                }
 
                switch (circuit->circ_type) {
@@ -1250,10 +1325,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
                                if (LSP_PSEUDO_ID(ne_id)) {
                                        if (area->oldmetric) {
                                                lsp_debug(
-                                                       "ISIS (%s): Adding DIS %s.%02x as old-style neighbor",
-                                                       area->area_tag,
-                                                       sysid_print(ne_id),
-                                                       LSP_PSEUDO_ID(ne_id));
+                                                       "ISIS (%s): Adding DIS %pPN as old-style neighbor",
+                                                       area->area_tag, ne_id);
                                                isis_tlvs_add_oldstyle_reach(
                                                        lsp->tlvs, ne_id,
                                                        metric);
@@ -1279,9 +1352,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
 
                                if (area->oldmetric) {
                                        lsp_debug(
-                                               "ISIS (%s): Adding old-style is reach for %s",
-                                               area->area_tag,
-                                               sysid_print(ne_id));
+                                               "ISIS (%s): Adding old-style is reach for %pSY",
+                                               area->area_tag, ne_id);
                                        isis_tlvs_add_oldstyle_reach(
                                                lsp->tlvs, ne_id, metric);
                                }
@@ -1424,12 +1496,12 @@ int lsp_generate(struct isis_area *area, int level)
                        refresh_time, &area->t_lsp_refresh[level - 1]);
 
        if (IS_DEBUG_UPDATE_PACKETS) {
-               zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
-                          area->area_tag, level,
-                          rawlspid_print(newlsp->hdr.lsp_id),
-                          newlsp->hdr.pdu_len, newlsp->hdr.seqno,
-                          newlsp->hdr.checksum, newlsp->hdr.rem_lifetime,
-                          refresh_time);
+               zlog_debug(
+                       "ISIS-Upd (%s): Building L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
+                       area->area_tag, level, newlsp->hdr.lsp_id,
+                       newlsp->hdr.pdu_len, newlsp->hdr.seqno,
+                       newlsp->hdr.checksum, newlsp->hdr.rem_lifetime,
+                       refresh_time);
        }
        sched_debug(
                "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.",
@@ -1506,8 +1578,8 @@ static int lsp_regenerate(struct isis_area *area, int level)
 
        if (IS_DEBUG_UPDATE_PACKETS) {
                zlog_debug(
-                       "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
-                       area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id),
+                       "ISIS-Upd (%s): Refreshed our L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus",
+                       area->area_tag, level, lsp->hdr.lsp_id,
                        lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
                        lsp->hdr.rem_lifetime, refresh_time);
        }
@@ -1698,9 +1770,9 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
        lsp_clear_data(lsp);
        lsp->tlvs = isis_alloc_tlvs();
        lsp_debug(
-               "ISIS (%s): Constructing pseudo LSP %s for interface %s level %d",
-               area->area_tag, rawlspid_print(lsp->hdr.lsp_id),
-               circuit->interface->name, level);
+               "ISIS (%s): Constructing pseudo LSP %pLS for interface %s level %d",
+               area->area_tag, lsp->hdr.lsp_id, circuit->interface->name,
+               level);
 
        lsp->level = level;
        /* RFC3787  section 4 SHOULD not set overload bit in pseudo LSPs */
@@ -1717,10 +1789,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
 
        if (circuit->area->oldmetric) {
                isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
-               lsp_debug(
-                       "ISIS (%s): Adding %s.%02x as old-style neighbor (self)",
-                       area->area_tag, sysid_print(ne_id),
-                       LSP_PSEUDO_ID(ne_id));
+               lsp_debug("ISIS (%s): Adding %pPN as old-style neighbor (self)",
+                         area->area_tag, ne_id);
        }
        if (circuit->area->newmetric) {
                if (area_is_mt(circuit->area))
@@ -1728,10 +1798,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
                else
                        mtid = ISIS_MT_DISABLE;
                isis_tlvs_add_extended_reach(lsp->tlvs, mtid, ne_id, 0, NULL);
-               lsp_debug(
-                       "ISIS (%s): Adding %s.%02x as te-style neighbor (self)",
-                       area->area_tag, sysid_print(ne_id),
-                       LSP_PSEUDO_ID(ne_id));
+               lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor (self)",
+                         area->area_tag, ne_id);
        }
 
        adj_list = list_new();
@@ -1740,8 +1808,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
        for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) {
                if (!(adj->level & level)) {
                        lsp_debug(
-                               "ISIS (%s): Ignoring neighbor %s, level does not intersect",
-                               area->area_tag, sysid_print(adj->sysid));
+                               "ISIS (%s): Ignoring neighbor %pSY, level does not intersect",
+                               area->area_tag, adj->sysid);
                        continue;
                }
 
@@ -1753,8 +1821,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
                    && !(level == IS_LEVEL_2
                         && adj->sys_type == ISIS_SYSTYPE_L2_IS)) {
                        lsp_debug(
-                               "ISIS (%s): Ignoring neighbor %s, level does not match",
-                               area->area_tag, sysid_print(adj->sysid));
+                               "ISIS (%s): Ignoring neighbor %pSY, level does not match",
+                               area->area_tag, adj->sysid);
                        continue;
                }
 
@@ -1762,18 +1830,16 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
                if (circuit->area->oldmetric) {
                        isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
                        lsp_debug(
-                               "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)",
-                               area->area_tag, sysid_print(ne_id),
-                               LSP_PSEUDO_ID(ne_id));
+                               "ISIS (%s): Adding %pPN as old-style neighbor (peer)",
+                               area->area_tag, ne_id);
                }
                if (circuit->area->newmetric) {
                        isis_tlvs_add_extended_reach(lsp->tlvs,
                                                     ISIS_MT_IPV4_UNICAST,
                                                     ne_id, 0, NULL);
                        lsp_debug(
-                               "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)",
-                               area->area_tag, sysid_print(ne_id),
-                               LSP_PSEUDO_ID(ne_id));
+                               "ISIS (%s): Adding %pPN as te-style neighbor (peer)",
+                               area->area_tag, ne_id);
                }
        }
        list_delete(&adj_list);
@@ -1832,10 +1898,9 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
 
        if (IS_DEBUG_UPDATE_PACKETS) {
                zlog_debug(
-                       "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
-                       circuit->area->area_tag, level,
-                       rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
-                       lsp->hdr.seqno, lsp->hdr.checksum,
+                       "ISIS-Upd (%s): Built L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
+                       circuit->area->area_tag, level, lsp->hdr.lsp_id,
+                       lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
                        lsp->hdr.rem_lifetime, refresh_time);
        }
 
@@ -1863,8 +1928,8 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
 
        if (!lsp) {
                flog_err(EC_LIB_DEVELOPMENT,
-                        "lsp_regenerate_pseudo: no l%d LSP %s found!", level,
-                        rawlspid_print(lsp_id));
+                        "lsp_regenerate_pseudo: no l%d LSP %pLS found!", level,
+                        lsp_id);
                return ISIS_ERROR;
        }
 
@@ -1887,10 +1952,9 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
 
        if (IS_DEBUG_UPDATE_PACKETS) {
                zlog_debug(
-                       "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
-                       circuit->area->area_tag, level,
-                       rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
-                       lsp->hdr.seqno, lsp->hdr.checksum,
+                       "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus",
+                       circuit->area->area_tag, level, lsp->hdr.lsp_id,
+                       lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
                        lsp->hdr.rem_lifetime, refresh_time);
        }
 
@@ -2101,10 +2165,9 @@ void lsp_tick(struct event *thread)
 
                        if (lsp->age_out == 0) {
                                zlog_debug(
-                                       "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out",
+                                       "ISIS-Upd (%s): L%u LSP %pLS seq 0x%08x aged out",
                                        area->area_tag, lsp->level,
-                                       rawlspid_print(lsp->hdr.lsp_id),
-                                       lsp->hdr.seqno);
+                                       lsp->hdr.lsp_id, lsp->hdr.seqno);
 
                                /* if we're aging out fragment 0, lsp_destroy()
                                 * below will delete all other fragments too,
@@ -2207,11 +2270,10 @@ void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit,
                const char *func, const char *file, int line)
 {
        if (IS_DEBUG_FLOODING) {
-               zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)",
-                          rawlspid_print(lsp->hdr.lsp_id),
-                          circuit ? " except on " : "",
-                          circuit ? circuit->interface->name : "",
-                          func, file, line);
+               zlog_debug("Flooding LSP %pLS%s%s (From %s %s:%d)",
+                          lsp->hdr.lsp_id, circuit ? " except on " : "",
+                          circuit ? circuit->interface->name : "", func, file,
+                          line);
        }
 
        if (!fabricd)
index 2f4e4e0bd6c5b2014c859681166aa7b09a890eb5..385cdcc35041e81b5e0abc1560713a58cd710549 100644 (file)
@@ -29,6 +29,7 @@
 #include "routemap.h"
 #include "affinitymap.h"
 
+#include "isisd/isis_affinitymap.h"
 #include "isisd/isis_constants.h"
 #include "isisd/isis_common.h"
 #include "isisd/isis_flags.h"
@@ -281,7 +282,7 @@ int main(int argc, char **argv, char **envp)
 #endif /* FABRICD */
 #ifndef FABRICD
        isis_cli_init();
-#endif /* ifdef FABRICD */
+#endif /* ifndef FABRICD */
        isis_spf_init();
        isis_redist_init();
        isis_route_map_init();
@@ -290,7 +291,9 @@ int main(int argc, char **argv, char **envp)
        lsp_init();
        mt_init();
 
-       affinity_map_init();
+#ifndef FABRICD
+       isis_affinity_map_init();
+#endif /* ifndef FABRICD */
 
        isis_zebra_init(master, instance);
        isis_bfd_init(master);
index 95d24036ec534f900abf913ca013cddc07a5c6c7..09ffa3479a62056f888498af636225aa96eed32b 100644 (file)
 #include "isisd/isis_dynhn.h"
 
 /* staticly assigned vars for printing purposes */
+static char sys_hostname[ISO_SYSID_STRLEN];
 struct in_addr new_prefix;
-/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */
-/* + place for #0 termination */
-char isonet[51];
 /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */
 char datestring[20];
 char nlpidstring[30];
 
-/*
- * This converts the isonet to its printable format
- */
-const char *isonet_print(const uint8_t *from, int len)
-{
-       int i = 0;
-       char tbuf[4];
-       isonet[0] = '\0';
-
-       if (!from)
-               return "unknown";
-
-       while (i < len) {
-               if (i & 1) {
-                       snprintf(tbuf, sizeof(tbuf), "%02x", *(from + i));
-                       strlcat(isonet, tbuf, sizeof(isonet));
-               } else {
-                       if (i == (len - 1)) { /* No dot at the end of address */
-                               snprintf(tbuf, sizeof(tbuf), "%02x",
-                                        *(from + i));
-                               strlcat(isonet, tbuf, sizeof(isonet));
-                       } else {
-                               snprintf(tbuf, sizeof(tbuf), "%02x.",
-                                        *(from + i));
-                               strlcat(isonet, tbuf, sizeof(isonet));
-                       }
-               }
-               i++;
-       }
-
-       return isonet;
-}
-
 /*
  * Returns 0 on error, length of buff on ok
  * extract dot from the dotted str, and insert all the number in a buff
@@ -307,60 +272,6 @@ const char *isis_hello_padding2string(int hello_padding_type)
        return NULL; /* not reached */
 }
 
-/*
- * Print functions - we print to static vars
- */
-const char *snpa_print(const uint8_t *from)
-{
-       return isis_format_id(from, ISIS_SYS_ID_LEN);
-}
-
-const char *sysid_print(const uint8_t *from)
-{
-       return isis_format_id(from, ISIS_SYS_ID_LEN);
-}
-
-const char *rawlspid_print(const uint8_t *from)
-{
-       return isis_format_id(from, 8);
-}
-
-#define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00")
-const char *isis_format_id(const uint8_t *id, size_t len)
-{
-#define FORMAT_BUF_COUNT 4
-       static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE];
-       static size_t cur_buf = 0;
-
-       char *rv;
-
-       cur_buf++;
-       if (cur_buf >= FORMAT_BUF_COUNT)
-               cur_buf = 0;
-
-       rv = buf_ring[cur_buf];
-
-       if (!id) {
-               snprintf(rv, FORMAT_ID_SIZE, "unknown");
-               return rv;
-       }
-
-       if (len < 6) {
-               snprintf(rv, FORMAT_ID_SIZE, "Short ID");
-               return rv;
-       }
-
-       snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1],
-                id[2], id[3], id[4], id[5]);
-
-       if (len > 6)
-               snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]);
-       if (len > 7)
-               snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]);
-
-       return rv;
-}
-
 const char *time2string(uint32_t time)
 {
        uint32_t rest;
@@ -474,7 +385,8 @@ const char *print_sys_hostname(const uint8_t *sysid)
                        return dyn->hostname;
        }
 
-       return sysid_print(sysid);
+       snprintfrr(sys_hostname, ISO_SYSID_STRLEN, "%pSY", sysid);
+       return sys_hostname;
 }
 
 /*
@@ -508,11 +420,11 @@ void zlog_dump_data(void *data, int len)
 
                /* store hex str (for left side) */
                snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
-               strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1);
+               strlcat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1);
 
                /* store char str (for right side) */
                snprintf(bytestr, sizeof(bytestr), "%c", c);
-               strncat(charstr, bytestr,
+               strlcat(charstr, bytestr,
                        sizeof(charstr) - strlen(charstr) - 1);
 
                if ((i % 16) == 0) {
@@ -523,9 +435,9 @@ void zlog_dump_data(void *data, int len)
                        charstr[0] = 0;
                } else if ((i % 8) == 0) {
                        /* half line: add whitespaces */
-                       strncat(hexstr, "  ",
+                       strlcat(hexstr, "  ",
                                sizeof(hexstr) - strlen(hexstr) - 1);
-                       strncat(charstr, " ",
+                       strlcat(charstr, " ",
                                sizeof(charstr) - strlen(charstr) - 1);
                }
                p++; /* next byte */
index 01d9abe8693346cf6e64a711894f1692dc1403a1..3a1d136b1d926e61020ef49df0fe9559a6334df1 100644 (file)
@@ -28,11 +28,6 @@ int sysid2buff(uint8_t *, const char *);
 /*
  * Printing functions
  */
-const char *isonet_print(const uint8_t *, int len);
-const char *sysid_print(const uint8_t *);
-const char *snpa_print(const uint8_t *);
-const char *rawlspid_print(const uint8_t *);
-const char *isis_format_id(const uint8_t *id, size_t len);
 const char *time2string(uint32_t);
 const char *nlpid2str(uint8_t nlpid);
 /* typedef struct nlpids nlpids; */
index fcc0f53815edd8d07a80ac708ac060c7e1d6875d..d04a24dc46838c5bc271be883f4cc6af3285f680 100644 (file)
@@ -507,8 +507,8 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
        /* Check if MT is enable for this area */
        if (!area_is_mt(area)) {
                lsp_debug(
-                       "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)",
-                       area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id));
+                       "ISIS (%s): Adding %pPN as te-style neighbor (MT disable)",
+                       area->area_tag, id);
                isis_tlvs_add_extended_reach(tlvs, ISIS_MT_DISABLE, id, metric,
                                             ext);
                return;
@@ -518,15 +518,12 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs,
        for (unsigned int i = 0; i < mt_count; i++) {
                uint16_t mtid = mt_set[i];
                if (mt_set[i] == ISIS_MT_IPV4_UNICAST) {
-                       lsp_debug(
-                               "ISIS (%s): Adding %s.%02x as te-style neighbor",
-                               area->area_tag, sysid_print(id),
-                               LSP_PSEUDO_ID(id));
+                       lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor",
+                                 area->area_tag, id);
                } else {
                        lsp_debug(
-                               "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s",
-                               area->area_tag, sysid_print(id),
-                               LSP_PSEUDO_ID(id), isis_mtid2str(mtid));
+                               "ISIS (%s): Adding %pPN as mt-style neighbor for %s",
+                               area->area_tag, id, isis_mtid2str(mtid));
                }
                isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext);
        }
index 7dc3a0eb3da6025cc425e478b903a29a26653f0f..6da8fa2d28e2a6952f0022ea23de4d8db3a090c8 100644 (file)
@@ -102,6 +102,20 @@ const struct frr_yang_module_info frr_isisd_info = {
                                .modify = isis_instance_purge_originator_modify,
                        },
                },
+               {
+                       .xpath = "/frr-isisd:isis/instance/admin-group-send-zero",
+                       .cbs = {
+                               .cli_show = cli_show_isis_admin_group_send_zero,
+                               .modify = isis_instance_admin_group_send_zero_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/asla-legacy-flag",
+                       .cbs = {
+                               .cli_show = cli_show_isis_asla_legacy_flag,
+                               .modify = isis_instance_asla_legacy_flag_modify,
+                       },
+               },
                {
                        .xpath = "/frr-isisd:isis/instance/lsp/mtu",
                        .cbs = {
@@ -558,6 +572,13 @@ const struct frr_yang_module_info frr_isisd_info = {
                                .modify = isis_instance_log_adjacency_changes_modify,
                        },
                },
+               {
+                       .xpath = "/frr-isisd:isis/instance/log-pdu-drops",
+                       .cbs = {
+                               .cli_show = cli_show_isis_log_pdu_drops,
+                               .modify = isis_instance_log_pdu_drops_modify,
+                       },
+               },
                {
                        .xpath = "/frr-isisd:isis/instance/mpls-te",
                        .cbs = {
@@ -681,6 +702,118 @@ const struct frr_yang_module_info frr_isisd_info = {
                                .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify,
                        }
                },
+               {
+                       .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid",
+                       .cbs = {
+                               .create = isis_instance_segment_routing_algorithm_prefix_sid_create,
+                               .destroy = isis_instance_segment_routing_algorithm_prefix_sid_destroy,
+                               .pre_validate = isis_instance_segment_routing_algorithm_prefix_sid_pre_validate,
+                               .apply_finish = isis_instance_segment_routing_algorithm_prefix_sid_apply_finish,
+                               .cli_show = cli_show_isis_prefix_sid_algorithm,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type",
+                       .cbs = {
+                               .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value",
+                       .cbs = {
+                               .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/last-hop-behavior",
+                       .cbs = {
+                               .modify = isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear",
+                       .cbs = {
+                               .modify = isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo",
+                       .cbs = {
+                               .cli_show = cli_show_isis_flex_algo,
+                               .cli_show_end = cli_show_isis_flex_algo_end,
+                               .create = isis_instance_flex_algo_create,
+                               .destroy = isis_instance_flex_algo_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition",
+                       .cbs = {
+                               .modify = isis_instance_flex_algo_advertise_definition_modify,
+                               .destroy = isis_instance_flex_algo_advertise_definition_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_affinity_include_all_create,
+                               .destroy = isis_instance_flex_algo_affinity_include_all_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_affinity_include_any_create,
+                               .destroy = isis_instance_flex_algo_affinity_include_any_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_affinity_exclude_any_create,
+                               .destroy = isis_instance_flex_algo_affinity_exclude_any_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_prefix_metric_create,
+                               .destroy = isis_instance_flex_algo_prefix_metric_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/metric-type",
+                       .cbs = {
+                               .modify = isis_instance_flex_algo_metric_type_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_dplane_sr_mpls_create,
+                               .destroy = isis_instance_flex_algo_dplane_sr_mpls_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_dplane_srv6_create,
+                               .destroy = isis_instance_flex_algo_dplane_srv6_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip",
+                       .cbs = {
+                               .create = isis_instance_flex_algo_dplane_ip_create,
+                               .destroy = isis_instance_flex_algo_dplane_ip_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/priority",
+                       .cbs = {
+                               .modify = isis_instance_flex_algo_priority_modify,
+                               .destroy = isis_instance_flex_algo_priority_destroy,
+                       },
+               },
                {
                        .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync",
                        .cbs = {
index 480b2ce04132927a660a08e543ce854b30408c43..13efa36d789e051bd26b5b4a2745c488c1fcb6a9 100644 (file)
@@ -29,6 +29,8 @@ int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args);
 int isis_instance_advertise_high_metrics_modify(struct nb_cb_modify_args *args);
 int isis_instance_metric_style_modify(struct nb_cb_modify_args *args);
 int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args);
+int isis_instance_admin_group_send_zero_modify(struct nb_cb_modify_args *args);
+int isis_instance_asla_legacy_flag_modify(struct nb_cb_modify_args *args);
 int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args);
 int isis_instance_advertise_passive_only_modify(struct nb_cb_modify_args *args);
 int isis_instance_lsp_refresh_interval_level_1_modify(
@@ -196,6 +198,7 @@ int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
 int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
        struct nb_cb_destroy_args *args);
 int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args);
+int isis_instance_log_pdu_drops_modify(struct nb_cb_modify_args *args);
 int isis_instance_mpls_te_create(struct nb_cb_create_args *args);
 int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args);
 int isis_instance_mpls_te_router_address_modify(struct nb_cb_modify_args *args);
@@ -248,6 +251,67 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo
        struct nb_cb_modify_args *args);
 int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify(
        struct nb_cb_modify_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_create(
+       struct nb_cb_create_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate(
+       struct nb_cb_pre_validate_args *args);
+void isis_instance_segment_routing_algorithm_prefix_sid_apply_finish(
+       struct nb_cb_apply_finish_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_create(struct nb_cb_create_args *args);
+int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_advertise_definition_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_advertise_definition_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_affinity_include_any_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_affinity_include_any_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_affinity_include_all_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_affinity_include_all_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_affinity_exclude_any_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_affinity_exclude_any_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_prefix_metric_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_prefix_metric_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_dplane_sr_mpls_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_dplane_sr_mpls_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args);
+int isis_instance_flex_algo_dplane_srv6_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args);
+int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_frr_disable_modify(struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_frr_disable_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_affinity_mapping_create(
+       struct nb_cb_create_args *args);
+int isis_instance_flex_algo_affinity_mapping_destroy(
+       struct nb_cb_destroy_args *args);
+int isis_instance_flex_algo_affinity_mapping_value_modify(
+       struct nb_cb_modify_args *args);
+int isis_instance_flex_algo_affinity_mapping_value_destroy(
+       struct nb_cb_destroy_args *args);
 int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args);
 int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args);
 int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args);
@@ -494,6 +558,12 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode,
                                bool show_defaults);
 void cli_show_isis_mpls_te(struct vty *vty, const struct lyd_node *dnode,
                           bool show_defaults);
+void cli_show_isis_admin_group_send_zero(struct vty *vty,
+                                        const struct lyd_node *dnode,
+                                        bool show_defaults);
+void cli_show_isis_asla_legacy_flag(struct vty *vty,
+                                   const struct lyd_node *dnode,
+                                   bool show_defaults);
 void cli_show_isis_mpls_te_router_addr(struct vty *vty,
                                       const struct lyd_node *dnode,
                                       bool show_defaults);
@@ -537,6 +607,9 @@ void cli_show_isis_node_msd(struct vty *vty, const struct lyd_node *dnode,
                            bool show_defaults);
 void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode,
                              bool show_defaults);
+void cli_show_isis_prefix_sid_algorithm(struct vty *vty,
+                                       const struct lyd_node *dnode,
+                                       bool show_defaults);
 void cli_show_isis_frr_lfa_priority_limit(struct vty *vty,
                                          const struct lyd_node *dnode,
                                          bool show_defaults);
@@ -609,6 +682,8 @@ void cli_show_ip_isis_priority(struct vty *vty, const struct lyd_node *dnode,
                               bool show_defaults);
 void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode,
                                 bool show_defaults);
+void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode,
+                                bool show_defaults);
 void cli_show_isis_mpls_ldp_sync(struct vty *vty, const struct lyd_node *dnode,
                                 bool show_defaults);
 void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty,
@@ -620,6 +695,9 @@ void cli_show_isis_mpls_if_ldp_sync(struct vty *vty,
 void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty,
                                             const struct lyd_node *dnode,
                                             bool show_defaults);
+void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode,
+                            bool show_defaults);
+void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode);
 
 /* Notifications. */
 void isis_notif_db_overload(const struct isis_area *area, bool overload);
index 3817465a643a3796b873051873f7a9ce30ab7708..8a111b301da7ea59d9c4cd2bb76267bb4c9a721c 100644 (file)
@@ -21,6 +21,7 @@
 #include "vrf.h"
 #include "ldp_sync.h"
 #include "link_state.h"
+#include "affinitymap.h"
 
 #include "isisd/isisd.h"
 #include "isisd/isis_nb.h"
 #include "isisd/isis_redist.h"
 #include "isisd/isis_ldp_sync.h"
 #include "isisd/isis_dr.h"
+#include "isisd/isis_sr.h"
+#include "isisd/isis_flex_algo.h"
 #include "isisd/isis_zebra.h"
 
+#define AFFINITY_INCLUDE_ANY 0
+#define AFFINITY_INCLUDE_ALL 1
+#define AFFINITY_EXCLUDE_ANY 2
+
 /*
  * XPath: /frr-isisd:isis/instance
  */
@@ -103,14 +110,14 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args)
 }
 
 struct sysid_iter {
-       struct area_addr *addr;
+       struct iso_address *addr;
        bool same;
 };
 
 static int sysid_iter_cb(const struct lyd_node *dnode, void *arg)
 {
        struct sysid_iter *iter = arg;
-       struct area_addr addr;
+       struct iso_address addr;
        const char *net;
 
        net = yang_dnode_get_string(dnode, NULL);
@@ -130,7 +137,7 @@ static int sysid_iter_cb(const struct lyd_node *dnode, void *arg)
 int isis_instance_area_address_create(struct nb_cb_create_args *args)
 {
        struct isis_area *area;
-       struct area_addr addr, *addrr = NULL, *addrp = NULL;
+       struct iso_address addr, *addrr = NULL, *addrp = NULL;
        struct listnode *node;
        struct sysid_iter iter;
        uint8_t buff[255];
@@ -161,7 +168,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
                }
                break;
        case NB_EV_PREPARE:
-               addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr));
+               addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR,
+                               sizeof(struct iso_address));
                addrr->addr_len = dotformat2buff(buff, net_title);
                memcpy(addrr->area_addr, buff, addrr->addr_len);
                args->resource->ptr = addrr;
@@ -217,7 +225,7 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
 
 int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args)
 {
-       struct area_addr addr, *addrp = NULL;
+       struct iso_address addr, *addrp = NULL;
        struct listnode *node;
        uint8_t buff[255];
        struct isis_area *area;
@@ -415,6 +423,71 @@ int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args)
        return NB_OK;
 }
 
+
+/*
+ * XPath: /frr-isisd:isis/instance/admin-group-send-zero
+ */
+int isis_instance_admin_group_send_zero_modify(struct nb_cb_modify_args *args)
+{
+       struct isis_circuit *circuit;
+       struct isis_area *area;
+       struct listnode *node;
+       struct flex_algo *fa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->admin_group_send_zero = yang_dnode_get_bool(args->dnode, NULL);
+
+       if (area->admin_group_send_zero) {
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_exclude_any);
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_include_any);
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_include_all);
+               }
+       } else {
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       admin_group_disallow_explicit_zero(
+                               &fa->admin_group_exclude_any);
+                       admin_group_disallow_explicit_zero(
+                               &fa->admin_group_include_any);
+                       admin_group_disallow_explicit_zero(
+                               &fa->admin_group_include_all);
+               }
+       }
+
+       for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
+               isis_link_params_update(circuit, circuit->interface);
+
+       lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+       return NB_OK;
+}
+
+
+/*
+ * XPath: /frr-isisd:isis/instance/asla-legacy-flag
+ */
+int isis_instance_asla_legacy_flag_modify(struct nb_cb_modify_args *args)
+{
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->asla_legacy_flag = yang_dnode_get_bool(args->dnode, NULL);
+       lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0);
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-isisd:isis/instance/lsp/mtu
  */
@@ -1829,6 +1902,23 @@ int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args)
        return NB_OK;
 }
 
+/*
+ * XPath: /frr-isisd:isis/instance/log-pdu-drops
+ */
+int isis_instance_log_pdu_drops_modify(struct nb_cb_modify_args *args)
+{
+       struct isis_area *area;
+       bool log = yang_dnode_get_bool(args->dnode, NULL);
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->log_pdu_drops = log ? 1 : 0;
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-isisd:isis/instance/mpls-te
  */
@@ -2258,7 +2348,7 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create(
        area = nb_running_get_entry(args->dnode, NULL, true);
        yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
 
-       pcfg = isis_sr_cfg_prefix_add(area, &prefix);
+       pcfg = isis_sr_cfg_prefix_add(area, &prefix, SR_ALGORITHM_SPF);
        nb_running_set_entry(args->dnode, pcfg);
 
        return NB_OK;
@@ -2448,6 +2538,833 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify(
        return NB_OK;
 }
 
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid
+ */
+int isis_instance_segment_routing_algorithm_prefix_sid_create(
+       struct nb_cb_create_args *args)
+{
+       struct isis_area *area;
+       struct prefix prefix;
+       struct sr_prefix_cfg *pcfg;
+       uint32_t algorithm;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+       algorithm = yang_dnode_get_uint32(args->dnode, "./algo");
+
+       pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm);
+       pcfg->algorithm = algorithm;
+       nb_running_set_entry(args->dnode, pcfg);
+
+       return NB_OK;
+}
+
+int isis_instance_segment_routing_algorithm_prefix_sid_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       pcfg = nb_running_unset_entry(args->dnode);
+       area = pcfg->area;
+       isis_sr_cfg_prefix_del(pcfg);
+       lsp_regenerate_schedule(area, area->is_type, 0);
+
+       return NB_OK;
+}
+
+int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate(
+       struct nb_cb_pre_validate_args *args)
+{
+       const struct lyd_node *area_dnode;
+       struct isis_area *area;
+       struct prefix prefix;
+       uint32_t srgb_lbound;
+       uint32_t srgb_ubound;
+       uint32_t srgb_range;
+       uint32_t sid;
+       enum sr_sid_value_type sid_type;
+       struct isis_prefix_sid psid = {};
+
+       yang_dnode_get_prefix(&prefix, args->dnode, "./prefix");
+       srgb_lbound = yang_dnode_get_uint32(
+               args->dnode, "../../label-blocks/srgb/lower-bound");
+       srgb_ubound = yang_dnode_get_uint32(
+               args->dnode, "../../label-blocks/srgb/upper-bound");
+       sid = yang_dnode_get_uint32(args->dnode, "./sid-value");
+       sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type");
+
+       /* Check for invalid indexes/labels. */
+       srgb_range = srgb_ubound - srgb_lbound + 1;
+       psid.value = sid;
+       switch (sid_type) {
+       case SR_SID_VALUE_TYPE_INDEX:
+               if (sid >= srgb_range) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "SID index %u falls outside local SRGB range",
+                                sid);
+                       return NB_ERR_VALIDATION;
+               }
+               break;
+       case SR_SID_VALUE_TYPE_ABSOLUTE:
+               if (!IS_MPLS_UNRESERVED_LABEL(sid)) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "Invalid absolute SID %u", sid);
+                       return NB_ERR_VALIDATION;
+               }
+               SET_FLAG(psid.flags, ISIS_PREFIX_SID_VALUE);
+               SET_FLAG(psid.flags, ISIS_PREFIX_SID_LOCAL);
+               break;
+       }
+
+       /* Check for Prefix-SID collisions. */
+       area_dnode = yang_dnode_get_parent(args->dnode, "instance");
+       area = nb_running_get_entry(area_dnode, NULL, false);
+       if (!area)
+               return NB_OK;
+
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
+                       struct isis_spftree *spftree;
+                       struct isis_vertex *vertex_psid;
+
+                       if (!(area->is_type & level))
+                               continue;
+                       spftree = area->spftree[tree][level - 1];
+                       if (!spftree)
+                               continue;
+
+                       vertex_psid =
+                               isis_spf_prefix_sid_lookup(spftree, &psid);
+                       if (vertex_psid &&
+                           !prefix_same(&vertex_psid->N.ip.p.dest, &prefix)) {
+                               snprintfrr(
+                                       args->errmsg, args->errmsg_len,
+                                       "Prefix-SID collision detected, SID %s %u is already in use by prefix %pFX (L%u)",
+                                       CHECK_FLAG(psid.flags,
+                                                  ISIS_PREFIX_SID_VALUE)
+                                               ? "label"
+                                               : "index",
+                                       psid.value, &vertex_psid->N.ip.p.dest,
+                                       level);
+                               return NB_ERR_VALIDATION;
+                       }
+               }
+       }
+
+       return NB_OK;
+}
+
+void isis_instance_segment_routing_algorithm_prefix_sid_apply_finish(
+       struct nb_cb_apply_finish_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+       struct isis_area *area;
+
+       pcfg = nb_running_get_entry(args->dnode, NULL, true);
+       area = pcfg->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type
+ */
+int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       pcfg = nb_running_get_entry(args->dnode, NULL, true);
+       pcfg->sid_type = yang_dnode_get_enum(args->dnode, NULL);
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value
+ */
+int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       pcfg = nb_running_get_entry(args->dnode, NULL, true);
+       pcfg->sid = yang_dnode_get_uint32(args->dnode, NULL);
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sid-map/algorithm-prefix-sid/last-hop-behavior
+ */
+int isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       pcfg = nb_running_get_entry(args->dnode, NULL, true);
+       pcfg->last_hop_behavior = yang_dnode_get_enum(args->dnode, NULL);
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear
+ */
+int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct sr_prefix_cfg *pcfg;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       pcfg = nb_running_get_entry(args->dnode, NULL, true);
+       pcfg->n_flag_clear = yang_dnode_get_bool(args->dnode, NULL);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo
+ */
+int isis_instance_flex_algo_create(struct nb_cb_create_args *args)
+{
+       struct isis_area *area;
+       struct flex_algo *fa;
+       bool advertise;
+       uint32_t algorithm;
+       uint32_t priority = FLEX_ALGO_PRIO_DEFAULT;
+       struct isis_flex_algo_alloc_arg arg;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo");
+       advertise = yang_dnode_exists(args->dnode, "./advertise-definition");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               area = nb_running_get_entry(args->dnode, NULL, true);
+               arg.algorithm = algorithm;
+               arg.area = area;
+               fa = flex_algo_alloc(area->flex_algos, algorithm, &arg);
+               fa->priority = priority;
+               fa->advertise_definition = advertise;
+               if (area->admin_group_send_zero) {
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_exclude_any);
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_include_any);
+                       admin_group_allow_explicit_zero(
+                               &fa->admin_group_include_all);
+               }
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args)
+{
+       struct isis_area *area;
+       uint32_t algorithm;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo");
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               flex_algo_delete(area->flex_algos, algorithm);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition
+ */
+int isis_instance_flex_algo_advertise_definition_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct isis_area *area;
+       struct flex_algo *fa;
+       bool advertise;
+       uint32_t algorithm;
+
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+       advertise = yang_dnode_exists(args->dnode, "./../advertise-definition");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               area = nb_running_get_entry(args->dnode, NULL, true);
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               fa->advertise_definition = advertise;
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+int isis_instance_flex_algo_advertise_definition_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct isis_area *area;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               fa->advertise_definition = false;
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+static int isis_instance_flex_algo_affinity_set(struct nb_cb_create_args *args,
+                                               int type)
+{
+       struct affinity_map *map;
+       struct isis_area *area;
+       struct admin_group *ag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+       const char *val;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo");
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       val = yang_dnode_get_string(args->dnode, ".");
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               map = affinity_map_get(val);
+               if (!map) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "affinity map %s isn't found", val);
+                       return NB_ERR_VALIDATION;
+               }
+               break;
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               map = affinity_map_get(val);
+               if (!map) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "affinity map %s isn't found", val);
+                       return NB_ERR_RESOURCE;
+               }
+               if (type == AFFINITY_INCLUDE_ANY)
+                       ag = &fa->admin_group_include_any;
+               else if (type == AFFINITY_INCLUDE_ALL)
+                       ag = &fa->admin_group_include_all;
+               else if (type == AFFINITY_EXCLUDE_ANY)
+                       ag = &fa->admin_group_exclude_any;
+               else
+                       break;
+
+               admin_group_set(ag, map->bit_position);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       }
+
+       return NB_OK;
+}
+
+static int
+isis_instance_flex_algo_affinity_unset(struct nb_cb_destroy_args *args,
+                                      int type)
+{
+       struct affinity_map *map;
+       struct isis_area *area;
+       struct admin_group *ag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+       const char *val;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo");
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       val = yang_dnode_get_string(args->dnode, ".");
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               map = affinity_map_get(val);
+               if (!map) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "affinity map %s isn't found", val);
+                       return NB_ERR_VALIDATION;
+               }
+               break;
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               map = affinity_map_get(val);
+               if (!map) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "affinity map %s isn't found", val);
+                       return NB_ERR_RESOURCE;
+               }
+               if (type == AFFINITY_INCLUDE_ANY)
+                       ag = &fa->admin_group_include_any;
+               else if (type == AFFINITY_INCLUDE_ALL)
+                       ag = &fa->admin_group_include_all;
+               else if (type == AFFINITY_EXCLUDE_ANY)
+                       ag = &fa->admin_group_exclude_any;
+               else
+                       break;
+
+               admin_group_unset(ag, map->bit_position);
+               if (area->admin_group_send_zero)
+                       admin_group_allow_explicit_zero(ag);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       }
+
+       return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any
+ */
+int isis_instance_flex_algo_affinity_include_any_create(
+       struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ANY);
+}
+
+int isis_instance_flex_algo_affinity_include_any_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_affinity_unset(args,
+                                                     AFFINITY_INCLUDE_ANY);
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all
+ */
+int isis_instance_flex_algo_affinity_include_all_create(
+       struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ALL);
+}
+
+int isis_instance_flex_algo_affinity_include_all_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_affinity_unset(args,
+                                                     AFFINITY_INCLUDE_ALL);
+}
+
+/*
+ * XPath:
+ * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any
+ */
+int isis_instance_flex_algo_affinity_exclude_any_create(
+       struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_affinity_set(args, AFFINITY_EXCLUDE_ANY);
+}
+
+int isis_instance_flex_algo_affinity_exclude_any_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_affinity_unset(args,
+                                                     AFFINITY_EXCLUDE_ANY);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric
+ */
+
+int isis_instance_flex_algo_prefix_metric_create(struct nb_cb_create_args *args)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               SET_FLAG(fa->flags, FAD_FLAG_M);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+int isis_instance_flex_algo_prefix_metric_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               UNSET_FLAG(fa->flags, FAD_FLAG_M);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+static int isis_instance_flex_algo_dplane_set(struct nb_cb_create_args *args,
+                                             int type)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               SET_FLAG(fa->dataplanes, type);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+               if (type == FLEX_ALGO_SRV6 || type == FLEX_ALGO_IP) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "%s Flex-algo dataplane is not yet supported.",
+                                type == FLEX_ALGO_SRV6 ? "SRv6" : "IP");
+                       return NB_ERR_VALIDATION;
+               }
+               break;
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+static int isis_instance_flex_algo_dplane_unset(struct nb_cb_destroy_args *args,
+                                               int type)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               UNSET_FLAG(fa->dataplanes, type);
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls
+ */
+
+int isis_instance_flex_algo_dplane_sr_mpls_create(
+       struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SR_MPLS);
+}
+
+int isis_instance_flex_algo_dplane_sr_mpls_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SR_MPLS);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6
+ */
+
+int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SRV6);
+}
+
+int isis_instance_flex_algo_dplane_srv6_destroy(struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SRV6);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip
+ */
+
+int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args)
+{
+       return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_IP);
+}
+
+int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args)
+{
+       return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_IP);
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/metric-type
+ */
+
+int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+       enum flex_algo_metric_type metric_type;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+       metric_type = yang_dnode_get_enum(args->dnode, NULL);
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               fa->metric_type = metric_type;
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/priority
+ */
+
+int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+       uint32_t priority;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+       priority = yang_dnode_get_uint32(args->dnode, NULL);
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               fa->priority = priority;
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
+int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args)
+{
+       struct isis_area *area;
+       const char *area_tag;
+       struct flex_algo *fa;
+       uint32_t algorithm;
+       uint32_t priority = FLEX_ALGO_PRIO_DEFAULT;
+
+       area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag");
+       area = isis_area_lookup(area_tag, VRF_DEFAULT);
+       if (!area)
+               return NB_ERR_RESOURCE;
+
+       algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo");
+       priority = yang_dnode_get_uint32(args->dnode, NULL);
+
+       switch (args->event) {
+       case NB_EV_APPLY:
+               fa = flex_algo_lookup(area->flex_algos, algorithm);
+               if (!fa) {
+                       snprintf(args->errmsg, args->errmsg_len,
+                                "flex-algo object not found");
+                       return NB_ERR_RESOURCE;
+               }
+               fa->priority = priority;
+               lsp_regenerate_schedule(area, area->is_type, 0);
+               break;
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       }
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-isisd:isis/instance/mpls/ldp-sync
  */
@@ -2570,7 +3487,8 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args)
        struct isis_circuit *circuit;
 
        if (args->event == NB_EV_VALIDATE) {
-               circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode), NULL, false);
+               circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode),
+                                                      NULL, false);
                if (circuit) {
                        snprintf(args->errmsg, args->errmsg_len,
                                 "Changing area tag is not allowed");
index 94b1c47d3efe15dce277d6f184c3d25e4300ee43..5a1e312b4dbfb4ed5ce5ade906fd10b2e6dbd59e 100644 (file)
@@ -134,6 +134,7 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
        const char *xpath = "/frr-isisd:lsp-too-large";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_area *area = circuit->area;
 
@@ -143,7 +144,8 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit,
        data = yang_data_new_uint32(xpath_arg, pdu_size);
        listnode_add(arguments, data);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        hook_call(isis_hook_lsp_too_large, circuit, pdu_size, lsp_id);
@@ -180,11 +182,13 @@ void isis_notif_corrupted_lsp(const struct isis_area *area,
        const char *xpath = "/frr-isisd:corrupted-lsp-detected";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        hook_call(isis_hook_corrupted_lsp, area);
@@ -201,11 +205,13 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area,
        const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        hook_call(isis_hook_lsp_exceed_max, area, lsp_id);
@@ -299,6 +305,7 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj,
        const char *xpath = "/frr-isisd:adjacency-state-change";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_circuit *circuit = adj->circuit;
        struct isis_area *area = circuit->area;
@@ -312,7 +319,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj,
                listnode_add(arguments, data);
        }
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath);
-       data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath);
@@ -389,13 +397,15 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit,
        const char *xpath = "/frr-isisd:lsp-received";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_area *area = circuit->area;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        notif_prepr_iface_hdr(xpath, circuit, arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
        data = yang_data_new_uint32(xpath_arg, seqno);
@@ -419,11 +429,13 @@ void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id,
        const char *xpath = "/frr-isisd:lsp-generation";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath);
        data = yang_data_new_uint32(xpath_arg, seqno);
@@ -503,13 +515,15 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit,
        const char *xpath = "/frr-isisd:lsp-error-detected";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_area *area = circuit->area;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        notif_prepr_iface_hdr(xpath, circuit, arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath);
        data = yang_data_new_binary(xpath_arg, raw_pdu, raw_pdu_len);
@@ -530,13 +544,15 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit,
        const char *xpath = "/frr-isisd:sequence-number-skipped";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_area *area = circuit->area;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        notif_prepr_iface_hdr(xpath, circuit, arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        hook_call(isis_hook_seqno_skipped, circuit, lsp_id);
@@ -553,13 +569,15 @@ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit,
        const char *xpath = "/frr-isisd:own-lsp-purge";
        struct list *arguments = yang_data_list_new();
        char xpath_arg[XPATH_MAXLEN];
+       char xpath_value[ISO_SYSID_STRLEN];
        struct yang_data *data;
        struct isis_area *area = circuit->area;
 
        notif_prep_instance_hdr(xpath, area, "default", arguments);
        notif_prepr_iface_hdr(xpath, circuit, arguments);
        snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath);
-       data = yang_data_new_string(xpath_arg, rawlspid_print(lsp_id));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id);
+       data = yang_data_new_string(xpath_arg, xpath_value);
        listnode_add(arguments, data);
 
        hook_call(isis_hook_own_lsp_purge, circuit, lsp_id);
index 13fdddf555c92f1f9dff2439a51b8f8da739057f..b7c33ed27b8b7b1b89fd11aec1d8f9af32a25beb 100644 (file)
@@ -132,8 +132,11 @@ lib_interface_state_isis_adjacencies_adjacency_neighbor_sysid_get_elem(
        struct nb_cb_get_elem_args *args)
 {
        const struct isis_adjacency *adj = args->list_entry;
+       char xpath_value[ISO_SYSID_STRLEN];
 
-       return yang_data_new_string(args->xpath, sysid_print(adj->sysid));
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid);
+
+       return yang_data_new_string(args->xpath, xpath_value);
 }
 
 /*
@@ -158,8 +161,11 @@ lib_interface_state_isis_adjacencies_adjacency_neighbor_snpa_get_elem(
        struct nb_cb_get_elem_args *args)
 {
        const struct isis_adjacency *adj = args->list_entry;
+       char xpath_value[ISO_SYSID_STRLEN];
+
+       snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->snpa);
 
-       return yang_data_new_string(args->xpath, snpa_print(adj->snpa));
+       return yang_data_new_string(args->xpath, xpath_value);
 }
 
 /*
index dd442840d045912f25ef27d6149b5b7c3ad22448..0cd43a7abc9e5cfef40c5878c6ed33e93f1e7972 100644 (file)
@@ -514,9 +514,9 @@ static int process_lan_hello(struct iih_info *iih)
 
        if (IS_DEBUG_ADJ_PACKETS) {
                zlog_debug(
-                       "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, cirID %u, length %zd",
-                       iih->circuit->area->area_tag, iih->level,
-                       snpa_print(iih->ssnpa), iih->circuit->interface->name,
+                       "ISIS-Adj (%s): Rcvd L%d LAN IIH from %pSY on %s, cirType %s, cirID %u, length %zd",
+                       iih->circuit->area->area_tag, iih->level, iih->ssnpa,
+                       iih->circuit->interface->name,
                        circuit_t2string(iih->circuit->is_type),
                        iih->circuit->circuit_id,
                        stream_get_endp(iih->circuit->rcv_stream));
@@ -862,31 +862,32 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
 
 #ifndef FABRICD
        /* send northbound notification */
+       char buf[ISO_SYSID_STRLEN];
+
+       snprintfrr(buf, ISO_SYSID_STRLEN, "%pSY", hdr.lsp_id);
        isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL),
-                               sysid_print(hdr.lsp_id));
+                               buf);
 #endif /* ifndef FABRICD */
 
        if (pdu_len_validate(hdr.pdu_len, circuit)) {
-               zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu",
-                          circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                          hdr.pdu_len);
+               zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP length %hu",
+                          circuit->area->area_tag, hdr.lsp_id, hdr.pdu_len);
                return ISIS_WARNING;
        }
 
        if (IS_DEBUG_UPDATE_PACKETS) {
-               zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s",
-                          circuit->area->area_tag, level,
-                          rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum,
-                          hdr.rem_lifetime, hdr.pdu_len,
-                          circuit->interface->name);
+               zlog_debug(
+                       "ISIS-Upd (%s): Rcvd L%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s",
+                       circuit->area->area_tag, level, hdr.lsp_id, hdr.seqno,
+                       hdr.checksum, hdr.rem_lifetime, hdr.pdu_len,
+                       circuit->interface->name);
        }
 
        /* lsp is_type check */
        if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) {
-               zlog_debug(
-                       "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%x",
-                       circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                       hdr.lsp_bits & IS_LEVEL_1_AND_2);
+               zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP is type 0x%x",
+                          circuit->area->area_tag, hdr.lsp_id,
+                          hdr.lsp_bits & IS_LEVEL_1_AND_2);
                /* continue as per RFC1122 Be liberal in what you accept, and
                 * conservative in what you send */
        }
@@ -896,27 +897,25 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
        if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12,
                            hdr.pdu_len - 12, hdr.checksum, 12)) {
                zlog_debug(
-                       "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx",
-                       circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                       hdr.checksum);
+                       "ISIS-Upd (%s): LSP %pLS invalid LSP checksum 0x%04hx",
+                       circuit->area->area_tag, hdr.lsp_id, hdr.checksum);
                return ISIS_WARNING;
        }
 
        /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
        if (circuit->ext_domain) {
                zlog_debug(
-                       "ISIS-Upd (%s): LSP %s received at level %d over circuit with externalDomain = true",
-                       circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                       level);
+                       "ISIS-Upd (%s): LSP %pLS received at level %d over circuit with externalDomain = true",
+                       circuit->area->area_tag, hdr.lsp_id, level);
                return ISIS_WARNING;
        }
 
        /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
        if (!(circuit->is_type & level)) {
                zlog_debug(
-                       "ISIS-Upd (%s): LSP %s received at level %d over circuit of type %s",
-                       circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                       level, circuit_t2string(circuit->is_type));
+                       "ISIS-Upd (%s): LSP %pLS received at level %d over circuit of type %s",
+                       circuit->area->area_tag, hdr.lsp_id, level,
+                       circuit_t2string(circuit->is_type));
                return ISIS_WARNING;
        }
 
@@ -1016,11 +1015,11 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
        if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
                if (!isis_adj_lookup_snpa(ssnpa,
                                          circuit->u.bc.adjdb[level - 1])) {
-                       zlog_debug("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
-                                  circuit->area->area_tag,
-                                  rawlspid_print(hdr.lsp_id), hdr.seqno,
-                                  hdr.checksum, hdr.rem_lifetime,
-                                  circuit->interface->name);
+                       zlog_debug(
+                               "(%s): DS ======= LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
+                               circuit->area->area_tag, hdr.lsp_id, hdr.seqno,
+                               hdr.checksum, hdr.rem_lifetime,
+                               circuit->interface->name);
                        goto out; /* Silently discard */
                }
        }
@@ -1057,9 +1056,9 @@ dontcheckadj:
        if (lsp && (lsp->hdr.seqno == hdr.seqno)
            && (lsp->hdr.checksum != hdr.checksum)
            && hdr.rem_lifetime) {
-               zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
-                         circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
-                         hdr.seqno);
+               zlog_warn(
+                       "ISIS-Upd (%s): LSP %pLS seq 0x%08x with confused checksum received.",
+                       circuit->area->area_tag, hdr.lsp_id, hdr.seqno);
                hdr.rem_lifetime = 0;
                lsp_confusion = true;
        } else
@@ -1153,10 +1152,9 @@ dontcheckadj:
                                }
                                if (IS_DEBUG_UPDATE_PACKETS)
                                        zlog_debug(
-                                               "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x",
+                                               "ISIS-Upd (%s): (1) re-originating LSP %pLS new seq 0x%08x",
                                                circuit->area->area_tag,
-                                               rawlspid_print(hdr.lsp_id),
-                                               lsp->hdr.seqno);
+                                               hdr.lsp_id, lsp->hdr.seqno);
                        } else {
                                /* our own LSP with 0 remaining life time */
 #ifndef FABRICD
@@ -1194,9 +1192,8 @@ dontcheckadj:
 #endif /* ifndef FABRICD */
                        if (IS_DEBUG_UPDATE_PACKETS) {
                                zlog_debug(
-                                       "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x",
-                                       circuit->area->area_tag,
-                                       rawlspid_print(hdr.lsp_id),
+                                       "ISIS-Upd (%s): (2) re-originating LSP %pLS new seq 0x%08x",
+                                       circuit->area->area_tag, hdr.lsp_id,
                                        lsp->hdr.seqno);
                        }
                        lsp_flood(lsp, NULL);
@@ -1361,9 +1358,9 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
        if (!is_csnp && (circuit->circ_type == CIRCUIT_T_BROADCAST)
            && !circuit->u.bc.is_dr[level - 1]) {
                zlog_debug(
-                       "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, skipping: we are not the DIS",
-                       circuit->area->area_tag, level, typechar,
-                       snpa_print(ssnpa), circuit->interface->name);
+                       "ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s, skipping: we are not the DIS",
+                       circuit->area->area_tag, level, typechar, ssnpa,
+                       circuit->interface->name);
 
                return ISIS_OK;
        }
@@ -1452,16 +1449,16 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
 
        /* debug isis snp-packets */
        if (IS_DEBUG_SNP_PACKETS) {
-               zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
-                          circuit->area->area_tag, level, typechar,
-                          snpa_print(ssnpa), circuit->interface->name);
+               zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s",
+                          circuit->area->area_tag, level, typechar, ssnpa,
+                          circuit->interface->name);
                for (struct isis_lsp_entry *entry = entry_head; entry;
                     entry = entry->next) {
                        zlog_debug(
-                               "ISIS-Snp (%s):         %cSNP entry %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus",
-                               circuit->area->area_tag, typechar,
-                               rawlspid_print(entry->id), entry->seqno,
-                               entry->checksum, entry->rem_lifetime);
+                               "ISIS-Snp (%s):         %cSNP entry %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus",
+                               circuit->area->area_tag, typechar, entry->id,
+                               entry->seqno, entry->checksum,
+                               entry->rem_lifetime);
                }
        }
 
@@ -1654,12 +1651,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        if (idrp == ISO9542_ESIS) {
                flog_err(EC_LIB_DEVELOPMENT,
                         "No support for ES-IS packet IDRP=%hhx", idrp);
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
        if (idrp != ISO10589_ISIS) {
                flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx",
                         idrp);
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
@@ -1670,6 +1669,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                isis_notif_version_skew(circuit, version1, raw_pdu,
                                        sizeof(raw_pdu));
 #endif /* ifndef FABRICD */
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_WARNING;
        }
 
@@ -1693,12 +1693,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                isis_notif_id_len_mismatch(circuit, id_len, raw_pdu,
                                           sizeof(raw_pdu));
 #endif /* ifndef FABRICD */
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
        uint8_t expected_length;
        if (pdu_size(pdu_type, &expected_length)) {
                zlog_warn("Unsupported ISIS PDU %hhu", pdu_type);
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_WARNING;
        }
 
@@ -1706,6 +1708,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                flog_err(EC_ISIS_PACKET,
                         "Expected fixed header length = %hhu but got %hhu",
                         expected_length, length);
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
@@ -1713,6 +1716,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                flog_err(
                        EC_ISIS_PACKET,
                        "PDU is too short to contain fixed header of given PDU type.");
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
@@ -1723,12 +1727,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                isis_notif_version_skew(circuit, version2, raw_pdu,
                                        sizeof(raw_pdu));
 #endif /* ifndef FABRICD */
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_WARNING;
        }
 
        if (circuit->is_passive) {
                zlog_warn("Received ISIS PDU on passive circuit %s",
                          circuit->interface->name);
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_WARNING;
        }
 
@@ -1747,6 +1753,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                isis_notif_max_area_addr_mismatch(circuit, max_area_addrs,
                                                  raw_pdu, sizeof(raw_pdu));
 #endif /* ifndef FABRICD */
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
@@ -1754,17 +1761,22 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
        case L1_LAN_HELLO:
        case L2_LAN_HELLO:
        case P2P_HELLO:
-               if (fabricd && pdu_type != P2P_HELLO)
+               if (fabricd && pdu_type != P2P_HELLO) {
+                       pdu_counter_count_drop(circuit->area, pdu_type);
                        return ISIS_ERROR;
+               }
+
                retval = process_hello(pdu_type, circuit, ssnpa);
                break;
        case L1_LINK_STATE:
        case L2_LINK_STATE:
        case FS_LINK_STATE:
-               if (fabricd
-                   && pdu_type != L2_LINK_STATE
-                   && pdu_type != FS_LINK_STATE)
+               if (fabricd && pdu_type != L2_LINK_STATE &&
+                   pdu_type != FS_LINK_STATE) {
+                       pdu_counter_count_drop(circuit->area, pdu_type);
                        return ISIS_ERROR;
+               }
+
                retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs);
                break;
        case L1_COMPLETE_SEQ_NUM:
@@ -1774,9 +1786,13 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
                retval = process_snp(pdu_type, circuit, ssnpa);
                break;
        default:
+               pdu_counter_count_drop(circuit->area, pdu_type);
                return ISIS_ERROR;
        }
 
+       if (retval != ISIS_OK)
+               pdu_counter_count_drop(circuit->area, pdu_type);
+
        return retval;
 }
 
@@ -2460,11 +2476,11 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
        if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
                flog_err(
                        EC_ISIS_PACKET,
-                       "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.",
-                       circuit->area->area_tag, lsp->level,
-                       rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
-                       lsp->hdr.checksum, lsp->hdr.rem_lifetime,
-                       circuit->interface->name, stream_get_endp(lsp->pdu),
+                       "ISIS-Upd (%s): Can't send L%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.",
+                       circuit->area->area_tag, lsp->level, lsp->hdr.lsp_id,
+                       lsp->hdr.seqno, lsp->hdr.checksum,
+                       lsp->hdr.rem_lifetime, circuit->interface->name,
+                       stream_get_endp(lsp->pdu),
                        stream_get_size(circuit->snd_stream));
 #ifndef FABRICD
                /* send a northbound notification */
@@ -2488,14 +2504,14 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
        }
 
        if (IS_DEBUG_UPDATE_PACKETS) {
-               zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
-                          circuit->area->area_tag,
-                          (tx_type == TX_LSP_CIRCUIT_SCOPED)
-                               ? "Circuit scoped " : "",
-                          lsp->level,
-                          rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
-                          lsp->hdr.checksum, lsp->hdr.rem_lifetime,
-                          circuit->interface->name);
+               zlog_debug(
+                       "ISIS-Upd (%s): Sending %sL%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
+                       circuit->area->area_tag,
+                       (tx_type == TX_LSP_CIRCUIT_SCOPED) ? "Circuit scoped "
+                                                          : "",
+                       lsp->level, lsp->hdr.lsp_id, lsp->hdr.seqno,
+                       lsp->hdr.checksum, lsp->hdr.rem_lifetime,
+                       circuit->interface->name);
                if (IS_DEBUG_PACKET_DUMP)
                        zlog_dump_data(STREAM_DATA(circuit->snd_stream),
                                       stream_get_endp(circuit->snd_stream));
@@ -2533,3 +2549,37 @@ out:
                isis_tx_queue_del(circuit->tx_queue, lsp);
        }
 }
+
+void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type)
+{
+       uint64_t total_drops = 0;
+
+       for (int i = 0; i < PDU_COUNTER_SIZE; i++) {
+               if (!area->pdu_drop_counters[i])
+                       continue;
+               total_drops += area->pdu_drop_counters[i];
+       }
+
+       zlog_info("PDU drop detected of type: %s. %" PRIu64
+                 " Total Drops; %" PRIu64 " L1 IIH drops;  %" PRIu64
+                 " L2 IIH drops; %" PRIu64 " P2P IIH drops; %" PRIu64
+                 " L1 LSP drops; %" PRIu64 " L2 LSP drops; %" PRIu64
+                 " FS LSP drops; %" PRIu64 " L1 CSNP drops; %" PRIu64
+                 " L2 CSNP drops; %" PRIu64 " L1 PSNP drops; %" PRIu64
+                 " L2 PSNP drops.",
+                 pdu_type, total_drops,
+                 pdu_counter_get_count(area->pdu_drop_counters, L1_LAN_HELLO),
+                 pdu_counter_get_count(area->pdu_drop_counters, L2_LAN_HELLO),
+                 pdu_counter_get_count(area->pdu_drop_counters, P2P_HELLO),
+                 pdu_counter_get_count(area->pdu_drop_counters, L1_LINK_STATE),
+                 pdu_counter_get_count(area->pdu_drop_counters, L2_LINK_STATE),
+                 pdu_counter_get_count(area->pdu_drop_counters, FS_LINK_STATE),
+                 pdu_counter_get_count(area->pdu_drop_counters,
+                                       L1_COMPLETE_SEQ_NUM),
+                 pdu_counter_get_count(area->pdu_drop_counters,
+                                       L2_COMPLETE_SEQ_NUM),
+                 pdu_counter_get_count(area->pdu_drop_counters,
+                                       L1_PARTIAL_SEQ_NUM),
+                 pdu_counter_get_count(area->pdu_drop_counters,
+                                       L2_PARTIAL_SEQ_NUM));
+}
index ccd89a70f1b0676c74b761bfa7cfd1300ed590d7..5303c61d38a273ea39bfadcb5ef000dce5ea3b7d 100644 (file)
@@ -206,4 +206,6 @@ void send_lsp(struct isis_circuit *circuit,
 void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream);
 int send_hello(struct isis_circuit *circuit, int level);
 int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa);
+void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type);
+
 #endif /* _ZEBRA_ISIS_PDU_H */
index 9d07b5e5989d3017296a93b38a59ae357f6683b0..a3605a32a16234db97ea524d474844565379a6f3 100644 (file)
@@ -8,10 +8,10 @@
 
 #include "vty.h"
 
-#include "isisd/isis_pdu_counter.h"
 #include "isisd/isisd.h"
 #include "isisd/isis_circuit.h"
 #include "isisd/isis_pdu.h"
+#include "isisd/isis_pdu_counter.h"
 
 static int pdu_type_to_counter_index(uint8_t pdu_type)
 {
@@ -91,3 +91,23 @@ void pdu_counter_print(struct vty *vty, const char *prefix,
                        pdu_counter_index_to_name(i), counter[i]);
        }
 }
+
+void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type)
+{
+       pdu_counter_count(area->pdu_drop_counters, pdu_type);
+
+       if (area->log_pdu_drops) {
+               isis_log_pdu_drops(
+                       area, pdu_counter_index_to_name(
+                                     pdu_type_to_counter_index(pdu_type)));
+       }
+}
+
+uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type)
+{
+       int index = pdu_type_to_counter_index(pdu_type);
+
+       if (index < 0)
+               return -1;
+       return counter[index];
+}
index c53c47368f46af0224ec346df9331e7e40474612..5c35b4fb51daf27cc7b0cd072f169b1bf02675cb 100644 (file)
@@ -24,5 +24,7 @@ typedef uint64_t pdu_counter_t[PDU_COUNTER_SIZE];
 void pdu_counter_print(struct vty *vty, const char *prefix,
                       pdu_counter_t counter);
 void pdu_counter_count(pdu_counter_t counter, uint8_t pdu_type);
+void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type);
+uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type);
 
 #endif
index 3b653194bf3d7a3b029a62049aeaaaa030e0cd25..be92dcc22ef6ca5694775f34165238564855c038 100644 (file)
 #include "isis_spf_private.h"
 #include "isis_route.h"
 #include "isis_zebra.h"
+#include "isis_flex_algo.h"
 
 DEFINE_MTYPE_STATIC(ISISD, ISIS_NEXTHOP,    "ISIS nexthop");
 DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_INFO, "ISIS route info");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_TABLE_INFO, "ISIS route table info");
+
 
 DEFINE_HOOK(isis_route_update_hook,
            (struct isis_area * area, struct prefix *prefix,
@@ -51,8 +54,25 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,
                              struct prefix_ipv6 *src_p,
                              struct isis_route_info *route_info);
 
-static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip,
-                                               ifindex_t ifindex)
+static struct mpls_label_stack *
+label_stack_dup(const struct mpls_label_stack *const orig)
+{
+       struct mpls_label_stack *copy;
+       int array_size;
+
+       if (orig == NULL)
+               return NULL;
+
+       array_size = orig->num_labels * sizeof(mpls_label_t);
+       copy = XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS,
+                      sizeof(struct mpls_label_stack) + array_size);
+       copy->num_labels = orig->num_labels;
+       memcpy(copy->label, orig->label, array_size);
+       return copy;
+}
+
+static struct isis_nexthop *
+isis_nexthop_create(int family, const union g_addr *const ip, ifindex_t ifindex)
 {
        struct isis_nexthop *nexthop;
 
@@ -65,12 +85,40 @@ static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip,
        return nexthop;
 }
 
+static struct isis_nexthop *
+isis_nexthop_dup(const struct isis_nexthop *const orig)
+{
+       struct isis_nexthop *nexthop;
+
+       nexthop = isis_nexthop_create(orig->family, &orig->ip, orig->ifindex);
+       memcpy(nexthop->sysid, orig->sysid, ISIS_SYS_ID_LEN);
+       nexthop->sr = orig->sr;
+       nexthop->label_stack = label_stack_dup(orig->label_stack);
+
+       return nexthop;
+}
+
 void isis_nexthop_delete(struct isis_nexthop *nexthop)
 {
        XFREE(MTYPE_ISIS_NEXTHOP_LABELS, nexthop->label_stack);
        XFREE(MTYPE_ISIS_NEXTHOP, nexthop);
 }
 
+static struct list *isis_nexthop_list_dup(const struct list *orig)
+{
+       struct list *copy;
+       struct listnode *node;
+       struct isis_nexthop *nh;
+       struct isis_nexthop *nhcopy;
+
+       copy = list_new();
+       for (ALL_LIST_ELEMENTS_RO(orig, node, nh)) {
+               nhcopy = isis_nexthop_dup(nh);
+               listnode_add(copy, nhcopy);
+       }
+       return copy;
+}
+
 static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family,
                                          union g_addr *ip, ifindex_t ifindex)
 {
@@ -238,13 +286,28 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
 
        rinfo->cost = cost;
        rinfo->depth = depth;
-       rinfo->sr = *sr;
+       rinfo->sr_algo[sr->algorithm] = *sr;
+       rinfo->sr_algo[sr->algorithm].nexthops = rinfo->nexthops;
+       rinfo->sr_algo[sr->algorithm].nexthops_backup =
+               rinfo->backup ? rinfo->backup->nexthops : NULL;
 
        return rinfo;
 }
 
 static void isis_route_info_delete(struct isis_route_info *route_info)
 {
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               if (!route_info->sr_algo[i].present)
+                       continue;
+
+               if (route_info->sr_algo[i].nexthops == route_info->nexthops)
+                       continue;
+
+               route_info->sr_algo[i].nexthops->del =
+                       (void (*)(void *))isis_nexthop_delete;
+               list_delete(&route_info->sr_algo[i].nexthops);
+       }
+
        if (route_info->nexthops) {
                route_info->nexthops->del =
                        (void (*)(void *))isis_nexthop_delete;
@@ -260,6 +323,27 @@ void isis_route_node_cleanup(struct route_table *table, struct route_node *node)
                isis_route_info_delete(node->info);
 }
 
+struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm)
+{
+       struct isis_route_table_info *info;
+
+       info = XCALLOC(MTYPE_ISIS_ROUTE_TABLE_INFO, sizeof(*info));
+       info->algorithm = algorithm;
+       return info;
+}
+
+void isis_route_table_info_free(void *info)
+{
+       XFREE(MTYPE_ISIS_ROUTE_TABLE_INFO, info);
+}
+
+uint8_t isis_route_table_algorithm(const struct route_table *table)
+{
+       const struct isis_route_table_info *info = table->info;
+
+       return info ? info->algorithm : 0;
+}
+
 static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new,
                                   struct isis_sr_psid_info *old)
 {
@@ -273,6 +357,9 @@ static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new,
            || new->sid.value != old->sid.value)
                return false;
 
+       if (new->sid.algorithm != old->sid.algorithm)
+               return false;
+
        return true;
 }
 
@@ -313,10 +400,22 @@ static int isis_route_info_same(struct isis_route_info *new,
                return 0;
        }
 
-       if (!isis_sr_psid_info_same(&new->sr, &old->sr)) {
-               if (buf)
-                       snprintf(buf, buf_size, "SR input label");
-               return 0;
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               struct isis_sr_psid_info new_sr_algo;
+               struct isis_sr_psid_info old_sr_algo;
+
+               new_sr_algo = new->sr_algo[i];
+               old_sr_algo = old->sr_algo[i];
+
+               if (!isis_sr_psid_info_same(&new_sr_algo, &old_sr_algo)) {
+                       if (buf)
+                               snprintf(
+                                       buf, buf_size,
+                                       "SR input label algo-%u (old: %s, new: %s)",
+                                       i, old_sr_algo.present ? "yes" : "no",
+                                       new_sr_algo.present ? "yes" : "no");
+                       return 0;
+               }
        }
 
        if (new->nexthops->count != old->nexthops->count) {
@@ -405,7 +504,9 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
                                zlog_debug(
                                        "ISIS-Rte (%s): route changed: %pFX, change: %s",
                                        area->area_tag, prefix, change_buf);
-                       rinfo_new->sr_previous = rinfo_old->sr;
+                       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                               rinfo_new->sr_algo_previous[i] =
+                                       rinfo_old->sr_algo[i];
                        isis_route_info_delete(rinfo_old);
                        route_info = rinfo_new;
                        UNSET_FLAG(route_info->flag,
@@ -461,11 +562,42 @@ static void isis_route_remove_previous_sid(struct isis_area *area,
         * Explicitly uninstall previous Prefix-SID label if it has
         * changed or was removed.
         */
-       if (route_info->sr_previous.present &&
-           (!route_info->sr.present ||
-            route_info->sr_previous.label != route_info->sr.label))
-               isis_zebra_prefix_sid_uninstall(area, prefix, route_info,
-                                               &route_info->sr_previous);
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               if (route_info->sr_algo_previous[i].present &&
+                   (!route_info->sr_algo[i].present ||
+                    route_info->sr_algo_previous[i].label !=
+                            route_info->sr_algo[i].label))
+                       isis_zebra_prefix_sid_uninstall(
+                               area, prefix, route_info,
+                               &route_info->sr_algo_previous[i]);
+       }
+}
+
+static void set_merge_route_info_sr_algo(struct isis_route_info *mrinfo,
+                                        struct isis_route_info *rinfo)
+{
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               if (rinfo->sr_algo[i].present) {
+                       assert(i == rinfo->sr_algo[i].algorithm);
+                       assert(rinfo->nexthops);
+                       assert(rinfo->backup ? rinfo->backup->nexthops != NULL
+                                            : true);
+
+                       if (mrinfo->sr_algo[i].nexthops != NULL &&
+                           mrinfo->sr_algo[i].nexthops != mrinfo->nexthops) {
+                               mrinfo->sr_algo[i].nexthops->del =
+                                       (void (*)(void *))isis_nexthop_delete;
+                               list_delete(&mrinfo->sr_algo[i].nexthops);
+                       }
+
+                       mrinfo->sr_algo[i] = rinfo->sr_algo[i];
+                       mrinfo->sr_algo[i].nexthops = isis_nexthop_list_dup(
+                               rinfo->sr_algo[i].nexthops);
+               }
+       }
+
+       UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+       UNSET_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
 }
 
 static void isis_route_update(struct isis_area *area, struct prefix *prefix,
@@ -484,19 +616,35 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,
                /* Install route. */
                isis_zebra_route_add_route(area->isis, prefix, src_p,
                                           route_info);
-               /* Install/reinstall Prefix-SID label. */
-               if (route_info->sr.present)
-                       isis_zebra_prefix_sid_install(area, prefix, route_info,
-                                                     &route_info->sr);
+
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+                       struct isis_sr_psid_info sr_algo;
+
+                       sr_algo = route_info->sr_algo[i];
+
+                       /*
+                        * Install/reinstall Prefix-SID label.
+                        */
+                       if (sr_algo.present)
+                               isis_zebra_prefix_sid_install(area, prefix,
+                                                             &sr_algo);
+
+                       hook_call(isis_route_update_hook, area, prefix,
+                                 route_info);
+               }
+
                hook_call(isis_route_update_hook, area, prefix, route_info);
 
                SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
        } else {
                /* Uninstall Prefix-SID label. */
-               if (route_info->sr.present)
-                       isis_zebra_prefix_sid_uninstall(
-                               area, prefix, route_info, &route_info->sr);
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                       if (route_info->sr_algo[i].present)
+                               isis_zebra_prefix_sid_uninstall(
+                                       area, prefix, route_info,
+                                       &route_info->sr_algo[i]);
+
                /* Uninstall route. */
                isis_zebra_route_del_route(area->isis, prefix, src_p,
                                           route_info);
@@ -516,6 +664,7 @@ static void _isis_route_verify_table(struct isis_area *area,
 #ifdef EXTREME_DEBUG
        char buff[SRCDEST2STR_BUFFER];
 #endif /* EXTREME_DEBUG */
+       uint8_t algorithm = isis_route_table_algorithm(table);
 
        for (rnode = route_top(table); rnode;
             rnode = srcdest_route_next(rnode)) {
@@ -538,10 +687,14 @@ static void _isis_route_verify_table(struct isis_area *area,
                                                         src_p);
                        if (rnode_bck) {
                                rinfo->backup = rnode_bck->info;
+                               rinfo->sr_algo[algorithm].nexthops_backup =
+                                       rinfo->backup->nexthops;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        } else if (rinfo->backup) {
                                rinfo->backup = NULL;
+                               rinfo->sr_algo[algorithm].nexthops_backup =
+                                       NULL;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        }
@@ -573,7 +726,7 @@ static void _isis_route_verify_table(struct isis_area *area,
                if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE))
                        continue;
 
-               /* Area is either L1 or L2 => we use level route tables
+               /* In case the verify is not for a merge, we use a single table
                 * directly for
                 * validating => no problems with deleting routes. */
                if (!tables) {
@@ -581,13 +734,12 @@ static void _isis_route_verify_table(struct isis_area *area,
                        continue;
                }
 
-               /* If area is L1L2, we work with merge table and
-                * therefore must
-                * delete node from level tables as well before deleting
+               /* If we work on a merged table,
+                * therefore we must
+                * delete node from each table as well before deleting
                 * route info. */
-               for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
-                       drnode = srcdest_rnode_lookup(tables[level - 1],
-                                                     dst_p, src_p);
+               for (int i = 0; tables[i]; i++) {
+                       drnode = srcdest_rnode_lookup(tables[i], dst_p, src_p);
                        if (!drnode)
                                continue;
 
@@ -604,10 +756,36 @@ static void _isis_route_verify_table(struct isis_area *area,
        }
 }
 
+static void _isis_route_verify_merge(struct isis_area *area,
+                                    struct route_table **tables,
+                                    struct route_table **tables_backup,
+                                    int tree);
+
 void isis_route_verify_table(struct isis_area *area, struct route_table *table,
-                            struct route_table *table_backup)
+                            struct route_table *table_backup, int tree)
 {
-       _isis_route_verify_table(area, table, table_backup, NULL);
+       struct route_table *tables[SR_ALGORITHM_COUNT] = {table};
+       struct route_table *tables_backup[SR_ALGORITHM_COUNT] = {table_backup};
+#ifndef FABRICD
+       int tables_next = 1;
+       int level = area->is_type == IS_LEVEL_1 ? ISIS_LEVEL1 : ISIS_LEVEL2;
+       struct listnode *node;
+       struct flex_algo *fa;
+       struct isis_flex_algo_data *data;
+
+       for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, fa)) {
+               data = fa->data;
+               tables[tables_next] =
+                       data->spftree[tree][level - 1]->route_table;
+               tables_backup[tables_next] =
+                       data->spftree[tree][level - 1]->route_table_backup;
+               _isis_route_verify_table(area, tables[tables_next],
+                                        tables_backup[tables_next], NULL);
+               tables_next++;
+       }
+#endif /* ifndef FABRICD */
+
+       _isis_route_verify_merge(area, tables, tables_backup, tree);
 }
 
 /* Function to validate route tables for L1L2 areas. In this case we can't use
@@ -624,18 +802,27 @@ void isis_route_verify_merge(struct isis_area *area,
                             struct route_table *level1_table,
                             struct route_table *level1_table_backup,
                             struct route_table *level2_table,
-                            struct route_table *level2_table_backup)
+                            struct route_table *level2_table_backup, int tree)
 {
-       struct route_table *tables[] = {level1_table, level2_table};
+       struct route_table *tables[] = {level1_table, level2_table, NULL};
        struct route_table *tables_backup[] = {level1_table_backup,
-                                              level2_table_backup};
+                                              level2_table_backup, NULL};
+       _isis_route_verify_merge(area, tables, tables_backup, tree);
+}
+
+static void _isis_route_verify_merge(struct isis_area *area,
+                                    struct route_table **tables,
+                                    struct route_table **tables_backup,
+                                    int tree)
+{
        struct route_table *merge;
        struct route_node *rnode, *mrnode;
 
        merge = srcdest_table_init();
 
-       for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
-               for (rnode = route_top(tables[level - 1]); rnode;
+       for (int i = 0; tables[i]; i++) {
+               uint8_t algorithm = isis_route_table_algorithm(tables[i]);
+               for (rnode = route_top(tables[i]); rnode;
                     rnode = srcdest_route_next(rnode)) {
                        struct isis_route_info *rinfo = rnode->info;
                        struct route_node *rnode_bck;
@@ -651,14 +838,18 @@ void isis_route_verify_merge(struct isis_area *area,
                                               (const struct prefix **)&src_p);
 
                        /* Link primary route to backup route. */
-                       rnode_bck = srcdest_rnode_lookup(
-                               tables_backup[level - 1], prefix, src_p);
+                       rnode_bck = srcdest_rnode_lookup(tables_backup[i],
+                                                        prefix, src_p);
                        if (rnode_bck) {
                                rinfo->backup = rnode_bck->info;
+                               rinfo->sr_algo[algorithm].nexthops_backup =
+                                       rinfo->backup->nexthops;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        } else if (rinfo->backup) {
                                rinfo->backup = NULL;
+                               rinfo->sr_algo[algorithm].nexthops_backup =
+                                       NULL;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        }
@@ -667,6 +858,8 @@ void isis_route_verify_merge(struct isis_area *area,
                        struct isis_route_info *mrinfo = mrnode->info;
                        if (mrinfo) {
                                route_unlock_node(mrnode);
+                               set_merge_route_info_sr_algo(mrinfo, rinfo);
+
                                if (CHECK_FLAG(mrinfo->flag,
                                               ISIS_ROUTE_FLAG_ACTIVE)) {
                                        /* Clear the ZEBRA_SYNCED flag on the
@@ -696,8 +889,9 @@ void isis_route_verify_merge(struct isis_area *area,
                                                ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) {
                                        continue;
                                }
+                       } else {
+                               mrnode->info = rnode->info;
                        }
-                       mrnode->info = rnode->info;
                }
        }
 
@@ -710,6 +904,7 @@ void isis_route_invalidate_table(struct isis_area *area,
 {
        struct route_node *rode;
        struct isis_route_info *rinfo;
+       uint8_t algorithm = isis_route_table_algorithm(table);
        for (rode = route_top(table); rode; rode = srcdest_route_next(rode)) {
                if (rode->info == NULL)
                        continue;
@@ -717,6 +912,7 @@ void isis_route_invalidate_table(struct isis_area *area,
 
                if (rinfo->backup) {
                        rinfo->backup = NULL;
+                       rinfo->sr_algo[algorithm].nexthops_backup = NULL;
                        /*
                         * For now, always force routes that have backup
                         * nexthops to be reinstalled.
index 40e7462898d4da027b98ac1d1dd10cbc6aecec63..4d49a5ae9cf5bdd94706488b53727c7969ede51e 100644 (file)
@@ -30,12 +30,16 @@ struct isis_route_info {
        uint8_t flag;
        uint32_t cost;
        uint32_t depth;
-       struct isis_sr_psid_info sr;
-       struct isis_sr_psid_info sr_previous;
+       struct isis_sr_psid_info sr_algo[SR_ALGORITHM_COUNT];
+       struct isis_sr_psid_info sr_algo_previous[SR_ALGORITHM_COUNT];
        struct list *nexthops;
        struct isis_route_info *backup;
 };
 
+struct isis_route_table_info {
+       uint8_t algorithm;
+};
+
 DECLARE_HOOK(isis_route_update_hook,
             (struct isis_area * area, struct prefix *prefix,
              struct isis_route_info *route_info),
@@ -56,14 +60,14 @@ void isis_route_delete(struct isis_area *area, struct route_node *rode,
 /* Walk the given table and install new routes to zebra and remove old ones.
  * route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
 void isis_route_verify_table(struct isis_area *area, struct route_table *table,
-                            struct route_table *table_backup);
+                            struct route_table *table_backup, int tree);
 
 /* Same as isis_route_verify_table, but merge L1 and L2 routes before */
 void isis_route_verify_merge(struct isis_area *area,
                             struct route_table *level1_table,
                             struct route_table *level1_table_backup,
                             struct route_table *level2_table,
-                            struct route_table *level2_table_backup);
+                            struct route_table *level2_table_backup, int tree);
 
 /* Unset ISIS_ROUTE_FLAG_ACTIVE on all routes. Used before running spf. */
 void isis_route_invalidate_table(struct isis_area *area,
@@ -73,9 +77,14 @@ void isis_route_invalidate_table(struct isis_area *area,
 void isis_route_node_cleanup(struct route_table *table,
                             struct route_node *node);
 
+
 void isis_route_switchover_nexthop(struct isis_area *area,
                                   struct route_table *table, int family,
                                   union g_addr *nexthop_addr,
                                   ifindex_t ifindex);
 
+struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm);
+void isis_route_table_info_free(void *info);
+uint8_t isis_route_table_algorithm(const struct route_table *table);
+
 #endif /* _ZEBRA_ISIS_ROUTE_H */
index fa566c5470dafc1cdb3bb46b34bd92f8efc5ab61..f9e3780e29e3c78ffcb34a038bdf8139fd5af266 100644 (file)
 /* Declare static local variables for convenience. */
 SNMP_LOCAL_VARIABLES
 
-/* If ARRAY_SIZE is not available use a primitive substitution */
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-#endif
-
 /*
  * Define time function, it serves two purposes
  * 1. Uses unint32_t for unix time and encapsulates
@@ -425,7 +420,7 @@ static struct isis_func_to_prefix isis_func_to_prefix_arr[] = {
        {isis_snmp_find_isadj_ipaddr, {ISIS_ISADJIPADDR_ENTRY}, 4},
        {isis_snmp_find_isadj_prot_supp, {ISIS_ISADJPROTSUPP_ENTRY}, 4},
 };
-static size_t isis_func_to_prefix_count = ARRAY_SIZE(isis_func_to_prefix_arr);
+static size_t isis_func_to_prefix_count = array_size(isis_func_to_prefix_arr);
 
 static struct variable isis_var_arr[] = {
        {ISIS_SYS_VERSION, INTEGER, RONLY, isis_snmp_find_sys_object},
@@ -554,7 +549,7 @@ static struct variable isis_var_arr[] = {
         isis_snmp_find_isadj_prot_supp},
 };
 
-static const size_t isis_var_count = ARRAY_SIZE(isis_var_arr);
+static const size_t isis_var_count = array_size(isis_var_arr);
 
 /* Minimal set of hard-coded data */
 #define ISIS_VERSION (1)
@@ -838,12 +833,12 @@ static int isis_snmp_conv_next(uint8_t *buf, size_t max_len, size_t *out_len,
  */
 static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len,
                                            struct isis_area **ret_area,
-                                           struct area_addr **ret_addr)
+                                           struct iso_address **ret_addr)
 {
        uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX];
        size_t addr_len;
        struct isis_area *area = NULL;
-       struct area_addr *addr = NULL;
+       struct iso_address *addr = NULL;
        struct listnode *addr_node;
        struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
 
@@ -885,15 +880,15 @@ static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len,
 
 static int isis_snmp_area_addr_lookup_next(oid *oid_idx, size_t oid_idx_len,
                                           struct isis_area **ret_area,
-                                          struct area_addr **ret_addr)
+                                          struct iso_address **ret_addr)
 {
        uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX];
        size_t addr_len;
        int try_exact = 0;
        struct isis_area *found_area = NULL;
        struct isis_area *area = NULL;
-       struct area_addr *found_addr = NULL;
-       struct area_addr *addr = NULL;
+       struct iso_address *found_addr = NULL;
+       struct iso_address *addr = NULL;
        struct listnode *addr_node;
        struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
 
@@ -1506,7 +1501,7 @@ static uint8_t *isis_snmp_find_man_area(struct variable *v, oid *name,
                                        WriteMethod **write_method)
 {
        int res;
-       struct area_addr *area_addr = NULL;
+       struct iso_address *area_addr = NULL;
        oid *oid_idx;
        size_t oid_idx_len;
        size_t off = 0;
@@ -2490,6 +2485,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
        uint32_t delta_ticks;
        time_t now_time;
 
+       /* Ring buffer to print SNPA */
+#define FORMAT_BUF_COUNT 4
+       static char snpa[FORMAT_BUF_COUNT][ISO_SYSID_STRLEN];
+       static size_t cur_buf = 0;
+
        *write_method = NULL;
 
        if (*length <= v->namelen) {
@@ -2536,9 +2536,10 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name,
                return SNMP_INTEGER(adj->threeway_state);
 
        case ISIS_ISADJ_NEIGHSNPAADDRESS: {
-               const char *snpa = (char *)snpa_print(adj->snpa);
-               *var_len = strlen(snpa);
-               return (uint8_t *)snpa;
+               cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT;
+               snprintfrr(snpa[cur_buf], ISO_SYSID_STRLEN, "%pSY", adj->snpa);
+               *var_len = strlen(snpa[cur_buf]);
+               return (uint8_t *)snpa[cur_buf];
        }
 
        case ISIS_ISADJ_NEIGHSYSTYPE:
@@ -2859,7 +2860,7 @@ static int isis_snmp_db_overload_update(const struct isis_area *area)
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)&isis_snmp_trap_val_db_overload,
                                  sizeof(isis_snmp_trap_val_db_overload));
 
@@ -2868,11 +2869,11 @@ static int isis_snmp_db_overload_update(const struct isis_area *area)
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
        /* Patch sys_level_state with proper index */
-       off = ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state) - 1;
+       off = array_size(isis_snmp_trap_data_var_sys_level_state) - 1;
        isis_snmp_trap_data_var_sys_level_state[off] = val;
 
        /* Prepare data */
@@ -2883,7 +2884,7 @@ static int isis_snmp_db_overload_update(const struct isis_area *area)
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_state,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_state), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
        send_v2trap(notification_vars);
@@ -2905,7 +2906,7 @@ static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area,
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)&isis_snmp_trap_val_lsp_exceed_max,
                                  sizeof(isis_snmp_trap_val_lsp_exceed_max));
 
@@ -2914,12 +2915,12 @@ static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2);
 
        send_v2trap(notification_vars);
@@ -2962,18 +2963,18 @@ static void isis_snmp_update_worker_a(const struct isis_circuit *circuit,
        /* Put in trap value */
        memcpy(var_name, isis_snmp_notifications,
               sizeof(isis_snmp_notifications));
-       var_count = ARRAY_SIZE(isis_snmp_notifications);
+       var_count = array_size(isis_snmp_notifications);
        var_name[var_count++] = trap_id;
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)var_name, var_count * sizeof(oid));
 
        val = circuit->is_type;
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
        if (oid_a_len != 0) {
@@ -2992,7 +2993,7 @@ static void isis_snmp_update_worker_a(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_circ_if_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
 
@@ -3042,18 +3043,18 @@ static void isis_snmp_update_worker_b(const struct isis_circuit *circuit,
        /* Put in trap value */
        memcpy(var_name, isis_snmp_notifications,
               sizeof(isis_snmp_notifications));
-       var_count = ARRAY_SIZE(isis_snmp_notifications);
+       var_count = array_size(isis_snmp_notifications);
        var_name[var_count++] = trap_id;
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)var_name, var_count * sizeof(oid));
 
        val = circuit->is_type;
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
        if (circuit->interface == NULL)
@@ -3063,7 +3064,7 @@ static void isis_snmp_update_worker_b(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_circ_if_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
 
@@ -3108,9 +3109,9 @@ static int isis_snmp_id_len_mismatch_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_ID_LEN_MISMATCH,
                isis_snmp_trap_data_var_pdu_field_len,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32,
                &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3133,10 +3134,10 @@ isis_snmp_max_area_addr_mismatch_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_MAX_AREA_ADDR_MISMATCH,
                isis_snmp_trap_data_var_pdu_max_area_addr,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_max_area_addr),
+               array_size(isis_snmp_trap_data_var_pdu_max_area_addr),
                UNSIGNED32, &val, sizeof(val),
                isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3150,7 +3151,7 @@ static int isis_snmp_own_lsp_purge_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_OWN_LSP_PURGE, NULL, 0, STRING, NULL, 0,
                isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2);
        return 0;
 }
@@ -3164,7 +3165,7 @@ static int isis_snmp_seqno_skipped_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_SEQNO_SKIPPED, NULL, 0, STRING, NULL, 0,
                isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2);
        return 0;
 }
@@ -3183,7 +3184,7 @@ isis_snmp_authentication_type_failure_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_AUTHEN_TYPE_FAILURE, NULL, 0, STRING, NULL,
                0, isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3201,7 +3202,7 @@ isis_snmp_authentication_failure_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_AUTHEN_FAILURE, NULL, 0, STRING, NULL, 0,
                isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3223,9 +3224,9 @@ static int isis_snmp_version_skew_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_b(
                circuit, ISIS_TRAP_VERSION_SKEW,
                isis_snmp_trap_data_var_pdu_proto_ver,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32,
                &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3248,7 +3249,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit,
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)&isis_snmp_trap_val_area_mismatch,
                                  sizeof(isis_snmp_trap_val_area_mismatch));
 
@@ -3260,7 +3261,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_circ_if_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
 
@@ -3269,7 +3270,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
 
        send_v2trap(notification_vars);
@@ -3292,7 +3293,7 @@ static int isis_snmp_reject_adjacency_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_a(
                circuit, ISIS_TRAP_REJ_ADJACENCY, NULL, 0, STRING, NULL, 0,
                isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
        return 0;
 }
@@ -3307,9 +3308,9 @@ static int isis_snmp_lsp_too_large_update(const struct isis_circuit *circuit,
        isis_snmp_update_worker_b(
                circuit, ISIS_TRAP_LSP_TOO_LARGE,
                isis_snmp_trap_data_var_pdu_lsp_size,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32,
                &pdu_size, sizeof(pdu_size), isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2);
        return 0;
 }
@@ -3334,9 +3335,9 @@ static int isis_snmp_adj_state_change_update(const struct isis_adjacency *adj)
        isis_snmp_update_worker_b(
                adj->circuit, ISIS_TRAP_ADJ_STATE_CHANGE,
                isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2, isis_snmp_trap_data_var_adj_state,
-               ARRAY_SIZE(isis_snmp_trap_data_var_adj_state), INTEGER, &val,
+               array_size(isis_snmp_trap_data_var_adj_state), INTEGER, &val,
                sizeof(val));
        return 0;
 }
@@ -3359,7 +3360,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        /* Put in trap value */
        snmp_varlist_add_variable(&notification_vars, isis_snmp_trap_var,
-                                 ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID,
+                                 array_size(isis_snmp_trap_var), ASN_OBJECT_ID,
                                  (uint8_t *)&isis_snmp_trap_val_lsp_error,
                                  sizeof(isis_snmp_trap_val_lsp_error));
 
@@ -3368,13 +3369,13 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_sys_level_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER,
+               array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER,
                (uint8_t *)&val, sizeof(val));
 
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_pdu_lsp_id,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
+               array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id,
                ISIS_SYS_ID_LEN + 2);
 
        /* Prepare data */
@@ -3385,7 +3386,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_circ_if_index,
-               ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
        /* Prepare data */
@@ -3394,7 +3395,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_pdu_fragment,
-               ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING,
+               array_size(isis_snmp_trap_data_var_pdu_fragment), STRING,
                raw_pdu, raw_pdu_len);
 
        /* Prepare data */
@@ -3402,7 +3403,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_error_offset,
-               ARRAY_SIZE(isis_snmp_trap_data_var_error_offset), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_error_offset), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
        /* Prepare data */
@@ -3410,7 +3411,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit,
 
        snmp_varlist_add_variable(
                &notification_vars, isis_snmp_trap_data_var_error_tlv_type,
-               ARRAY_SIZE(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32,
+               array_size(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32,
                (uint8_t *)&val, sizeof(val));
 
        send_v2trap(notification_vars);
index bfe3758cd898d77e18aad50b3d08eb6a658616ab..de467c8262d73af0e5ce08d65678cae3fccc8ea7 100644 (file)
@@ -26,6 +26,7 @@
 #include "spf_backoff.h"
 #include "srcdest_table.h"
 #include "vrf.h"
+#include "lib/json.h"
 
 #include "isis_errors.h"
 #include "isis_constants.h"
@@ -43,6 +44,7 @@
 #include "isis_csm.h"
 #include "isis_mt.h"
 #include "isis_tlvs.h"
+#include "isis_flex_algo.h"
 #include "isis_zebra.h"
 #include "fabricd.h"
 #include "isis_spf_private.h"
@@ -322,28 +324,41 @@ static void isis_spf_adj_free(void *arg)
        XFREE(MTYPE_ISIS_SPF_ADJ, sadj);
 }
 
-struct isis_spftree *isis_spftree_new(struct isis_area *area,
-                                     struct lspdb_head *lspdb,
-                                     const uint8_t *sysid, int level,
-                                     enum spf_tree_id tree_id,
-                                     enum spf_type type, uint8_t flags)
+static void _isis_spftree_init(struct isis_spftree *tree)
 {
-       struct isis_spftree *tree;
-
-       tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree));
-
        isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
        isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
        tree->route_table = srcdest_table_init();
        tree->route_table->cleanup = isis_route_node_cleanup;
+       tree->route_table->info = isis_route_table_info_alloc(tree->algorithm);
        tree->route_table_backup = srcdest_table_init();
+       tree->route_table_backup->info =
+               isis_route_table_info_alloc(tree->algorithm);
        tree->route_table_backup->cleanup = isis_route_node_cleanup;
-       tree->area = area;
-       tree->lspdb = lspdb;
        tree->prefix_sids = hash_create(prefix_sid_key_make, prefix_sid_cmp,
                                        "SR Prefix-SID Entries");
        tree->sadj_list = list_new();
        tree->sadj_list->del = isis_spf_adj_free;
+       isis_rlfa_list_init(tree);
+       tree->lfa.remote.pc_spftrees = list_new();
+       tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
+       if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
+               isis_spf_node_list_init(&tree->lfa.p_space);
+               isis_spf_node_list_init(&tree->lfa.q_space);
+       }
+}
+
+struct isis_spftree *
+isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb,
+                const uint8_t *sysid, int level, enum spf_tree_id tree_id,
+                enum spf_type type, uint8_t flags, uint8_t algorithm)
+{
+       struct isis_spftree *tree;
+
+       tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree));
+
+       tree->area = area;
+       tree->lspdb = lspdb;
        tree->last_run_timestamp = 0;
        tree->last_run_monotime = 0;
        tree->last_run_duration = 0;
@@ -354,18 +369,14 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
        tree->tree_id = tree_id;
        tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6;
        tree->flags = flags;
-       isis_rlfa_list_init(tree);
-       tree->lfa.remote.pc_spftrees = list_new();
-       tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
-       if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
-               isis_spf_node_list_init(&tree->lfa.p_space);
-               isis_spf_node_list_init(&tree->lfa.q_space);
-       }
+       tree->algorithm = algorithm;
+
+       _isis_spftree_init(tree);
 
        return tree;
 }
 
-void isis_spftree_del(struct isis_spftree *spftree)
+static void _isis_spftree_del(struct isis_spftree *spftree)
 {
        hash_clean_and_free(&spftree->prefix_sids, NULL);
        isis_zebra_rlfa_unregister_all(spftree);
@@ -380,14 +391,30 @@ void isis_spftree_del(struct isis_spftree *spftree)
        list_delete(&spftree->sadj_list);
        isis_vertex_queue_free(&spftree->tents);
        isis_vertex_queue_free(&spftree->paths);
+       isis_route_table_info_free(spftree->route_table->info);
+       isis_route_table_info_free(spftree->route_table_backup->info);
        route_table_finish(spftree->route_table);
        route_table_finish(spftree->route_table_backup);
+}
+
+void isis_spftree_del(struct isis_spftree *spftree)
+{
+       _isis_spftree_del(spftree);
+
        spftree->route_table = NULL;
 
        XFREE(MTYPE_ISIS_SPFTREE, spftree);
        return;
 }
 
+#ifndef FABRICD
+static void isis_spftree_clear(struct isis_spftree *spftree)
+{
+       _isis_spftree_del(spftree);
+       _isis_spftree_init(spftree);
+}
+#endif /* ifndef FABRICD */
+
 static void isis_spftree_adj_del(struct isis_spftree *spftree,
                                 struct isis_adjacency *adj)
 {
@@ -410,10 +437,10 @@ void spftree_area_init(struct isis_area *area)
                        if (area->spftree[tree][level - 1])
                                continue;
 
-                       area->spftree[tree][level - 1] =
-                               isis_spftree_new(area, &area->lspdb[level - 1],
-                                                area->isis->sysid, level, tree,
-                                                SPF_TYPE_FORWARD, 0);
+                       area->spftree[tree][level - 1] = isis_spftree_new(
+                               area, &area->lspdb[level - 1],
+                               area->isis->sysid, level, tree,
+                               SPF_TYPE_FORWARD, 0, SR_ALGORITHM_SPF);
                }
        }
 }
@@ -495,8 +522,8 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree)
 #ifdef EXTREME_DEBUG
        if (IS_DEBUG_SPF_EVENTS)
                zlog_debug(
-                       "ISIS-SPF: added this IS %s %s depth %d dist %d to PATHS",
-                       vtype2string(vertex->type),
+                       "ISIS-SPF: A:%hhu added this IS %s %s depth %d dist %d to PATHS",
+                       spftree->algorithm, vtype2string(vertex->type),
                        vid2string(vertex, buff, sizeof(buff)), vertex->depth,
                        vertex->d_N);
 #endif /* EXTREME_DEBUG */
@@ -585,9 +612,20 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
                        vertex->N.ip.sr.sid = *psid;
                        vertex->N.ip.sr.label =
                                sr_prefix_in_label(area, psid, local);
+                       vertex->N.ip.sr.algorithm = psid->algorithm;
+
                        if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL)
                                vertex->N.ip.sr.present = true;
 
+#ifndef FABRICD
+                       if (flex_algo_id_valid(spftree->algorithm) &&
+                           !isis_flex_algo_elected_supported(
+                                   spftree->algorithm, spftree->area)) {
+                               vertex->N.ip.sr.present = false;
+                               vertex->N.ip.sr.label = MPLS_INVALID_LABEL;
+                       }
+#endif /* ifndef FABRICD */
+
                        (void)hash_get(spftree->prefix_sids, vertex,
                                       hash_alloc_intern);
                }
@@ -615,8 +653,8 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
 #ifdef EXTREME_DEBUG
        if (IS_DEBUG_SPF_EVENTS)
                zlog_debug(
-                       "ISIS-SPF: add to TENT %s %s %s depth %d dist %d adjcount %d",
-                       print_sys_hostname(vertex->N.id),
+                       "ISIS-SPF: A:%hhu add to TENT %s %s %s depth %d dist %d adjcount %d",
+                       spftree->algorithm, print_sys_hostname(vertex->N.id),
                        vtype2string(vertex->type),
                        vid2string(vertex, buff, sizeof(buff)), vertex->depth,
                        vertex->d_N, listcount(vertex->Adj_N));
@@ -709,7 +747,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
 #ifdef EXTREME_DEBUG
                if (IS_DEBUG_SPF_EVENTS)
                        zlog_debug(
-                               "ISIS-SPF: process_N %s %s %s dist %d already found from PATH",
+                               "ISIS-SPF: A:%hhu process_N %s %s %s dist %d already found from PATH",
+                               spftree->algorithm,
                                print_sys_hostname(vertex->N.id),
                                vtype2string(vtype),
                                vid2string(vertex, buff, sizeof(buff)), dist);
@@ -725,7 +764,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
 #ifdef EXTREME_DEBUG
                if (IS_DEBUG_SPF_EVENTS)
                        zlog_debug(
-                               "ISIS-SPF: process_N %s %s %s dist %d parent %s adjcount %d",
+                               "ISIS-SPF: A:%hhu process_N %s %s %s dist %d parent %s adjcount %d",
+                               spftree->algorithm,
                                print_sys_hostname(vertex->N.id),
                                vtype2string(vtype),
                                vid2string(vertex, buff, sizeof(buff)), dist,
@@ -771,8 +811,9 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
 #ifdef EXTREME_DEBUG
        if (IS_DEBUG_SPF_EVENTS)
                zlog_debug(
-                       "ISIS-SPF: process_N add2tent %s %s dist %d parent %s",
-                       print_sys_hostname(id), vtype2string(vtype), dist,
+                       "ISIS-SPF: A:%hhu process_N add2tent %s %s dist %d parent %s",
+                       spftree->algorithm, print_sys_hostname(id),
+                       vtype2string(vtype), dist,
                        (parent ? print_sys_hostname(parent->N.id) : "null"));
 #endif /* EXTREME_DEBUG */
 
@@ -832,7 +873,8 @@ lspfragloop:
 
 #ifdef EXTREME_DEBUG
        if (IS_DEBUG_SPF_EVENTS)
-               zlog_debug("ISIS-SPF: process_lsp %s",
+               zlog_debug("ISIS-SPF: A:%hhu process_lsp %s",
+                          spftree->algorithm,
                           print_sys_hostname(lsp->hdr.lsp_id));
 #endif /* EXTREME_DEBUG */
 
@@ -889,6 +931,16 @@ lspfragloop:
                                    && !memcmp(er->id, null_sysid,
                                               ISIS_SYS_ID_LEN))
                                        continue;
+#ifndef FABRICD
+
+                               if (flex_algo_id_valid(spftree->algorithm) &&
+                                   (!sr_algorithm_participated(
+                                            lsp, spftree->algorithm) ||
+                                    isis_flex_algo_constraint_drop(spftree,
+                                                                   lsp, er)))
+                                       continue;
+#endif /* ifndef FABRICD */
+
                                dist = cost
                                       + (CHECK_FLAG(spftree->flags,
                                                     F_SPFTREE_HOPCOUNT_METRIC)
@@ -965,9 +1017,21 @@ lspfragloop:
                                        struct isis_prefix_sid *psid =
                                                (struct isis_prefix_sid *)i;
 
-                                       if (psid->algorithm != SR_ALGORITHM_SPF)
+                                       if (psid->algorithm !=
+                                           spftree->algorithm)
                                                continue;
 
+#ifndef FABRICD
+                                       if (flex_algo_id_valid(
+                                                   spftree->algorithm) &&
+                                           (!sr_algorithm_participated(
+                                                    lsp, spftree->algorithm) ||
+                                            !isis_flex_algo_elected_supported(
+                                                    spftree->algorithm,
+                                                    spftree->area)))
+                                               continue;
+#endif /* ifndef FABRICD */
+
                                        has_valid_psid = true;
                                        process_N(spftree, VTYPE_IPREACH_TE,
                                                  &ip_info, dist, depth + 1,
@@ -1033,8 +1097,20 @@ lspfragloop:
                                        struct isis_prefix_sid *psid =
                                                (struct isis_prefix_sid *)i;
 
-                                       if (psid->algorithm != SR_ALGORITHM_SPF)
+                                       if (psid->algorithm !=
+                                           spftree->algorithm)
+                                               continue;
+
+#ifndef FABRICD
+                                       if (flex_algo_id_valid(
+                                                   spftree->algorithm) &&
+                                           (!sr_algorithm_participated(
+                                                    lsp, spftree->algorithm) ||
+                                            !isis_flex_algo_elected_supported(
+                                                    spftree->algorithm,
+                                                    spftree->area)))
                                                continue;
+#endif /* ifndef FABRICD */
 
                                        has_valid_psid = true;
                                        process_N(spftree, vtype, &ip_info,
@@ -1067,8 +1143,8 @@ end:
            && !isis_level2_adj_up(spftree->area)) {
                struct prefix_pair ip_info = { {0} };
                if (IS_DEBUG_RTE_EVENTS)
-                       zlog_debug("ISIS-Spf (%s): add default %s route",
-                                  rawlspid_print(lsp->hdr.lsp_id),
+                       zlog_debug("ISIS-Spf (%pLS): add default %s route",
+                                  lsp->hdr.lsp_id,
                                   spftree->family == AF_INET ? "ipv4"
                                                              : "ipv6");
 
@@ -1157,7 +1233,7 @@ static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix,
                        struct isis_prefix_sid *psid =
                                (struct isis_prefix_sid *)i;
 
-                       if (psid->algorithm != SR_ALGORITHM_SPF)
+                       if (psid->algorithm != spftree->algorithm)
                                continue;
 
                        has_valid_psid = true;
@@ -1207,9 +1283,8 @@ static void isis_spf_preload_tent(struct isis_spftree *spftree,
 
                if (isis_lfa_excise_adj_check(spftree, adj_id)) {
                        if (IS_DEBUG_LFA)
-                               zlog_debug("ISIS-SPF: excising adjacency %s",
-                                          isis_format_id(sadj->id,
-                                                         ISIS_SYS_ID_LEN + 1));
+                               zlog_debug("ISIS-SPF: excising adjacency %pPN",
+                                          sadj->id);
                        continue;
                }
 
@@ -1324,8 +1399,8 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
        LSP_FRAGMENT(lspid) = 0;
        lsp = lsp_search(spftree->lspdb, lspid);
        if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
-               zlog_warn("ISIS-SPF: No LSP found from root to L%d %s",
-                         spftree->level, rawlspid_print(lspid));
+               zlog_warn("ISIS-SPF: No LSP found from root to L%d %pLS",
+                         spftree->level, lspid);
                return;
        }
 
@@ -1421,6 +1496,19 @@ static void spf_adj_list_parse_lsp(struct isis_spftree *spftree,
                        for (struct isis_extended_reach *reach =
                                     (struct isis_extended_reach *)head;
                             reach; reach = reach->next) {
+#ifndef FABRICD
+                               /*
+                                * cutting out adjacency by flex-algo link
+                                * affinity attribute
+                                */
+                               if (flex_algo_id_valid(spftree->algorithm) &&
+                                   (!sr_algorithm_participated(
+                                            lsp, spftree->algorithm) ||
+                                    isis_flex_algo_constraint_drop(
+                                            spftree, lsp, reach)))
+                                       continue;
+#endif /* ifndef FABRICD */
+
                                spf_adj_list_parse_tlv(
                                        spftree, adj_list, reach->id,
                                        pseudo_nodeid, pseudo_metric,
@@ -1476,11 +1564,13 @@ static void add_to_paths(struct isis_spftree *spftree,
 
 #ifdef EXTREME_DEBUG
        if (IS_DEBUG_SPF_EVENTS)
-               zlog_debug("ISIS-SPF: added %s %s %s depth %d dist %d to PATHS",
-                          print_sys_hostname(vertex->N.id),
-                          vtype2string(vertex->type),
-                          vid2string(vertex, buff, sizeof(buff)),
-                          vertex->depth, vertex->d_N);
+               zlog_debug(
+                       "ISIS-SPF: A:%hhu S:%p added %s %s %s depth %d dist %d to PATHS",
+                       spftree->algorithm, spftree,
+                       print_sys_hostname(vertex->N.id),
+                       vtype2string(vertex->type),
+                       vid2string(vertex, buff, sizeof(buff)), vertex->depth,
+                       vertex->d_N);
 #endif /* EXTREME_DEBUG */
 }
 
@@ -1626,10 +1716,23 @@ static void spf_path_process(struct isis_spftree *spftree,
                                break;
                        }
 
-                       isis_route_create(
-                               &vertex->N.ip.p.dest, &vertex->N.ip.p.src,
-                               vertex->d_N, vertex->depth, &vertex->N.ip.sr,
-                               vertex->Adj_N, allow_ecmp, area, route_table);
+#ifdef EXTREME_DEBUG
+                       struct isis_route_info *ri =
+#endif /* EXTREME_DEBUG */
+                               isis_route_create(&vertex->N.ip.p.dest,
+                                                 &vertex->N.ip.p.src,
+                                                 vertex->d_N, vertex->depth,
+                                                 &vertex->N.ip.sr,
+                                                 vertex->Adj_N, allow_ecmp,
+                                                 area, route_table);
+
+#ifdef EXTREME_DEBUG
+                       zlog_debug(
+                               "ISIS-SPF: A:%hhu create route pfx %pFX dist %d, sr.algo %d, table %p, rv %p",
+                               spftree->algorithm, &vertex->N.ip.p.dest,
+                               vertex->d_N, vertex->N.ip.sr.algorithm,
+                               route_table, ri);
+#endif /* EXTREME_DEBUG */
                } else if (IS_DEBUG_SPF_EVENTS)
                        zlog_debug(
                                "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
@@ -1649,12 +1752,10 @@ static void isis_spf_loop(struct isis_spftree *spftree,
                vertex = isis_vertex_queue_pop(&spftree->tents);
 
 #ifdef EXTREME_DEBUG
-               if (IS_DEBUG_SPF_EVENTS)
-                       zlog_debug(
-                               "ISIS-SPF: get TENT node %s %s depth %d dist %d to PATHS",
-                               print_sys_hostname(vertex->N.id),
-                               vtype2string(vertex->type), vertex->depth,
-                               vertex->d_N);
+               zlog_debug(
+                       "ISIS-SPF: A:%hhu get TENT node %s %s depth %d dist %d to PATHS",
+                       spftree->algorithm, print_sys_hostname(vertex->N.id),
+                       vtype2string(vertex->type), vertex->depth, vertex->d_N);
 #endif /* EXTREME_DEBUG */
 
                add_to_paths(spftree, vertex);
@@ -1663,9 +1764,8 @@ static void isis_spf_loop(struct isis_spftree *spftree,
 
                lsp = lsp_for_vertex(spftree, vertex);
                if (!lsp) {
-                       zlog_warn("ISIS-SPF: No LSP found for %s",
-                                 isis_format_id(vertex->N.id,
-                                                sizeof(vertex->N.id)));
+                       zlog_warn("ISIS-SPF: No LSP found for %pPN",
+                                 vertex->N.id);
                        continue;
                }
 
@@ -1703,10 +1803,10 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
                                           struct isis_spftree *spftree)
 {
        if (!spftree)
-               spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1],
-                                          sysid, ISIS_LEVEL2, SPFTREE_IPV4,
-                                          SPF_TYPE_FORWARD,
-                                          F_SPFTREE_HOPCOUNT_METRIC);
+               spftree = isis_spftree_new(
+                       area, &area->lspdb[IS_LEVEL_2 - 1], sysid, ISIS_LEVEL2,
+                       SPFTREE_IPV4, SPF_TYPE_FORWARD,
+                       F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF);
 
        init_spt(spftree, ISIS_MT_IPV4_UNICAST);
        if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) {
@@ -1777,6 +1877,27 @@ void isis_run_spf(struct isis_spftree *spftree)
                exit(1);
        }
 
+#ifndef FABRICD
+       /* If a node is configured to participate in a particular Flexible-
+        * Algorithm, but there is no valid Flex-Algorithm definition available
+        * for it, or the selected Flex-Algorithm definition includes
+        * calculation-type, metric-type, constraint, flag, or Sub-TLV that is
+        * not supported by the node, it MUST stop participating in such
+        * Flexible-Algorithm.
+        */
+       if (flex_algo_id_valid(spftree->algorithm) &&
+           !flex_algo_get_state(spftree->area->flex_algos,
+                                spftree->algorithm)) {
+               if (!CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) {
+                       isis_spftree_clear(spftree);
+                       SET_FLAG(spftree->flags, F_SPFTREE_DISABLED);
+                       lsp_regenerate_schedule(spftree->area,
+                                               spftree->area->is_type, 0);
+               }
+               goto out;
+       }
+#endif /* ifndef FABRICD */
+
        /*
         * C.2.5 Step 0
         */
@@ -1797,6 +1918,18 @@ void isis_run_spf(struct isis_spftree *spftree)
        }
 
        isis_spf_loop(spftree, spftree->sysid);
+
+
+#ifndef FABRICD
+       /* flex-algo */
+       if (CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) {
+               UNSET_FLAG(spftree->flags, F_SPFTREE_DISABLED);
+               lsp_regenerate_schedule(spftree->area, spftree->area->is_type,
+                                       0);
+       }
+
+out:
+#endif /* ifndef FABRICD */
        spftree->runcount++;
        spftree->last_run_timestamp = time(NULL);
        spftree->last_run_monotime = monotime(&time_end);
@@ -1818,29 +1951,37 @@ static void isis_run_spf_with_protection(struct isis_area *area,
                isis_spf_run_lfa(area, spftree);
 }
 
-void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees)
+void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees,
+                           int tree)
 {
        if (area->is_type == IS_LEVEL_1) {
                isis_route_verify_table(area, trees[0]->route_table,
-                                       trees[0]->route_table_backup);
+                                       trees[0]->route_table_backup, tree);
        } else if (area->is_type == IS_LEVEL_2) {
                isis_route_verify_table(area, trees[1]->route_table,
-                                       trees[1]->route_table_backup);
+                                       trees[1]->route_table_backup, tree);
        } else {
                isis_route_verify_merge(area, trees[0]->route_table,
                                        trees[0]->route_table_backup,
                                        trees[1]->route_table,
-                                       trees[1]->route_table_backup);
+                                       trees[1]->route_table_backup, tree);
        }
 }
 
 void isis_spf_invalidate_routes(struct isis_spftree *tree)
 {
+       struct isis_route_table_info *backup_info;
+
        isis_route_invalidate_table(tree->area, tree->route_table);
 
        /* Delete backup routes. */
+
+       backup_info = tree->route_table_backup->info;
        route_table_finish(tree->route_table_backup);
+       isis_route_table_info_free(backup_info);
        tree->route_table_backup = srcdest_table_init();
+       tree->route_table_backup->info =
+               isis_route_table_info_alloc(tree->algorithm);
        tree->route_table_backup->cleanup = isis_route_node_cleanup;
 }
 
@@ -1859,6 +2000,12 @@ static void isis_run_spf_cb(struct event *thread)
        struct isis_area *area = run->area;
        int level = run->level;
        int have_run = 0;
+       struct listnode *node;
+       struct isis_circuit *circuit;
+#ifndef FABRICD
+       struct flex_algo *fa;
+       struct isis_flex_algo_data *data;
+#endif /* ifndef FABRICD */
 
        XFREE(MTYPE_ISIS_SPF_RUN, run);
 
@@ -1879,11 +2026,27 @@ static void isis_run_spf_cb(struct event *thread)
        if (area->ip_circuits) {
                isis_run_spf_with_protection(
                        area, area->spftree[SPFTREE_IPV4][level - 1]);
+#ifndef FABRICD
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       data = fa->data;
+                       isis_run_spf_with_protection(
+                               area, data->spftree[SPFTREE_IPV4][level - 1]);
+               }
+#endif /* ifndef FABRICD */
                have_run = 1;
        }
        if (area->ipv6_circuits) {
                isis_run_spf_with_protection(
                        area, area->spftree[SPFTREE_IPV6][level - 1]);
+#ifndef FABRICD
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       data = fa->data;
+                       isis_run_spf_with_protection(
+                               area, data->spftree[SPFTREE_IPV6][level - 1]);
+               }
+#endif /* ifndef FABRICD */
                have_run = 1;
        }
        if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) {
@@ -1898,8 +2061,6 @@ static void isis_run_spf_cb(struct event *thread)
        isis_area_verify_routes(area);
 
        /* walk all circuits and reset any spf specific flags */
-       struct listnode *node;
-       struct isis_circuit *circuit;
        for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
                UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
 
@@ -2105,8 +2266,13 @@ void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree)
 }
 
 static void show_isis_topology_common(struct vty *vty, int levels,
-                                     struct isis *isis)
+                                     struct isis *isis, uint8_t algo)
 {
+#ifndef FABRICD
+       struct isis_flex_algo_data *fa_data;
+       struct flex_algo *fa;
+#endif /* ifndef FABRICD */
+       struct isis_spftree *spftree;
        struct listnode *node;
        struct isis_area *area;
 
@@ -2114,27 +2280,68 @@ static void show_isis_topology_common(struct vty *vty, int levels,
                return;
 
        for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
-               vty_out(vty, "Area %s:\n",
-                       area->area_tag ? area->area_tag : "null");
+               vty_out(vty,
+                       "Area %s:", area->area_tag ? area->area_tag : "null");
+
+#ifndef FABRICD
+               /*
+                * The shapes of the flex algo spftree 2-dimensional array
+                * and the area spftree 2-dimensional array are not guaranteed
+                * to be identical.
+                */
+               fa = NULL;
+               if (flex_algo_id_valid(algo)) {
+                       fa = flex_algo_lookup(area->flex_algos, algo);
+                       if (!fa)
+                               continue;
+                       fa_data = (struct isis_flex_algo_data *)fa->data;
+               } else
+                       fa_data = NULL;
+
+               if (algo != SR_ALGORITHM_SPF)
+                       vty_out(vty, " Algorithm %hhu\n", algo);
+               else
+#endif /* ifndef FABRICD */
+                       vty_out(vty, "\n");
 
                for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
                        if ((level & levels) == 0)
                                continue;
 
                        if (area->ip_circuits > 0) {
-                               isis_print_spftree(
-                                       vty,
-                                       area->spftree[SPFTREE_IPV4][level - 1]);
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree = fa_data->spftree[SPFTREE_IPV4]
+                                                                 [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_IPV4]
+                                                              [level - 1];
+
+                               isis_print_spftree(vty, spftree);
                        }
                        if (area->ipv6_circuits > 0) {
-                               isis_print_spftree(
-                                       vty,
-                                       area->spftree[SPFTREE_IPV6][level - 1]);
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree = fa_data->spftree[SPFTREE_IPV6]
+                                                                 [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_IPV6]
+                                                              [level - 1];
+                               isis_print_spftree(vty, spftree);
                        }
                        if (isis_area_ipv6_dstsrc_enabled(area)) {
-                               isis_print_spftree(vty,
-                                                  area->spftree[SPFTREE_DSTSRC]
-                                                               [level - 1]);
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree =
+                                               fa_data->spftree[SPFTREE_DSTSRC]
+                                                               [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_DSTSRC]
+                                                              [level - 1];
+                               isis_print_spftree(vty, spftree);
                        }
                }
 
@@ -2154,7 +2361,8 @@ DEFUN(show_isis_topology, show_isis_topology_cmd,
       " [vrf <NAME|all>] topology"
 #ifndef FABRICD
       " [<level-1|level-2>]"
-#endif
+      " [algorithm (128-255)]"
+#endif /* ifndef FABRICD */
       ,
       SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
       "All VRFs\n"
@@ -2162,49 +2370,238 @@ DEFUN(show_isis_topology, show_isis_topology_cmd,
 #ifndef FABRICD
       "Paths to all level-1 routers in the area\n"
       "Paths to all level-2 routers in the domain\n"
-#endif
+      "Show Flex-algo routes\n"
+      "Algorithm number\n"
+#endif /* ifndef FABRICD */
 )
 {
        int levels = ISIS_LEVELS;
        struct listnode *node;
        struct isis *isis = NULL;
-       int idx = 0;
        const char *vrf_name = VRF_DEFAULT_NAME;
        bool all_vrf = false;
        int idx_vrf = 0;
+       uint8_t algorithm = SR_ALGORITHM_SPF;
+#ifndef FABRICD
+       int idx = 0;
 
-       if (argv_find(argv, argc, "topology", &idx)) {
-               if (argc < idx + 2)
-                       levels = ISIS_LEVEL1 | ISIS_LEVEL2;
-               else if (strmatch(argv[idx + 1]->arg, "level-1"))
-                       levels = ISIS_LEVEL1;
-               else
-                       levels = ISIS_LEVEL2;
+       levels = ISIS_LEVEL1 | ISIS_LEVEL2;
+       if (argv_find(argv, argc, "level-1", &idx))
+               levels = ISIS_LEVEL1;
+       if (argv_find(argv, argc, "level-2", &idx))
+               levels = ISIS_LEVEL2;
+       if (argv_find(argv, argc, "algorithm", &idx))
+               algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
+#endif /* ifndef FABRICD */
+
+       if (!im) {
+               vty_out(vty, "IS-IS Routing Process not enabled\n");
+               return CMD_SUCCESS;
        }
+       ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+       if (vrf_name) {
+               if (all_vrf) {
+                       for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
+                               show_isis_topology_common(vty, levels, isis,
+                                                         algorithm);
+                       return CMD_SUCCESS;
+               }
+               isis = isis_lookup_by_vrfname(vrf_name);
+               if (isis != NULL)
+                       show_isis_topology_common(vty, levels, isis, algorithm);
+       }
+
+       return CMD_SUCCESS;
+}
+
+#ifndef FABRICD
+static void show_isis_flex_algo_display_eag(struct vty *vty, char *buf,
+                                           int indent,
+                                           struct admin_group *admin_group)
+{
+       if (admin_group_zero(admin_group))
+               vty_out(vty, "not-set\n");
+       else {
+               vty_out(vty, "%s\n",
+                       admin_group_string(buf, ADMIN_GROUP_PRINT_MAX_SIZE,
+                                          indent, admin_group));
+               admin_group_print(buf, indent, admin_group);
+               if (buf[0] != '\0')
+                       vty_out(vty, "            Bit positions: %s\n", buf);
+       }
+}
+
+static void show_isis_flex_algo_common(struct vty *vty, struct isis *isis,
+                                      uint8_t algorithm)
+{
+       struct isis_router_cap_fad *router_fad;
+       char buf[ADMIN_GROUP_PRINT_MAX_SIZE];
+       struct admin_group *admin_group;
+       struct isis_area *area;
+       struct listnode *node;
+       struct flex_algo *fa;
+       int indent, algo;
+       bool fad_identical, fad_supported;
+
+       if (!isis->area_list || isis->area_list->count == 0)
+               return;
+
+       for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+               /*
+                * The shapes of the flex algo spftree 2-dimensional array
+                * and the area spftree 2-dimensional array are not guaranteed
+                * to be identical.
+                */
+
+               for (algo = 0; algo < SR_ALGORITHM_COUNT; algo++) {
+                       if (algorithm != SR_ALGORITHM_UNSET &&
+                           algorithm != algo)
+                               continue;
+
+                       fa = flex_algo_lookup(area->flex_algos, algo);
+                       if (!fa)
+                               continue;
+
+                       vty_out(vty, "Area %s:",
+                               area->area_tag ? area->area_tag : "null");
+
+                       vty_out(vty, " Algorithm %d\n", algo);
+                       vty_out(vty, "\n");
+
+                       vty_out(vty, " Enabled Data-Planes:");
+                       if (fa->dataplanes == 0) {
+                               vty_out(vty, " None\n\n");
+                               continue;
+                       }
+                       if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SR_MPLS))
+                               vty_out(vty, " SR-MPLS");
+                       if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SRV6))
+                               vty_out(vty, " SRv6");
+                       if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_IP))
+                               vty_out(vty, " IP");
+                       vty_out(vty, "\n\n");
+
+
+                       router_fad = isis_flex_algo_elected(algo, area);
+                       vty_out(vty,
+                               " Elected and running Flexible-Algorithm Definition:\n");
+                       if (router_fad)
+                               vty_out(vty, "  Source: %pSY\n",
+                                       router_fad->sysid);
+                       else
+                               vty_out(vty, "  Source: Not found\n");
+
+                       if (!router_fad) {
+                               vty_out(vty, "\n");
+                               continue;
+                       }
+
+                       fad_identical =
+                               flex_algo_definition_cmp(fa, &router_fad->fad);
+                       fad_supported =
+                               isis_flex_algo_supported(&router_fad->fad);
+                       vty_out(vty, "  Priority: %d\n",
+                               router_fad->fad.priority);
+                       vty_out(vty, "  Equal to local: %s\n",
+                               fad_identical ? "yes" : "no");
+                       vty_out(vty, "  Local state: %s\n",
+                               fad_supported
+                                       ? "enabled"
+                                       : "disabled (unsupported definition)");
+                       vty_out(vty, "  Calculation type: ");
+                       if (router_fad->fad.calc_type == 0)
+                               vty_out(vty, "spf\n");
+                       else
+                               vty_out(vty, "%d\n", router_fad->fad.calc_type);
+                       vty_out(vty, "  Metric type: %s\n",
+                               flex_algo_metric_type_print(
+                                       buf, sizeof(buf),
+                                       router_fad->fad.metric_type));
+                       vty_out(vty, "  Prefix-metric: %s\n",
+                               CHECK_FLAG(router_fad->fad.flags, FAD_FLAG_M)
+                                       ? "enabled"
+                                       : "disabled");
+                       if (router_fad->fad.flags != 0 &&
+                           router_fad->fad.flags != FAD_FLAG_M)
+                               vty_out(vty, "  Flags: 0x%x\n",
+                                       router_fad->fad.flags);
+                       vty_out(vty, "  Exclude SRLG: %s\n",
+                               router_fad->fad.exclude_srlg ? "enabled"
+                                                            : "disabled");
+
+                       admin_group = &router_fad->fad.admin_group_exclude_any;
+                       indent = vty_out(vty, "  Exclude-any admin-group: ");
+                       show_isis_flex_algo_display_eag(vty, buf, indent,
+                                                       admin_group);
+
+                       admin_group = &router_fad->fad.admin_group_include_all;
+                       indent = vty_out(vty, "  Include-all admin-group: ");
+                       show_isis_flex_algo_display_eag(vty, buf, indent,
+                                                       admin_group);
+
+                       admin_group = &router_fad->fad.admin_group_include_any;
+                       indent = vty_out(vty, "  Include-any admin-group: ");
+                       show_isis_flex_algo_display_eag(vty, buf, indent,
+                                                       admin_group);
+
+                       if (router_fad->fad.unsupported_subtlv)
+                               vty_out(vty,
+                                       "  Unsupported sub-TLV: Present (see logs)");
+
+                       vty_out(vty, "\n");
+               }
+       }
+}
+
+DEFUN(show_isis_flex_algo, show_isis_flex_algo_cmd,
+      "show " PROTO_NAME
+      " [vrf <NAME|all>] flex-algo"
+      " [(128-255)]",
+      SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
+      "All VRFs\n"
+      "IS-IS Flex-algo information\n"
+      "Algorithm number\n")
+{
+       struct isis *isis;
+       struct listnode *node;
+       const char *vrf_name = VRF_DEFAULT_NAME;
+       bool all_vrf = false;
+       int idx = 0;
+       int idx_vrf = 0;
+       uint8_t flex_algo;
 
        if (!im) {
                vty_out(vty, "IS-IS Routing Process not enabled\n");
                return CMD_SUCCESS;
        }
+
+       if (argv_find(argv, argc, "flex-algo", &idx) && (idx + 1) < argc)
+               flex_algo = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
+       else
+               flex_algo = SR_ALGORITHM_UNSET;
+
        ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
 
        if (vrf_name) {
                if (all_vrf) {
                        for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
-                               show_isis_topology_common(vty, levels, isis);
+                               show_isis_flex_algo_common(vty, isis,
+                                                          flex_algo);
                        return CMD_SUCCESS;
                }
                isis = isis_lookup_by_vrfname(vrf_name);
                if (isis != NULL)
-                       show_isis_topology_common(vty, levels, isis);
+                       show_isis_flex_algo_common(vty, isis, flex_algo);
        }
 
        return CMD_SUCCESS;
 }
+#endif /* ifndef FABRICD */
 
 static void isis_print_route(struct ttable *tt, const struct prefix *prefix,
                             struct isis_route_info *rinfo, bool prefix_sid,
-                            bool no_adjacencies)
+                            bool no_adjacencies, bool json)
 {
        struct isis_nexthop *nexthop;
        struct listnode *node;
@@ -2212,96 +2609,116 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix,
        char buf_prefix[BUFSIZ];
 
        (void)prefix2str(prefix, buf_prefix, sizeof(buf_prefix));
-       for (ALL_LIST_ELEMENTS_RO(rinfo->nexthops, node, nexthop)) {
-               struct interface *ifp;
-               char buf_iface[BUFSIZ];
-               char buf_nhop[BUFSIZ];
-
-               if (!no_adjacencies) {
-                       inet_ntop(nexthop->family, &nexthop->ip, buf_nhop,
-                                 sizeof(buf_nhop));
-                       ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
-                       if (ifp)
-                               strlcpy(buf_iface, ifp->name,
-                                       sizeof(buf_iface));
-                       else
-                               snprintf(buf_iface, sizeof(buf_iface),
-                                        "ifindex %u", nexthop->ifindex);
-               } else {
-                       strlcpy(buf_nhop, print_sys_hostname(nexthop->sysid),
-                               sizeof(buf_nhop));
-                       strlcpy(buf_iface, "-", sizeof(buf_iface));
-               }
-
-               if (prefix_sid) {
-                       char buf_sid[BUFSIZ] = {};
-                       char buf_lblop[BUFSIZ] = {};
-
-                       if (nexthop->sr.present) {
-                               snprintf(buf_sid, sizeof(buf_sid), "%u",
-                                        nexthop->sr.sid.value);
-                               sr_op2str(buf_lblop, sizeof(buf_lblop),
-                                         rinfo->sr.label, nexthop->sr.label);
+       for (int alg = 0; alg < SR_ALGORITHM_COUNT; alg++) {
+               for (ALL_LIST_ELEMENTS_RO(rinfo->sr_algo[alg].nexthops, node,
+                                         nexthop)) {
+                       struct interface *ifp;
+                       char buf_iface[BUFSIZ];
+                       char buf_nhop[BUFSIZ];
+
+                       if (!no_adjacencies) {
+                               inet_ntop(nexthop->family, &nexthop->ip,
+                                         buf_nhop, sizeof(buf_nhop));
+                               ifp = if_lookup_by_index(nexthop->ifindex,
+                                                        VRF_DEFAULT);
+                               if (ifp)
+                                       strlcpy(buf_iface, ifp->name,
+                                               sizeof(buf_iface));
+                               else
+                                       snprintf(buf_iface, sizeof(buf_iface),
+                                                "ifindex %u",
+                                                nexthop->ifindex);
                        } else {
-                               strlcpy(buf_sid, "-", sizeof(buf_sid));
-                               strlcpy(buf_lblop, "-", sizeof(buf_lblop));
+                               strlcpy(buf_nhop,
+                                       print_sys_hostname(nexthop->sysid),
+                                       sizeof(buf_nhop));
+                               strlcpy(buf_iface, "-", sizeof(buf_iface));
                        }
 
-                       if (first) {
-                               ttable_add_row(tt, "%s|%u|%s|%s|%s|%s",
-                                              buf_prefix, rinfo->cost,
-                                              buf_iface, buf_nhop, buf_sid,
-                                              buf_lblop);
-                               first = false;
-                       } else
-                               ttable_add_row(tt, "||%s|%s|%s|%s", buf_iface,
-                                              buf_nhop, buf_sid, buf_lblop);
-               } else {
-                       char buf_labels[BUFSIZ] = {};
-
-                       if (nexthop->label_stack) {
-                               for (int i = 0;
-                                    i < nexthop->label_stack->num_labels;
-                                    i++) {
-                                       char buf_label[BUFSIZ];
-
-                                       label2str(
-                                               nexthop->label_stack->label[i],
-                                               0, buf_label,
-                                               sizeof(buf_label));
-                                       if (i != 0)
-                                               strlcat(buf_labels, "/",
+                       if (prefix_sid) {
+                               char buf_sid[BUFSIZ] = {};
+                               char buf_lblop[BUFSIZ] = {};
+
+                               if (rinfo->sr_algo[alg].present) {
+                                       snprintf(buf_sid, sizeof(buf_sid), "%u",
+                                                rinfo->sr_algo[alg].sid.value);
+                                       sr_op2str(buf_lblop, sizeof(buf_lblop),
+                                                 rinfo->sr_algo[alg].label,
+                                                 nexthop->sr.label);
+                               } else if (alg == SR_ALGORITHM_SPF) {
+                                       strlcpy(buf_sid, "-", sizeof(buf_sid));
+                                       strlcpy(buf_lblop, "-",
+                                               sizeof(buf_lblop));
+                               } else {
+                                       continue;
+                               }
+
+                               if (first || json) {
+                                       ttable_add_row(tt,
+                                                      "%s|%u|%s|%s|%s|%s|%d",
+                                                      buf_prefix, rinfo->cost,
+                                                      buf_iface, buf_nhop,
+                                                      buf_sid, buf_lblop, alg);
+                                       first = false;
+                               } else
+                                       ttable_add_row(tt, "||%s|%s|%s|%s|%d",
+                                                      buf_iface, buf_nhop,
+                                                      buf_sid, buf_lblop, alg);
+                       } else {
+                               char buf_labels[BUFSIZ] = {};
+
+                               if (nexthop->label_stack) {
+                                       for (int i = 0;
+                                            i <
+                                            nexthop->label_stack->num_labels;
+                                            i++) {
+                                               char buf_label[BUFSIZ];
+
+                                               label2str(nexthop->label_stack
+                                                                 ->label[i],
+                                                         0, buf_label,
+                                                         sizeof(buf_label));
+                                               if (i != 0)
+                                                       strlcat(buf_labels, "/",
+                                                               sizeof(buf_labels));
+                                               strlcat(buf_labels, buf_label,
                                                        sizeof(buf_labels));
-                                       strlcat(buf_labels, buf_label,
+                                       }
+                               } else if (nexthop->sr.present)
+                                       label2str(nexthop->sr.label, 0,
+                                                 buf_labels,
+                                                 sizeof(buf_labels));
+                               else
+                                       strlcpy(buf_labels, "-",
                                                sizeof(buf_labels));
-                               }
-                       } else if (nexthop->sr.present)
-                               label2str(nexthop->sr.label, 0, buf_labels,
-                                         sizeof(buf_labels));
-                       else
-                               strlcpy(buf_labels, "-", sizeof(buf_labels));
-
-                       if (first) {
-                               ttable_add_row(tt, "%s|%u|%s|%s|%s", buf_prefix,
-                                              rinfo->cost, buf_iface, buf_nhop,
-                                              buf_labels);
-                               first = false;
-                       } else
-                               ttable_add_row(tt, "||%s|%s|%s", buf_iface,
-                                              buf_nhop, buf_labels);
+
+                               if (first || json) {
+                                       ttable_add_row(tt, "%s|%u|%s|%s|%s",
+                                                      buf_prefix, rinfo->cost,
+                                                      buf_iface, buf_nhop,
+                                                      buf_labels);
+                                       first = false;
+                               } else
+                                       ttable_add_row(tt, "||%s|%s|%s",
+                                                      buf_iface, buf_nhop,
+                                                      buf_labels);
+                       }
                }
        }
+
        if (list_isempty(rinfo->nexthops)) {
                if (prefix_sid) {
                        char buf_sid[BUFSIZ] = {};
                        char buf_lblop[BUFSIZ] = {};
 
-                       if (rinfo->sr.present) {
+                       if (rinfo->sr_algo[SR_ALGORITHM_SPF].present) {
                                snprintf(buf_sid, sizeof(buf_sid), "%u",
-                                        rinfo->sr.sid.value);
-                               sr_op2str(buf_lblop, sizeof(buf_lblop),
-                                         rinfo->sr.label,
-                                         MPLS_LABEL_IMPLICIT_NULL);
+                                        rinfo->sr_algo[SR_ALGORITHM_SPF]
+                                                .sid.value);
+                               sr_op2str(
+                                       buf_lblop, sizeof(buf_lblop),
+                                       rinfo->sr_algo[SR_ALGORITHM_SPF].label,
+                                       MPLS_LABEL_IMPLICIT_NULL);
                        } else {
                                strlcpy(buf_sid, "-", sizeof(buf_sid));
                                strlcpy(buf_lblop, "-", sizeof(buf_lblop));
@@ -2317,7 +2734,7 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix,
 }
 
 void isis_print_routes(struct vty *vty, struct isis_spftree *spftree,
-                      bool prefix_sid, bool backup)
+                      struct json_object **json, bool prefix_sid, bool backup)
 {
        struct route_table *route_table;
        struct ttable *tt;
@@ -2343,13 +2760,16 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree,
                return;
        }
 
-       vty_out(vty, "IS-IS %s %s routing table:\n\n",
-               circuit_t2string(spftree->level), tree_id_text);
+       if (json == NULL)
+               vty_out(vty, "IS-IS %s %s routing table:\n\n",
+                       circuit_t2string(spftree->level), tree_id_text);
 
        /* Prepare table. */
        tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
        if (prefix_sid)
-               ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|SID|Label Op.");
+               ttable_add_row(
+                       tt,
+                       "Prefix|Metric|Interface|Nexthop|SID|Label Op.|Algo");
        else
                ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)");
        tt->style.cell.rpad = 2;
@@ -2369,55 +2789,160 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree,
                if (!rinfo)
                        continue;
 
-               isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies);
+               isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies,
+                                json != NULL);
        }
 
        /* Dump the generated table. */
-       if (tt->nrows > 1) {
+       if (json == NULL && tt->nrows > 1) {
                char *table;
 
                table = ttable_dump(tt, "\n");
                vty_out(vty, "%s\n", table);
                XFREE(MTYPE_TMP, table);
+       } else if (json) {
+               *json = ttable_json(tt, prefix_sid ? "sdssdsdd" : "sdsss");
        }
        ttable_del(tt);
 }
 
 static void show_isis_route_common(struct vty *vty, int levels,
                                   struct isis *isis, bool prefix_sid,
-                                  bool backup)
+                                  bool backup, uint8_t algo,
+                                  json_object **json)
 {
+       json_object *json_level = NULL, *jstr = NULL, *json_val;
+#ifndef FABRICD
+       struct isis_flex_algo_data *fa_data;
+       struct flex_algo *fa;
+#endif /* ifndef FABRICD */
+       struct isis_spftree *spftree;
        struct listnode *node;
        struct isis_area *area;
+       char key[8];
 
        if (!isis->area_list || isis->area_list->count == 0)
                return;
 
+       if (json)
+               *json = json_object_new_object();
+
        for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
-               vty_out(vty, "Area %s:\n",
-                       area->area_tag ? area->area_tag : "null");
+#ifndef FABRICD
+               /*
+                * The shapes of the flex algo spftree 2-dimensional array
+                * and the area spftree 2-dimensional array are not guaranteed
+                * to be identical.
+                */
+               fa = NULL;
+               if (flex_algo_id_valid(algo)) {
+                       fa = flex_algo_lookup(area->flex_algos, algo);
+                       if (!fa)
+                               continue;
+                       fa_data = (struct isis_flex_algo_data *)fa->data;
+               } else {
+                       fa_data = NULL;
+               }
+#endif /* ifndef FABRICD */
+
+               if (json) {
+                       jstr = json_object_new_string(
+                               area->area_tag ? area->area_tag : "null");
+                       json_object_object_add(*json, "area", jstr);
+               } else {
+                       vty_out(vty, "Area %s:",
+                               area->area_tag ? area->area_tag : "null");
+#ifndef FABRICD
+                       if (algo != SR_ALGORITHM_SPF)
+                               vty_out(vty, " Algorithm %hhu\n", algo);
+                       else
+#endif /* ifndef FABRICD */
+                               vty_out(vty, "\n");
+               }
 
                for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
                        if ((level & levels) == 0)
                                continue;
 
+                       if (json) {
+                               json_level = json_object_new_object();
+                               jstr = json_object_new_string(
+                                       area->area_tag ? area->area_tag
+                                                      : "null");
+                               json_object_object_add(json_level, "area",
+                                                      jstr);
+                       }
+
                        if (area->ip_circuits > 0) {
-                               isis_print_routes(
-                                       vty,
-                                       area->spftree[SPFTREE_IPV4][level - 1],
-                                       prefix_sid, backup);
+                               json_val = NULL;
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree = fa_data->spftree[SPFTREE_IPV4]
+                                                                 [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_IPV4]
+                                                              [level - 1];
+
+                               if (!json)
+                                       isis_print_spftree(vty, spftree);
+
+                               isis_print_routes(vty, spftree,
+                                                 json ? &json_val : NULL,
+                                                 prefix_sid, backup);
+                               if (json && json_val) {
+                                       json_object_object_add(
+                                               json_level, "ipv4", json_val);
+                               }
                        }
                        if (area->ipv6_circuits > 0) {
-                               isis_print_routes(
-                                       vty,
-                                       area->spftree[SPFTREE_IPV6][level - 1],
-                                       prefix_sid, backup);
+                               json_val = NULL;
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree = fa_data->spftree[SPFTREE_IPV6]
+                                                                 [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_IPV6]
+                                                              [level - 1];
+
+                               if (!json)
+                                       isis_print_spftree(vty, spftree);
+
+                               isis_print_routes(vty, spftree,
+                                                 json ? &json_val : NULL,
+                                                 prefix_sid, backup);
+                               if (json && json_val) {
+                                       json_object_object_add(
+                                               json_level, "ipv6", json_val);
+                               }
                        }
                        if (isis_area_ipv6_dstsrc_enabled(area)) {
-                               isis_print_routes(vty,
-                                                 area->spftree[SPFTREE_DSTSRC]
-                                                              [level - 1],
+                               json_val = NULL;
+#ifndef FABRICD
+                               if (fa_data)
+                                       spftree =
+                                               fa_data->spftree[SPFTREE_DSTSRC]
+                                                               [level - 1];
+                               else
+#endif /* ifndef FABRICD */
+                                       spftree = area->spftree[SPFTREE_DSTSRC]
+                                                              [level - 1];
+
+                               if (!json)
+                                       isis_print_spftree(vty, spftree);
+                               isis_print_routes(vty, spftree,
+                                                 json ? &json_val : NULL,
                                                  prefix_sid, backup);
+                               if (json && json_val) {
+                                       json_object_object_add(json_level,
+                                                              "ipv6-dstsrc",
+                                                              json_val);
+                               }
+                       }
+                       if (json) {
+                               snprintf(key, sizeof(key), "level-%d", level);
+                               json_object_object_add(*json, key, json_level);
                        }
                }
        }
@@ -2428,16 +2953,25 @@ DEFUN(show_isis_route, show_isis_route_cmd,
       " [vrf <NAME|all>] route"
 #ifndef FABRICD
       " [<level-1|level-2>]"
-#endif
-      " [<prefix-sid|backup>]",
+#endif /* ifndef FABRICD */
+      " [<prefix-sid|backup>]"
+#ifndef FABRICD
+      " [algorithm (128-255)]"
+#endif /* ifndef FABRICD */
+      " [json$uj]",
       SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
       "IS-IS routing table\n"
 #ifndef FABRICD
       "level-1 routes\n"
       "level-2 routes\n"
-#endif
+#endif /* ifndef FABRICD */
       "Show Prefix-SID information\n"
-      "Show backup routes\n")
+      "Show backup routes\n"
+#ifndef FABRICD
+      "Show Flex-algo routes\n"
+      "Algorithm number\n"
+#endif /* ifndef FABRICD */
+      JSON_STR)
 {
        int levels;
        struct isis *isis;
@@ -2446,7 +2980,10 @@ DEFUN(show_isis_route, show_isis_route_cmd,
        bool all_vrf = false;
        bool prefix_sid = false;
        bool backup = false;
+       bool uj = use_json(argc, argv);
        int idx = 0;
+       json_object *json = NULL, *json_vrf = NULL;
+       uint8_t algorithm = SR_ALGORITHM_SPF;
 
        if (argv_find(argv, argc, "level-1", &idx))
                levels = ISIS_LEVEL1;
@@ -2466,17 +3003,50 @@ DEFUN(show_isis_route, show_isis_route_cmd,
        if (argv_find(argv, argc, "backup", &idx))
                backup = true;
 
+#ifndef FABRICD
+       if (argv_find(argv, argc, "algorithm", &idx))
+               algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
+#endif /* ifndef FABRICD */
+
+       if (uj)
+               json = json_object_new_array();
+
        if (vrf_name) {
                if (all_vrf) {
-                       for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
-                               show_isis_route_common(vty, levels, isis,
-                                                      prefix_sid, backup);
-                       return CMD_SUCCESS;
+                       for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+                               show_isis_route_common(
+                                       vty, levels, isis, prefix_sid, backup,
+                                       algorithm, uj ? &json_vrf : NULL);
+                               if (uj) {
+                                       json_object_object_add(
+                                               json_vrf, "vrf_id",
+                                               json_object_new_int(
+                                                       isis->vrf_id));
+                                       json_object_array_add(json, json_vrf);
+                               }
+                       }
+                       goto out;
                }
                isis = isis_lookup_by_vrfname(vrf_name);
-               if (isis != NULL)
+               if (isis != NULL) {
                        show_isis_route_common(vty, levels, isis, prefix_sid,
-                                              backup);
+                                              backup, algorithm,
+                                              uj ? &json_vrf : NULL);
+                       if (uj) {
+                               json_object_object_add(
+                                       json_vrf, "vrf_id",
+                                       json_object_new_int(isis->vrf_id));
+                               json_object_array_add(json, json_vrf);
+                       }
+               }
+       }
+
+out:
+       if (uj) {
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               json, JSON_C_TO_STRING_PRETTY));
+               json_object_free(json);
        }
 
        return CMD_SUCCESS;
@@ -2696,6 +3266,9 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd,
 
 void isis_spf_init(void)
 {
+#ifndef FABRICD
+       install_element(VIEW_NODE, &show_isis_flex_algo_cmd);
+#endif /* ifndef FABRICD */
        install_element(VIEW_NODE, &show_isis_topology_cmd);
        install_element(VIEW_NODE, &show_isis_route_cmd);
        install_element(VIEW_NODE, &show_isis_frr_summary_cmd);
index 7f4ab707e7672b7cd2bb322ad95fe5bd6c20becd..7e9754d9bfa5175d225c3b2028e10d13cf5acbd0 100644 (file)
@@ -12,6 +12,7 @@
 #define _ZEBRA_ISIS_SPF_H
 
 #include "isisd/isis_lfa.h"
+#include "lib/json.h"
 
 struct isis_spftree;
 
@@ -37,16 +38,15 @@ struct isis_spf_adj {
 #define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04
 };
 
-struct isis_spftree *isis_spftree_new(struct isis_area *area,
-                                     struct lspdb_head *lspdb,
-                                     const uint8_t *sysid, int level,
-                                     enum spf_tree_id tree_id,
-                                     enum spf_type type, uint8_t flags);
+struct isis_spftree *
+isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb,
+                const uint8_t *sysid, int level, enum spf_tree_id tree_id,
+                enum spf_type type, uint8_t flags, uint8_t algorithm);
 struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
                                               struct isis_prefix_sid *psid);
 void isis_spf_invalidate_routes(struct isis_spftree *tree);
-void isis_spf_verify_routes(struct isis_area *area,
-                           struct isis_spftree **trees);
+void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees,
+                           int tree);
 void isis_spf_switchover_routes(struct isis_area *area,
                                struct isis_spftree **trees, int family,
                                union g_addr *nexthop_ip, ifindex_t ifindex,
@@ -63,7 +63,7 @@ int _isis_spf_schedule(struct isis_area *area, int level,
                       const char *func, const char *file, int line);
 void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree);
 void isis_print_routes(struct vty *vty, struct isis_spftree *spftree,
-                      bool prefix_sid, bool backup);
+                      json_object **json, bool prefix_sid, bool backup);
 void isis_spf_init(void);
 void isis_spf_print(struct isis_spftree *spftree, struct vty *vty);
 void isis_spf_print_json(struct isis_spftree *spftree,
index d8293727020908ed3c1fa5b304da4f1dfa98e4ce..763673063cfa344eb81d181b6e05f1a09e3233ee 100644 (file)
@@ -349,11 +349,16 @@ struct isis_spftree {
                        uint32_t total[SPF_PREFIX_PRIO_MAX];
                } protection_counters;
        } lfa;
+       uint8_t algorithm;
        uint8_t flags;
 };
 #define F_SPFTREE_HOPCOUNT_METRIC 0x01
 #define F_SPFTREE_NO_ROUTES 0x02
 #define F_SPFTREE_NO_ADJACENCIES 0x04
+#ifndef FABRICD
+/* flex-algo */
+#define F_SPFTREE_DISABLED 0x08
+#endif /* ifndef FABRICD */
 
 __attribute__((__unused__))
 static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id,
index cb330603e4198588eb1cc6b84e9f349f9f26dab5..f928185ffbc4aedfd88a76812fdc688d28339967 100644 (file)
@@ -57,7 +57,17 @@ static void sr_adj_sid_del(struct sr_adjacency *sra);
 static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a,
                                            const struct sr_prefix_cfg *b)
 {
-       return prefix_cmp(&a->prefix, &b->prefix);
+       int ret;
+
+       ret = prefix_cmp(&a->prefix, &b->prefix);
+       if (ret != 0)
+               return ret;
+
+       ret = a->algorithm - b->algorithm;
+       if (ret != 0)
+               return ret;
+
+       return 0;
 }
 DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry,
                    sr_prefix_sid_cfg_compare);
@@ -331,7 +341,8 @@ int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
  * @return       Newly added Prefix-SID configuration structure
  */
 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
-                                            const struct prefix *prefix)
+                                            const struct prefix *prefix,
+                                            uint8_t algorithm)
 {
        struct sr_prefix_cfg *pcfg;
        struct interface *ifp;
@@ -341,6 +352,7 @@ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
        pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg));
        pcfg->prefix = *prefix;
        pcfg->area = area;
+       pcfg->algorithm = algorithm;
 
        /* Pull defaults from the YANG module. */
        pcfg->sid_type = yang_get_default_enum(
@@ -386,11 +398,13 @@ void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg)
  * @return       Configured Prefix-SID structure if found, NULL otherwise
  */
 struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area,
-                                             union prefixconstptr prefix)
+                                             union prefixconstptr prefix,
+                                             uint8_t algorithm)
 {
        struct sr_prefix_cfg pcfg = {};
 
        prefix_copy(&pcfg.prefix, prefix.p);
+       pcfg.algorithm = algorithm;
        return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg);
 }
 
@@ -405,7 +419,7 @@ void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external,
                               struct isis_prefix_sid *psid)
 {
        /* Set SID algorithm. */
-       psid->algorithm = SR_ALGORITHM_SPF;
+       psid->algorithm = pcfg->algorithm;
 
        /* Set SID flags. */
        psid->flags = 0;
@@ -917,10 +931,12 @@ static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family,
  */
 static int sr_if_new_hook(struct interface *ifp)
 {
+       struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL};
        struct isis_circuit *circuit;
        struct isis_area *area;
        struct connected *connected;
        struct listnode *node;
+       bool need_lsp_regenerate = false;
 
        /* Get corresponding circuit */
        circuit = circuit_scan_by_ifp(ifp);
@@ -937,18 +953,24 @@ static int sr_if_new_hook(struct interface *ifp)
         * configuration before receiving interface information from zebra.
         */
        FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) {
-               struct sr_prefix_cfg *pcfg;
 
-               pcfg = isis_sr_cfg_prefix_find(area, connected->address);
-               if (!pcfg)
-                       continue;
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+                       pcfgs[i] = isis_sr_cfg_prefix_find(
+                               area, connected->address, i);
+
+                       if (!pcfgs[i])
+                               continue;
 
-               if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) {
-                       pcfg->node_sid = true;
-                       lsp_regenerate_schedule(area, area->is_type, 0);
+                       if (sr_prefix_is_node_sid(ifp, &pcfgs[i]->prefix)) {
+                               pcfgs[i]->node_sid = true;
+                               need_lsp_regenerate = true;
+                       }
                }
        }
 
+       if (need_lsp_regenerate)
+               lsp_regenerate_schedule(area, area->is_type, 0);
+
        return 0;
 }
 
@@ -998,10 +1020,12 @@ char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
  * @param area IS-IS area
  * @param level        IS-IS level
  */
-static void show_node(struct vty *vty, struct isis_area *area, int level)
+static void show_node(struct vty *vty, struct isis_area *area, int level,
+                     uint8_t algo)
 {
        struct isis_lsp *lsp;
        struct ttable *tt;
+       char buf[128];
 
        vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level));
 
@@ -1021,15 +1045,24 @@ static void show_node(struct vty *vty, struct isis_area *area, int level)
                cap = lsp->tlvs->router_cap;
                if (!cap)
                        continue;
+               if (cap->algo[algo] == SR_ALGORITHM_UNSET)
+                       continue;
 
-               ttable_add_row(
-                       tt, "%s|%u - %u|%u - %u|%s|%u",
-                       sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound,
-                       cap->srgb.lower_bound + cap->srgb.range_size - 1,
-                       cap->srlb.lower_bound,
-                       cap->srlb.lower_bound + cap->srlb.range_size - 1,
-                       cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF",
-                       cap->msd);
+               if (cap->algo[algo] == SR_ALGORITHM_SPF)
+                       snprintf(buf, sizeof(buf), "SPF");
+               else if (cap->algo[algo] == SR_ALGORITHM_STRICT_SPF)
+                       snprintf(buf, sizeof(buf), "S-SPF");
+#ifndef FABRICD
+               else
+                       snprintf(buf, sizeof(buf), "Flex-Algo %d", algo);
+#endif /* ifndef FABRICD */
+
+               ttable_add_row(tt, "%pSY|%u - %u|%u - %u|%s|%u",
+                              lsp->hdr.lsp_id, cap->srgb.lower_bound,
+                              cap->srgb.lower_bound + cap->srgb.range_size - 1,
+                              cap->srlb.lower_bound,
+                              cap->srlb.lower_bound + cap->srlb.range_size - 1,
+                              buf, cap->msd);
        }
 
        /* Dump the generated table. */
@@ -1044,15 +1077,31 @@ static void show_node(struct vty *vty, struct isis_area *area, int level)
 }
 
 DEFUN(show_sr_node, show_sr_node_cmd,
-      "show " PROTO_NAME " segment-routing node",
-      SHOW_STR
-      PROTO_HELP
+      "show " PROTO_NAME
+      " segment-routing node"
+#ifndef FABRICD
+      " [algorithm (128-255)]"
+#endif /* ifndef FABRICD */
+      ,
+      SHOW_STR PROTO_HELP
       "Segment-Routing\n"
-      "Segment-Routing node\n")
+      "Segment-Routing node\n"
+#ifndef FABRICD
+      "Show Flex-algo nodes\n"
+      "Algorithm number\n"
+#endif /* ifndef FABRICD */
+)
 {
        struct listnode *node, *inode;
        struct isis_area *area;
+       uint8_t algorithm = SR_ALGORITHM_SPF;
        struct isis *isis;
+#ifndef FABRICD
+       int idx = 0;
+
+       if (argv_find(argv, argc, "algorithm", &idx))
+               algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
+#endif /* ifndef FABRICD */
 
        for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) {
                for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
@@ -1064,7 +1113,7 @@ DEFUN(show_sr_node, show_sr_node_cmd,
                        }
                        for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
                             level++)
-                               show_node(vty, area, level);
+                               show_node(vty, area, level, algorithm);
                }
        }
 
index 4ced5f4e83ffd5fd48650452eef67277079f7352..f5f0adf241e28f7f31873a2862adfb7c87313fec 100644 (file)
@@ -61,6 +61,11 @@ struct isis_sr_psid_info {
 
        /* Indicates whether the Prefix-SID is present or not. */
        bool present;
+
+       uint8_t algorithm;
+
+       struct list *nexthops;
+       struct list *nexthops_backup;
 };
 
 /* Segment Routing Local Block allocation */
@@ -147,6 +152,9 @@ struct sr_prefix_cfg {
 
        /* Backpointer to IS-IS area. */
        struct isis_area *area;
+
+       /* SR Algorithm number */
+       uint8_t algorithm;
 };
 
 /* Per-area IS-IS Segment Routing Data Base (SRDB). */
@@ -198,11 +206,13 @@ extern int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound,
                                   uint32_t upper_bound);
 extern int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound,
                                   uint32_t upper_bound);
-extern struct sr_prefix_cfg *
-isis_sr_cfg_prefix_add(struct isis_area *area, const struct prefix *prefix);
+extern struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area,
+                                                   const struct prefix *prefix,
+                                                   uint8_t algorithm);
 extern void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg);
 extern struct sr_prefix_cfg *
-isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix);
+isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix,
+                       uint8_t algorithm);
 extern void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg,
                                      bool external,
                                      struct isis_prefix_sid *psid);
index 45d763fd698150d9e7bec924534fabe5f17254b6..90b53c540ef58281c82a69b99772984854b28c08 100644 (file)
@@ -164,6 +164,154 @@ void isis_mpls_te_term(struct isis_area *area)
        XFREE(MTYPE_ISIS_MPLS_TE, area->mta);
 }
 
+static void isis_link_params_update_asla(struct isis_circuit *circuit,
+                                        struct interface *ifp)
+{
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node, *nnode;
+       struct isis_ext_subtlvs *ext = circuit->ext;
+       int i;
+
+       if (!HAS_LINK_PARAMS(ifp)) {
+               list_delete_all_node(ext->aslas);
+               return;
+       }
+
+#ifndef FABRICD
+       /* RFC 8919 Application Specific Link-Attributes
+        * is required by flex-algo application ISIS_SABM_FLAG_X
+        */
+       if (list_isempty(circuit->area->flex_algos->flex_algos))
+               isis_tlvs_free_asla(ext, ISIS_SABM_FLAG_X);
+       else
+               isis_tlvs_find_alloc_asla(ext, ISIS_SABM_FLAG_X);
+#endif /* ifndef FABRICD */
+
+       if (list_isempty(ext->aslas))
+               return;
+
+       for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) {
+               asla->legacy = circuit->area->asla_legacy_flag;
+               RESET_SUBTLV(asla);
+
+               if (asla->legacy)
+                       continue;
+
+               /* Fulfill ASLA subTLVs from interface link parameters */
+               if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) {
+                       asla->admin_group = ifp->link_params->admin_grp;
+                       SET_SUBTLV(asla, EXT_ADM_GRP);
+               } else
+                       UNSET_SUBTLV(asla, EXT_ADM_GRP);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) {
+                       admin_group_copy(&asla->ext_admin_group,
+                                        &ifp->link_params->ext_admin_grp);
+                       SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
+               } else
+                       UNSET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
+
+               /* Send admin-group zero for better compatibility
+                * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2
+                */
+               if (circuit->area->admin_group_send_zero &&
+                   !IS_SUBTLV(asla, EXT_ADM_GRP) &&
+                   !IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP)) {
+                       asla->admin_group = 0;
+                       SET_SUBTLV(asla, EXT_ADM_GRP);
+                       admin_group_clear(&asla->ext_admin_group);
+                       admin_group_allow_explicit_zero(&asla->ext_admin_group);
+                       SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
+               }
+
+               if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) {
+                       asla->te_metric = ifp->link_params->te_metric;
+                       SET_SUBTLV(asla, EXT_TE_METRIC);
+               } else
+                       UNSET_SUBTLV(asla, EXT_TE_METRIC);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) {
+                       asla->delay = ifp->link_params->av_delay;
+                       SET_SUBTLV(asla, EXT_DELAY);
+               } else
+                       UNSET_SUBTLV(asla, EXT_DELAY);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) {
+                       asla->min_delay = ifp->link_params->min_delay;
+                       asla->max_delay = ifp->link_params->max_delay;
+                       SET_SUBTLV(asla, EXT_MM_DELAY);
+               } else {
+                       UNSET_SUBTLV(asla, EXT_MM_DELAY);
+               }
+
+               if (asla->standard_apps == ISIS_SABM_FLAG_X)
+                       /* Flex-Algo ASLA does not need the following TE
+                        * sub-TLVs
+                        */
+                       continue;
+
+               if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) {
+                       asla->max_bw = ifp->link_params->max_bw;
+                       SET_SUBTLV(asla, EXT_MAX_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_MAX_BW);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) {
+                       asla->max_rsv_bw = ifp->link_params->max_rsv_bw;
+                       SET_SUBTLV(asla, EXT_MAX_RSV_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_MAX_RSV_BW);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) {
+                       for (i = 0; i < MAX_CLASS_TYPE; i++)
+                               asla->unrsv_bw[i] =
+                                       ifp->link_params->unrsv_bw[i];
+                       SET_SUBTLV(asla, EXT_UNRSV_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_UNRSV_BW);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) {
+                       asla->delay_var = ifp->link_params->delay_var;
+                       SET_SUBTLV(asla, EXT_DELAY_VAR);
+               } else
+                       UNSET_SUBTLV(asla, EXT_DELAY_VAR);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) {
+                       asla->pkt_loss = ifp->link_params->pkt_loss;
+                       SET_SUBTLV(asla, EXT_PKT_LOSS);
+               } else
+                       UNSET_SUBTLV(asla, EXT_PKT_LOSS);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) {
+                       asla->res_bw = ifp->link_params->res_bw;
+                       SET_SUBTLV(asla, EXT_RES_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_RES_BW);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) {
+                       asla->ava_bw = ifp->link_params->ava_bw;
+                       SET_SUBTLV(asla, EXT_AVA_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_AVA_BW);
+
+               if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) {
+                       asla->use_bw = ifp->link_params->use_bw;
+                       SET_SUBTLV(asla, EXT_USE_BW);
+               } else
+                       UNSET_SUBTLV(asla, EXT_USE_BW);
+       }
+
+
+       for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) {
+               if (!asla->legacy && NO_SUBTLV(asla) &&
+                   admin_group_nb_words(&asla->ext_admin_group) == 0)
+                       /* remove ASLA without info from the list of ASLAs to
+                        * not send void ASLA
+                        */
+                       isis_tlvs_del_asla_flex_algo(ext, asla);
+       }
+}
+
 /* Main initialization / update function of the MPLS TE Circuit context */
 /* Call when interface TE Link parameters are modified */
 void isis_link_params_update(struct isis_circuit *circuit,
@@ -210,6 +358,19 @@ void isis_link_params_update(struct isis_circuit *circuit,
                } else
                        UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP);
 
+               /* Send admin-group zero for better compatibility
+                * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2
+                */
+               if (circuit->area->admin_group_send_zero &&
+                   !IS_SUBTLV(ext, EXT_ADM_GRP) &&
+                   !IS_SUBTLV(ext, EXT_EXTEND_ADM_GRP)) {
+                       ext->adm_group = 0;
+                       SET_SUBTLV(ext, EXT_ADM_GRP);
+                       admin_group_clear(&ext->ext_admin_group);
+                       admin_group_allow_explicit_zero(&ext->ext_admin_group);
+                       SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP);
+               }
+
                /* If known, register local IPv4 addr from ip_addr list */
                if (listcount(circuit->ip_addrs) != 0) {
                        addr = (struct prefix_ipv4 *)listgetdata(
@@ -331,6 +492,8 @@ void isis_link_params_update(struct isis_circuit *circuit,
                        ext->status = 0;
        }
 
+       isis_link_params_update_asla(circuit, ifp);
+
        return;
 }
 
@@ -503,7 +666,12 @@ int isis_mpls_te_update(struct interface *ifp)
        isis_link_params_update(circuit, ifp);
 
        /* ... and LSP */
-       if (circuit->area && IS_MPLS_TE(circuit->area->mta))
+       if (circuit->area &&
+           (IS_MPLS_TE(circuit->area->mta)
+#ifndef FABRICD
+            || !list_isempty(circuit->area->flex_algos->flex_algos)
+#endif /* ifndef FABRICD */
+                    ))
                lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
 
        rc = 0;
@@ -611,7 +779,7 @@ static struct ls_vertex *lsp_to_vertex(struct ls_ted *ted, struct isis_lsp *lsp)
                                lnode.srgb.flag = cap->srgb.flags;
                                lnode.srgb.lower_bound = cap->srgb.lower_bound;
                                lnode.srgb.range_size = cap->srgb.range_size;
-                               for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                               for (int i = 0; i < LIB_LS_SR_ALGO_COUNT; i++)
                                        lnode.algo[i] = cap->algo[i];
                        }
 
@@ -657,7 +825,7 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr)
 {
        struct ls_edge *edge;
        struct ls_standard *std;
-       uint64_t key = 0;
+       struct ls_edge_key key;
 
        /* Check parameters */
        if (!ted || !attr)
@@ -666,19 +834,22 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr)
        std = &attr->standard;
 
        /* Compute keys in function of local address (IPv4/v6) or identifier */
-       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
-               key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff;
-       else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
-               key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32
-                      | (uint64_t)ntohl(std->local6.s6_addr32[3]));
-       else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
-               key = ((uint64_t)std->remote_id << 32)
-                      | (((uint64_t)std->local_id) & 0xffffffff);
-       else
-               key = 0;
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
+               key.family = AF_INET;
+               IPV4_ADDR_COPY(&key.k.addr, &std->local);
+       } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
+               key.family = AF_INET6;
+               IPV6_ADDR_COPY(&key.k.addr6, &std->local6);
+       } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) {
+               key.family = AF_LOCAL;
+               key.k.link_id = (((uint64_t)std->local_id) & 0xffffffff) |
+                               ((uint64_t)std->remote_id << 32);
+       } else {
+               key.family = AF_UNSPEC;
+       }
 
        /* Stop here if we don't got a valid key */
-       if (key == 0)
+       if (key.family == AF_UNSPEC)
                return NULL;
 
        /* Get corresponding Edge by key from Link State Data Base */
@@ -697,18 +868,17 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr)
        }
 
        if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR))
-               te_debug("    |- %s Edge (%" PRIu64
-                        ") from Extended Reach. %pI4",
-                        edge->status == NEW ? "Create" : "Found", edge->key,
-                        &attr->standard.local);
+               te_debug("    |- %s Edge (%pI4) from Extended Reach. %pI4",
+                        edge->status == NEW ? "Create" : "Found",
+                        &edge->key.k.addr, &attr->standard.local);
        else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6))
-               te_debug("    |- %s Edge (%" PRIu64
-                        ") from Extended Reach. %pI6",
-                        edge->status == NEW ? "Create" : "Found", edge->key,
-                        &attr->standard.local6);
+               te_debug("    |- %s Edge (%pI6) from Extended Reach. %pI6",
+                        edge->status == NEW ? "Create" : "Found",
+                        &edge->key.k.addr6, &attr->standard.local6);
        else
                te_debug("    |- %s Edge (%" PRIu64 ")",
-                        edge->status == NEW ? "Create" : "Found", edge->key);
+                        edge->status == NEW ? "Create" : "Found",
+                        edge->key.k.link_id);
 
        return edge;
 }
@@ -903,7 +1073,7 @@ static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric,
        struct ls_edge *edge, *dst;
        struct ls_attributes *attr;
 
-       te_debug("  |- Process Extended IS for %s", sysid_print(id));
+       te_debug("  |- Process Extended IS for %pSY", id);
 
        /* Check parameters */
        if (old_metric || !args || !tlvs)
@@ -950,8 +1120,21 @@ static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric,
        }
 
        /* Try to update remote Link from remote address or reachability ID */
-       te_debug("    |- Link Edge (%" PRIu64 ") to destination vertex (%s)",
-                edge->key, print_sys_hostname(id));
+       if (edge->key.family == AF_INET)
+               te_debug("    |- Link Edge (%pI4) to destination vertex (%s)",
+                        &edge->key.k.addr, print_sys_hostname(id));
+       else if (edge->key.family == AF_INET6)
+               te_debug("    |- Link Edge (%pI6) to destination vertex (%s)",
+                        &edge->key.k.addr6, print_sys_hostname(id));
+       else if (edge->key.family == AF_LOCAL)
+               te_debug("    |- Link Edge (%" PRIu64
+                        ") to destination vertex (%s)",
+                        edge->key.k.link_id, print_sys_hostname(id));
+       else
+               te_debug(
+                       "    |- Link Edge (Unknown) to destination vertex (%s)",
+                       print_sys_hostname(id));
+
        dst = ls_find_edge_by_destination(args->ted, edge->attributes);
        if (dst) {
                /* Attach remote link if not set */
@@ -1075,16 +1258,29 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric,
        }
        if (!std)
                prefix_copy(&p, prefix);
-       else
+       else {
+               /* Remove old subnet if any before prefix adjustment */
+               subnet = ls_find_subnet(args->ted, prefix);
+               if (subnet) {
+                       if (args->export) {
+                               subnet->status = DELETE;
+                               isis_te_export(LS_MSG_TYPE_PREFIX, subnet);
+                       }
+                       te_debug("   |- Remove subnet with prefix %pFX",
+                                &subnet->key);
+                       ls_subnet_del_all(args->ted, subnet);
+               }
                te_debug("   |- Adjust prefix %pFX with local address to: %pFX",
                         prefix, &p);
+       }
 
        /* Search existing Subnet in TED ... */
-       subnet = ls_find_subnet(args->ted, p);
+       subnet = ls_find_subnet(args->ted, &p);
        /* ... and create a new Subnet if not found */
        if (!subnet) {
-               ls_pref = ls_prefix_new(vertex->node->adv, p);
+               ls_pref = ls_prefix_new(vertex->node->adv, &p);
                subnet = ls_subnet_add(args->ted, ls_pref);
+               /* Stop processing if we are unable to create a new subnet */
                if (!subnet)
                        return LSP_ITER_CONTINUE;
        }
@@ -1167,14 +1363,14 @@ static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp)
 
        ted = mta->ted;
 
-       te_debug("ISIS-TE(%s): Parse LSP %s", lsp->area->area_tag,
-                sysid_print(lsp->hdr.lsp_id));
+       te_debug("ISIS-TE(%s): Parse LSP %pSY", lsp->area->area_tag,
+                lsp->hdr.lsp_id);
 
        /* First parse LSP to obtain the corresponding Vertex */
        vertex = lsp_to_vertex(ted, lsp);
        if (!vertex) {
-               zlog_warn("Unable to build Vertex from LSP %s. Abort!",
-                         sysid_print(lsp->hdr.lsp_id));
+               zlog_warn("Unable to build Vertex from LSP %pSY. Abort!",
+                         lsp->hdr.lsp_id);
                return;
        }
 
@@ -1238,8 +1434,8 @@ static void isis_te_delete_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp)
        if (!IS_MPLS_TE(mta) || !mta->ted || !lsp)
                return;
 
-       te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %s",
-                lsp->area->area_tag, sysid_print(lsp->hdr.lsp_id));
+       te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %pSY",
+                lsp->area->area_tag, lsp->hdr.lsp_id);
 
        /* Compute Link State Node ID from IS-IS sysID ... */
        if (lsp->level == ISIS_LEVEL1)
@@ -1728,7 +1924,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
        struct ls_vertex *vertex;
        struct ls_edge *edge;
        struct ls_subnet *subnet;
-       uint64_t key;
+       struct ls_edge_key key;
        bool detail = false;
        bool uj = use_json(argc, argv);
        json_object *json = NULL;
@@ -1782,7 +1978,8 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Edge from the Link State Database */
-                       key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
+                       key.family = AF_INET;
+                       IPV4_ADDR_COPY(&key.k.addr, &ip_addr);
                        edge = ls_find_edge_by_key(ted, key);
                        if (!edge) {
                                vty_out(vty, "No edge found for ID %pI4\n",
@@ -1797,8 +1994,8 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Edge from the Link State Database */
-                       key = (uint64_t)ntohl(ip6_addr.s6_addr32[3])
-                             | ((uint64_t)ntohl(ip6_addr.s6_addr32[2]) << 32);
+                       key.family = AF_INET6;
+                       IPV6_ADDR_COPY(&key.k.addr6, &ip6_addr);
                        edge = ls_find_edge_by_key(ted, key);
                        if (!edge) {
                                vty_out(vty, "No edge found for ID %pI6\n",
@@ -1822,7 +2019,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Subnet from the Link State Database */
-                       subnet = ls_find_subnet(ted, pref);
+                       subnet = ls_find_subnet(ted, &pref);
                        if (!subnet) {
                                vty_out(vty, "No subnet found for ID %pFX\n",
                                        &pref);
@@ -1835,7 +2032,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Subnet from the Link State Database */
-                       subnet = ls_find_subnet(ted, pref);
+                       subnet = ls_find_subnet(ted, &pref);
                        if (!subnet) {
                                vty_out(vty, "No subnet found for ID %pFX\n",
                                        &pref);
index b52a38be7f878398158d446fdf10ea003cbfb829..4ad877ce0f19054c4ad91606a61f82d3e8a94ae7 100644 (file)
 #include "isisd/isis_lsp.h"
 #include "isisd/isis_te.h"
 #include "isisd/isis_sr.h"
+#include "isisd/isis_flex_algo.h"
+
+#define TLV_SIZE_MISMATCH(log, indent, target)                                 \
+       sbuf_push(log, indent,                                                 \
+                 "TLV size does not match expected size for " target "!\n")
 
 DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs");
 DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBSUBTLV, "ISIS Sub-Sub-TLVs");
 DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists");
 
 typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
@@ -119,6 +125,7 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
        ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs));
        init_item_list(&ext->adj_sid);
        init_item_list(&ext->lan_sid);
+       ext->aslas = list_new();
 
        admin_group_init(&ext->ext_admin_group);
 
@@ -128,6 +135,8 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
 void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext)
 {
        struct isis_item *item, *next_item;
+       struct listnode *node, *nnode;
+       struct isis_asla_subtlvs *asla;
 
        if (!ext)
                return;
@@ -142,6 +151,11 @@ void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext)
                XFREE(MTYPE_ISIS_SUBTLV, item);
        }
 
+       for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla))
+               isis_tlvs_del_asla_flex_algo(ext, asla);
+
+       list_delete(&ext->aslas);
+
        admin_group_term(&ext->ext_admin_group);
 
        XFREE(MTYPE_ISIS_SUBTLV, ext);
@@ -158,6 +172,8 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid)
        struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
        struct isis_adj_sid *adj;
        struct isis_lan_adj_sid *lan;
+       struct listnode *node, *nnode;
+       struct isis_asla_subtlvs *new_asla, *asla;
 
        /* Copy the Extended IS main part */
        memcpy(rv, exts, sizeof(struct isis_ext_subtlvs));
@@ -222,12 +238,133 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid)
                SET_SUBTLV(rv, EXT_LAN_ADJ_SID);
        }
 
+       rv->aslas = list_new();
+
+       for (ALL_LIST_ELEMENTS(exts->aslas, node, nnode, asla)) {
+               new_asla = XCALLOC(MTYPE_ISIS_SUBTLV,
+                                  sizeof(struct isis_asla_subtlvs));
+               memcpy(new_asla, asla, sizeof(struct isis_asla_subtlvs));
+
+               new_asla->ext_admin_group.bitmap.data = NULL;
+               admin_group_copy(&new_asla->ext_admin_group,
+                                &asla->ext_admin_group);
+
+               listnode_add(rv->aslas, new_asla);
+       }
+
        rv->ext_admin_group.bitmap.data = NULL;
        admin_group_copy(&rv->ext_admin_group, &exts->ext_admin_group);
 
        return rv;
 }
 
+static void format_item_asla_subtlvs(struct isis_asla_subtlvs *asla,
+                                    struct sbuf *buf, int indent)
+{
+       char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
+
+       sbuf_push(buf, indent, "Application Specific Link Attributes:\n");
+       sbuf_push(buf, indent + 2,
+                 "L flag: %u, SA-Length: %u, UDA-Length: %u\n", asla->legacy,
+                 asla->standard_apps_length, asla->user_def_apps_length);
+       sbuf_push(buf, indent + 2, "Standard Applications: 0x%02x",
+                 asla->standard_apps);
+       if (asla->standard_apps) {
+               uint8_t bit = asla->standard_apps;
+               if (bit & ISIS_SABM_FLAG_R)
+                       sbuf_push(buf, 0, " RSVP-TE");
+               if (bit & ISIS_SABM_FLAG_S)
+                       sbuf_push(buf, 0, " SR-Policy");
+               if (bit & ISIS_SABM_FLAG_L)
+                       sbuf_push(buf, 0, " Loop-Free-Alternate");
+               if (bit & ISIS_SABM_FLAG_X)
+                       sbuf_push(buf, 0, " Flex-Algo");
+       }
+       sbuf_push(buf, 0, "\n");
+       sbuf_push(buf, indent + 2, "User Defined Applications: 0x%02x\n",
+                 asla->user_def_apps);
+
+       if (IS_SUBTLV(asla, EXT_ADM_GRP)) {
+               sbuf_push(buf, indent + 2, "Admin Group: 0x%08x\n",
+                         asla->admin_group);
+               sbuf_push(buf, indent + 4, "Bit positions: %s\n",
+                         admin_group_standard_print(
+                                 admin_group_buf,
+                                 indent + 2 + strlen("Admin Group: "),
+                                 asla->admin_group));
+       }
+       if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) &&
+           admin_group_nb_words(&asla->ext_admin_group) != 0) {
+               sbuf_push(buf, indent + 2, "Ext Admin Group: %s\n",
+                         admin_group_string(
+                                 admin_group_buf, ADMIN_GROUP_PRINT_MAX_SIZE,
+                                 indent + 2 + strlen("Ext Admin Group: "),
+                                 &asla->ext_admin_group));
+               admin_group_print(admin_group_buf,
+                                 indent + 2 + strlen("Ext Admin Group: "),
+                                 &asla->ext_admin_group);
+               if (admin_group_buf[0] != '\0' &&
+                   (buf->pos + strlen(admin_group_buf) +
+                    SBUF_DEFAULT_SIZE / 2) < buf->size)
+                       sbuf_push(buf, indent + 4, "Bit positions: %s\n",
+                                 admin_group_buf);
+       }
+       if (IS_SUBTLV(asla, EXT_MAX_BW))
+               sbuf_push(buf, indent + 2,
+                         "Maximum Bandwidth: %g (Bytes/sec)\n", asla->max_bw);
+       if (IS_SUBTLV(asla, EXT_MAX_RSV_BW))
+               sbuf_push(buf, indent + 2,
+                         "Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
+                         asla->max_rsv_bw);
+       if (IS_SUBTLV(asla, EXT_UNRSV_BW)) {
+               sbuf_push(buf, indent + 2, "Unreserved Bandwidth:\n");
+               for (int j = 0; j < MAX_CLASS_TYPE; j += 2) {
+                       sbuf_push(
+                               buf, indent + 2,
+                               "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
+                               j, asla->unrsv_bw[j], j + 1,
+                               asla->unrsv_bw[j + 1]);
+               }
+       }
+       if (IS_SUBTLV(asla, EXT_TE_METRIC))
+               sbuf_push(buf, indent + 2, "Traffic Engineering Metric: %u\n",
+                         asla->te_metric);
+       /* Extended metrics */
+       if (IS_SUBTLV(asla, EXT_DELAY))
+               sbuf_push(buf, indent + 2,
+                         "%s Average Link Delay: %u (micro-sec)\n",
+                         IS_ANORMAL(asla->delay) ? "Anomalous" : "Normal",
+                         asla->delay);
+       if (IS_SUBTLV(asla, EXT_MM_DELAY)) {
+               sbuf_push(buf, indent + 2,
+                         "%s Min/Max Link Delay: %u / %u (micro-sec)\n",
+                         IS_ANORMAL(asla->min_delay) ? "Anomalous" : "Normal",
+                         asla->min_delay & TE_EXT_MASK,
+                         asla->max_delay & TE_EXT_MASK);
+       }
+       if (IS_SUBTLV(asla, EXT_DELAY_VAR)) {
+               sbuf_push(buf, indent + 2, "Delay Variation: %u (micro-sec)\n",
+                         asla->delay_var & TE_EXT_MASK);
+       }
+       if (IS_SUBTLV(asla, EXT_PKT_LOSS))
+               sbuf_push(buf, indent + 2, "%s Link Packet Loss: %g (%%)\n",
+                         IS_ANORMAL(asla->pkt_loss) ? "Anomalous" : "Normal",
+                         (float)((asla->pkt_loss & TE_EXT_MASK) *
+                                 LOSS_PRECISION));
+       if (IS_SUBTLV(asla, EXT_RES_BW))
+               sbuf_push(buf, indent + 2,
+                         "Unidir. Residual Bandwidth: %g (Bytes/sec)\n",
+                         asla->res_bw);
+       if (IS_SUBTLV(asla, EXT_AVA_BW))
+               sbuf_push(buf, indent + 2,
+                         "Unidir. Available Bandwidth: %g (Bytes/sec)\n",
+                         asla->ava_bw);
+       if (IS_SUBTLV(asla, EXT_USE_BW))
+               sbuf_push(buf, indent + 2,
+                         "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n",
+                         asla->use_bw);
+}
+
 /* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */
 static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                                    struct sbuf *buf, struct json_object *json,
@@ -236,6 +373,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
        char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
        char aux_buf[255];
        char cnt_buf[255];
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node;
 
        /* Standard metrics */
        if (IS_SUBTLV(exts, EXT_ADM_GRP)) {
@@ -244,7 +383,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                                   exts->adm_group);
                        json_object_string_add(json, "adm-group", aux_buf);
                } else {
-                       sbuf_push(buf, indent, "Administrative Group: 0x%x\n",
+                       sbuf_push(buf, indent, "Admin Group: 0x%08x\n",
                                  exts->adm_group);
                        sbuf_push(buf, indent + 2, "Bit positions: %s\n",
                                  admin_group_standard_print(
@@ -661,7 +800,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                                sbuf_push(
                                        buf, indent,
                                        "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
-                                       "  Neighbor-ID: %s\n",
+                                       "  Neighbor-ID: %pSY\n",
                                        lan->sid, lan->weight,
                                        lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
                                                ? '1'
@@ -681,9 +820,11 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                                        lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
                                                ? '1'
                                                : '0',
-                                       isis_format_id(lan->neighbor_id, 6));
+                                       lan->neighbor_id);
                        }
        }
+       for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla))
+               format_item_asla_subtlvs(asla, buf, indent);
 }
 
 static void free_item_ext_subtlvs(struct  isis_ext_subtlvs *exts)
@@ -691,10 +832,124 @@ static void free_item_ext_subtlvs(struct  isis_ext_subtlvs *exts)
        isis_del_ext_subtlvs(exts);
 }
 
+static int pack_item_ext_subtlv_asla(struct isis_asla_subtlvs *asla,
+                                    struct stream *s, size_t *min_len)
+{
+       size_t subtlv_len;
+       size_t subtlv_len_pos;
+
+       /* Sub TLV header */
+       stream_putc(s, ISIS_SUBTLV_ASLA);
+
+       subtlv_len_pos = stream_get_endp(s);
+       stream_putc(s, 0); /* length will be filled later */
+
+       /* SABM Flag/Length */
+       if (asla->legacy)
+               stream_putc(s, ASLA_LEGACY_FLAG | asla->standard_apps_length);
+       else
+               stream_putc(s, asla->standard_apps_length);
+       stream_putc(s, asla->user_def_apps_length); /* UDABM Flag/Length */
+       stream_putc(s, asla->standard_apps);
+       stream_putc(s, asla->user_def_apps);
+
+       /* Administrative Group */
+       if (IS_SUBTLV(asla, EXT_ADM_GRP)) {
+               stream_putc(s, ISIS_SUBTLV_ADMIN_GRP);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putl(s, asla->admin_group);
+       }
+
+       /* Extended Administrative Group */
+       if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) &&
+           admin_group_nb_words(&asla->ext_admin_group) != 0) {
+               size_t ag_length;
+               size_t ag_length_pos;
+               struct admin_group *ag;
+
+               stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP);
+               ag_length_pos = stream_get_endp(s);
+               stream_putc(s, 0); /* length will be filled later*/
+
+               ag = &asla->ext_admin_group;
+               for (size_t i = 0; i < admin_group_nb_words(ag); i++)
+                       stream_putl(s, ag->bitmap.data[i]);
+
+               ag_length = stream_get_endp(s) - ag_length_pos - 1;
+               stream_putc_at(s, ag_length_pos, ag_length);
+       }
+
+       if (IS_SUBTLV(asla, EXT_MAX_BW)) {
+               stream_putc(s, ISIS_SUBTLV_MAX_BW);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putf(s, asla->max_bw);
+       }
+       if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) {
+               stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putf(s, asla->max_rsv_bw);
+       }
+       if (IS_SUBTLV(asla, EXT_UNRSV_BW)) {
+               stream_putc(s, ISIS_SUBTLV_UNRSV_BW);
+               stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE);
+               for (int j = 0; j < MAX_CLASS_TYPE; j++)
+                       stream_putf(s, asla->unrsv_bw[j]);
+       }
+       if (IS_SUBTLV(asla, EXT_TE_METRIC)) {
+               stream_putc(s, ISIS_SUBTLV_TE_METRIC);
+               stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE);
+               stream_put3(s, asla->te_metric);
+       }
+       if (IS_SUBTLV(asla, EXT_DELAY)) {
+               stream_putc(s, ISIS_SUBTLV_AV_DELAY);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putl(s, asla->delay);
+       }
+       if (IS_SUBTLV(asla, EXT_MM_DELAY)) {
+               stream_putc(s, ISIS_SUBTLV_MM_DELAY);
+               stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE);
+               stream_putl(s, asla->min_delay);
+               stream_putl(s, asla->max_delay);
+       }
+       if (IS_SUBTLV(asla, EXT_DELAY_VAR)) {
+               stream_putc(s, ISIS_SUBTLV_DELAY_VAR);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putl(s, asla->delay_var);
+       }
+       if (IS_SUBTLV(asla, EXT_PKT_LOSS)) {
+               stream_putc(s, ISIS_SUBTLV_PKT_LOSS);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putl(s, asla->pkt_loss);
+       }
+       if (IS_SUBTLV(asla, EXT_RES_BW)) {
+               stream_putc(s, ISIS_SUBTLV_RES_BW);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putf(s, asla->res_bw);
+       }
+       if (IS_SUBTLV(asla, EXT_AVA_BW)) {
+               stream_putc(s, ISIS_SUBTLV_AVA_BW);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putf(s, asla->ava_bw);
+       }
+       if (IS_SUBTLV(asla, EXT_USE_BW)) {
+               stream_putc(s, ISIS_SUBTLV_USE_BW);
+               stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
+               stream_putf(s, asla->use_bw);
+       }
+
+       subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
+       stream_putc_at(s, subtlv_len_pos, subtlv_len);
+
+       return 0;
+}
+
 static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                                 struct stream *s, size_t *min_len)
 {
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node;
        uint8_t size;
+       int ret;
 
        if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) {
                *min_len = ISIS_SUBTLV_MAX_SIZE;
@@ -858,6 +1113,219 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
                }
        }
 
+       for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) {
+               ret = pack_item_ext_subtlv_asla(asla, s, min_len);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int unpack_item_ext_subtlv_asla(uint16_t mtid, uint8_t subtlv_len,
+                                      struct stream *s, struct sbuf *log,
+                                      int indent,
+                                      struct isis_ext_subtlvs *exts)
+{
+       /* Standard App Identifier Bit Flags/Length */
+       uint8_t sabm_flag_len;
+       /* User-defined App Identifier Bit Flags/Length */
+       uint8_t uabm_flag_len;
+       uint8_t sabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0};
+       uint8_t uabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0};
+       uint8_t readable;
+       uint8_t subsubtlv_type;
+       uint8_t subsubtlv_len;
+       size_t nb_groups;
+       struct isis_asla_subtlvs *asla;
+
+       if (subtlv_len < ISIS_SUBSUBTLV_HDR_SIZE) {
+               TLV_SIZE_MISMATCH(log, indent, "ASLA");
+               return -1;
+       }
+
+
+       asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*asla));
+
+       admin_group_init(&asla->ext_admin_group);
+
+
+       sabm_flag_len = stream_getc(s);
+       uabm_flag_len = stream_getc(s);
+       asla->legacy = CHECK_FLAG(sabm_flag_len, ASLA_LEGACY_FLAG);
+       asla->standard_apps_length = ASLA_APPS_LENGTH_MASK & sabm_flag_len;
+       asla->user_def_apps_length = ASLA_APPS_LENGTH_MASK & uabm_flag_len;
+
+       for (int i = 0; i < asla->standard_apps_length; i++)
+               sabm[i] = stream_getc(s);
+       for (int i = 0; i < asla->user_def_apps_length; i++)
+               uabm[i] = stream_getc(s);
+
+       asla->standard_apps = sabm[0];
+       asla->user_def_apps = uabm[0];
+
+       readable = subtlv_len - 4;
+       while (readable > 0) {
+               if (readable < ISIS_SUBSUBTLV_HDR_SIZE) {
+                       TLV_SIZE_MISMATCH(log, indent, "ASLA Sub TLV");
+                       return -1;
+               }
+
+               subsubtlv_type = stream_getc(s);
+               subsubtlv_len = stream_getc(s);
+               readable -= ISIS_SUBSUBTLV_HDR_SIZE;
+
+
+               switch (subsubtlv_type) {
+               case ISIS_SUBTLV_ADMIN_GRP:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "ASLA Adm Group");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->admin_group = stream_getl(s);
+                               SET_SUBTLV(asla, EXT_ADM_GRP);
+                       }
+                       break;
+
+               case ISIS_SUBTLV_EXT_ADMIN_GRP:
+                       nb_groups = subsubtlv_len / sizeof(uint32_t);
+                       for (size_t i = 0; i < nb_groups; i++) {
+                               uint32_t val = stream_getl(s);
+
+                               admin_group_bulk_set(&asla->ext_admin_group,
+                                                    val, i);
+                       }
+                       SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
+                       break;
+               case ISIS_SUBTLV_MAX_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Maximum Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->max_bw = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_MAX_BW);
+                       }
+                       break;
+               case ISIS_SUBTLV_MAX_RSV_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Maximum Reservable Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->max_rsv_bw = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_MAX_RSV_BW);
+                       }
+                       break;
+               case ISIS_SUBTLV_UNRSV_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Unreserved Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               for (int i = 0; i < MAX_CLASS_TYPE; i++)
+                                       asla->unrsv_bw[i] = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_UNRSV_BW);
+                       }
+                       break;
+               case ISIS_SUBTLV_TE_METRIC:
+                       if (subsubtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Traffic Engineering Metric");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->te_metric = stream_get3(s);
+                               SET_SUBTLV(asla, EXT_TE_METRIC);
+                       }
+                       break;
+               /* Extended Metrics as defined in RFC 7810 */
+               case ISIS_SUBTLV_AV_DELAY:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Average Link Delay");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->delay = stream_getl(s);
+                               SET_SUBTLV(asla, EXT_DELAY);
+                       }
+                       break;
+               case ISIS_SUBTLV_MM_DELAY:
+                       if (subsubtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Min/Max Link Delay");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->min_delay = stream_getl(s);
+                               asla->max_delay = stream_getl(s);
+                               SET_SUBTLV(asla, EXT_MM_DELAY);
+                       }
+                       break;
+               case ISIS_SUBTLV_DELAY_VAR:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Delay Variation");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->delay_var = stream_getl(s);
+                               SET_SUBTLV(asla, EXT_DELAY_VAR);
+                       }
+                       break;
+               case ISIS_SUBTLV_PKT_LOSS:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Link Packet Loss");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->pkt_loss = stream_getl(s);
+                               SET_SUBTLV(asla, EXT_PKT_LOSS);
+                       }
+                       break;
+               case ISIS_SUBTLV_RES_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Residual Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->res_bw = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_RES_BW);
+                       }
+                       break;
+               case ISIS_SUBTLV_AVA_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Available Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->ava_bw = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_AVA_BW);
+                       }
+                       break;
+               case ISIS_SUBTLV_USE_BW:
+                       if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Utilized Bandwidth");
+                               stream_forward_getp(s, subsubtlv_len);
+                       } else {
+                               asla->use_bw = stream_getf(s);
+                               SET_SUBTLV(asla, EXT_USE_BW);
+                       }
+                       break;
+               default:
+                       zlog_debug("unknown (t,l)=(%u,%u)", subsubtlv_type,
+                                  subsubtlv_len);
+                       stream_forward_getp(s, subsubtlv_len);
+                       break;
+               }
+               readable -= subsubtlv_len;
+       }
+
+       listnode_add(exts->aslas, asla);
+
        return 0;
 }
 
@@ -896,8 +1364,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                /* Standard Metric as defined in RFC5305 */
                case ISIS_SUBTLV_ADMIN_GRP:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Administrative Group!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Administrative Group");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->adm_group = stream_getl(s);
@@ -915,8 +1383,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_LLRI:
                        if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Link ID!\n");
+                               TLV_SIZE_MISMATCH(log, indent, "Link ID");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->local_llri = stream_getl(s);
@@ -926,8 +1393,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_LOCAL_IPADDR:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Local IP address!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Local IP address");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                stream_get(&exts->local_addr.s_addr, s, 4);
@@ -936,8 +1403,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_RMT_IPADDR:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Remote IP address!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Remote IP address");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                stream_get(&exts->neigh_addr.s_addr, s, 4);
@@ -946,8 +1413,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_LOCAL_IPADDR6:
                        if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Local IPv6 address!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Local IPv6 address");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                stream_get(&exts->local_addr6, s, 16);
@@ -956,8 +1423,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_RMT_IPADDR6:
                        if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Remote IPv6 address!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Remote IPv6 address");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                stream_get(&exts->neigh_addr6, s, 16);
@@ -966,8 +1433,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_MAX_BW:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Maximum Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Maximum Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->max_bw = stream_getf(s);
@@ -976,8 +1443,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_MAX_RSV_BW:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Maximum Reservable Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Maximum Reservable Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->max_rsv_bw = stream_getf(s);
@@ -986,8 +1454,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_UNRSV_BW:
                        if (subtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Unreserved Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Unreserved Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                for (int i = 0; i < MAX_CLASS_TYPE; i++)
@@ -997,8 +1465,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_TE_METRIC:
                        if (subtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Traffic Engineering Metric!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Traffic Engineering Metric");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->te_metric = stream_get3(s);
@@ -1007,8 +1475,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_RAS:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Remote AS number!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Remote AS number");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->remote_as = stream_getl(s);
@@ -1017,8 +1485,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_RIP:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Remote ASBR IP Address!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Remote ASBR IP Address");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                stream_get(&exts->remote_ip.s_addr, s, 4);
@@ -1028,8 +1496,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                /* Extended Metrics as defined in RFC 7810 */
                case ISIS_SUBTLV_AV_DELAY:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Average Link Delay!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Average Link Delay");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->delay = stream_getl(s);
@@ -1038,8 +1506,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_MM_DELAY:
                        if (subtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Min/Max Link Delay!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Min/Max Link Delay");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->min_delay = stream_getl(s);
@@ -1049,8 +1517,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_DELAY_VAR:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Delay Variation!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Delay Variation");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->delay_var = stream_getl(s);
@@ -1059,8 +1527,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_PKT_LOSS:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Link Packet Loss!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "Link Packet Loss");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->pkt_loss = stream_getl(s);
@@ -1069,8 +1537,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_RES_BW:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Residual Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->res_bw = stream_getf(s);
@@ -1079,8 +1548,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_AVA_BW:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Unidirectional Available Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Available Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->ava_bw = stream_getf(s);
@@ -1089,8 +1559,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                        break;
                case ISIS_SUBTLV_USE_BW:
                        if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n");
+                               TLV_SIZE_MISMATCH(
+                                       log, indent,
+                                       "Unidirectional Utilized Bandwidth");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                exts->use_bw = stream_getf(s);
@@ -1101,8 +1572,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                case ISIS_SUBTLV_ADJ_SID:
                        if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE
                            && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for Adjacency SID!\n");
+                               TLV_SIZE_MISMATCH(log, indent, "Adjacency SID");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                struct isis_adj_sid *adj;
@@ -1113,9 +1583,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                                adj->weight = stream_getc(s);
                                if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
                                    && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE) {
-                                       sbuf_push(
-                                               log, indent,
-                                               "TLV size does not match expected size for Adjacency SID!\n");
+                                       TLV_SIZE_MISMATCH(log, indent,
+                                                         "Adjacency SID");
                                        stream_forward_getp(s, subtlv_len - 2);
                                        XFREE(MTYPE_ISIS_SUBTLV, adj);
                                        break;
@@ -1125,9 +1594,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                                    && subtlv_len
                                               != ISIS_SUBTLV_ADJ_SID_SIZE
                                                          + 1) {
-                                       sbuf_push(
-                                               log, indent,
-                                               "TLV size does not match expected size for Adjacency SID!\n");
+                                       TLV_SIZE_MISMATCH(log, indent,
+                                                         "Adjacency SID");
                                        stream_forward_getp(s, subtlv_len - 2);
                                        XFREE(MTYPE_ISIS_SUBTLV, adj);
                                        break;
@@ -1152,8 +1620,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                case ISIS_SUBTLV_LAN_ADJ_SID:
                        if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE
                            && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) {
-                               sbuf_push(log, indent,
-                                         "TLV size does not match expected size for LAN-Adjacency SID!\n");
+                               TLV_SIZE_MISMATCH(log, indent,
+                                                 "LAN-Adjacency SID");
                                stream_forward_getp(s, subtlv_len);
                        } else {
                                struct isis_lan_adj_sid *lan;
@@ -1168,9 +1636,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                                if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
                                    && subtlv_len
                                               != ISIS_SUBTLV_LAN_ADJ_SID_SIZE) {
-                                       sbuf_push(
-                                               log, indent,
-                                               "TLV size does not match expected size for LAN-Adjacency SID!\n");
+                                       TLV_SIZE_MISMATCH(log, indent,
+                                                         "LAN-Adjacency SID");
                                        stream_forward_getp(
                                                s, subtlv_len - 2
                                                           - ISIS_SYS_ID_LEN);
@@ -1182,9 +1649,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                                    && subtlv_len
                                               != ISIS_SUBTLV_LAN_ADJ_SID_SIZE
                                                          + 1) {
-                                       sbuf_push(
-                                               log, indent,
-                                               "TLV size does not match expected size for LAN-Adjacency SID!\n");
+                                       TLV_SIZE_MISMATCH(log, indent,
+                                                         "LAN-Adjacency SID");
                                        stream_forward_getp(
                                                s, subtlv_len - 2
                                                           - ISIS_SYS_ID_LEN);
@@ -1207,6 +1673,13 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
                                SET_SUBTLV(exts, EXT_LAN_ADJ_SID);
                        }
                        break;
+               case ISIS_SUBTLV_ASLA:
+                       if (unpack_item_ext_subtlv_asla(mtid, subtlv_len, s,
+                                                       log, indent,
+                                                       exts) < 0) {
+                               sbuf_push(log, indent, "TLV parse error");
+                       }
+                       break;
                default:
                        /* Skip unknown TLV */
                        stream_forward_getp(s, subtlv_len);
@@ -1590,14 +2063,14 @@ static void format_item_area_address(uint16_t mtid, struct isis_item *i,
                                     int indent)
 {
        struct isis_area_address *addr = (struct isis_area_address *)i;
+       struct iso_address iso_addr;
 
-       if (json) {
-               json_object_string_add(json, "area-addr",
-                                      isonet_print(addr->addr, addr->len));
-       } else {
-               sbuf_push(buf, indent, "Area Address: %s\n",
-                         isonet_print(addr->addr, addr->len));
-       }
+       memcpy(iso_addr.area_addr, addr->addr, ISO_ADDR_SIZE);
+       iso_addr.addr_len = addr->len;
+       if (json)
+               json_object_string_addf(json, "area-addr", "%pIS", &iso_addr);
+       else
+               sbuf_push(buf, indent, "Area Address: %pIS\n", &iso_addr);
 }
 
 static void free_item_area_address(struct isis_item *i)
@@ -1678,17 +2151,18 @@ static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
                                       struct json_object *json, int indent)
 {
        struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
+       char sys_id[ISO_SYSID_STRLEN];
 
+       snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id);
        if (json) {
                struct json_object *old_json;
                old_json = json_object_new_object();
                json_object_object_add(json, "old-reach-style", old_json);
-               json_object_string_add(old_json, "is-reach",
-                                      isis_format_id(r->id, 7));
+               json_object_string_add(old_json, "is-reach", sys_id);
                json_object_int_add(old_json, "metric", r->metric);
        } else
                sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n",
-                         isis_format_id(r->id, 7), r->metric);
+                         sys_id, r->metric);
 }
 
 static void free_item_oldstyle_reach(struct isis_item *i)
@@ -1760,13 +2234,13 @@ static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i,
                                     int indent)
 {
        struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
+       char sys_id[ISO_SYSID_STRLEN];
 
-       if (json) {
-               json_object_string_add(json, "lan-neighbor",
-                                      isis_format_id(n->mac, 6));
-       } else
-               sbuf_push(buf, indent, "LAN Neighbor: %s\n",
-                         isis_format_id(n->mac, 6));
+       snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", n->mac);
+       if (json)
+               json_object_string_add(json, "lan-neighbor", sys_id);
+       else
+               sbuf_push(buf, indent, "LAN Neighbor: %s\n", sys_id);
 }
 
 static void free_item_lan_neighbor(struct isis_item *i)
@@ -1831,23 +2305,25 @@ static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
                                  int indent)
 {
        struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
+       char sys_id[ISO_SYSID_STRLEN];
 
+       snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pLS", e->id);
        if (json) {
                char buf[255];
                struct json_object *lsp_json;
                lsp_json = json_object_new_object();
                json_object_object_add(json, "lsp-entry", lsp_json);
-               json_object_string_add(lsp_json, "id", isis_format_id(e->id, 8));
+               json_object_string_add(lsp_json, "id", sys_id);
                snprintfrr(buf,sizeof(buf),"0x%08x",e->seqno);
                json_object_string_add(lsp_json, "seq", buf);
                snprintfrr(buf,sizeof(buf),"0x%04hx",e->checksum);
                json_object_string_add(lsp_json, "chksum", buf);
                json_object_int_add(lsp_json, "lifetime", e->checksum);
        } else
-       sbuf_push(buf, indent,
-                 "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n",
-                 isis_format_id(e->id, 8), e->seqno, e->checksum,
-                 e->rem_lifetime);
+               sbuf_push(
+                       buf, indent,
+                       "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n",
+                       sys_id, e->seqno, e->checksum, e->rem_lifetime);
 }
 
 static void free_item_lsp_entry(struct isis_item *i)
@@ -1919,7 +2395,9 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
                                       struct json_object *json, int indent)
 {
        struct isis_extended_reach *r = (struct isis_extended_reach *)i;
+       char sys_id[ISO_SYSID_STRLEN];
 
+       snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id);
        if (json) {
                struct json_object *reach_json;
                reach_json = json_object_new_object();
@@ -1927,8 +2405,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
                json_object_string_add(
                        reach_json, "mt-id",
                        (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT");
-               json_object_string_add(reach_json, "id",
-                                      isis_format_id(r->id, 7));
+               json_object_string_add(reach_json, "id", sys_id);
                json_object_int_add(reach_json, "metric", r->metric);
                if (mtid != ISIS_MT_IPV4_UNICAST)
                        json_object_string_add(reach_json, "mt-name",
@@ -1940,7 +2417,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
        } else {
                sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)",
                          (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
-                         isis_format_id(r->id, 7), r->metric);
+                         sys_id, r->metric);
                if (mtid != ISIS_MT_IPV4_UNICAST)
                        sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
                sbuf_push(buf, 0, "\n");
@@ -3125,9 +3602,12 @@ static void
 format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
                        struct sbuf *buf, struct json_object *json, int indent)
 {
+       char sys_id[ISO_SYSID_STRLEN];
+
        if (!threeway_adj)
                return;
 
+       snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", threeway_adj->neighbor_id);
        if (json) {
                struct json_object *three_json;
                three_json = json_object_new_object();
@@ -3140,9 +3620,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
                                    threeway_adj->local_circuit_id);
                if (!threeway_adj->neighbor_set)
                        return;
-               json_object_string_add(
-                       three_json, "neigh-system-id",
-                       isis_format_id(threeway_adj->neighbor_id, 6));
+               json_object_string_add(three_json, "neigh-system-id", sys_id);
                json_object_int_add(three_json, "neigh-ext-circuit-id",
                                    threeway_adj->neighbor_circuit_id);
        } else {
@@ -3155,8 +3633,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
                if (!threeway_adj->neighbor_set)
                        return;
 
-               sbuf_push(buf, indent, "  Neighbor System ID: %s\n",
-                         isis_format_id(threeway_adj->neighbor_id, 6));
+               sbuf_push(buf, indent, "  Neighbor System ID: %s\n", sys_id);
                sbuf_push(buf, indent, "  Neighbor Extended Circuit ID: %u\n",
                          threeway_adj->neighbor_circuit_id);
        }
@@ -3437,6 +3914,39 @@ static struct isis_router_cap *copy_tlv_router_cap(
 
        memcpy(rv, router_cap, sizeof(*rv));
 
+#ifndef FABRICD
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               struct isis_router_cap_fad *sc_fad;
+               struct isis_router_cap_fad *rv_fad;
+
+               sc_fad = router_cap->fads[i];
+               if (!sc_fad)
+                       continue;
+               rv_fad = XMALLOC(MTYPE_ISIS_TLV,
+                                sizeof(struct isis_router_cap_fad));
+               *rv_fad = *sc_fad;
+               rv_fad->fad.admin_group_exclude_any.bitmap.data = NULL;
+               rv_fad->fad.admin_group_include_any.bitmap.data = NULL;
+               rv_fad->fad.admin_group_include_all.bitmap.data = NULL;
+
+               assert(bf_is_inited(
+                       sc_fad->fad.admin_group_exclude_any.bitmap));
+               assert(bf_is_inited(
+                       sc_fad->fad.admin_group_include_any.bitmap));
+               assert(bf_is_inited(
+                       sc_fad->fad.admin_group_include_all.bitmap));
+
+               admin_group_copy(&rv_fad->fad.admin_group_exclude_any,
+                                &sc_fad->fad.admin_group_exclude_any);
+               admin_group_copy(&rv_fad->fad.admin_group_include_any,
+                                &sc_fad->fad.admin_group_include_any);
+               admin_group_copy(&rv_fad->fad.admin_group_include_all,
+                                &sc_fad->fad.admin_group_include_all);
+
+               rv->fads[i] = rv_fad;
+       }
+#endif /* ifndef FABRICD */
+
        return rv;
 }
 
@@ -3548,46 +4058,186 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap,
                for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
                        if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
                                sbuf_push(buf, indent, "    %u: %s\n", i,
-                                         router_cap->algo[i] == 0
-                                                 ? "SPF"
-                                                 : "Strict SPF");
+                                         sr_algorithm_string(
+                                                 router_cap->algo[i]));
        }
 
        /* Segment Routing Node MSD as per RFC8491 section #2 */
        if (router_cap->msd != 0)
                sbuf_push(buf, indent, "  Node Maximum SID Depth: %u\n",
                          router_cap->msd);
+
+#ifndef FABRICD
+       /* Flex-Algo */
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
+               int indent2;
+               struct admin_group *admin_group;
+               struct isis_router_cap_fad *fad;
+
+               fad = router_cap->fads[i];
+               if (!fad)
+                       continue;
+
+               sbuf_push(buf, indent, "  Flex-Algo Definition: %d\n",
+                         fad->fad.algorithm);
+               sbuf_push(buf, indent, "    Metric-Type: %d\n",
+                         fad->fad.metric_type);
+               sbuf_push(buf, indent, "    Calc-Type: %d\n",
+                         fad->fad.calc_type);
+               sbuf_push(buf, indent, "    Priority: %d\n", fad->fad.priority);
+
+               indent2 = indent + strlen("    Exclude-Any: ");
+               admin_group = &fad->fad.admin_group_exclude_any;
+               sbuf_push(buf, indent, "    Exclude-Any: ");
+               sbuf_push(buf, 0, "%s\n",
+                         admin_group_string(admin_group_buf,
+                                            ADMIN_GROUP_PRINT_MAX_SIZE,
+                                            indent2, admin_group));
+
+               indent2 = indent + strlen("    Include-Any: ");
+               admin_group = &fad->fad.admin_group_include_any;
+               sbuf_push(buf, indent, "    Include-Any: ");
+               sbuf_push(buf, 0, "%s\n",
+                         admin_group_string(admin_group_buf,
+                                            ADMIN_GROUP_PRINT_MAX_SIZE,
+                                            indent2, admin_group));
+
+               indent2 = indent + strlen("    Include-All: ");
+               admin_group = &fad->fad.admin_group_include_all;
+               sbuf_push(buf, indent, "    Include-All: ");
+               sbuf_push(buf, 0, "%s\n",
+                         admin_group_string(admin_group_buf,
+                                            ADMIN_GROUP_PRINT_MAX_SIZE,
+                                            indent2, admin_group));
+
+               sbuf_push(buf, indent, "    M-Flag: %c\n",
+                         CHECK_FLAG(fad->fad.flags, FAD_FLAG_M) ? '1' : '0');
+
+               if (fad->fad.flags != 0 && fad->fad.flags != FAD_FLAG_M)
+                       sbuf_push(buf, indent, "    Flags: 0x%x\n",
+                                 fad->fad.flags);
+               if (fad->fad.exclude_srlg)
+                       sbuf_push(buf, indent, "    Exclude SRLG: Enabled\n");
+               if (fad->fad.unsupported_subtlv)
+                       sbuf_push(buf, indent,
+                                 "    Got an unsupported sub-TLV: Yes\n");
+       }
+#endif /* ifndef FABRICD */
 }
 
 static void free_tlv_router_cap(struct isis_router_cap *router_cap)
 {
+       if (!router_cap)
+               return;
+
+#ifndef FABRICD
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               struct isis_router_cap_fad *fad;
+
+               fad = router_cap->fads[i];
+               if (!fad)
+                       continue;
+               admin_group_term(&fad->fad.admin_group_exclude_any);
+               admin_group_term(&fad->fad.admin_group_include_any);
+               admin_group_term(&fad->fad.admin_group_include_all);
+               XFREE(MTYPE_ISIS_TLV, fad);
+       }
+#endif /* ifndef FABRICD */
+
        XFREE(MTYPE_ISIS_TLV, router_cap);
 }
 
+#ifndef FABRICD
+static size_t
+isis_router_cap_fad_sub_tlv_len(const struct isis_router_cap_fad *fad)
+{
+       size_t sz = ISIS_SUBTLV_FAD_MIN_SIZE;
+       uint32_t admin_group_length;
+
+       admin_group_length =
+               admin_group_nb_words(&fad->fad.admin_group_exclude_any);
+       if (admin_group_length)
+               sz += sizeof(uint32_t) * admin_group_length + 2;
+
+       admin_group_length =
+               admin_group_nb_words(&fad->fad.admin_group_include_any);
+       if (admin_group_length)
+               sz += sizeof(uint32_t) * admin_group_length + 2;
+
+       admin_group_length =
+               admin_group_nb_words(&fad->fad.admin_group_include_all);
+       if (admin_group_length)
+               sz += sizeof(uint32_t) * admin_group_length + 2;
+
+       if (fad->fad.flags != 0)
+               sz += ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE + 2;
+
+       /* TODO: add exclude SRLG sub-sub-TLV length when supported */
+
+       return sz;
+}
+#endif /* ifndef FABRICD */
+
+static size_t isis_router_cap_tlv_size(const struct isis_router_cap *router_cap)
+{
+       size_t sz = 2 + ISIS_ROUTER_CAP_SIZE;
+#ifndef FABRICD
+       size_t fad_sz;
+#endif /* ifndef FABRICD */
+       int nb_algo;
+
+       if ((router_cap->srgb.range_size != 0) &&
+           (router_cap->srgb.lower_bound != 0)) {
+               sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE;
+               sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE;
+
+               nb_algo = isis_tlvs_sr_algo_count(router_cap);
+               if (nb_algo != 0)
+                       sz += 2 + nb_algo;
+
+               if ((router_cap->srlb.range_size != 0) &&
+                   (router_cap->srlb.lower_bound != 0)) {
+                       sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE;
+                       sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE;
+               }
+
+               if (router_cap->msd != 0)
+                       sz += 2 + ISIS_SUBTLV_NODE_MSD_SIZE;
+       }
+
+#ifndef FABRICD
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               if (!router_cap->fads[i])
+                       continue;
+               fad_sz = 2 +
+                        isis_router_cap_fad_sub_tlv_len(router_cap->fads[i]);
+               if (((sz + fad_sz) % 256) < (sz % 256))
+                       sz += 2 + ISIS_ROUTER_CAP_SIZE + fad_sz;
+               else
+                       sz += fad_sz;
+       }
+#endif /* ifndef FABRICD */
+
+       return sz;
+}
+
 static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
                               struct stream *s)
 {
-       size_t tlv_len = ISIS_ROUTER_CAP_SIZE;
-       size_t len_pos;
+       size_t tlv_len, len_pos;
        uint8_t nb_algo;
 
        if (!router_cap)
                return 0;
 
-       /* Compute Maximum TLV size */
-       tlv_len += ISIS_SUBTLV_SID_LABEL_RANGE_SIZE
-               + ISIS_SUBTLV_HDR_SIZE
-               + ISIS_SUBTLV_ALGORITHM_SIZE
-               + ISIS_SUBTLV_NODE_MSD_SIZE;
-
-       if (STREAM_WRITEABLE(s) < (unsigned int)(2 + tlv_len))
+       if (STREAM_WRITEABLE(s) < isis_router_cap_tlv_size(router_cap))
                return 1;
 
        /* Add Router Capability TLV 242 with Router ID and Flags */
        stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY);
-       /* Real length will be adjusted later */
        len_pos = stream_get_endp(s);
-       stream_putc(s, tlv_len);
+       stream_putc(s, 0); /* Real length will be adjusted later */
        stream_put_ipv4(s, router_cap->router_id.s_addr);
        stream_putc(s, router_cap->flags);
 
@@ -3603,14 +4253,13 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
                stream_put3(s, router_cap->srgb.lower_bound);
 
                /* Then SR Algorithm if set as per RFC8667 section #3.2 */
-               for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++)
-                       if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET)
-                               break;
+               nb_algo = isis_tlvs_sr_algo_count(router_cap);
                if (nb_algo > 0) {
                        stream_putc(s, ISIS_SUBTLV_ALGORITHM);
                        stream_putc(s, nb_algo);
-                       for (int i = 0; i < nb_algo; i++)
-                               stream_putc(s, router_cap->algo[i]);
+                       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                               if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
+                                       stream_putc(s, router_cap->algo[i]);
                }
 
                /* Local Block if defined as per RFC8667 section #3.3 */
@@ -3635,6 +4284,80 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
                }
        }
 
+#ifndef FABRICD
+       /* Flex Algo Definitions */
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+               struct isis_router_cap_fad *fad;
+               size_t subtlv_len;
+               struct admin_group *ag;
+               uint32_t admin_group_length;
+
+               fad = router_cap->fads[i];
+               if (!fad)
+                       continue;
+
+               subtlv_len = isis_router_cap_fad_sub_tlv_len(fad);
+
+               if ((stream_get_endp(s) - len_pos - 1) > 250) {
+                       /* Adjust TLV length which depends on subTLVs presence
+                        */
+                       tlv_len = stream_get_endp(s) - len_pos - 1;
+                       stream_putc_at(s, len_pos, tlv_len);
+
+                       /* Add Router Capability TLV 242 with Router ID and
+                        * Flags
+                        */
+                       stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY);
+                       /* Real length will be adjusted later */
+                       len_pos = stream_get_endp(s);
+                       stream_putc(s, 0);
+                       stream_put_ipv4(s, router_cap->router_id.s_addr);
+                       stream_putc(s, router_cap->flags);
+               }
+
+               stream_putc(s, ISIS_SUBTLV_FAD);
+               stream_putc(s, subtlv_len); /* length will be filled later */
+
+               stream_putc(s, fad->fad.algorithm);
+               stream_putc(s, fad->fad.metric_type);
+               stream_putc(s, fad->fad.calc_type);
+               stream_putc(s, fad->fad.priority);
+
+               ag = &fad->fad.admin_group_exclude_any;
+               admin_group_length = admin_group_nb_words(ag);
+               if (admin_group_length) {
+                       stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG);
+                       stream_putc(s, sizeof(uint32_t) * admin_group_length);
+                       for (size_t i = 0; i < admin_group_length; i++)
+                               stream_putl(s, admin_group_get_offset(ag, i));
+               }
+
+               ag = &fad->fad.admin_group_include_any;
+               admin_group_length = admin_group_nb_words(ag);
+               if (admin_group_length) {
+                       stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG);
+                       stream_putc(s, sizeof(uint32_t) * admin_group_length);
+                       for (size_t i = 0; i < admin_group_length; i++)
+                               stream_putl(s, admin_group_get_offset(ag, i));
+               }
+
+               ag = &fad->fad.admin_group_include_all;
+               admin_group_length = admin_group_nb_words(ag);
+               if (admin_group_length) {
+                       stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG);
+                       stream_putc(s, sizeof(uint32_t) * admin_group_length);
+                       for (size_t i = 0; i < admin_group_length; i++)
+                               stream_putl(s, admin_group_get_offset(ag, i));
+               }
+
+               if (fad->fad.flags != 0) {
+                       stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS);
+                       stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE);
+                       stream_putc(s, fad->fad.flags);
+               }
+       }
+#endif /* ifndef FABRICD */
+
        /* Adjust TLV length which depends on subTLVs presence */
        tlv_len = stream_get_endp(s) - len_pos - 1;
        stream_putc_at(s, len_pos, tlv_len);
@@ -3661,18 +4384,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context,
                return 0;
        }
 
-       if (tlvs->router_cap) {
-               sbuf_push(log, indent,
-                         "WARNING: Router Capability TLV present multiple times.\n");
-               stream_forward_getp(s, tlv_len);
-               return 0;
+       if (tlvs->router_cap)
+               /* Multiple Router Capability found */
+               rcap = tlvs->router_cap;
+       else {
+               /* Allocate router cap structure and initialize SR Algorithms */
+               rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap));
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                       rcap->algo[i] = SR_ALGORITHM_UNSET;
        }
 
-       /* Allocate router cap structure and initialize SR Algorithms */
-       rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap));
-       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
-               rcap->algo[i] = SR_ALGORITHM_UNSET;
-
        /* Get Router ID and Flags */
        rcap->router_id.s_addr = stream_get_ipv4(s);
        rcap->flags = stream_getc(s);
@@ -3680,6 +4401,10 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context,
        /* Parse remaining part of the TLV if present */
        subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
        while (subtlv_len > 2) {
+#ifndef FABRICD
+               struct isis_router_cap_fad *fad;
+               uint8_t subsubtlvs_len;
+#endif /* ifndef FABRICD */
                uint8_t msd_type;
 
                type = stream_getc(s);
@@ -3752,14 +4477,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context,
                case ISIS_SUBTLV_ALGORITHM:
                        if (length == 0)
                                break;
-                       /* Only 2 algorithms are supported: SPF & Strict SPF */
-                       stream_get(&rcap->algo, s,
-                                  length > SR_ALGORITHM_COUNT
-                                          ? SR_ALGORITHM_COUNT
-                                          : length);
-                       if (length > SR_ALGORITHM_COUNT)
-                               stream_forward_getp(
-                                       s, length - SR_ALGORITHM_COUNT);
+
+                       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+                               rcap->algo[i] = SR_ALGORITHM_UNSET;
+
+                       for (int i = 0; i < length; i++) {
+                               uint8_t algo;
+
+                               algo = stream_getc(s);
+                               rcap->algo[algo] = algo;
+                       }
                        break;
                case ISIS_SUBTLV_SRLB:
                        /* Check that SRLB is correctly formated */
@@ -3831,6 +4558,80 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context,
                        if (length > MSD_TLV_SIZE)
                                stream_forward_getp(s, length - MSD_TLV_SIZE);
                        break;
+#ifndef FABRICD
+               case ISIS_SUBTLV_FAD:
+                       fad = XCALLOC(MTYPE_ISIS_TLV,
+                                     sizeof(struct isis_router_cap_fad));
+                       fad->fad.algorithm = stream_getc(s);
+                       fad->fad.metric_type = stream_getc(s);
+                       fad->fad.calc_type = stream_getc(s);
+                       fad->fad.priority = stream_getc(s);
+                       rcap->fads[fad->fad.algorithm] = fad;
+                       admin_group_init(&fad->fad.admin_group_exclude_any);
+                       admin_group_init(&fad->fad.admin_group_include_any);
+                       admin_group_init(&fad->fad.admin_group_include_all);
+
+                       subsubtlvs_len = length - 4;
+                       while (subsubtlvs_len > 2) {
+                               struct admin_group *ag;
+                               uint8_t subsubtlv_type;
+                               uint8_t subsubtlv_len;
+                               uint32_t v;
+                               int n_ag, i;
+
+                               subsubtlv_type = stream_getc(s);
+                               subsubtlv_len = stream_getc(s);
+
+                               switch (subsubtlv_type) {
+                               case ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG:
+                                       ag = &fad->fad.admin_group_exclude_any;
+                                       n_ag = subsubtlv_len / sizeof(uint32_t);
+                                       for (i = 0; i < n_ag; i++) {
+                                               v = stream_getl(s);
+                                               admin_group_bulk_set(ag, v, i);
+                                       }
+                                       break;
+                               case ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG:
+                                       ag = &fad->fad.admin_group_include_any;
+                                       n_ag = subsubtlv_len / sizeof(uint32_t);
+                                       for (i = 0; i < n_ag; i++) {
+                                               v = stream_getl(s);
+                                               admin_group_bulk_set(ag, v, i);
+                                       }
+                                       break;
+                               case ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG:
+                                       ag = &fad->fad.admin_group_include_all;
+                                       n_ag = subsubtlv_len / sizeof(uint32_t);
+                                       for (i = 0; i < n_ag; i++) {
+                                               v = stream_getl(s);
+                                               admin_group_bulk_set(ag, v, i);
+                                       }
+                                       break;
+                               case ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS:
+                                       if (subsubtlv_len == 0)
+                                               break;
+
+                                       fad->fad.flags = stream_getc(s);
+                                       for (i = subsubtlv_len - 1; i > 0; --i)
+                                               stream_getc(s);
+                                       break;
+                               case ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG:
+                                       fad->fad.exclude_srlg = true;
+                                       stream_forward_getp(s, subsubtlv_len);
+                                       break;
+                               default:
+                                       sbuf_push(
+                                               log, indent,
+                                               "Received an unsupported Flex-Algo sub-TLV type %u\n",
+                                               subsubtlv_type);
+                                       fad->fad.unsupported_subtlv = true;
+                                       stream_forward_getp(s, subsubtlv_len);
+                                       break;
+                               }
+                               subsubtlvs_len -= 2 + subsubtlv_len;
+                       }
+                       break;
+#endif /* ifndef FABRICD */
                default:
                        stream_forward_getp(s, length);
                        break;
@@ -3988,33 +4789,29 @@ static void format_tlv_purge_originator(struct isis_purge_originator *poi,
                                        struct sbuf *buf,
                                        struct json_object *json, int indent)
 {
+       char sen_id[ISO_SYSID_STRLEN];
+       char gen_id[ISO_SYSID_STRLEN];
+
        if (!poi)
                return;
 
+       snprintfrr(gen_id, ISO_SYSID_STRLEN, "%pSY", poi->generator);
+       if (poi->sender_set)
+               snprintfrr(sen_id, ISO_SYSID_STRLEN, "%pSY", poi->sender);
+
        if (json) {
                struct json_object *purge_json;
                purge_json = json_object_new_object();
                json_object_object_add(json, "purge_originator", purge_json);
 
-               json_object_string_add(
-                       purge_json, "id",
-                       isis_format_id(poi->generator, sizeof(poi->generator)));
-               if (poi->sender_set) {
-                       json_object_string_add(
-                               purge_json, "rec-from",
-                               isis_format_id(poi->sender,
-                                              sizeof(poi->sender)));
-               }
+               json_object_string_add(purge_json, "id", gen_id);
+               if (poi->sender_set)
+                       json_object_string_add(purge_json, "rec-from", sen_id);
        } else {
                sbuf_push(buf, indent, "Purge Originator Identification:\n");
-               sbuf_push(
-                       buf, indent, "  Generator: %s\n",
-                       isis_format_id(poi->generator, sizeof(poi->generator)));
-               if (poi->sender_set) {
-                       sbuf_push(buf, indent, "  Received-From: %s\n",
-                                 isis_format_id(poi->sender,
-                                                sizeof(poi->sender)));
-               }
+               sbuf_push(buf, indent, "  Generator: %s\n", gen_id);
+               if (poi->sender_set)
+                       sbuf_push(buf, indent, "  Received-From: %s\n", sen_id);
        }
 }
 
@@ -5271,14 +6068,14 @@ void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
                                  struct list *addresses)
 {
        struct listnode *node;
-       struct area_addr *area_addr;
+       struct iso_address *area_addr;
 
        for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
                struct isis_area_address *a =
                        XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
 
                a->len = area_addr->addr_len;
-               memcpy(a->addr, area_addr->area_addr, 20);
+               memcpy(a->addr, area_addr->area_addr, ISO_ADDR_SIZE);
                append_item(&tlvs->area_addresses, (struct isis_item *)a);
        }
 }
@@ -5475,7 +6272,7 @@ bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
        for (struct isis_area_address *addr = addr_head; addr;
             addr = addr->next) {
                struct listnode *node;
-               struct area_addr *a;
+               struct iso_address *a;
 
                for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
                        if (a->addr_len == addr->len
@@ -5782,16 +6579,61 @@ void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
                tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
 }
 
-/* Set Router Capability TLV parameters */
-void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs,
-                                    const struct isis_router_cap *cap)
+/* Init Router Capability TLV parameters */
+struct isis_router_cap *isis_tlvs_init_router_capability(struct isis_tlvs *tlvs)
 {
-       XFREE(MTYPE_ISIS_TLV, tlvs->router_cap);
-       if (!cap)
-               return;
-
        tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap));
-       *tlvs->router_cap = *cap;
+
+       /* init SR algo list content to the default value */
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+               tlvs->router_cap->algo[i] = SR_ALGORITHM_UNSET;
+
+       return tlvs->router_cap;
+}
+
+#ifndef FABRICD
+void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs,
+                                        struct flex_algo *fa, int algorithm,
+                                        uint8_t *sysid)
+{
+       struct isis_router_cap_fad *rcap_fad;
+
+       assert(tlvs->router_cap);
+
+       rcap_fad = tlvs->router_cap->fads[algorithm];
+
+       if (!rcap_fad)
+               rcap_fad = XCALLOC(MTYPE_ISIS_TLV,
+                                  sizeof(struct isis_router_cap_fad));
+
+       memset(rcap_fad->sysid, 0, ISIS_SYS_ID_LEN + 2);
+       memcpy(rcap_fad->sysid, sysid, ISIS_SYS_ID_LEN);
+
+       memcpy(&rcap_fad->fad, fa, sizeof(struct flex_algo));
+
+       rcap_fad->fad.admin_group_exclude_any.bitmap.data = NULL;
+       rcap_fad->fad.admin_group_include_any.bitmap.data = NULL;
+       rcap_fad->fad.admin_group_include_all.bitmap.data = NULL;
+
+       admin_group_copy(&rcap_fad->fad.admin_group_exclude_any,
+                        &fa->admin_group_exclude_any);
+       admin_group_copy(&rcap_fad->fad.admin_group_include_any,
+                        &fa->admin_group_include_any);
+       admin_group_copy(&rcap_fad->fad.admin_group_include_all,
+                        &fa->admin_group_include_all);
+
+       tlvs->router_cap->fads[algorithm] = rcap_fad;
+}
+#endif /* ifndef FABRICD */
+
+int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap)
+{
+       int count = 0;
+
+       for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+               if (cap->algo[i] != SR_ALGORITHM_UNSET)
+                       count++;
+       return count;
 }
 
 void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
@@ -5861,42 +6703,107 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
                UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID);
 }
 
+void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext,
+                                 struct isis_asla_subtlvs *asla)
+{
+       admin_group_term(&asla->ext_admin_group);
+       listnode_delete(ext->aslas, asla);
+       XFREE(MTYPE_ISIS_SUBTLV, asla);
+}
+
+struct isis_asla_subtlvs *
+isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps)
+{
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node;
+
+       if (!list_isempty(ext->aslas)) {
+               for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) {
+                       if (CHECK_FLAG(asla->standard_apps, standard_apps))
+                               return asla;
+               }
+       }
+
+       asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_asla_subtlvs));
+       admin_group_init(&asla->ext_admin_group);
+       SET_FLAG(asla->standard_apps, standard_apps);
+       SET_FLAG(asla->user_def_apps, standard_apps);
+       asla->standard_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH;
+       asla->user_def_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH;
+
+       listnode_add(ext->aslas, asla);
+       return asla;
+}
+
+void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps)
+{
+       struct isis_asla_subtlvs *asla;
+       struct listnode *node;
+
+       if (!ext)
+               return;
+
+       for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) {
+               if (!CHECK_FLAG(asla->standard_apps, standard_apps))
+                       continue;
+               isis_tlvs_del_asla_flex_algo(ext, asla);
+               break;
+       }
+}
+
 void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
                                     struct prefix_ipv4 *dest, uint32_t metric,
-                                    bool external, struct sr_prefix_cfg *pcfg)
+                                    bool external,
+                                    struct sr_prefix_cfg **pcfgs)
 {
        struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
 
        r->metric = metric;
        memcpy(&r->prefix, dest, sizeof(*dest));
        apply_mask_ipv4(&r->prefix);
-       if (pcfg) {
-               struct isis_prefix_sid *psid =
-                       XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
 
-               isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+       if (pcfgs) {
                r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
-               append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+                       struct isis_prefix_sid *psid;
+                       struct sr_prefix_cfg *pcfg = pcfgs[i];
+
+                       if (!pcfg)
+                               continue;
+
+                       psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
+                       isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+                       append_item(&r->subtlvs->prefix_sids,
+                                   (struct isis_item *)psid);
+               }
        }
+
        append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
 }
 
 void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
                              struct prefix_ipv6 *dest, uint32_t metric,
-                             bool external, struct sr_prefix_cfg *pcfg)
+                             bool external, struct sr_prefix_cfg **pcfgs)
 {
        struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
 
        r->metric = metric;
        memcpy(&r->prefix, dest, sizeof(*dest));
        apply_mask_ipv6(&r->prefix);
-       if (pcfg) {
-               struct isis_prefix_sid *psid =
-                       XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
-
-               isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+       if (pcfgs) {
                r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
-               append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
+               for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
+                       struct isis_prefix_sid *psid;
+                       struct sr_prefix_cfg *pcfg = pcfgs[i];
+
+                       if (!pcfg)
+                               continue;
+
+                       psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
+                       isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
+                       append_item(&r->subtlvs->prefix_sids,
+                                   (struct isis_item *)psid);
+               }
        }
 
        struct isis_item_list *l;
index 51058f1af1f40554e3972c11f16ca85d653bcfd8..03e2b2edccca44ed27fa347b96020e1a4a849b03 100644 (file)
@@ -9,8 +9,12 @@
 #ifndef ISIS_TLVS_H
 #define ISIS_TLVS_H
 
+#include "segment_routing.h"
 #include "openbsd-tree.h"
 #include "prefix.h"
+#include "flex_algo.h"
+#include "affinitymap.h"
+
 
 DECLARE_MTYPE(ISIS_SUBTLV);
 
@@ -102,7 +106,7 @@ struct isis_spine_leaf {
 enum isis_threeway_state {
        ISIS_THREEWAY_DOWN = 2,
        ISIS_THREEWAY_INITIALIZING = 1,
-       ISIS_THREEWAY_UP = 0
+       ISIS_THREEWAY_UP = 0,
 };
 
 struct isis_threeway_adj {
@@ -177,19 +181,18 @@ struct isis_lan_adj_sid {
 #define ISIS_ROUTER_CAP_FLAG_D 0x02
 #define ISIS_ROUTER_CAP_SIZE   5
 
-/* Number of supported algorithm for Segment Routing.
- * Right now only 2 have been standardized:
- *  - 0: SPF
- *  - 1: Strict SPF
- */
-#define SR_ALGORITHM_COUNT     2
-#define SR_ALGORITHM_SPF       0
-#define SR_ALGORITHM_STRICT_SPF        1
-#define SR_ALGORITHM_UNSET     255
-
 #define MSD_TYPE_BASE_MPLS_IMPOSITION  0x01
 #define MSD_TLV_SIZE            2
 
+#ifndef FABRICD
+struct isis_router_cap_fad;
+struct isis_router_cap_fad {
+       uint8_t sysid[ISIS_SYS_ID_LEN + 2];
+
+       struct flex_algo fad;
+};
+#endif /* ifndef FABRICD */
+
 struct isis_router_cap {
        struct in_addr router_id;
        uint8_t flags;
@@ -200,6 +203,11 @@ struct isis_router_cap {
        uint8_t algo[SR_ALGORITHM_COUNT];
        /* RFC 8491 */
        uint8_t msd;
+
+#ifndef FABRICD
+       /* RFC9350 Flex-Algorithm */
+       struct isis_router_cap_fad *fads[SR_ALGORITHM_COUNT];
+#endif /* ifndef FABRICD */
 };
 
 struct isis_item {
@@ -309,7 +317,7 @@ enum isis_tlv_context {
        ISIS_CONTEXT_SUBTLV_NE_REACH,
        ISIS_CONTEXT_SUBTLV_IP_REACH,
        ISIS_CONTEXT_SUBTLV_IPV6_REACH,
-       ISIS_CONTEXT_MAX
+       ISIS_CONTEXT_MAX,
 };
 
 struct isis_subtlvs {
@@ -394,7 +402,22 @@ enum isis_tlv_type {
        ISIS_SUBTLV_AVA_BW = 38,
        ISIS_SUBTLV_USE_BW = 39,
 
-       ISIS_SUBTLV_MAX = 40
+       /* RFC 7308 */
+       ISIS_SUBTLV_EXT_ADMIN_GRP = 14,
+
+       /* RFC 8919 */
+       ISIS_SUBTLV_ASLA = 16,
+
+       /* draft-ietf-lsr-isis-srv6-extensions */
+       ISIS_SUBTLV_SID_END = 5,
+       ISIS_SUBTLV_SID_END_X = 43,
+
+       ISIS_SUBTLV_MAX = 40,
+
+       /* draft-ietf-lsr-isis-srv6-extensions */
+       ISIS_SUBSUBTLV_SID_STRUCTURE = 1,
+
+       ISIS_SUBSUBTLV_MAX = 256,
 };
 
 /* subTLVs size for TE and SR */
@@ -422,19 +445,39 @@ enum ext_subtlv_size {
        /* RFC 7810 */
        ISIS_SUBTLV_MM_DELAY_SIZE = 8,
 
+       /* RFC9350 - Flex-Algorithm */
+       ISIS_SUBTLV_FAD = 26,
+       ISIS_SUBTLV_FAD_MIN_SIZE = 4,
+
        ISIS_SUBTLV_HDR_SIZE = 2,
        ISIS_SUBTLV_DEF_SIZE = 4,
 
-       /* RFC 7308 */
-       ISIS_SUBTLV_EXT_ADMIN_GRP = 14,
+       ISIS_SUBTLV_MAX_SIZE = 180,
+
+       /* draft-ietf-lsr-isis-srv6-extensions */
+       ISIS_SUBSUBTLV_SID_STRUCTURE_SIZE = 4,
+
+       ISIS_SUBSUBTLV_HDR_SIZE = 2,
+       ISIS_SUBSUBTLV_MAX_SIZE = 180,
+
+       /* RFC9350 - Flex-Algorithm */
+       ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE = 1,
+};
 
-       ISIS_SUBTLV_MAX_SIZE = 180
+enum ext_subsubtlv_types {
+       ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG = 1,
+       ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG = 2,
+       ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG = 3,
+       ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS = 4,
+       ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG = 5,
 };
 
 /* Macros to manage the optional presence of EXT subTLVs */
 #define SET_SUBTLV(s, t) ((s->status) |= (t))
 #define UNSET_SUBTLV(s, t) ((s->status) &= ~(t))
 #define IS_SUBTLV(s, t) (s->status & t)
+#define RESET_SUBTLV(s) (s->status = 0)
+#define NO_SUBTLV(s) (s->status == 0)
 
 #define EXT_DISABLE            0x000000
 #define EXT_ADM_GRP            0x000001
@@ -506,6 +549,45 @@ struct isis_ext_subtlvs {
        /* Segment Routing Adjacency & LAN Adjacency Segment ID */
        struct isis_item_list adj_sid;
        struct isis_item_list lan_sid;
+
+       struct list *aslas;
+};
+
+/* RFC 8919 */
+#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */
+#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */
+#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */
+#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */
+
+#define ASLA_APP_IDENTIFIER_BIT_LENGTH 1
+#define ASLA_LEGACY_FLAG 0x80
+#define ASLA_APPS_LENGTH_MASK 0x7f
+
+struct isis_asla_subtlvs {
+       uint32_t status;
+
+       /* Application Specific Link Attribute - RFC 8919 */
+       bool legacy; /* L-Flag */
+       uint8_t standard_apps_length;
+       uint8_t user_def_apps_length;
+       uint8_t standard_apps;
+       uint8_t user_def_apps;
+
+       /* Sub-TLV list - rfc8919 section-3.1 */
+       uint32_t admin_group;
+       struct admin_group ext_admin_group; /* Res. Class/Color - RFC 7308 */
+       float max_bw;                       /* Maximum Bandwidth - RFC 5305 */
+       float max_rsv_bw;   /* Maximum Reservable Bandwidth - RFC 5305 */
+       float unrsv_bw[8];  /* Unreserved Bandwidth - RFC 5305 */
+       uint32_t te_metric; /* Traffic Engineering Metric - RFC 5305 */
+       uint32_t delay;     /* Average Link Delay  - RFC 8570 */
+       uint32_t min_delay; /* Low Link Delay  - RFC 8570 */
+       uint32_t max_delay; /* High Link Delay  - RFC 8570 */
+       uint32_t delay_var; /* Link Delay Variation i.e. Jitter - RFC 8570 */
+       uint32_t pkt_loss;  /* Unidirectional Link Packet Loss - RFC 8570 */
+       float res_bw;       /* Unidirectional Residual Bandwidth - RFC 8570 */
+       float ava_bw;       /* Unidirectional Available Bandwidth - RFC 8570 */
+       float use_bw;       /* Unidirectional Utilized Bandwidth - RFC 8570 */
 };
 
 #define IS_COMPAT_MT_TLV(tlv_type)                                             \
@@ -536,6 +618,12 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size);
 #define ISIS_MT_AT_MASK        0x4000
 #endif
 
+/* RFC 8919 */
+#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */
+#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */
+#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */
+#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */
+
 void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd);
 void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
                                  struct list *addresses);
@@ -567,8 +655,19 @@ void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
                                struct isis_lsp **last_lsp);
 void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
                                    const char *hostname);
-void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs,
-                     const struct isis_router_cap *cap);
+struct isis_router_cap *
+isis_tlvs_init_router_capability(struct isis_tlvs *tlvs);
+
+struct isis_area;
+struct isis_flex_algo;
+void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs,
+                                        struct flex_algo *fa, int algorithm,
+                                        uint8_t *sysid);
+
+struct isis_area;
+
+int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap);
+
 void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
                                const struct in_addr *id);
 void isis_tlvs_set_te_router_id_ipv6(struct isis_tlvs *tlvs,
@@ -577,10 +676,11 @@ void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
                                     struct prefix_ipv4 *dest, uint8_t metric);
 void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
                                     struct prefix_ipv4 *dest, uint32_t metric,
-                                    bool external, struct sr_prefix_cfg *pcfg);
+                                    bool external,
+                                    struct sr_prefix_cfg **pcfgs);
 void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
                              struct prefix_ipv6 *dest, uint32_t metric,
-                             bool external, struct sr_prefix_cfg *pcfg);
+                             bool external, struct sr_prefix_cfg **pcfgs);
 void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
                                     struct prefix_ipv6 *dest,
                                     struct prefix_ipv6 *src,
@@ -596,6 +696,12 @@ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts,
 void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
                               struct isis_lan_adj_sid *lan);
 
+void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext,
+                                 struct isis_asla_subtlvs *asla);
+struct isis_asla_subtlvs *
+isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps);
+void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps);
+
 void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
                                  uint8_t metric);
 void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
index ec2d50d60a47d643d504f06f8564bb681f07bee4..caf97f11749c9315b6b5e33753e4af0c959c44cf 100644 (file)
@@ -126,12 +126,12 @@ void _isis_tx_queue_add(struct isis_tx_queue *queue,
                return;
 
        if (IS_DEBUG_TX_QUEUE) {
-               zlog_debug("Add LSP %s to %s queue as %s LSP. (From %s %s:%d)",
-                          rawlspid_print(lsp->hdr.lsp_id),
-                          queue->circuit->interface->name,
-                          (type == TX_LSP_CIRCUIT_SCOPED) ?
-                          "circuit scoped" : "regular",
-                          func, file, line);
+               zlog_debug(
+                       "Add LSP %pLS to %s queue as %s LSP. (From %s %s:%d)",
+                       lsp->hdr.lsp_id, queue->circuit->interface->name,
+                       (type == TX_LSP_CIRCUIT_SCOPED) ? "circuit scoped"
+                                                       : "regular",
+                       func, file, line);
        }
 
        struct isis_tx_queue_entry *e = tx_queue_find(queue, lsp);
@@ -164,9 +164,8 @@ void _isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp,
                return;
 
        if (IS_DEBUG_TX_QUEUE) {
-               zlog_debug("Remove LSP %s from %s queue. (From %s %s:%d)",
-                          rawlspid_print(lsp->hdr.lsp_id),
-                          queue->circuit->interface->name,
+               zlog_debug("Remove LSP %pLS from %s queue. (From %s %s:%d)",
+                          lsp->hdr.lsp_id, queue->circuit->interface->name,
                           func, file, line);
        }
 
index 8cd8f57c4704c6d97f11d75f79a8ea853a5e7e3e..59b80c1e20a1c5d31970abd5dd508ebef2d09481 100644 (file)
@@ -327,14 +327,13 @@ void isis_zebra_route_del_route(struct isis *isis,
  */
 void isis_zebra_prefix_sid_install(struct isis_area *area,
                                   struct prefix *prefix,
-                                  struct isis_route_info *rinfo,
                                   struct isis_sr_psid_info *psid)
 {
        struct zapi_labels zl;
        int count = 0;
 
-       sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX",
-                area->area_tag, psid->label, prefix);
+       sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX algorithm %u",
+                area->area_tag, psid->label, prefix, psid->algorithm);
 
        /* Prepare message. */
        memset(&zl, 0, sizeof(zl));
@@ -342,7 +341,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area,
        zl.local_label = psid->label;
 
        /* Local routes don't have any nexthop and require special handling. */
-       if (list_isempty(rinfo->nexthops)) {
+       if (list_isempty(psid->nexthops)) {
                struct zapi_nexthop *znh;
                struct interface *ifp;
 
@@ -361,9 +360,9 @@ void isis_zebra_prefix_sid_install(struct isis_area *area,
                znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL;
        } else {
                /* Add backup nexthops first. */
-               if (rinfo->backup) {
+               if (psid->nexthops_backup) {
                        count = isis_zebra_add_nexthops(
-                               area->isis, rinfo->backup->nexthops,
+                               area->isis, psid->nexthops_backup,
                                zl.backup_nexthops, ISIS_NEXTHOP_BACKUP, true,
                                0);
                        if (count > 0) {
@@ -373,7 +372,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area,
                }
 
                /* Add primary nexthops. */
-               count = isis_zebra_add_nexthops(area->isis, rinfo->nexthops,
+               count = isis_zebra_add_nexthops(area->isis, psid->nexthops,
                                                zl.nexthops, ISIS_NEXTHOP_MAIN,
                                                true, count);
                if (!count)
@@ -400,8 +399,8 @@ void isis_zebra_prefix_sid_uninstall(struct isis_area *area,
 {
        struct zapi_labels zl;
 
-       sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX",
-                area->area_tag, psid->label, prefix);
+       sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX algorithm %u",
+                area->area_tag, psid->label, prefix, psid->algorithm);
 
        /* Prepare message. */
        memset(&zl, 0, sizeof(zl));
index 359e39b59dd03ca7a3516680e35718a5068feb98..045c75874a26f015b894b01295e54afb92af91ad 100644 (file)
@@ -36,7 +36,6 @@ void isis_zebra_route_del_route(struct isis *isis,
                                struct isis_route_info *route_info);
 void isis_zebra_prefix_sid_install(struct isis_area *area,
                                   struct prefix *prefix,
-                                  struct isis_route_info *rinfo,
                                   struct isis_sr_psid_info *psid);
 void isis_zebra_prefix_sid_uninstall(struct isis_area *area,
                                     struct prefix *prefix,
index d216d100e1128cc2bff5636ac38ee4c04fc5db4c..ea304ba5efdff909598bb6f85541a871623621e6 100644 (file)
@@ -27,6 +27,7 @@
 #include "zclient.h"
 #include "vrf.h"
 #include "spf_backoff.h"
+#include "flex_algo.h"
 #include "lib/northbound_cli.h"
 #include "bfd.h"
 
@@ -49,6 +50,7 @@
 #include "isisd/isis_te.h"
 #include "isisd/isis_mt.h"
 #include "isisd/isis_sr.h"
+#include "isisd/isis_flex_algo.h"
 #include "isisd/fabricd.h"
 #include "isisd/isis_nb.h"
 
@@ -272,7 +274,7 @@ void isis_area_del_circuit(struct isis_area *area, struct isis_circuit *circuit)
 
 static void delete_area_addr(void *arg)
 {
-       struct area_addr *addr = (struct area_addr *)arg;
+       struct iso_address *addr = (struct iso_address *)arg;
 
        XFREE(MTYPE_ISIS_AREA_ADDR, addr);
 }
@@ -317,6 +319,12 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
        if (area->is_type & IS_LEVEL_2)
                lsp_db_init(&area->lspdb[1]);
 
+#ifndef FABRICD
+       /* Flex-Algo */
+       area->flex_algos = flex_algos_alloc(isis_flex_algo_data_alloc,
+                                           isis_flex_algo_data_free);
+#endif /* ifndef FABRICD */
+
        spftree_area_init(area);
 
        area->circuit_list = list_new();
@@ -512,6 +520,10 @@ void isis_area_destroy(struct isis_area *area)
        isis_area_invalidate_routes(area, area->is_type);
        isis_area_verify_routes(area);
 
+#ifndef FABRICD
+       flex_algos_free(area->flex_algos);
+#endif /* ifndef FABRICD */
+
        isis_sr_area_term(area);
 
        isis_mpls_te_term(area);
@@ -809,8 +821,8 @@ static void area_set_mt_overload(struct isis_area *area, uint16_t mtid,
 int area_net_title(struct vty *vty, const char *net_title)
 {
        VTY_DECLVAR_CONTEXT(isis_area, area);
-       struct area_addr *addr;
-       struct area_addr *addrp;
+       struct iso_address *addr;
+       struct iso_address *addrp;
        struct listnode *node;
 
        uint8_t buff[255];
@@ -823,14 +835,14 @@ int area_net_title(struct vty *vty, const char *net_title)
                return CMD_ERR_NOTHING_TODO;
        }
 
-       addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr));
+       addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct iso_address));
        addr->addr_len = dotformat2buff(buff, net_title);
        memcpy(addr->area_addr, buff, addr->addr_len);
 #ifdef EXTREME_DEBUG
        zlog_debug("added area address %s for area %s (address length %d)",
                   net_title, area->area_tag, addr->addr_len);
 #endif /* EXTREME_DEBUG */
-       if (addr->addr_len < 8 || addr->addr_len > 20) {
+       if (addr->addr_len < ISO_ADDR_MIN || addr->addr_len > ISO_ADDR_SIZE) {
                vty_out(vty,
                        "area address must be at least 8..20 octets long (%d)\n",
                        addr->addr_len);
@@ -852,8 +864,8 @@ int area_net_title(struct vty *vty, const char *net_title)
                memcpy(area->isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN);
                area->isis->sysid_set = 1;
                if (IS_DEBUG_EVENTS)
-                       zlog_debug("Router has SystemID %s",
-                                  sysid_print(area->isis->sysid));
+                       zlog_debug("Router has SystemID %pSY",
+                                  area->isis->sysid);
        } else {
                /*
                 * Check that the SystemID portions match
@@ -899,12 +911,12 @@ int area_net_title(struct vty *vty, const char *net_title)
 int area_clear_net_title(struct vty *vty, const char *net_title)
 {
        VTY_DECLVAR_CONTEXT(isis_area, area);
-       struct area_addr addr, *addrp = NULL;
+       struct iso_address addr, *addrp = NULL;
        struct listnode *node;
        uint8_t buff[255];
 
        addr.addr_len = dotformat2buff(buff, net_title);
-       if (addr.addr_len < 8 || addr.addr_len > 20) {
+       if (addr.addr_len < ISO_ADDR_MIN || addr.addr_len > ISO_ADDR_SIZE) {
                vty_out(vty,
                        "Unsupported area address length %d, should be 8...20 \n",
                        addr.addr_len);
@@ -2348,11 +2360,11 @@ static void common_isis_summary_json(struct json_object *json,
        time_t cur;
        char uptime[MONOTIME_STRLEN];
        char stier[5];
+
        json_object_string_add(json, "vrf", isis->name);
        json_object_int_add(json, "process-id", isis->process_id);
        if (isis->sysid_set)
-               json_object_string_add(json, "system-id",
-                                      sysid_print(isis->sysid));
+               json_object_string_addf(json, "system-id", "%pSY", isis->sysid);
 
        cur = time(NULL);
        cur -= isis->uptime;
@@ -2380,16 +2392,11 @@ static void common_isis_summary_json(struct json_object *json,
                }
 
                if (listcount(area->area_addrs) > 0) {
-                       struct area_addr *area_addr;
+                       struct iso_address *area_addr;
                        for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2,
-                                                 area_addr)) {
-                               json_object_string_add(
-                                       area_json, "net",
-                                       isonet_print(area_addr->area_addr,
-                                                    area_addr->addr_len +
-                                                            ISIS_SYS_ID_LEN +
-                                                            1));
-                       }
+                                                 area_addr))
+                               json_object_string_addf(area_json, "net",
+                                                       "%pISl", area_addr);
                }
 
                tx_pdu_json = json_object_new_object();
@@ -2462,8 +2469,7 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis)
        vty_out(vty, "vrf             : %s\n", isis->name);
        vty_out(vty, "Process Id      : %ld\n", isis->process_id);
        if (isis->sysid_set)
-               vty_out(vty, "System Id       : %s\n",
-                       sysid_print(isis->sysid));
+               vty_out(vty, "System Id       : %pSY\n", isis->sysid);
 
        vty_out(vty, "Up time         : ");
        vty_out_timestr(vty, isis->uptime);
@@ -2485,15 +2491,10 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis)
                }
 
                if (listcount(area->area_addrs) > 0) {
-                       struct area_addr *area_addr;
+                       struct iso_address *area_addr;
                        for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2,
-                                                 area_addr)) {
-                               vty_out(vty, "  Net: %s\n",
-                                       isonet_print(area_addr->area_addr,
-                                                    area_addr->addr_len
-                                                            + ISIS_SYS_ID_LEN
-                                                            + 1));
-                       }
+                                                 area_addr))
+                               vty_out(vty, "  Net: %pISl\n", area_addr);
                }
 
                vty_out(vty, "  TX counters per PDU type:\n");
@@ -2503,6 +2504,9 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis)
                vty_out(vty, "  RX counters per PDU type:\n");
                pdu_counter_print(vty, "    ", area->pdu_rx_counters);
 
+               vty_out(vty, "  Drop counters per PDU type:\n");
+               pdu_counter_print(vty, "    ", area->pdu_drop_counters);
+
                vty_out(vty, "  Advertise high metrics: %s\n",
                        area->advertise_high_metrics ? "Enabled" : "Disabled");
 
@@ -3069,12 +3073,27 @@ int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level,
 
 void isis_area_invalidate_routes(struct isis_area *area, int levels)
 {
+#ifndef FABRICD
+       struct flex_algo *fa;
+       struct listnode *node;
+       struct isis_flex_algo_data *data;
+#endif /* ifndef FABRICD */
+
        for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
                if (!(level & levels))
                        continue;
                for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
                        isis_spf_invalidate_routes(
                                        area->spftree[tree][level - 1]);
+
+#ifndef FABRICD
+                       for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos,
+                                                 node, fa)) {
+                               data = fa->data;
+                               isis_spf_invalidate_routes(
+                                       data->spftree[tree][level - 1]);
+                       }
+#endif /* ifndef FABRICD */
                }
        }
 }
@@ -3082,7 +3101,7 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels)
 void isis_area_verify_routes(struct isis_area *area)
 {
        for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++)
-               isis_spf_verify_routes(area, area->spftree[tree]);
+               isis_spf_verify_routes(area, area->spftree[tree], tree);
 }
 
 void isis_area_switchover_routes(struct isis_area *area, int family,
@@ -3106,6 +3125,12 @@ void isis_area_switchover_routes(struct isis_area *area, int family,
 
 static void area_resign_level(struct isis_area *area, int level)
 {
+#ifndef FABRICD
+       struct flex_algo *fa;
+       struct listnode *node;
+       struct isis_flex_algo_data *data;
+#endif /* ifndef FABRICD */
+
        isis_area_invalidate_routes(area, level);
        isis_area_verify_routes(area);
 
@@ -3118,6 +3143,20 @@ static void area_resign_level(struct isis_area *area, int level)
                }
        }
 
+#ifndef FABRICD
+       for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+               for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
+                                         fa)) {
+                       data = fa->data;
+                       if (data->spftree[tree][level - 1]) {
+                               isis_spftree_del(
+                                       data->spftree[tree][level - 1]);
+                               data->spftree[tree][level - 1] = NULL;
+                       }
+               }
+       }
+#endif /* ifndef FABRICD */
+
        if (area->spf_timer[level - 1])
                isis_spf_timer_free(EVENT_ARG(area->spf_timer[level - 1]));
 
@@ -3494,15 +3533,10 @@ static int isis_config_write(struct vty *vty)
                        write++;
                        /* ISIS - Net */
                        if (listcount(area->area_addrs) > 0) {
-                               struct area_addr *area_addr;
+                               struct iso_address *area_addr;
                                for (ALL_LIST_ELEMENTS_RO(area->area_addrs,
                                                          node2, area_addr)) {
-                                       vty_out(vty, " net %s\n",
-                                               isonet_print(
-                                                       area_addr->area_addr,
-                                                       area_addr->addr_len
-                                                               + ISIS_SYS_ID_LEN
-                                                               + 1));
+                                       vty_out(vty, " net %pISl\n", area_addr);
                                        write++;
                                }
                        }
@@ -3761,7 +3795,8 @@ struct cmd_node router_node = {
        .prompt = "%s(config-router)# ",
        .config_write = isis_config_write,
 };
-#else
+#endif /* ifdef FABRICD */
+#ifndef FABRICD
 /* IS-IS configuration write function */
 static int isis_config_write(struct vty *vty)
 {
@@ -3784,7 +3819,14 @@ struct cmd_node router_node = {
        .prompt = "%s(config-router)# ",
        .config_write = isis_config_write,
 };
-#endif /* ifdef FABRICD */
+
+struct cmd_node isis_flex_algo_node = {
+       .name = "isis-flex-algo",
+       .node = ISIS_FLEX_ALGO_NODE,
+       .parent_node = ISIS_NODE,
+       .prompt = "%s(config-router-flex-algo)# ",
+};
+#endif /* ifdnef FABRICD */
 
 void isis_init(void)
 {
@@ -3893,6 +3935,10 @@ void isis_init(void)
        install_element(ROUTER_NODE, &log_adj_changes_cmd);
        install_element(ROUTER_NODE, &no_log_adj_changes_cmd);
 #endif /* ifdef FABRICD */
+#ifndef FABRICD
+       install_node(&isis_flex_algo_node);
+       install_default(ISIS_FLEX_ALGO_NODE);
+#endif /* ifdnef FABRICD */
 
        spf_backoff_cmd_init();
 }
index a06dc46a38a41aca3d3a720ba291f8e96de7313e..f0d236b6437620b97c18e4fc4ca9ab07fafc7468 100644 (file)
@@ -24,6 +24,7 @@
 #include "isis_lfa.h"
 #include "qobj.h"
 #include "ldp_sync.h"
+#include "iso.h"
 
 DECLARE_MGROUP(ISISD);
 
@@ -87,7 +88,7 @@ struct isis {
        uint32_t router_id;             /* Router ID from zebra */
        struct list *area_list; /* list of IS-IS areas */
        uint8_t max_area_addrs;           /* maximumAreaAdresses */
-       struct area_addr *man_area_addrs; /* manualAreaAddresses */
+       struct iso_address *man_area_addrs; /* manualAreaAddresses */
        time_t uptime;                    /* when did we start */
        struct event *t_dync_clean; /* dynamic hostname cache cleanup thread */
        uint32_t circuit_ids_used[8];     /* 256 bits to track circuit ids 1 through 255 */
@@ -162,6 +163,10 @@ struct isis_area {
        /* do we support new style metrics?  */
        char newmetric;
        char oldmetric;
+       /* Allow sending the default admin-group value of 0x00000000. */
+       bool admin_group_send_zero;
+       /* Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV */
+       bool asla_legacy_flag;
        /* identifies the routing instance   */
        char *area_tag;
        /* area addresses for this area      */
@@ -195,6 +200,8 @@ struct isis_area {
        int ip_circuits;
        /* logging adjacency changes? */
        uint8_t log_adj_changes;
+       /* logging pdu drops? */
+       uint8_t log_pdu_drops;
        /* multi topology settings */
        struct list *mt_settings;
        /* MPLS-TE settings */
@@ -217,6 +224,10 @@ struct isis_area {
        size_t tilfa_protected_links[ISIS_LEVELS];
        /* MPLS LDP-IGP Sync */
        struct ldp_sync_info_cmd ldp_sync_cmd;
+#ifndef FABRICD
+       /* Flex-Algo */
+       struct flex_algos *flex_algos;
+#endif /* ifndef FABRICD */
        /* Counters */
        uint32_t circuit_state_changes;
        struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT]
@@ -232,6 +243,7 @@ struct isis_area {
 
        pdu_counter_t pdu_tx_counters;
        pdu_counter_t pdu_rx_counters;
+       pdu_counter_t pdu_drop_counters;
        uint64_t lsp_rxmt_count;
 
        /* Area counters */
index dabf6a925ea0551ae3bf22794154d5ead3a2796c..6bd2477b198b2363d5e0ba32a37e8728d18f66ca 100644 (file)
@@ -19,6 +19,7 @@ vtysh_daemons += fabricd
 endif
 
 noinst_HEADERS += \
+       isisd/isis_affinitymap.h \
        isisd/isis_adjacency.h \
        isisd/isis_bfd.h \
        isisd/isis_circuit.h \
@@ -45,6 +46,7 @@ noinst_HEADERS += \
        isisd/isis_spf.h \
        isisd/isis_spf_private.h \
        isisd/isis_sr.h \
+       isisd/isis_flex_algo.h \
        isisd/isis_te.h \
        isisd/isis_tlvs.h \
        isisd/isis_tx_queue.h \
@@ -55,6 +57,7 @@ noinst_HEADERS += \
        # end
 
 LIBISIS_SOURCES = \
+       isisd/isis_affinitymap.c \
        isisd/isis_adjacency.c \
        isisd/isis_bfd.c \
        isisd/isis_circuit.c \
@@ -76,6 +79,7 @@ LIBISIS_SOURCES = \
        isisd/isis_routemap.c \
        isisd/isis_spf.c \
        isisd/isis_sr.c \
+       isisd/isis_flex_algo.c \
        isisd/isis_te.c \
        isisd/isis_tlvs.c \
        isisd/isis_tx_queue.c \
index 97ea200ff4b8c2ee6b9359036bafb2a3c200383b..7a7ce3f5dcb38d7aed22abb1e93463cde46c85d5 100644 (file)
@@ -31,6 +31,8 @@
 #include "jhash.h"
 #include "hook.h"
 #include "lib_errors.h"
+#include "mgmt_be_client.h"
+#include "mgmt_fe_client.h"
 #include "northbound_cli.h"
 #include "network.h"
 #include "routemap.h"
@@ -1301,6 +1303,14 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
        while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
                ++(*line_num);
 
+               if (vty_log_commands) {
+                       int len = strlen(vty->buf);
+
+                       /* now log the command */
+                       zlog_notice("config-from-file# %.*s", len ? len - 1 : 0,
+                                   vty->buf);
+               }
+
                ret = command_config_read_one_line(vty, NULL, *line_num, 0);
 
                if (ret != CMD_SUCCESS && ret != CMD_WARNING
@@ -2438,6 +2448,8 @@ const char *host_config_get(void)
 void cmd_show_lib_debugs(struct vty *vty)
 {
        route_map_show_debug(vty);
+       mgmt_debug_be_client_show_debug(vty);
+       mgmt_debug_fe_client_show_debug(vty);
 }
 
 void install_default(enum node_type node)
index 8856f9f09f2adf1cf4821c0b0f63fb48ae304904..39fbfa661a0244f4370c5d1acc74a367ed75f0fa 100644 (file)
@@ -129,6 +129,7 @@ enum node_type {
        LDP_L2VPN_NODE,          /* LDP L2VPN node */
        LDP_PSEUDOWIRE_NODE,     /* LDP Pseudowire node */
        ISIS_NODE,               /* ISIS protocol mode */
+       ISIS_FLEX_ALGO_NODE,    /* ISIS Flex Algo mode */
        ACCESS_NODE,             /* Access list node. */
        PREFIX_NODE,             /* Prefix list node. */
        ACCESS_IPV6_NODE,       /* Access list node. */
index d12e2828326f1d914ad93b61697919591d047097..29fcfbefbf2d6c0593d38abe8bf587bd5e5e1281 100644 (file)
@@ -439,6 +439,14 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8,
 #pragma diag_suppress 167
 #endif /* __INTELISENSE__ */
 
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define likely(_x) __builtin_expect(!!(_x), 1)
+#define unlikely(_x) __builtin_expect(!!(_x), 0)
+#else
+#define likely(_x) !!(_x)
+#define unlikely(_x) !!(_x)
+#endif
+
 #ifdef __cplusplus
 }
 #endif
index b92c9cb395de42e350e721cf50d514a94b887225..6a0fb7f63cc20b3bb0c3dd27bbdd2bea8f44f9ca 100644 (file)
@@ -88,7 +88,7 @@ static struct c_path *cpath_copy(struct c_path *dest, const struct c_path *src)
  *
  * @param path Constrained Path structure to be deleted
  */
-static void cpath_del(struct c_path *path)
+void cpath_del(struct c_path *path)
 {
        if (!path)
                return;
index 3eceaa04af05302a9dffe6f6daa22dd298f0518c..bba685a6172d907b2a328f19246cb65d684b8a81 100644 (file)
@@ -191,6 +191,8 @@ extern void cspf_del(struct cspf *algo);
  */
 extern struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted);
 
+extern void cpath_del(struct c_path *path);
+
 #ifdef __cplusplus
 }
 #endif
index 05f5aef766fb4080fbc52d7e61d37103d49e824b..d473dc10cbf0ece5eeb465f419bec8a768e5d235 100644 (file)
@@ -1140,7 +1140,8 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args,
        fd = open(filename, O_RDONLY | O_NOCTTY);
        if (fd < 0 || fstat(fd, &st)) {
                PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
-               close(fd);
+               if (fd > 0)
+                       close(fd);
                goto out;
        }
        w->len = st.st_size;
diff --git a/lib/flex_algo.c b/lib/flex_algo.c
new file mode 100644 (file)
index 0000000..f48117f
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * flex_algo.c: Flexible Algorithm library
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#include "zebra.h"
+
+#include "flex_algo.h"
+
+DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO_DATABASE, "Flex-Algo database");
+DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo algorithm information");
+
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+                             struct flex_algo *fa);
+
+struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
+                                   flex_algo_releaser_t releaser)
+{
+       struct flex_algos *flex_algos;
+
+       flex_algos =
+               XCALLOC(MTYPE_FLEX_ALGO_DATABASE, sizeof(struct flex_algos));
+       flex_algos->flex_algos = list_new();
+       flex_algos->allocator = allocator;
+       flex_algos->releaser = releaser;
+       return flex_algos;
+}
+
+void flex_algos_free(struct flex_algos *flex_algos)
+{
+       struct listnode *node, *nnode;
+       struct flex_algo *fa;
+
+       for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa))
+               _flex_algo_delete(flex_algos, fa);
+       list_delete(&flex_algos->flex_algos);
+       XFREE(MTYPE_FLEX_ALGO_DATABASE, flex_algos);
+}
+
+struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
+                                 uint8_t algorithm, void *arg)
+{
+       struct flex_algo *fa;
+
+       fa = XCALLOC(MTYPE_FLEX_ALGO, sizeof(struct flex_algo));
+       fa->algorithm = algorithm;
+       if (flex_algos->allocator)
+               fa->data = flex_algos->allocator(arg);
+       admin_group_init(&fa->admin_group_exclude_any);
+       admin_group_init(&fa->admin_group_include_any);
+       admin_group_init(&fa->admin_group_include_all);
+       listnode_add(flex_algos->flex_algos, fa);
+       return fa;
+}
+
+static void _flex_algo_delete(struct flex_algos *flex_algos,
+                             struct flex_algo *fa)
+{
+       if (flex_algos->releaser)
+               flex_algos->releaser(fa->data);
+       admin_group_term(&fa->admin_group_exclude_any);
+       admin_group_term(&fa->admin_group_include_any);
+       admin_group_term(&fa->admin_group_include_all);
+       listnode_delete(flex_algos->flex_algos, fa);
+       XFREE(MTYPE_FLEX_ALGO, fa);
+}
+
+
+void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm)
+{
+       struct listnode *node, *nnode;
+       struct flex_algo *fa;
+
+       for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) {
+               if (fa->algorithm != algorithm)
+                       continue;
+               _flex_algo_delete(flex_algos, fa);
+       }
+}
+
+/**
+ * @brief Look up the local flex-algo object by its algorithm number.
+ * @param algorithm flex-algo algorithm number
+ * @param area area pointer of flex-algo
+ * @return local flex-algo object if exist, else NULL
+ */
+struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos,
+                                  uint8_t algorithm)
+{
+       struct listnode *node;
+       struct flex_algo *fa;
+
+       for (ALL_LIST_ELEMENTS_RO(flex_algos->flex_algos, node, fa))
+               if (fa->algorithm == algorithm)
+                       return fa;
+       return NULL;
+}
+
+/**
+ * @brief Compare two Flex-Algo Definitions (FAD)
+ * @param Flex algo 1
+ * @param Flex algo 2
+ * @return true if the definition is equal, else false
+ */
+bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2)
+{
+       if (fa1->algorithm != fa2->algorithm)
+               return false;
+       if (fa1->calc_type != fa2->calc_type)
+               return false;
+       if (fa1->metric_type != fa2->metric_type)
+               return false;
+       if (fa1->exclude_srlg != fa2->exclude_srlg)
+               return false;
+       if (fa1->flags != fa2->flags)
+               return false;
+       if (fa1->unsupported_subtlv != fa2->unsupported_subtlv)
+               return false;
+
+       if (!admin_group_cmp(&fa1->admin_group_exclude_any,
+                            &fa2->admin_group_exclude_any))
+               return false;
+       if (!admin_group_cmp(&fa1->admin_group_include_all,
+                            &fa2->admin_group_include_all))
+               return false;
+       if (!admin_group_cmp(&fa1->admin_group_include_any,
+                            &fa2->admin_group_include_any))
+               return false;
+
+       return true;
+}
+
+/**
+ * Check SR Algorithm is Flex-Algo
+ * according to RFC9350 section 4
+ *
+ * @param algorithm SR Algorithm
+ */
+bool flex_algo_id_valid(uint16_t algorithm)
+{
+       return algorithm >= SR_ALGORITHM_FLEX_MIN &&
+              algorithm <= SR_ALGORITHM_FLEX_MAX;
+}
+
+char *flex_algo_metric_type_print(char *type_str, size_t sz,
+                                 enum flex_algo_metric_type metric_type)
+{
+       switch (metric_type) {
+       case MT_IGP:
+               snprintf(type_str, sz, "igp");
+               break;
+       case MT_MIN_UNI_LINK_DELAY:
+               snprintf(type_str, sz, "delay");
+               break;
+       case MT_TE_DEFAULT:
+               snprintf(type_str, sz, "te");
+               break;
+       }
+       return type_str;
+}
+
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm)
+{
+       struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+       if (!fa)
+               return false;
+
+       return fa->state;
+}
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+                        bool state)
+{
+       struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm);
+
+       if (!fa)
+               return;
+
+       fa->state = state;
+}
diff --git a/lib/flex_algo.h b/lib/flex_algo.h
new file mode 100644 (file)
index 0000000..e617e7c
--- /dev/null
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * flex_algo.h: Flexible Algorithm library
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#ifndef _FRR_FLEX_ALGO_H
+#define _FRR_FLEX_ALGO_H
+
+#include "admin_group.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "segment_routing.h"
+
+#define FLEX_ALGO_PRIO_DEFAULT 128
+
+#define CALC_TYPE_SPF 0
+
+/* flex-algo definition flags */
+
+/* M-flag (aka. prefix-metric)
+ * Flex-Algorithm specific prefix and ASBR metric MUST be used
+ */
+#define FAD_FLAG_M 0x80
+
+/*
+ * Metric Type values from RFC9350 section 5.1
+ */
+enum flex_algo_metric_type {
+       MT_IGP = 0,
+       MT_MIN_UNI_LINK_DELAY = 1,
+       MT_TE_DEFAULT = 2,
+};
+
+
+/* Flex-Algo data about a given algorithm.
+ * It includes the definition and some local data.
+ */
+struct flex_algo {
+       /* Flex-Algo definition */
+       uint8_t algorithm;
+       enum flex_algo_metric_type metric_type;
+       uint8_t calc_type;
+       uint8_t priority;
+       uint8_t flags;
+
+       /* extended admin-groups */
+       struct admin_group admin_group_exclude_any;
+       struct admin_group admin_group_include_any;
+       struct admin_group admin_group_include_all;
+
+       /* Exclude SRLG Sub-TLV is not yet supported by IS-IS
+        * True if a Exclude SRLG Sub-TLV has been found
+        */
+       bool exclude_srlg;
+
+       /* True if an unsupported sub-TLV other Exclude SRLG
+        * has been received.
+        * A router that receives an unsupported definition
+        * that is elected must not participate in the algorithm.
+        * This boolean prevents future sub-TLV from being considered
+        * as supported.
+        */
+       bool unsupported_subtlv;
+
+       /* Flex-Algo local data */
+
+       /* True if the local definition must be advertised */
+       bool advertise_definition;
+
+       /* which dataplane must be used for the algorithm */
+#define FLEX_ALGO_SR_MPLS 0x01
+#define FLEX_ALGO_SRV6 0x02
+#define FLEX_ALGO_IP 0x04
+       uint8_t dataplanes;
+
+       /* True if the Algorithm is locally enabled (ie. a definition has been
+        * found and is supported).
+        */
+       bool state;
+
+       /*
+        * This property can be freely extended among different routing
+        * protocols. Since Flex-Algo is an IGP protocol agnostic, both IS-IS
+        * and OSPF can implement Flex-Algo. The struct flex_algo thus provides
+        * the general data structure of Flex-Algo, and the value of extending
+        * it with the IGP protocol is provided by this property.
+        */
+       void *data;
+};
+
+typedef void *(*flex_algo_allocator_t)(void *);
+typedef void (*flex_algo_releaser_t)(void *);
+
+struct flex_algos {
+       flex_algo_allocator_t allocator;
+       flex_algo_releaser_t releaser;
+       struct list *flex_algos;
+};
+
+/*
+ * Flex-Algo Utilities
+ */
+struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator,
+                                   flex_algo_releaser_t releaser);
+void flex_algos_free(struct flex_algos *flex_algos);
+struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos,
+                                 uint8_t algorithm, void *arg);
+struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos,
+                                  uint8_t algorithm);
+void flex_algos_free(struct flex_algos *flex_algos);
+bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2);
+void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm);
+bool flex_algo_id_valid(uint16_t algorithm);
+char *flex_algo_metric_type_print(char *type_str, size_t sz,
+                                 enum flex_algo_metric_type metric_type);
+
+bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm);
+
+void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm,
+                        bool state);
+#endif /* _FRR_FLEX_ALGO_H */
diff --git a/lib/getopt.c b/lib/getopt.c
deleted file mode 100644 (file)
index 9d0a311..0000000
+++ /dev/null
@@ -1,1011 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Getopt for GNU.
- * NOTE: getopt is now part of the C library, so if you don't know what
- * "Keep this file name-space clean" means, talk to drepper@gnu.org
- * before changing it!
- *
- * Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
- *     Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
-   Ditto for AIX 3.2 and <stdlib.h>.  */
-#ifndef _NO_PROTO
-# define _NO_PROTO
-#endif
-
-#include <zebra.h>
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-#ifndef const
-#  define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#  define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
-   contain conflicting prototypes for getopt.  */
-#include <stdlib.h>
-#include <unistd.h>
-#endif /* GNU C library.  */
-
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.
-   When compiling libc, the _ macro is predefined.  */
-#ifdef HAVE_LIBINTL_H
-#include <libintl.h>
-#  define _(msgid)     gettext (msgid)
-#else
-#  define _(msgid)     (msgid)
-#endif
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
-   but it behaves differently for the user, since it allows the user
-   to intersperse the options with the other arguments.
-
-   As `getopt' works, it permutes the elements of ARGV so that,
-   when it is done, all the options precede everything else.  Thus
-   all application programs are extended to handle flexible argument order.
-
-   Setting the environment variable POSIXLY_CORRECT disables permutation.
-   Then the behavior is completely standard.
-
-   GNU application programs can use a third alternative mode in which
-   they can distinguish the relative order of options and other arguments.  */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
-   the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
-   each non-option ARGV-element is returned here.  */
-
-char *optarg = NULL;
-
-/* Index in ARGV of the next element to be scanned.
-   This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
-
-   On entry to `getopt', zero means this is the first call; initialize.
-
-   When `getopt' returns -1, this is the index of the first of the
-   non-option elements that the caller should itself scan.
-
-   Otherwise, `optind' communicates from one call to the next
-   how much of ARGV has been scanned so far.  */
-
-/* 1003.2 says this must be 1 before any call.  */
-int optind = 1;
-
-/* Formerly, initialization of getopt depended on optind==0, which
-   causes problems with re-calling getopt as programs generally don't
-   know that. */
-
-int __getopt_initialized = 0;
-
-/* The next char to be scanned in the option-element
-   in which the last option character we returned was found.
-   This allows us to pick up the scan where we left off.
-
-   If this is zero, or a null string, it means resume the scan
-   by advancing to the next ARGV-element.  */
-
-static char *nextchar;
-
-/* Callers store zero here to inhibit the error message
-   for unrecognized options.  */
-
-int opterr = 1;
-
-/* Set to an option character which was unrecognized.
-   This must be initialized on some systems to avoid linking in the
-   system's own getopt implementation.  */
-
-int optopt = '?';
-
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
-   If the caller did not specify anything,
-   the default is REQUIRE_ORDER if the environment variable
-   POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
-   REQUIRE_ORDER means don't recognize them as options;
-   stop option processing when the first non-option is seen.
-   This is what Unix does.
-   This mode of operation is selected by either setting the environment
-   variable POSIXLY_CORRECT, or using `+' as the first character
-   of the list of option characters.
-
-   PERMUTE is the default.  We permute the contents of ARGV as we scan,
-   so that eventually all the non-options are at the end.  This allows options
-   to be given in any order, even with programs that were not written to
-   expect this.
-
-   RETURN_IN_ORDER is an option available to programs that were written
-   to expect options and other ARGV-elements in any order and that care about
-   the ordering of the two.  We describe each non-option ARGV-element
-   as if it were the argument of an option with character code 1.
-   Using `-' as the first character of the list of option characters
-   selects this mode of operation.
-
-   The special argument `--' forces an end of option-scanning regardless
-   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
-   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
-
-static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
-
-/* Value of POSIXLY_CORRECT environment variable.  */
-static char *posixly_correct;
-
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
-   because there are many ways it can cause trouble.
-   On some systems, it contains special magic macros that don't work
-   in GCC.  */
-#include <string.h>
-# define my_index      strchr
-#else
-
-#if HAVE_STRING_H
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-
-/* Avoid depending on library functions or files
-   whose names are inconsistent.  */
-
-#ifndef getenv
-extern char *getenv(const char *);
-#endif
-
-static char *my_index(const char *str, int chr)
-{
-       while (*str) {
-               if (*str == chr)
-                       return (char *)str;
-               str++;
-       }
-       return 0;
-}
-
-/* If using GCC, we can safely declare strlen this way.
-   If not using GCC, it is ok not to declare it.  */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
-   That was relevant to code that was here before.  */
-#if (!defined __STDC__ || !__STDC__) && !defined strlen
-/* gcc with -traditional declares the built-in strlen to return int,
-   and has done so at least since version 2.4.5. -- rms.  */
-extern int strlen(const char *);
-#endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
-
-/* Handle permutation of arguments.  */
-
-/* Describe the part of ARGV that contains non-options that have
-   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
-   `last_nonopt' is the index after the last of them.  */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Bash 2.0 gives us an environment variable containing flags
-   indicating ARGV elements that should not be considered arguments.  */
-
-/* Defined in getopt_init.c  */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-
-static int original_argc;
-static char *const *original_argv;
-
-/* Make sure the environment variable bash 2.0 puts in the environment
-   is valid for the getopt call we must make sure that the ARGV passed
-   to getopt is that one passed to the process.  */
-static void __attribute__((unused))
-store_args_and_env(int argc, char *const *argv)
-{
-       /* XXX This is no good solution.  We should rather copy the args so
-          that we can compare them later.  But we must not use malloc(3).  */
-       original_argc = argc;
-       original_argv = argv;
-}
-#ifdef text_set_element
-text_set_element(__libc_subinit, store_args_and_env);
-#endif /* text_set_element */
-
-#define SWAP_FLAGS(ch1, ch2)                                                   \
-       if (nonoption_flags_len > 0) {                                         \
-               char __tmp = __getopt_nonoption_flags[ch1];                    \
-               __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
-               __getopt_nonoption_flags[ch2] = __tmp;                         \
-       }
-#else  /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
-/* Exchange two adjacent subsequences of ARGV.
-   One subsequence is elements [first_nonopt,last_nonopt)
-   which contains all the non-options that have been skipped so far.
-   The other is elements [last_nonopt,optind), which contains all
-   the options processed since those non-options were skipped.
-
-   `first_nonopt' and `last_nonopt' are relocated so that they describe
-   the new indices of the non-options in ARGV after they are moved.  */
-
-#if defined __STDC__ && __STDC__
-static void exchange(char **);
-#endif
-
-static void exchange(argv) char **argv;
-{
-       int bottom = first_nonopt;
-       int middle = last_nonopt;
-       int top = optind;
-       char *tem;
-
-/* Exchange the shorter segment with the far end of the longer segment.
-   That puts the shorter segment into the right place.
-   It leaves the longer segment in the right place overall,
-   but it consists of two parts that need to be swapped next.  */
-
-#ifdef _LIBC
-       /* First make sure the handling of the `__getopt_nonoption_flags'
-          string can work normally.  Our top argument must be in the range
-          of the string.  */
-       if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) {
-               /* We must extend the array.  The user plays games with us and
-                  presents new arguments.  */
-               char *new_str = malloc(top + 1);
-               if (new_str == NULL)
-                       nonoption_flags_len = nonoption_flags_max_len = 0;
-               else {
-                       memset(__mempcpy(new_str, __getopt_nonoption_flags,
-                                        nonoption_flags_max_len),
-                              '\0', top + 1 - nonoption_flags_max_len);
-                       nonoption_flags_max_len = top + 1;
-                       __getopt_nonoption_flags = new_str;
-               }
-       }
-#endif
-
-       while (top > middle && middle > bottom) {
-               if (top - middle > middle - bottom) {
-                       /* Bottom segment is the short one.  */
-                       int len = middle - bottom;
-                       register int i;
-
-                       /* Swap it with the top part of the top segment.  */
-                       for (i = 0; i < len; i++) {
-                               tem = argv[bottom + i];
-                               argv[bottom + i] =
-                                       argv[top - (middle - bottom) + i];
-                               argv[top - (middle - bottom) + i] = tem;
-                               SWAP_FLAGS(bottom + i,
-                                          top - (middle - bottom) + i);
-                       }
-                       /* Exclude the moved bottom segment from further
-                        * swapping.  */
-                       top -= len;
-               } else {
-                       /* Top segment is the short one.  */
-                       int len = top - middle;
-                       register int i;
-
-                       /* Swap it with the bottom part of the bottom segment.
-                        */
-                       for (i = 0; i < len; i++) {
-                               tem = argv[bottom + i];
-                               argv[bottom + i] = argv[middle + i];
-                               argv[middle + i] = tem;
-                               SWAP_FLAGS(bottom + i, middle + i);
-                       }
-                       /* Exclude the moved top segment from further swapping.
-                        */
-                       bottom += len;
-               }
-       }
-
-       /* Update records for the slots the non-options now occupy.  */
-
-       first_nonopt += (optind - last_nonopt);
-       last_nonopt = optind;
-}
-
-/* Initialize the internal data when the first call is made.  */
-
-#if defined __STDC__ && __STDC__
-static const char *_getopt_initialize(int, char *const *, const char *);
-#endif
-static const char *_getopt_initialize(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
-       /* Start processing options with ARGV-element 1 (since ARGV-element 0
-          is the program name); the sequence of previously skipped
-          non-option ARGV-elements is empty.  */
-
-       first_nonopt = last_nonopt = optind;
-
-       nextchar = NULL;
-
-       posixly_correct = getenv("POSIXLY_CORRECT");
-
-       /* Determine how to handle the ordering of options and nonoptions.  */
-
-       if (optstring[0] == '-') {
-               ordering = RETURN_IN_ORDER;
-               ++optstring;
-       } else if (optstring[0] == '+') {
-               ordering = REQUIRE_ORDER;
-               ++optstring;
-       } else if (posixly_correct != NULL)
-               ordering = REQUIRE_ORDER;
-       else
-               ordering = PERMUTE;
-
-#ifdef _LIBC
-       if (posixly_correct == NULL && argc == original_argc
-           && argv == original_argv) {
-               if (nonoption_flags_max_len == 0) {
-                       if (__getopt_nonoption_flags == NULL
-                           || __getopt_nonoption_flags[0] == '\0')
-                               nonoption_flags_max_len = -1;
-                       else {
-                               const char *orig_str = __getopt_nonoption_flags;
-                               int len = nonoption_flags_max_len =
-                                       strlen(orig_str);
-                               if (nonoption_flags_max_len < argc)
-                                       nonoption_flags_max_len = argc;
-                               __getopt_nonoption_flags =
-                                       (char *)malloc(nonoption_flags_max_len);
-                               if (__getopt_nonoption_flags == NULL)
-                                       nonoption_flags_max_len = -1;
-                               else
-                                       memset(__mempcpy(
-                                                      __getopt_nonoption_flags,
-                                                      orig_str, len),
-                                              '\0',
-                                              nonoption_flags_max_len - len);
-                       }
-               }
-               nonoption_flags_len = nonoption_flags_max_len;
-       } else
-               nonoption_flags_len = 0;
-#endif
-
-       return optstring;
-}
-
-/* Scan elements of ARGV (whose length is ARGC) for option characters
-   given in OPTSTRING.
-
-   If an element of ARGV starts with '-', and is not exactly "-" or "--",
-   then it is an option element.  The characters of this element
-   (aside from the initial '-') are option characters.  If `getopt'
-   is called repeatedly, it returns successively each of the option characters
-   from each of the option elements.
-
-   If `getopt' finds another option character, it returns that character,
-   updating `optind' and `nextchar' so that the next call to `getopt' can
-   resume the scan with the following option character or ARGV-element.
-
-   If there are no more option characters, `getopt' returns -1.
-   Then `optind' is the index in ARGV of the first ARGV-element
-   that is not an option.  (The ARGV-elements have been permuted
-   so that those that are not options now come last.)
-
-   OPTSTRING is a string containing the legitimate option characters.
-   If an option character is seen that is not listed in OPTSTRING,
-   return '?' after printing an error message.  If you set `opterr' to
-   zero, the error message is suppressed but we still return '?'.
-
-   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
-   so the following text in the same ARGV-element, or the text of the following
-   ARGV-element, is returned in `optarg'.  Two colons mean an option that
-   wants an optional arg; if there is text in the current ARGV-element,
-   it is returned in `optarg', otherwise `optarg' is set to zero.
-
-   If OPTSTRING starts with `-' or `+', it requests different methods of
-   handling the non-option ARGV-elements.
-   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
-
-   Long-named options begin with `--' instead of `-'.
-   Their names may be abbreviated as long as the abbreviation is unique
-   or is an exact match for some defined option.  If they have an
-   argument, it follows the option name in the same ARGV-element, separated
-   from the option name by a `=', or else the in next ARGV-element.
-   When `getopt' finds a long-named option, it returns 0 if that option's
-   `flag' field is nonzero, the value of the option's `val' field
-   if the `flag' field is zero.
-
-   The elements of ARGV aren't really const, because we permute them.
-   But we pretend they're const in the prototype to be compatible
-   with other systems.
-
-   LONGOPTS is a vector of `struct option' terminated by an
-   element containing a name which is zero.
-
-   LONGIND returns the index in LONGOPT of the long-named option found.
-   It is only valid when a long-named option has been found by the most
-   recent call.
-
-   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
-   long-named options.  */
-
-int _getopt_internal(argc, argv, optstring, longopts, longind,
-                    long_only) int argc;
-char *const *argv;
-const char *optstring;
-const struct option *longopts;
-int *longind;
-int long_only;
-{
-       optarg = NULL;
-
-       if (optind == 0 || !__getopt_initialized) {
-               if (optind == 0)
-                       optind = 1; /* Don't scan ARGV[0], the program name.  */
-               optstring = _getopt_initialize(argc, argv, optstring);
-               __getopt_initialized = 1;
-       }
-
-/* Test whether ARGV[optind] points to a non-option argument.
-   Either it does not have option syntax, or there is an environment flag
-   from the shell indicating it is not an option.  The later information
-   is only used when the used in the GNU libc.  */
-#ifdef _LIBC
-#define NONOPTION_P                                                            \
-       (argv[optind][0] != '-' || argv[optind][1] == '\0'                     \
-        || (optind < nonoption_flags_len                                      \
-            && __getopt_nonoption_flags[optind] == '1'))
-#else
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
-
-       if (nextchar == NULL || *nextchar == '\0') {
-               /* Advance to the next ARGV-element.  */
-
-               /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has
-                  been
-                  moved back by the user (who may also have changed the
-                  arguments).  */
-               if (last_nonopt > optind)
-                       last_nonopt = optind;
-               if (first_nonopt > optind)
-                       first_nonopt = optind;
-
-               if (ordering == PERMUTE) {
-                       /* If we have just processed some options following some
-                          non-options,
-                          exchange them so that the options come first.  */
-
-                       if (first_nonopt != last_nonopt
-                           && last_nonopt != optind)
-                               exchange((char **)argv);
-                       else if (last_nonopt != optind)
-                               first_nonopt = optind;
-
-                       /* Skip any additional non-options
-                          and extend the range of non-options previously
-                          skipped.  */
-
-                       while (optind < argc && NONOPTION_P)
-                               optind++;
-                       last_nonopt = optind;
-               }
-
-               /* The special ARGV-element `--' means premature end of options.
-                  Skip it like a null option,
-                  then exchange with previous non-options as if it were an
-                  option,
-                  then skip everything else like a non-option.  */
-
-               if (optind != argc && !strcmp(argv[optind], "--")) {
-                       optind++;
-
-                       if (first_nonopt != last_nonopt
-                           && last_nonopt != optind)
-                               exchange((char **)argv);
-                       else if (first_nonopt == last_nonopt)
-                               first_nonopt = optind;
-                       last_nonopt = argc;
-
-                       optind = argc;
-               }
-
-               /* If we have done all the ARGV-elements, stop the scan
-                  and back over any non-options that we skipped and permuted.
-                  */
-
-               if (optind == argc) {
-                       /* Set the next-arg-index to point at the non-options
-                          that we previously skipped, so the caller will digest
-                          them.  */
-                       if (first_nonopt != last_nonopt)
-                               optind = first_nonopt;
-                       return -1;
-               }
-
-               /* If we have come to a non-option and did not permute it,
-                  either stop the scan or describe it to the caller and pass it
-                  by.  */
-
-               if (NONOPTION_P) {
-                       if (ordering == REQUIRE_ORDER)
-                               return -1;
-                       optarg = argv[optind++];
-                       return 1;
-               }
-
-               /* We have found another option-ARGV-element.
-                  Skip the initial punctuation.  */
-
-               nextchar = (argv[optind] + 1
-                           + (longopts != NULL && argv[optind][1] == '-'));
-       }
-
-       /* Decode the current option-ARGV-element.  */
-
-       /* Check whether the ARGV-element is a long option.
-
-          If long_only and the ARGV-element has the form "-f", where f is
-          a valid short option, don't consider it an abbreviated form of
-          a long option that starts with f.  Otherwise there would be no
-          way to give the -f short option.
-
-          On the other hand, if there's a long option "fubar" and
-          the ARGV-element is "-fu", do consider that an abbreviation of
-          the long option, just like "--fu", and not "-f" with arg "u".
-
-          This distinction seems to be the most useful approach.  */
-
-       if (longopts != NULL
-           && (argv[optind][1] == '-'
-               || (long_only && (argv[optind][2]
-                                 || !my_index(optstring, argv[optind][1]))))) {
-               char *nameend;
-               const struct option *p;
-               const struct option *pfound = NULL;
-               int exact = 0;
-               int ambig = 0;
-               int indfound = -1;
-               int option_index;
-
-               for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
-                       /* Do nothing.  */;
-
-               /* Test all long options for either exact match
-                  or abbreviated matches.  */
-               for (p = longopts, option_index = 0; p->name;
-                    p++, option_index++)
-                       if (!strncmp(p->name, nextchar, nameend - nextchar)) {
-                               if ((unsigned int)(nameend - nextchar)
-                                   == (unsigned int)strlen(p->name)) {
-                                       /* Exact match found.  */
-                                       pfound = p;
-                                       indfound = option_index;
-                                       exact = 1;
-                                       break;
-                               } else if (pfound == NULL) {
-                                       /* First nonexact match found.  */
-                                       pfound = p;
-                                       indfound = option_index;
-                               } else
-                                       /* Second or later nonexact match found.
-                                        */
-                                       ambig = 1;
-                       }
-
-               if (ambig && !exact) {
-                       if (opterr)
-                               fprintf(stderr,
-                                       _("%s: option `%s' is ambiguous\n"),
-                                       argv[0], argv[optind]);
-                       nextchar += strlen(nextchar);
-                       optind++;
-                       optopt = 0;
-                       return '?';
-               }
-
-               if (pfound != NULL) {
-                       option_index = indfound;
-                       optind++;
-                       if (*nameend) {
-                               /* Don't test has_arg with >, because some C
-                                  compilers don't
-                                  allow it to be used on enums.  */
-                               if (pfound->has_arg)
-                                       optarg = nameend + 1;
-                               else {
-                                       if (opterr) {
-                                               if (argv[optind - 1][1] == '-')
-                                                       /* --option */
-                                                       fprintf(stderr,
-                                                               _("%s: option `--%s' doesn't allow an argument\n"),
-                                                               argv[0],
-                                                               pfound->name);
-                                               else
-                                                       /* +option or -option */
-                                                       fprintf(stderr,
-                                                               _("%s: option `%c%s' doesn't allow an argument\n"),
-                                                               argv[0],
-                                                               argv[optind - 1]
-                                                                   [0],
-                                                               pfound->name);
-                                       }
-
-                                       nextchar += strlen(nextchar);
-
-                                       optopt = pfound->val;
-                                       return '?';
-                               }
-                       } else if (pfound->has_arg == 1) {
-                               if (optind < argc)
-                                       optarg = argv[optind++];
-                               else {
-                                       if (opterr)
-                                               fprintf(stderr,
-                                                       _("%s: option `%s' requires an argument\n"),
-                                                       argv[0],
-                                                       argv[optind - 1]);
-                                       nextchar += strlen(nextchar);
-                                       optopt = pfound->val;
-                                       return optstring[0] == ':' ? ':' : '?';
-                               }
-                       }
-                       nextchar += strlen(nextchar);
-                       if (longind != NULL)
-                               *longind = option_index;
-                       if (pfound->flag) {
-                               *(pfound->flag) = pfound->val;
-                               return 0;
-                       }
-                       return pfound->val;
-               }
-
-               /* Can't find it as a long option.  If this is not
-                  getopt_long_only,
-                  or the option starts with '--' or is not a valid short
-                  option, then it's an error.
-                  Otherwise interpret it as a short option.  */
-               if (!long_only || argv[optind][1] == '-'
-                   || my_index(optstring, *nextchar) == NULL) {
-                       if (opterr) {
-                               if (argv[optind][1] == '-')
-                                       /* --option */
-                                       fprintf(stderr,
-                                               _("%s: unrecognized option `--%s'\n"),
-                                               argv[0], nextchar);
-                               else
-                                       /* +option or -option */
-                                       fprintf(stderr,
-                                               _("%s: unrecognized option `%c%s'\n"),
-                                               argv[0], argv[optind][0],
-                                               nextchar);
-                       }
-                       nextchar = (char *)"";
-                       optind++;
-                       optopt = 0;
-                       return '?';
-               }
-       }
-
-       /* Look at and handle the next short option-character.  */
-
-       {
-               char c = *nextchar++;
-               char *temp = my_index(optstring, c);
-
-               /* Increment `optind' when we start to process its last
-                * character.  */
-               if (*nextchar == '\0')
-                       ++optind;
-
-               if (temp == NULL || c == ':') {
-                       if (opterr) {
-                               if (posixly_correct)
-                                       /* 1003.2 specifies the format of this
-                                        * message.  */
-                                       fprintf(stderr,
-                                               _("%s: illegal option -- %c\n"),
-                                               argv[0], c);
-                               else
-                                       fprintf(stderr,
-                                               _("%s: invalid option -- %c\n"),
-                                               argv[0], c);
-                       }
-                       optopt = c;
-                       return '?';
-               }
-               /* Convenience. Treat POSIX -W foo same as long option --foo */
-               if (temp[0] == 'W' && temp[1] == ';') {
-                       char *nameend;
-                       const struct option *p;
-                       const struct option *pfound = NULL;
-                       int exact = 0;
-                       int ambig = 0;
-                       int indfound = 0;
-                       int option_index;
-
-                       /* This is an option that requires an argument.  */
-                       if (*nextchar != '\0') {
-                               optarg = nextchar;
-                               /* If we end this ARGV-element by taking the
-                                  rest as an arg,
-                                  we must advance to the next element now.  */
-                               optind++;
-                       } else if (optind == argc) {
-                               if (opterr) {
-                                       /* 1003.2 specifies the format of this
-                                        * message.  */
-                                       fprintf(stderr,
-                                               _("%s: option requires an argument -- %c\n"),
-                                               argv[0], c);
-                               }
-                               optopt = c;
-                               if (optstring[0] == ':')
-                                       c = ':';
-                               else
-                                       c = '?';
-                               return c;
-                       } else
-                               /* We already incremented `optind' once;
-                                  increment it again when taking next ARGV-elt
-                                  as argument.  */
-                               optarg = argv[optind++];
-
-                       /* optarg is now the argument, see if it's in the
-                          table of longopts.  */
-
-                       for (nextchar = nameend = optarg;
-                            *nameend && *nameend != '='; nameend++)
-                               /* Do nothing.  */;
-
-                       /* Test all long options for either exact match
-                          or abbreviated matches.  */
-                       for (p = longopts, option_index = 0; p->name;
-                            p++, option_index++)
-                               if (!strncmp(p->name, nextchar,
-                                            nameend - nextchar)) {
-                                       if ((unsigned int)(nameend - nextchar)
-                                           == strlen(p->name)) {
-                                               /* Exact match found.  */
-                                               pfound = p;
-                                               indfound = option_index;
-                                               exact = 1;
-                                               break;
-                                       } else if (pfound == NULL) {
-                                               /* First nonexact match found.
-                                                */
-                                               pfound = p;
-                                               indfound = option_index;
-                                       } else
-                                               /* Second or later nonexact
-                                                * match found.  */
-                                               ambig = 1;
-                               }
-                       if (ambig && !exact) {
-                               if (opterr)
-                                       fprintf(stderr,
-                                               _("%s: option `-W %s' is ambiguous\n"),
-                                               argv[0], argv[optind]);
-                               nextchar += strlen(nextchar);
-                               optind++;
-                               return '?';
-                       }
-                       if (pfound != NULL) {
-                               option_index = indfound;
-                               if (*nameend) {
-                                       /* Don't test has_arg with >, because
-                                          some C compilers don't
-                                          allow it to be used on enums.  */
-                                       if (pfound->has_arg)
-                                               optarg = nameend + 1;
-                                       else {
-                                               if (opterr)
-                                                       fprintf(stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
-                                                               argv[0],
-                                                               pfound->name);
-
-                                               nextchar += strlen(nextchar);
-                                               return '?';
-                                       }
-                               } else if (pfound->has_arg == 1) {
-                                       if (optind < argc)
-                                               optarg = argv[optind++];
-                                       else {
-                                               if (opterr)
-                                                       fprintf(stderr,
-                                                               _("%s: option `%s' requires an argument\n"),
-                                                               argv[0],
-                                                               argv[optind
-                                                                    - 1]);
-                                               nextchar += strlen(nextchar);
-                                               return optstring[0] == ':'
-                                                              ? ':'
-                                                              : '?';
-                                       }
-                               }
-                               nextchar += strlen(nextchar);
-                               if (longind != NULL)
-                                       *longind = option_index;
-                               if (pfound->flag) {
-                                       *(pfound->flag) = pfound->val;
-                                       return 0;
-                               }
-                               return pfound->val;
-                       }
-                       nextchar = NULL;
-                       return 'W'; /* Let the application handle it.   */
-               }
-               if (temp[1] == ':') {
-                       if (temp[2] == ':') {
-                               /* This is an option that accepts an argument
-                                * optionally.  */
-                               if (*nextchar != '\0') {
-                                       optarg = nextchar;
-                                       optind++;
-                               } else
-                                       optarg = NULL;
-                               nextchar = NULL;
-                       } else {
-                               /* This is an option that requires an argument.
-                                */
-                               if (*nextchar != '\0') {
-                                       optarg = nextchar;
-                                       /* If we end this ARGV-element by taking
-                                          the rest as an arg,
-                                          we must advance to the next element
-                                          now.  */
-                                       optind++;
-                               } else if (optind == argc) {
-                                       if (opterr) {
-                                               /* 1003.2 specifies the format
-                                                * of this message.  */
-                                               fprintf(stderr,
-                                                       _("%s: option requires an argument -- %c\n"),
-                                                       argv[0], c);
-                                       }
-                                       optopt = c;
-                                       if (optstring[0] == ':')
-                                               c = ':';
-                                       else
-                                               c = '?';
-                               } else
-                                       /* We already incremented `optind' once;
-                                          increment it again when taking next
-                                          ARGV-elt as argument.  */
-                                       optarg = argv[optind++];
-                               nextchar = NULL;
-                       }
-               }
-               return c;
-       }
-}
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-int getopt(argc, argv, optstring) int argc;
-char *const *argv;
-const char *optstring;
-{
-       return _getopt_internal(argc, argv, optstring, (const struct option *)0,
-                               (int *)0, 0);
-}
-
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-#endif /* Not ELIDE_CODE.  */
-
-#ifdef TEST
-
-/* Compile with -DTEST to make an executable for use in testing
-   the above definition of `getopt'.  */
-
-int main(argc, argv) int argc;
-char **argv;
-{
-       int c;
-       int digit_optind = 0;
-
-       while (1) {
-               int this_option_optind = optind ? optind : 1;
-
-               c = getopt(argc, argv, "abc:d:0123456789");
-               if (c == -1)
-                       break;
-
-               switch (c) {
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                       if (digit_optind != 0
-                           && digit_optind != this_option_optind)
-                               printf("digits occur in two different argv-elements.\n");
-                       digit_optind = this_option_optind;
-                       printf("option %c\n", c);
-                       break;
-
-               case 'a':
-                       printf("option a\n");
-                       break;
-
-               case 'b':
-                       printf("option b\n");
-                       break;
-
-               case 'c':
-                       printf("option c with value `%s'\n", optarg);
-                       break;
-
-               case '?':
-                       break;
-
-               default:
-                       printf("?? getopt returned character code 0%o ??\n", c);
-               }
-       }
-
-       if (optind < argc) {
-               printf("non-option ARGV-elements: ");
-               while (optind < argc)
-                       printf("%s ", argv[optind++]);
-               printf("\n");
-       }
-
-       exit(0);
-}
-
-#endif /* TEST */
diff --git a/lib/getopt.h b/lib/getopt.h
deleted file mode 100644 (file)
index 7863bdb..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Declarations for getopt.
- * Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#ifndef _GETOPT_H
-#define _GETOPT_H 1
-
-/*
- * The operating system may or may not provide getopt_long(), and if
- * so it may or may not be a version we are willing to use.  Our
- * strategy is to declare getopt here, and then provide code unless
- * the supplied version is adequate.  The difficult case is when a
- * declaration for getopt is provided, as our declaration must match.
- *
- * XXX Arguably this version should be named differently, and the
- * local names defined to refer to the system version when we choose
- * to use the system version.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
-   the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
-   each non-option ARGV-element is returned here.  */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
-   This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
-
-   On entry to `getopt', zero means this is the first call; initialize.
-
-   When `getopt' returns -1, this is the index of the first of the
-   non-option elements that the caller should itself scan.
-
-   Otherwise, `optind' communicates from one call to the next
-   how much of ARGV has been scanned so far.  */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
-   for unrecognized options.  */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized.  */
-
-extern int optopt;
-
-/* Describe the long-named options requested by the application.
-   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
-   of `struct option' terminated by an element containing a name which is
-   zero.
-
-   The field `has_arg' is:
-   no_argument         (or 0) if the option does not take an argument,
-   required_argument   (or 1) if the option requires an argument,
-   optional_argument   (or 2) if the option takes an optional argument.
-
-   If the field `flag' is not NULL, it points to a variable that is set
-   to the value given in the field `val' when the option is found, but
-   left unchanged if the option is not found.
-
-   To have a long-named option do something other than set an `int' to
-   a compiled-in constant, such as set a value from `optarg', set the
-   option's `flag' field to zero and its `val' field to a nonzero
-   value (the equivalent single-letter option character, if there is
-   one).  For long options that have a zero `flag' field, `getopt'
-   returns the contents of the `val' field.  */
-
-struct option {
-#if defined(__STDC__) && __STDC__
-       const char *name;
-#else
-       char *name;
-#endif
-       /* has_arg can't be an enum because some compilers complain about
-          type mismatches in all the code that assumes it is an int.  */
-       int has_arg;
-       int *flag;
-       int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'.  */
-
-#define        no_argument             0
-#define required_argument      1
-#define optional_argument      2
-
-#if defined(__STDC__) && __STDC__
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-
-/*
- * getopt is defined in POSIX.2.  Assume that if the system defines
- * getopt that it complies with POSIX.2.  If not, an autoconf test
- * should be written to define NONPOSIX_GETOPT_DEFINITION.
- */
-#ifndef NONPOSIX_GETOPT_DEFINITION
-extern int getopt(int argc, char *const *argv, const char *shortopts);
-#else  /* NONPOSIX_GETOPT_DEFINITION */
-extern int getopt(void);
-#endif /* NONPOSIX_GETOPT_DEFINITION */
-
-#endif
-
-
-extern int getopt_long(int argc, char *const *argv, const char *shortopts,
-                      const struct option *longopts, int *longind);
-extern int getopt_long_only(int argc, char *const *argv, const char *shortopts,
-                           const struct option *longopts, int *longind);
-
-/* Internal only.  Users should not call this directly.  */
-extern int _getopt_internal(int argc, char *const *argv, const char *shortopts,
-                           const struct option *longopts, int *longind,
-                           int long_only);
-#else /* not __STDC__ */
-
-#ifdef REALLY_NEED_PLAIN_GETOPT
-extern int getopt();
-#endif /* REALLY_NEED_PLAIN_GETOPT */
-
-extern int getopt_long();
-extern int getopt_long_only();
-
-extern int _getopt_internal();
-
-#endif /* __STDC__ */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* getopt.h */
diff --git a/lib/getopt1.c b/lib/getopt1.c
deleted file mode 100644 (file)
index cf21c3a..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* getopt_long and getopt_long_only entry points for GNU getopt.
- * Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
- * Free Software Foundation, Inc.
- *
- * NOTE: The canonical source of this file is maintained with the GNU C Library.
- * Bugs can be reported to bug-glibc@gnu.org.
- */
-
-#include <zebra.h>
-#include "getopt.h"
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-int getopt_long(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
-       return _getopt_internal(argc, argv, options, long_options, opt_index,
-                               0);
-}
-
-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
-   If an option that starts with '-' (not '--') doesn't match a long option,
-   but does match a short option, it is parsed as a short option
-   instead.  */
-
-int getopt_long_only(argc, argv, options, long_options, opt_index) int argc;
-char *const *argv;
-const char *options;
-const struct option *long_options;
-int *opt_index;
-{
-       return _getopt_internal(argc, argv, options, long_options, opt_index,
-                               1);
-}
-
-
-#endif /* Not ELIDE_CODE.  */
-
-#ifdef TEST
-
-#include <stdio.h>
-
-int main(argc, argv) int argc;
-char **argv;
-{
-       int c;
-       int digit_optind = 0;
-
-       while (1) {
-               int this_option_optind = optind ? optind : 1;
-               int option_index = 0;
-               static struct option long_options[] = {
-                       {"add", 1, 0, 0},    {"append", 0, 0, 0},
-                       {"delete", 1, 0, 0}, {"verbose", 0, 0, 0},
-                       {"create", 0, 0, 0}, {"file", 1, 0, 0},
-                       {0, 0, 0, 0}};
-
-               c = getopt_long(argc, argv, "abc:d:0123456789", long_options,
-                               &option_index);
-               if (c == -1)
-                       break;
-
-               switch (c) {
-               case 0:
-                       printf("option %s", long_options[option_index].name);
-                       if (optarg)
-                               printf(" with arg %s", optarg);
-                       printf("\n");
-                       break;
-
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                       if (digit_optind != 0
-                           && digit_optind != this_option_optind)
-                               printf("digits occur in two different argv-elements.\n");
-                       digit_optind = this_option_optind;
-                       printf("option %c\n", c);
-                       break;
-
-               case 'a':
-                       printf("option a\n");
-                       break;
-
-               case 'b':
-                       printf("option b\n");
-                       break;
-
-               case 'c':
-                       printf("option c with value `%s'\n", optarg);
-                       break;
-
-               case 'd':
-                       printf("option d with value `%s'\n", optarg);
-                       break;
-
-               case '?':
-                       break;
-
-               default:
-                       printf("?? getopt returned character code 0%o ??\n", c);
-               }
-       }
-
-       if (optind < argc) {
-               printf("non-option ARGV-elements: ");
-               while (optind < argc)
-                       printf("%s ", argv[optind++]);
-               printf("\n");
-       }
-
-       exit(0);
-}
-
-#endif /* TEST */
index 27c41aaa27b430a33e481ff011606e1f2ce09366..42e162072ff3d98ac9d87c7d9364fff0dbbc8121 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /* route-map for interface.
  * Copyright (C) 1999 Kunihiro Ishiguro
+ * Copyright (C) 2023 LabN Consulting, L.L.C.
  */
 
 #include <zebra.h>
@@ -10,6 +11,9 @@
 #include "memory.h"
 #include "if.h"
 #include "if_rmap.h"
+#include "northbound_cli.h"
+
+#include "lib/if_rmap_clippy.c"
 
 DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container");
 DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME,
@@ -17,8 +21,6 @@ DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME,
 DEFINE_MTYPE_STATIC(LIB, IF_RMAP, "Interface route map");
 DEFINE_MTYPE_STATIC(LIB, IF_RMAP_NAME, "I.f. route map name");
 
-static struct list *if_rmap_ctx_list;
-
 static struct if_rmap *if_rmap_new(void)
 {
        struct if_rmap *new;
@@ -30,7 +32,9 @@ static struct if_rmap *if_rmap_new(void)
 
 static void if_rmap_free(struct if_rmap *if_rmap)
 {
-       XFREE(MTYPE_IF_RMAP_NAME, if_rmap->ifname);
+       char *no_const_ifname = (char *)if_rmap->ifname;
+
+       XFREE(MTYPE_IF_RMAP_NAME, no_const_ifname);
 
        XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
        XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
@@ -40,22 +44,16 @@ static void if_rmap_free(struct if_rmap *if_rmap)
 
 struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname)
 {
-       struct if_rmap key;
+       struct if_rmap key = {.ifname = ifname};
        struct if_rmap *if_rmap;
 
-       /* temporary copy */
-       key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL;
-
        if_rmap = hash_lookup(ctx->ifrmaphash, &key);
 
-       XFREE(MTYPE_IF_RMAP_NAME, key.ifname);
-
        return if_rmap;
 }
 
 void if_rmap_hook_add(struct if_rmap_ctx *ctx,
-                     void (*func)(struct if_rmap_ctx *ctx,
-                                  struct if_rmap *))
+                     void (*func)(struct if_rmap_ctx *ctx, struct if_rmap *))
 {
        ctx->if_rmap_add_hook = func;
 }
@@ -80,16 +78,11 @@ static void *if_rmap_hash_alloc(void *arg)
 
 static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname)
 {
-       struct if_rmap key;
+       struct if_rmap key = {.ifname = ifname};
        struct if_rmap *ret;
 
-       /* temporary copy */
-       key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL;
-
        ret = hash_get(ctx->ifrmaphash, &key, if_rmap_hash_alloc);
 
-       XFREE(MTYPE_IF_RMAP_NAME, key.ifname);
-
        return ret;
 }
 
@@ -108,177 +101,184 @@ static bool if_rmap_hash_cmp(const void *arg1, const void *arg2)
        return strcmp(if_rmap1->ifname, if_rmap2->ifname) == 0;
 }
 
-static struct if_rmap *if_rmap_set(struct if_rmap_ctx *ctx,
-                                  const char *ifname, enum if_rmap_type type,
-                                  const char *routemap_name)
+static void if_rmap_set(struct if_rmap_ctx *ctx, const char *ifname,
+                       enum if_rmap_type type, const char *routemap_name)
 {
-       struct if_rmap *if_rmap;
-
-       if_rmap = if_rmap_get(ctx, ifname);
+       struct if_rmap *if_rmap = if_rmap_get(ctx, ifname);
 
-       if (type == IF_RMAP_IN) {
-               XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
-               if_rmap->routemap[IF_RMAP_IN] =
-                       XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name);
-       }
-       if (type == IF_RMAP_OUT) {
-               XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
-               if_rmap->routemap[IF_RMAP_OUT] =
-                       XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name);
-       }
+       assert(type == IF_RMAP_IN || type == IF_RMAP_OUT);
+       XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]);
+       if_rmap->routemap[type] = XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name);
 
        if (ctx->if_rmap_add_hook)
                (ctx->if_rmap_add_hook)(ctx, if_rmap);
-
-       return if_rmap;
 }
 
-static int if_rmap_unset(struct if_rmap_ctx *ctx,
-                        const char *ifname, enum if_rmap_type type,
-                        const char *routemap_name)
+static void if_rmap_unset(struct if_rmap_ctx *ctx, const char *ifname,
+                         enum if_rmap_type type)
 {
-       struct if_rmap *if_rmap;
+       struct if_rmap *if_rmap = if_rmap_lookup(ctx, ifname);
 
-       if_rmap = if_rmap_lookup(ctx, ifname);
        if (!if_rmap)
-               return 0;
-
-       if (type == IF_RMAP_IN) {
-               if (!if_rmap->routemap[IF_RMAP_IN])
-                       return 0;
-               if (strcmp(if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0)
-                       return 0;
-
-               XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]);
-       }
+               return;
 
-       if (type == IF_RMAP_OUT) {
-               if (!if_rmap->routemap[IF_RMAP_OUT])
-                       return 0;
-               if (strcmp(if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0)
-                       return 0;
+       assert(type == IF_RMAP_IN || type == IF_RMAP_OUT);
+       if (!if_rmap->routemap[type])
+               return;
 
-               XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]);
-       }
+       XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]);
 
        if (ctx->if_rmap_delete_hook)
                ctx->if_rmap_delete_hook(ctx, if_rmap);
 
-       if (if_rmap->routemap[IF_RMAP_IN] == NULL
-           && if_rmap->routemap[IF_RMAP_OUT] == NULL) {
+       if (if_rmap->routemap[IF_RMAP_IN] == NULL &&
+           if_rmap->routemap[IF_RMAP_OUT] == NULL) {
                hash_release(ctx->ifrmaphash, if_rmap);
                if_rmap_free(if_rmap);
        }
-
-       return 1;
 }
 
-DEFUN (if_rmap,
-       if_rmap_cmd,
-       "route-map RMAP_NAME <in|out> IFNAME",
-       "Route map set\n"
-       "Route map name\n"
-       "Route map set for input filtering\n"
-       "Route map set for output filtering\n"
-       "Route map interface name\n")
+static int if_route_map_handler(struct vty *vty, bool no, const char *dir,
+                               const char *other_dir, const char *ifname,
+                               const char *route_map)
 {
-       int idx_rmap_name = 1;
-       int idx_in_out = 2;
-       int idx_ifname = 3;
-       enum if_rmap_type type;
-       struct if_rmap_ctx *ctx =
-               (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list);
-
-       if (strncmp(argv[idx_in_out]->text, "in", 1) == 0)
-               type = IF_RMAP_IN;
-       else if (strncmp(argv[idx_in_out]->text, "out", 1) == 0)
-               type = IF_RMAP_OUT;
-       else {
-               vty_out(vty, "route-map direction must be [in|out]\n");
-               return CMD_WARNING_CONFIG_FAILED;
+       enum nb_operation op = no ? NB_OP_DESTROY : NB_OP_MODIFY;
+       const struct lyd_node *dnode;
+       char xpath[XPATH_MAXLEN];
+
+       if (!no) {
+               snprintf(
+                       xpath, sizeof(xpath),
+                       "./if-route-maps/if-route-map[interface='%s']/%s-route-map",
+                       ifname, dir);
+       } else {
+               /*
+                * If we are deleting the last policy for this interface,
+                * (i.e., no `in` or `out` policy). delete the interface list
+                * node instead.
+                */
+               dnode = yang_dnode_get(vty->candidate_config->dnode,
+                                      VTY_CURR_XPATH);
+               if (yang_dnode_existsf(
+                           dnode,
+                           "./if-route-maps/if-route-map[interface='%s']/%s-route-map",
+                           ifname, other_dir)) {
+                       snprintf(
+                               xpath, sizeof(xpath),
+                               "./if-route-maps/if-route-map[interface='%s']/%s-route-map",
+                               ifname, dir);
+               } else {
+                       /* both dir will be empty so delete the list node */
+                       snprintf(xpath, sizeof(xpath),
+                                "./if-route-maps/if-route-map[interface='%s']",
+                                ifname);
+               }
        }
+       nb_cli_enqueue_change(vty, xpath, op, route_map);
 
-       if_rmap_set(ctx, argv[idx_ifname]->arg,
-                   type, argv[idx_rmap_name]->arg);
+       return nb_cli_apply_changes(vty, NULL);
+}
 
-       return CMD_SUCCESS;
+DEFPY_YANG(if_ipv4_route_map, if_ipv4_route_map_cmd,
+          "route-map ROUTE-MAP <in$in|out> IFNAME",
+          "Route map set\n"
+          "Route map name\n"
+          "Route map set for input filtering\n"
+          "Route map set for output filtering\n" INTERFACE_STR)
+{
+       const char *dir = in ? "in" : "out";
+       const char *other_dir = in ? "out" : "in";
+
+       return if_route_map_handler(vty, false, dir, other_dir, ifname,
+                                   route_map);
 }
 
-DEFUN (no_if_rmap,
-       no_if_rmap_cmd,
-       "no route-map ROUTEMAP_NAME <in|out> IFNAME",
-       NO_STR
-       "Route map unset\n"
-       "Route map name\n"
-       "Route map for input filtering\n"
-       "Route map for output filtering\n"
-       "Route map interface name\n")
+DEFPY_YANG(no_if_ipv4_route_map, no_if_ipv4_route_map_cmd,
+          "no route-map [ROUTE-MAP] <in$in|out> IFNAME",
+          NO_STR
+          "Route map set\n"
+          "Route map name\n"
+          "Route map set for input filtering\n"
+          "Route map set for output filtering\n" INTERFACE_STR)
 {
-       int idx_routemap_name = 2;
-       int idx_in_out = 3;
-       int idx_ifname = 4;
-       int ret;
-       enum if_rmap_type type;
-       struct if_rmap_ctx *ctx =
-               (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list);
-
-       if (strncmp(argv[idx_in_out]->arg, "i", 1) == 0)
-               type = IF_RMAP_IN;
-       else if (strncmp(argv[idx_in_out]->arg, "o", 1) == 0)
-               type = IF_RMAP_OUT;
-       else {
-               vty_out(vty, "route-map direction must be [in|out]\n");
-               return CMD_WARNING_CONFIG_FAILED;
-       }
+       const char *dir = in ? "in" : "out";
+       const char *other_dir = in ? "out" : "in";
 
-       ret = if_rmap_unset(ctx, argv[idx_ifname]->arg, type,
-                           argv[idx_routemap_name]->arg);
-       if (!ret) {
-               vty_out(vty, "route-map doesn't exist\n");
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-       return CMD_SUCCESS;
+       return if_route_map_handler(vty, true, dir, other_dir, ifname,
+                                   route_map);
 }
 
+/*
+ * CLI infra requires new handlers for ripngd
+ */
+DEFPY_YANG(if_ipv6_route_map, if_ipv6_route_map_cmd,
+          "route-map ROUTE-MAP <in$in|out> IFNAME",
+          "Route map set\n"
+          "Route map name\n"
+          "Route map set for input filtering\n"
+          "Route map set for output filtering\n" INTERFACE_STR)
+{
+       const char *dir = in ? "in" : "out";
+       const char *other_dir = in ? "out" : "in";
 
-/* Configuration write function. */
-int config_write_if_rmap(struct vty *vty,
-                        struct if_rmap_ctx *ctx)
+       return if_route_map_handler(vty, false, dir, other_dir, ifname,
+                                   route_map);
+}
+
+DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd,
+          "no route-map [ROUTE-MAP] <in$in|out> IFNAME",
+          NO_STR
+          "Route map set\n"
+          "Route map name\n"
+          "Route map set for input filtering\n"
+          "Route map set for output filtering\n" INTERFACE_STR)
 {
-       unsigned int i;
-       struct hash_bucket *mp;
-       int write = 0;
-       struct hash *ifrmaphash = ctx->ifrmaphash;
-
-       for (i = 0; i < ifrmaphash->size; i++)
-               for (mp = ifrmaphash->index[i]; mp; mp = mp->next) {
-                       struct if_rmap *if_rmap;
-
-                       if_rmap = mp->data;
-
-                       if (if_rmap->routemap[IF_RMAP_IN]) {
-                               vty_out(vty, " route-map %s in %s\n",
-                                       if_rmap->routemap[IF_RMAP_IN],
-                                       if_rmap->ifname);
-                               write++;
-                       }
-
-                       if (if_rmap->routemap[IF_RMAP_OUT]) {
-                               vty_out(vty, " route-map %s out %s\n",
-                                       if_rmap->routemap[IF_RMAP_OUT],
-                                       if_rmap->ifname);
-                               write++;
-                       }
-               }
-       return write;
+       const char *dir = in ? "in" : "out";
+       const char *other_dir = in ? "out" : "in";
+
+       return if_route_map_handler(vty, true, dir, other_dir, ifname,
+                                   route_map);
+}
+
+void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode,
+                          bool show_defaults)
+{
+       if (yang_dnode_exists(dnode, "./in-route-map"))
+               vty_out(vty, " route-map %s in %s\n",
+                       yang_dnode_get_string(dnode, "./in-route-map"),
+                       yang_dnode_get_string(dnode, "./interface"));
+       if (yang_dnode_exists(dnode, "./out-route-map"))
+               vty_out(vty, " route-map %s out %s\n",
+                       yang_dnode_get_string(dnode, "./out-route-map"),
+                       yang_dnode_get_string(dnode, "./interface"));
+}
+
+void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx,
+                           const struct lyd_node *dnode,
+                           enum if_rmap_type type, bool del)
+{
+
+       const char *mapname = yang_dnode_get_string(dnode, NULL);
+       const char *ifname = yang_dnode_get_string(dnode, "../interface");
+
+       if (del)
+               if_rmap_unset(ctx, ifname, type);
+       else
+               if_rmap_set(ctx, ifname, type, mapname);
+}
+
+void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx,
+                            const struct lyd_node *dnode)
+{
+       const char *ifname = yang_dnode_get_string(dnode, "interface");
+       if_rmap_unset(ctx, ifname, IF_RMAP_IN);
+       if_rmap_unset(ctx, ifname, IF_RMAP_OUT);
 }
 
 void if_rmap_ctx_delete(struct if_rmap_ctx *ctx)
 {
-       listnode_delete(if_rmap_ctx_list, ctx);
        hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free);
-       if (ctx->name)
-               XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx);
+       XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name);
        XFREE(MTYPE_IF_RMAP_CTX, ctx);
 }
 
@@ -289,29 +289,24 @@ struct if_rmap_ctx *if_rmap_ctx_create(const char *name)
 
        ctx = XCALLOC(MTYPE_IF_RMAP_CTX, sizeof(struct if_rmap_ctx));
 
-       if (ctx->name)
-               ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name);
-       ctx->ifrmaphash = hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp,
-                                          "Interface Route-Map Hash");
-       if (!if_rmap_ctx_list)
-               if_rmap_ctx_list = list_new();
-       listnode_add(if_rmap_ctx_list, ctx);
+       ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name);
+       ctx->ifrmaphash =
+               hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp,
+                                "Interface Route-Map Hash");
        return ctx;
 }
 
 void if_rmap_init(int node)
 {
-       if (node == RIPNG_NODE) {
-       } else if (node == RIP_NODE) {
-               install_element(RIP_NODE, &if_rmap_cmd);
-               install_element(RIP_NODE, &no_if_rmap_cmd);
+       if (node == RIP_NODE) {
+               install_element(RIP_NODE, &if_ipv4_route_map_cmd);
+               install_element(RIP_NODE, &no_if_ipv4_route_map_cmd);
+       } else if (node == RIPNG_NODE) {
+               install_element(RIPNG_NODE, &if_ipv6_route_map_cmd);
+               install_element(RIPNG_NODE, &no_if_ipv6_route_map_cmd);
        }
-       if_rmap_ctx_list = list_new();
 }
 
 void if_rmap_terminate(void)
 {
-       if (!if_rmap_ctx_list)
-               return;
-       list_delete(&if_rmap_ctx_list);
 }
index 3bdbc2a3b2cd1b7282ec93c2fbd005f96de7a7d1..a9f811e2210c788c0abc437899480d4c2ee770af 100644 (file)
@@ -6,15 +6,20 @@
 #ifndef _ZEBRA_IF_RMAP_H
 #define _ZEBRA_IF_RMAP_H
 
+#include "typesafe.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+struct lyd_node;
+struct vty;
+
 enum if_rmap_type { IF_RMAP_IN, IF_RMAP_OUT, IF_RMAP_MAX };
 
 struct if_rmap {
        /* Name of the interface. */
-       char *ifname;
+       const char *ifname;
 
        char *routemap[IF_RMAP_MAX];
 };
@@ -45,7 +50,14 @@ void if_rmap_hook_delete(struct if_rmap_ctx *ctx,
                                      struct if_rmap *));
 extern struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx,
                                      const char *ifname);
+extern void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx,
+                                  const struct lyd_node *dnode,
+                                  enum if_rmap_type type, bool del);
+extern void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx,
+                                   const struct lyd_node *dnode);
 extern int config_write_if_rmap(struct vty *, struct if_rmap_ctx *ctx);
+void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode,
+                          bool show_defaults);
 
 #ifdef __cplusplus
 }
diff --git a/lib/iso.c b/lib/iso.c
new file mode 100644 (file)
index 0000000..fe97776
--- /dev/null
+++ b/lib/iso.c
@@ -0,0 +1,144 @@
+/*
+ * ISO Network functions - iso_net.c
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2023 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "compiler.h"
+
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "printfrr.h"
+#include "iso.h"
+
+/**
+ * Print ISO System ID as 0000.0000.0000
+ *
+ * @param      Print buffer
+ * @param      Print argument
+ * @param      Pointer to the System ID to be printed
+ *
+ * @return     Number of printed characters
+ */
+printfrr_ext_autoreg_p("SY", printfrr_iso_sysid);
+static ssize_t printfrr_iso_sysid(struct fbuf *buf, struct printfrr_eargs *ea,
+                                 const void *vptr)
+{
+       const uint8_t *id = vptr;
+
+       if (!id)
+               return bputs(buf, "(null)");
+
+       return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x",
+                        id[0], id[1], id[2], id[3], id[4], id[5]);
+}
+
+/**
+ * Print ISO Pseudo Node system ID as 0000.0000.0000.00
+ *
+ * @param      Print buffer
+ * @param      Print argument
+ * @param      Pointer to the System ID to be printed
+ *
+ * @return     Number of printed characters
+ */
+printfrr_ext_autoreg_p("PN", printfrr_iso_pseudo);
+static ssize_t printfrr_iso_pseudo(struct fbuf *buf, struct printfrr_eargs *ea,
+                                  const void *vptr)
+{
+       const uint8_t *id = vptr;
+
+       if (!id)
+               return bputs(buf, "(null)");
+
+       return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x",
+                        id[0], id[1], id[2], id[3], id[4], id[5], id[6]);
+}
+
+/**
+ * Print ISO LSP Fragment System ID as 0000.0000.0000.00-00
+ *
+ * @param      Print buffer
+ * @param      Print argument
+ * @param      Pointer to the System ID to be printed
+ *
+ * @return     Number of printed characters
+ */
+printfrr_ext_autoreg_p("LS", printfrr_iso_frag_id);
+static ssize_t printfrr_iso_frag_id(struct fbuf *buf, struct printfrr_eargs *ea,
+                                   const void *vptr)
+{
+       const uint8_t *id = vptr;
+
+       if (!id)
+               return bputs(buf, "(null)");
+
+       return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x-%02x",
+                        id[0], id[1], id[2], id[3], id[4], id[5], id[6],
+                        id[7]);
+}
+
+/**
+ * Print ISO Network address as 00.0000.0000.0000 ... with the System ID
+ * as 0000.0000.0000.00 when long 'l' option is added to '%pIS'
+ *
+ * @param      Print buffer
+ * @param      Print argument
+ * @param      Pointer to the ISO Network address
+ *
+ * @return     Number of printed characters
+ */
+printfrr_ext_autoreg_p("IS", printfrr_iso_addr);
+static ssize_t printfrr_iso_addr(struct fbuf *buf, struct printfrr_eargs *ea,
+                                const void *vptr)
+{
+       const struct iso_address *ia = vptr;
+       uint8_t len = 0;
+       int i = 0;
+       ssize_t ret = 0;
+
+       if (ea->fmt[0] == 'l') {
+               len = 7; /* ISO SYSTEM ID + 1 */
+               ea->fmt++;
+       }
+
+       if (!ia)
+               return bputs(buf, "(null)");
+
+       len += ia->addr_len;
+       while (i < len) {
+               /* No dot for odd index and at the end of address */
+               if ((i & 1) || (i == (len - 1)))
+                       ret += bprintfrr(buf, "%02x", ia->area_addr[i]);
+               else
+                       ret += bprintfrr(buf, "%02x.", ia->area_addr[i]);
+               i++;
+       }
+
+       return ret;
+}
+
diff --git a/lib/iso.h b/lib/iso.h
new file mode 100644 (file)
index 0000000..975d3c1
--- /dev/null
+++ b/lib/iso.h
@@ -0,0 +1,49 @@
+/*
+ * ISO Network definition - iso_net.h
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2023 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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
+ */
+
+#ifndef LIB_ISO_H_
+#define LIB_ISO_H_
+
+#include "compiler.h"
+
+/* len of "xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx" + '\0' */
+#define ISO_ADDR_STRLEN        51
+#define ISO_ADDR_MIN   8
+#define ISO_ADDR_SIZE  20
+struct iso_address {
+       uint8_t addr_len;
+       uint8_t area_addr[ISO_ADDR_SIZE];
+};
+
+/* len of "xxxx.xxxx.xxxx.xx-xx" + '\0' */
+#define ISO_SYSID_STRLEN 21
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pSY" (uint8_t *)
+#pragma FRR printfrr_ext "%pPN" (uint8_t *)
+#pragma FRR printfrr_ext "%pLS" (uint8_t *)
+#pragma FRR printfrr_ext "%pIS" (struct iso_address *)
+#endif
+
+#endif /* LIB_ISO_H_ */
index 589c0ae704c4c38147a171a0db5e850b04919790..58727a568b5304039631b31747966d954e125018 100644 (file)
@@ -26,6 +26,7 @@
 #include "printfrr.h"
 #include <lib/json.h>
 #include "link_state.h"
+#include "iso.h"
 
 /* Link State Memory allocation */
 DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database");
@@ -333,7 +334,7 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
 /**
  *  Link State prefix management functions
  */
-struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
+struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p)
 {
        struct ls_prefix *new;
 
@@ -342,7 +343,7 @@ struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
 
        new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
        new->adv = adv;
-       new->pref = p;
+       new->pref = *p;
 
        return new;
 }
@@ -496,7 +497,6 @@ void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
        /* Then remove Vertex from Link State Data Base and free memory */
        vertices_del(&ted->vertices, vertex);
        XFREE(MTYPE_LS_DB, vertex);
-       vertex = NULL;
 }
 
 void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
@@ -673,9 +673,9 @@ static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge)
        }
 }
 
-static uint64_t get_edge_key(struct ls_attributes *attr, bool dst)
+static struct ls_edge_key get_edge_key(struct ls_attributes *attr, bool dst)
 {
-       uint64_t key = 0;
+       struct ls_edge_key key = {.family = AF_UNSPEC};
        struct ls_standard *std;
 
        if (!attr)
@@ -684,30 +684,37 @@ static uint64_t get_edge_key(struct ls_attributes *attr, bool dst)
        std = &attr->standard;
 
        if (dst) {
-               /* Key is the IPv4 remote address */
-               if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
-                       key = ((uint64_t)ntohl(std->remote.s_addr))
-                             & 0xffffffff;
-               /* or the 64 bits LSB of IPv6 remote address */
-               else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
-                       key = ((uint64_t)ntohl(std->remote6.s6_addr32[2]) << 32
-                              | (uint64_t)ntohl(std->remote6.s6_addr32[3]));
-               /* of remote identifier if no IP addresses are defined */
-               else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
-                       key = (((uint64_t)std->remote_id) & 0xffffffff)
-                             | ((uint64_t)std->local_id << 32);
+               if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) {
+                       /* Key is the IPv4 remote address */
+                       key.family = AF_INET;
+                       IPV4_ADDR_COPY(&key.k.addr, &std->remote);
+               } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) {
+                       /* or the IPv6 remote address */
+                       key.family = AF_INET6;
+                       IPV6_ADDR_COPY(&key.k.addr6, &std->remote6);
+               } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) {
+                       /* or Remote identifier if IP addr. are not defined */
+                       key.family = AF_LOCAL;
+                       key.k.link_id =
+                               (((uint64_t)std->remote_id) & 0xffffffff) |
+                               ((uint64_t)std->local_id << 32);
+               }
        } else {
-               /* Key is the IPv4 local address */
-               if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
-                       key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff;
-               /* or the 64 bits LSB of IPv6 local address */
-               else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
-                       key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32
-                              | (uint64_t)ntohl(std->local6.s6_addr32[3]));
-               /* of local identifier if no IP addresses are defined */
-               else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
-                       key = (((uint64_t)std->local_id) & 0xffffffff)
-                             | ((uint64_t)std->remote_id << 32);
+               if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
+                       /* Key is the IPv4 local address */
+                       key.family = AF_INET;
+                       IPV4_ADDR_COPY(&key.k.addr, &std->local);
+               } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
+                       /* or the 64 bits LSB of IPv6 local address */
+                       key.family = AF_INET6;
+                       IPV6_ADDR_COPY(&key.k.addr6, &std->local6);
+               } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) {
+                       /* or Remote identifier if IP addr. are not defined */
+                       key.family = AF_LOCAL;
+                       key.k.link_id =
+                               (((uint64_t)std->local_id) & 0xffffffff) |
+                               ((uint64_t)std->remote_id << 32);
+               }
        }
 
        return key;
@@ -717,13 +724,13 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted,
                            struct ls_attributes *attributes)
 {
        struct ls_edge *new;
-       uint64_t key = 0;
+       struct ls_edge_key key;
 
        if (attributes == NULL)
                return NULL;
 
        key = get_edge_key(attributes, false);
-       if (key == 0)
+       if (key.family == AF_UNSPEC)
                return NULL;
 
        /* Create Edge and add it to the TED */
@@ -741,11 +748,12 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted,
        return new;
 }
 
-struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
+struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
+                                   const struct ls_edge_key key)
 {
        struct ls_edge edge = {};
 
-       if (key == 0)
+       if (key.family == AF_UNSPEC)
                return NULL;
 
        edge.key = key;
@@ -761,7 +769,7 @@ struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted,
                return NULL;
 
        edge.key = get_edge_key(attributes, false);
-       if (edge.key == 0)
+       if (edge.key.family == AF_UNSPEC)
                return NULL;
 
        return edges_find(&ted->edges, &edge);
@@ -776,7 +784,7 @@ struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted,
                return NULL;
 
        edge.key = get_edge_key(attributes, true);
-       if (edge.key == 0)
+       if (edge.key.family == AF_UNSPEC)
                return NULL;
 
        return edges_find(&ted->edges, &edge);
@@ -814,7 +822,7 @@ int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2)
        if (!e1 && !e2)
                return 1;
 
-       if (e1->key != e2->key)
+       if (edge_cmp(e1, e2) != 0)
                return 0;
 
        if (e1->attributes == e2->attributes)
@@ -889,7 +897,7 @@ struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
        if (pref == NULL)
                return NULL;
 
-       old = ls_find_subnet(ted, pref->pref);
+       old = ls_find_subnet(ted, &pref->pref);
        if (old) {
                if (!ls_prefix_same(old->ls_pref, pref)) {
                        ls_prefix_del(old->ls_pref);
@@ -942,11 +950,15 @@ void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet)
        ls_subnet_del(ted, subnet);
 }
 
-struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
+                                const struct prefix *prefix)
 {
        struct ls_subnet subnet = {};
 
-       subnet.key = prefix;
+       if (!prefix)
+               return NULL;
+
+       prefix_copy(&subnet.key, prefix);
        return subnets_find(&ted->subnets, &subnet);
 }
 
@@ -1769,9 +1781,10 @@ struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg,
        case LS_MSG_EVENT_DELETE:
                vertex = ls_find_vertex_by_id(ted, node->adv);
                if (vertex) {
-                       if (delete)
+                       if (delete) {
                                ls_vertex_del_all(ted, vertex);
-                       else
+                               vertex = NULL;
+                       } else
                                vertex->status = DELETE;
                }
                break;
@@ -1846,11 +1859,12 @@ struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg,
                        subnet->status = UPDATE;
                break;
        case LS_MSG_EVENT_DELETE:
-               subnet = ls_find_subnet(ted, pref->pref);
+               subnet = ls_find_subnet(ted, &pref->pref);
                if (subnet) {
-                       if (delete)
+                       if (delete) {
                                ls_subnet_del_all(ted, subnet);
-                       else
+                               subnet = NULL;
+                       } else
                                subnet->status = DELETE;
                }
                break;
@@ -1905,6 +1919,20 @@ void ls_delete_msg(struct ls_message *msg)
        if (msg == NULL)
                return;
 
+       if (msg->event == LS_MSG_EVENT_DELETE) {
+               switch (msg->type) {
+               case LS_MSG_TYPE_NODE:
+                       ls_node_del(msg->data.node);
+                       break;
+               case LS_MSG_TYPE_ATTRIBUTES:
+                       ls_attributes_del(msg->data.attr);
+                       break;
+               case LS_MSG_TYPE_PREFIX:
+                       ls_prefix_del(msg->data.prefix);
+                       break;
+               }
+       }
+
        XFREE(MTYPE_LS_DB, msg);
 }
 
@@ -1965,13 +1993,9 @@ static const char *const status2txt[] = {
 static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str,
                                      size_t size)
 {
-       if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) {
-               uint8_t *id;
-
-               id = lnid.id.iso.sys_id;
-               snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0],
-                          id[1], id[2], id[3], id[4], id[5]);
-       } else
+       if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2)
+               snprintfrr(str, size, "%pSY", lnid.id.iso.sys_id);
+       else
                snprintfrr(str, size, "%pI4", &lnid.id.ip.addr);
 
        return str;
@@ -2177,6 +2201,34 @@ void ls_show_vertices(struct ls_ted *ted, struct vty *vty,
        }
 }
 
+static const char *edge_key_to_text(struct ls_edge_key key)
+{
+#define FORMAT_BUF_COUNT 4
+       static char buf_ring[FORMAT_BUF_COUNT][INET6_BUFSIZ];
+       static size_t cur_buf = 0;
+       char *rv;
+
+       rv = buf_ring[cur_buf];
+       cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT;
+
+       switch (key.family) {
+       case AF_INET:
+               snprintfrr(rv, INET6_BUFSIZ, "%pI4", &key.k.addr);
+               break;
+       case AF_INET6:
+               snprintfrr(rv, INET6_BUFSIZ, "%pI6", &key.k.addr6);
+               break;
+       case AF_LOCAL:
+               snprintfrr(rv, INET6_BUFSIZ, "%" PRIu64, key.k.link_id);
+               break;
+       default:
+               snprintfrr(rv, INET6_BUFSIZ, "(Unknown)");
+               break;
+       }
+
+       return rv;
+}
+
 static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty,
                             bool verbose)
 {
@@ -2189,7 +2241,7 @@ static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty,
        attr = edge->attributes;
        sbuf_init(&sbuf, NULL, 0);
 
-       sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key);
+       sbuf_push(&sbuf, 2, "Edge (%s): ", edge_key_to_text(edge->key));
        if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
                sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local);
        else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
@@ -2348,7 +2400,7 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json)
 
        attr = edge->attributes;
 
-       json_object_int_add(json, "edge-id", edge->key);
+       json_object_string_add(json, "edge-id", edge_key_to_text(edge->key));
        json_object_string_add(json, "status", status2txt[edge->status]);
        json_object_string_add(json, "origin", origin2txt[attr->adv.origin]);
        ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ);
@@ -2726,8 +2778,8 @@ void ls_dump_ted(struct ls_ted *ted)
                for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
                                          vertex_edge)) {
                        zlog_debug(
-                               "        inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
-                               vertex_edge->key,
+                               "        inc edge key:%s attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+                               edge_key_to_text(vertex_edge->key),
                                &vertex_edge->attributes->adv.id.ip.addr,
                                &vertex_edge->attributes->standard.local,
                                &vertex_edge->attributes->standard.remote);
@@ -2735,15 +2787,16 @@ void ls_dump_ted(struct ls_ted *ted)
                for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
                                          vertex_edge)) {
                        zlog_debug(
-                               "        out edge key:%" PRIu64 " attr key:%pI4  loc:(%pI4) rmt:(%pI4)",
-                               vertex_edge->key,
+                               "        out edge key:%s attr key:%pI4  loc:(%pI4) rmt:(%pI4)",
+                               edge_key_to_text(vertex_edge->key),
                                &vertex_edge->attributes->adv.id.ip.addr,
                                &vertex_edge->attributes->standard.local,
                                &vertex_edge->attributes->standard.remote);
                }
        }
        frr_each (edges, &ted->edges, edge) {
-               zlog_debug("    Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key,
+               zlog_debug("    Ted edge key:%s src:%pI4 dst:%pI4",
+                          edge_key_to_text(edge->key),
                           edge->source ? &edge->source->node->router_id
                                        : &inaddr_any,
                           edge->destination
index e6a6388ba41334b8f38afade0f19259750a6d603..d3a0ce39da99abcb87f678db4697ae62a0fce8c1 100644 (file)
@@ -92,6 +92,9 @@ struct ls_node_id {
  */
 extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2);
 
+/* Supported number of algorithm by the link-state library */
+#define LIB_LS_SR_ALGO_COUNT 2
+
 /* Link State flags to indicate which Node parameters are valid */
 #define LS_NODE_UNSET          0x0000
 #define LS_NODE_NAME           0x0001
@@ -123,7 +126,7 @@ struct ls_node {
                uint32_t lower_bound;           /* MPLS label lower bound */
                uint32_t range_size;            /* MPLS label range size */
        } srlb;
-       uint8_t algo[2];                /* Segment Routing Algorithms */
+       uint8_t algo[LIB_LS_SR_ALGO_COUNT]; /* Segment Routing Algorithms */
        uint8_t msd;                    /* Maximum Stack Depth */
 };
 
@@ -314,7 +317,7 @@ extern int ls_attributes_same(struct ls_attributes *a1,
  *
  * @return     New Link State Prefix
  */
-extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p);
+extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p);
 
 /**
  * Remove Link State Prefix. Data Structure is freed.
@@ -382,13 +385,23 @@ struct ls_vertex {
        struct list *prefixes;          /* List of advertised prefix */
 };
 
+/* Link State Edge Key structure */
+struct ls_edge_key {
+       uint8_t family;
+       union {
+               struct in_addr addr;
+               struct in6_addr addr6;
+               uint64_t link_id;
+       } k;
+};
+
 /* Link State Edge structure */
 PREDECL_RBTREE_UNIQ(edges);
 struct ls_edge {
        enum ls_type type;              /* Link State Type */
        enum ls_status status;          /* Status of the Edge in the TED */
        struct edges_item entry;        /* Entry in RB tree */
-       uint64_t key;                   /* Unique Key identifier */
+       struct ls_edge_key key;         /* Unique Key identifier */
        struct ls_attributes *attributes;       /* Link State attributes */
        struct ls_vertex *source;       /* Pointer to the source Vertex */
        struct ls_vertex *destination;  /* Pointer to the destination Vertex */
@@ -416,13 +429,25 @@ DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp);
 macro_inline int edge_cmp(const struct ls_edge *edge1,
                          const struct ls_edge *edge2)
 {
-       return numcmp(edge1->key, edge2->key);
+       if (edge1->key.family != edge2->key.family)
+               return numcmp(edge1->key.family, edge2->key.family);
+
+       switch (edge1->key.family) {
+       case AF_INET:
+               return memcmp(&edge1->key.k.addr, &edge2->key.k.addr, 4);
+       case AF_INET6:
+               return memcmp(&edge1->key.k.addr6, &edge2->key.k.addr6, 16);
+       case AF_LOCAL:
+               return numcmp(edge1->key.k.link_id, edge2->key.k.link_id);
+       default:
+               return 0;
+       }
 }
 DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp);
 
 /*
  * Prefix comparison are done to the host part so, 10.0.0.1/24
- * and 10.0.0.2/24 are considered come different
+ * and 10.0.0.2/24 are considered different
  */
 macro_inline int subnet_cmp(const struct ls_subnet *a,
                            const struct ls_subnet *b)
@@ -619,7 +644,7 @@ extern void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge);
  * @return     Edge if found, NULL otherwise
  */
 extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
-                                          const uint64_t key);
+                                          const struct ls_edge_key key);
 
 /**
  * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6
@@ -709,7 +734,7 @@ extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet);
  * @return             Subnet if found, NULL otherwise
  */
 extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
-                                       const struct prefix prefix);
+                                       const struct prefix *prefix);
 
 /**
  * Create a new Link State Data Base.
index fc28ffc6faf4c695806d1ceff315b555d3dc1bb2..26e608d16b9b0784cbc74c2c8301ab0a9882d1e1 100644 (file)
@@ -34,18 +34,23 @@ static int log_cmdline_syslog_lvl = ZLOG_DISABLED;
 
 static struct zlog_cfg_file zt_file_cmdline = {
        .prio_min = ZLOG_DISABLED,
+       .ts_subsec = LOG_TIMESTAMP_PRECISION,
 };
 static struct zlog_cfg_file zt_file = {
        .prio_min = ZLOG_DISABLED,
+       .ts_subsec = LOG_TIMESTAMP_PRECISION,
 };
 static struct zlog_cfg_filterfile zt_filterfile = {
-       .parent = {
-               .prio_min = ZLOG_DISABLED,
-       },
+       .parent =
+               {
+                       .prio_min = ZLOG_DISABLED,
+                       .ts_subsec = LOG_TIMESTAMP_PRECISION,
+               },
 };
 
 static struct zlog_cfg_file zt_stdout_file = {
        .prio_min = ZLOG_DISABLED,
+       .ts_subsec = LOG_TIMESTAMP_PRECISION,
 };
 static struct zlog_cfg_5424 zt_stdout_journald = {
        .prio_min = ZLOG_DISABLED,
index 36c78052bcfaf9b9c3244d70ce35b24f2aa3d2ee..9427f7cf3dd10c5fe03d6f860c813e3ffcf87949 100644 (file)
@@ -6,29 +6,25 @@
  */
 
 #include <zebra.h>
+#include "debug.h"
 #include "libfrr.h"
 #include "mgmtd/mgmt.h"
 #include "mgmt_be_client.h"
 #include "mgmt_msg.h"
 #include "mgmt_pb.h"
 #include "network.h"
+#include "northbound.h"
 #include "stream.h"
 #include "sockopt.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_CLIENT_DBG(fmt, ...)                                         \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_CLIENT_ERR(fmt, ...)                                         \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_BE_CLIENT_DBG(fmt, ...)                                         \
-       do {                                                                   \
-               if (mgmt_debug_be_client)                                     \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);         \
-       } while (0)
-#define MGMTD_BE_CLIENT_ERR(fmt, ...)                                         \
+#include "lib/mgmt_be_client_clippy.c"
+
+#define MGMTD_BE_CLIENT_DBG(fmt, ...)                                          \
+       DEBUGD(&mgmt_dbg_be_client, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_BE_CLIENT_ERR(fmt, ...)                                          \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_DBG_BE_CLIENT_CHECK()                                            \
+       DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL)
 
 DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH,
                    "MGMTD backend transaction batch data");
@@ -118,8 +114,6 @@ struct mgmt_be_client_ctx {
        struct nb_config *candidate_config;
        struct nb_config *running_config;
 
-       unsigned long num_batch_find;
-       unsigned long avg_batch_find_tm;
        unsigned long num_edit_nb_cfg;
        unsigned long avg_edit_nb_cfg_tm;
        unsigned long num_prep_nb_cfg;
@@ -136,7 +130,7 @@ struct mgmt_be_client_ctx {
 #define FOREACH_BE_TXN_IN_LIST(client_ctx, txn)                                \
        frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn))
 
-static bool mgmt_debug_be_client;
+struct debug mgmt_dbg_be_client = {0, "Management backend client operations"};
 
 static struct mgmt_be_client_ctx mgmt_be_client_ctx = {
        .conn_fd = -1,
@@ -479,7 +473,6 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
        bool error;
        char err_buf[BUFSIZ];
        size_t num_processed;
-       bool debug_be = mgmt_debug_be_client;
        int err;
 
        assert(txn && txn->client_ctx);
@@ -499,8 +492,8 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
                         * interested in validating it.
                         */
                        error = false;
-                       if (debug_be)
-                               gettimeofday(&edit_nb_cfg_start, NULL);
+
+                       gettimeofday(&edit_nb_cfg_start, NULL);
                        nb_candidate_edit_config_changes(
                                client_ctx->candidate_config,
                                txn_req->req.set_cfg.cfg_changes,
@@ -516,16 +509,14 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
                                        err_buf);
                                return -1;
                        }
-                       if (debug_be) {
-                               gettimeofday(&edit_nb_cfg_end, NULL);
-                               edit_nb_cfg_tm = timeval_elapsed(
-                                       edit_nb_cfg_end, edit_nb_cfg_start);
-                               client_ctx->avg_edit_nb_cfg_tm =
-                                       ((client_ctx->avg_edit_nb_cfg_tm
-                                         * client_ctx->num_edit_nb_cfg)
-                                        + edit_nb_cfg_tm)
-                                       / (client_ctx->num_edit_nb_cfg + 1);
-                       }
+                       gettimeofday(&edit_nb_cfg_end, NULL);
+                       edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end,
+                                                        edit_nb_cfg_start);
+                       client_ctx->avg_edit_nb_cfg_tm =
+                               ((client_ctx->avg_edit_nb_cfg_tm *
+                                 client_ctx->num_edit_nb_cfg) +
+                                edit_nb_cfg_tm) /
+                               (client_ctx->num_edit_nb_cfg + 1);
                        client_ctx->num_edit_nb_cfg++;
                }
 
@@ -540,8 +531,8 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
         */
        nb_ctx.client = NB_CLIENT_CLI;
        nb_ctx.user = (void *)client_ctx->client_params.user_data;
-       if (debug_be)
-               gettimeofday(&prep_nb_cfg_start, NULL);
+
+       gettimeofday(&prep_nb_cfg_start, NULL);
        err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config,
                                          "MGMTD Backend Txn", &txn->nb_txn,
 #ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
@@ -569,16 +560,13 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
                        "Prepared configs for Txn %llx, %u Batches! successfully!",
                        (unsigned long long)txn->txn_id,
                        (uint32_t)num_processed);
-       if (debug_be) {
-               gettimeofday(&prep_nb_cfg_end, NULL);
-               prep_nb_cfg_tm =
-                       timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
-               client_ctx->avg_prep_nb_cfg_tm =
-                       ((client_ctx->avg_prep_nb_cfg_tm
-                         * client_ctx->num_prep_nb_cfg)
-                        + prep_nb_cfg_tm)
-                       / (client_ctx->num_prep_nb_cfg + 1);
-       }
+
+       gettimeofday(&prep_nb_cfg_end, NULL);
+       prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start);
+       client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm *
+                                          client_ctx->num_prep_nb_cfg) +
+                                         prep_nb_cfg_tm) /
+                                        (client_ctx->num_prep_nb_cfg + 1);
        client_ctx->num_prep_nb_cfg++;
 
        FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) {
@@ -593,11 +581,10 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
                }
        }
 
-       if (debug_be)
-               MGMTD_BE_CLIENT_DBG(
-                       "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
-                       client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
-                       client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
+       MGMTD_BE_CLIENT_DBG(
+               "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
+               client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
+               client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
 
        if (error)
                mgmt_be_txn_cfg_abort(txn);
@@ -735,7 +722,6 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
        char err_buf[BUFSIZ];
        size_t num_processed;
        static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ];
-       bool debug_be = mgmt_debug_be_client;
 
        assert(txn && txn->client_ctx);
        client_ctx = txn->client_ctx;
@@ -746,20 +732,16 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
        /*
         * Now apply all the batches we have applied in one go.
         */
-       if (debug_be)
-               gettimeofday(&apply_nb_cfg_start, NULL);
+       gettimeofday(&apply_nb_cfg_start, NULL);
        (void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id,
                                        err_buf, sizeof(err_buf) - 1);
-       if (debug_be) {
-               gettimeofday(&apply_nb_cfg_end, NULL);
-               apply_nb_cfg_tm =
-                       timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
-               client_ctx->avg_apply_nb_cfg_tm =
-                       ((client_ctx->avg_apply_nb_cfg_tm
-                         * client_ctx->num_apply_nb_cfg)
-                        + apply_nb_cfg_tm)
-                       / (client_ctx->num_apply_nb_cfg + 1);
-       }
+       gettimeofday(&apply_nb_cfg_end, NULL);
+
+       apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
+       client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm *
+                                           client_ctx->num_apply_nb_cfg) +
+                                          apply_nb_cfg_tm) /
+                                         (client_ctx->num_apply_nb_cfg + 1);
        client_ctx->num_apply_nb_cfg++;
        txn->nb_txn = NULL;
 
@@ -788,10 +770,8 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
        mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids,
                                    num_processed, true, NULL);
 
-       if (debug_be)
-               MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
-                                    apply_nb_cfg_tm,
-                                    client_ctx->avg_apply_nb_cfg_tm);
+       MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec",
+                           apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm);
 
        return 0;
 }
@@ -902,7 +882,7 @@ static void mgmt_be_client_proc_msgbufs(struct event *thread)
        struct mgmt_be_client_ctx *client_ctx = EVENT_ARG(thread);
 
        if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_be_client_process_msg,
-                             client_ctx, mgmt_debug_be_client))
+                             client_ctx, MGMTD_DBG_BE_CLIENT_CHECK()))
                mgmt_be_client_register_event(client_ctx, MGMTD_BE_PROC_MSG);
 }
 
@@ -912,7 +892,7 @@ static void mgmt_be_client_read(struct event *thread)
        enum mgmt_msg_rsched rv;
 
        rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
-                          mgmt_debug_be_client);
+                          MGMTD_DBG_BE_CLIENT_CHECK());
        if (rv == MSR_DISCONNECT) {
                mgmt_be_server_disconnect(client_ctx, true);
                return;
@@ -957,7 +937,7 @@ static int mgmt_be_client_send_msg(struct mgmt_be_client_ctx *client_ctx,
                &client_ctx->mstate, be_msg,
                mgmtd__be_message__get_packed_size(be_msg),
                (size_t(*)(void *, void *))mgmtd__be_message__pack,
-               mgmt_debug_be_client);
+               MGMTD_DBG_BE_CLIENT_CHECK());
        mgmt_be_client_sched_msg_write(client_ctx);
        return rv;
 }
@@ -968,7 +948,7 @@ static void mgmt_be_client_write(struct event *thread)
        enum mgmt_msg_wsched rv;
 
        rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
-                           mgmt_debug_be_client);
+                           MGMTD_DBG_BE_CLIENT_CHECK());
        if (rv == MSW_SCHED_STREAM)
                mgmt_be_client_register_event(client_ctx, MGMTD_BE_CONN_WRITE);
        else if (rv == MSW_DISCONNECT)
@@ -1016,7 +996,7 @@ static int mgmt_be_send_subscr_req(struct mgmt_be_client_ctx *client_ctx,
 
 static void mgmt_be_server_connect(struct mgmt_be_client_ctx *client_ctx)
 {
-       const char *dbgtag = mgmt_debug_be_client ? "BE-client" : NULL;
+       const char *dbgtag = MGMTD_DBG_BE_CLIENT_CHECK() ? "BE-client" : NULL;
 
        assert(client_ctx->conn_fd == -1);
        client_ctx->conn_fd = mgmt_msg_connect(
@@ -1096,7 +1076,47 @@ mgmt_be_client_schedule_conn_retry(struct mgmt_be_client_ctx *client_ctx,
                         &client_ctx->conn_retry_tmr);
 }
 
-extern struct nb_config *running_config;
+DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
+      "[no] debug mgmt client backend",
+      NO_STR DEBUG_STR MGMTD_STR
+      "client\n"
+      "backend\n")
+{
+       uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+       DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no);
+
+       return CMD_SUCCESS;
+}
+
+static void mgmt_debug_client_be_set_all(uint32_t flags, bool set)
+{
+       DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set);
+}
+
+static int mgmt_debug_be_client_config_write(struct vty *vty)
+{
+       if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF))
+               vty_out(vty, "debug mgmt client frontend\n");
+
+       return 1;
+}
+
+void mgmt_debug_be_client_show_debug(struct vty *vty)
+{
+       if (MGMTD_DBG_BE_CLIENT_CHECK())
+               vty_out(vty, "debug mgmt client backend\n");
+}
+
+static struct debug_callbacks mgmt_dbg_be_client_cbs = {
+       .debug_set_all = mgmt_debug_client_be_set_all};
+
+static struct cmd_node mgmt_dbg_node = {
+       .name = "mgmt backend client",
+       .node = DEBUG_NODE,
+       .prompt = "",
+       .config_write = mgmt_debug_be_client_config_write,
+};
 
 /*
  * Initialize library and try connecting with MGMTD.
@@ -1133,6 +1153,16 @@ uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
        return (uintptr_t)&mgmt_be_client_ctx;
 }
 
+
+void mgmt_be_client_lib_vty_init(void)
+{
+       debug_init(&mgmt_dbg_be_client_cbs);
+       install_node(&mgmt_dbg_node);
+       install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd);
+       install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd);
+}
+
+
 /*
  * Subscribe with MGMTD for one or more YANG subtree(s).
  */
index db427457a4e219d53ff188f30024ca31c68962f1..d4f2d86fdf78d52db3a78aa3389bf2eaf3d89d98 100644 (file)
@@ -191,6 +191,20 @@ mgmt_be_client_name2id(const char *name)
 extern uintptr_t mgmt_be_client_lib_init(struct mgmt_be_client_params *params,
                                         struct event_loop *master_thread);
 
+/*
+ * Initialize library vty (adds debug support).
+ *
+ * This call should be added to your component when enabling other vty code to
+ * enable mgmtd client debugs. When adding, one needs to also add a their
+ * component in `xref2vtysh.py` as well.
+ */
+extern void mgmt_be_client_lib_vty_init(void);
+
+/*
+ * Print enabled debugging commands.
+ */
+extern void mgmt_debug_be_client_show_debug(struct vty *vty);
+
 /*
  * Subscribe with MGMTD for one or more YANG subtree(s).
  *
index a2c4fd6572a75878ead0f242491b7d6ac38d0bea..b8266bfa82177021d6e40e90c0ecfd181ab8faa8 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <zebra.h>
+#include "debug.h"
 #include "memory.h"
 #include "libfrr.h"
 #include "mgmt_fe_client.h"
 #include "stream.h"
 #include "sockopt.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_CLIENT_DBG(fmt, ...)                                        \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...)                                        \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_FE_CLIENT_DBG(fmt, ...)                                        \
-       do {                                                                 \
-               if (mgmt_debug_fe_client)                                    \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);     \
-       } while (0)
-#define MGMTD_FE_CLIENT_ERR(fmt, ...)                                        \
+#include "lib/mgmt_fe_client_clippy.c"
+
+#define MGMTD_FE_CLIENT_DBG(fmt, ...)                                          \
+       DEBUGD(&mgmt_dbg_fe_client, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_FE_CLIENT_ERR(fmt, ...)                                          \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_DBG_FE_CLIENT_CHECK()                                            \
+       DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_ALL)
 
 struct mgmt_fe_client_ctx;
 
@@ -69,7 +64,7 @@ struct mgmt_fe_client_ctx {
 #define FOREACH_SESSION_IN_LIST(client_ctx, session)                           \
        frr_each_safe (mgmt_sessions, &(client_ctx)->client_sessions, (session))
 
-static bool mgmt_debug_fe_client;
+struct debug mgmt_dbg_fe_client = {0, "Management frontend client operations"};
 
 static struct mgmt_fe_client_ctx mgmt_fe_client_ctx = {
        .conn_fd = -1,
@@ -109,8 +104,8 @@ mgmt_fe_find_session_by_session_id(struct mgmt_fe_client_ctx *client_ctx,
        FOREACH_SESSION_IN_LIST (client_ctx, session) {
                if (session->session_id == session_id) {
                        MGMTD_FE_CLIENT_DBG(
-                               "Found session %p for session-id %llu.", session,
-                               (unsigned long long)session_id);
+                               "Found session %p for session-id %llu.",
+                               session, (unsigned long long)session_id);
                        return session;
                }
        }
@@ -169,7 +164,7 @@ static int mgmt_fe_client_send_msg(struct mgmt_fe_client_ctx *client_ctx,
                &client_ctx->mstate, fe_msg,
                mgmtd__fe_message__get_packed_size(fe_msg),
                (size_t(*)(void *, void *))mgmtd__fe_message__pack,
-               mgmt_debug_fe_client);
+               MGMTD_DBG_FE_CLIENT_CHECK());
        mgmt_fe_client_sched_msg_write(client_ctx);
        return rv;
 }
@@ -181,7 +176,7 @@ static void mgmt_fe_client_write(struct event *thread)
 
        client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
        rv = mgmt_msg_write(&client_ctx->mstate, client_ctx->conn_fd,
-                           mgmt_debug_fe_client);
+                           MGMTD_DBG_FE_CLIENT_CHECK());
        if (rv == MSW_SCHED_STREAM)
                mgmt_fe_client_register_event(client_ctx, MGMTD_FE_CONN_WRITE);
        else if (rv == MSW_DISCONNECT)
@@ -274,7 +269,8 @@ mgmt_fe_send_lockds_req(struct mgmt_fe_client_ctx *client_ctx,
 
        MGMTD_FE_CLIENT_DBG(
                "Sending %sLOCK_REQ message for Ds:%d session %llu to MGMTD Frontend server",
-               lock ? "" : "UN", ds_id, (unsigned long long)session->client_id);
+               lock ? "" : "UN", ds_id,
+               (unsigned long long)session->client_id);
 
        return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
 }
@@ -310,12 +306,12 @@ mgmt_fe_send_setcfg_req(struct mgmt_fe_client_ctx *client_ctx,
        return mgmt_fe_client_send_msg(client_ctx, &fe_msg);
 }
 
-static int
-mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx,
-                              struct mgmt_fe_client_session *session,
-                              uint64_t req_id, Mgmtd__DatastoreId src_ds_id,
-                              Mgmtd__DatastoreId dest_ds_id, bool validate_only,
-                              bool abort)
+static int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client_ctx *client_ctx,
+                                     struct mgmt_fe_client_session *session,
+                                     uint64_t req_id,
+                                     Mgmtd__DatastoreId src_ds_id,
+                                     Mgmtd__DatastoreId dest_ds_id,
+                                     bool validate_only, bool abort)
 {
        (void)req_id;
        Mgmtd__FeMessage fe_msg;
@@ -450,15 +446,17 @@ mgmt_fe_client_handle_msg(struct mgmt_fe_client_ctx *client_ctx,
                        if (session && fe_msg->session_reply->success) {
                                MGMTD_FE_CLIENT_DBG(
                                        "Session Create for client-id %llu successful.",
-                                       (unsigned long long)fe_msg
-                                               ->session_reply->client_conn_id);
+                                       (unsigned long long)
+                                               fe_msg->session_reply
+                                                       ->client_conn_id);
                                session->session_id =
                                        fe_msg->session_reply->session_id;
                        } else {
                                MGMTD_FE_CLIENT_ERR(
                                        "Session Create for client-id %llu failed.",
-                                       (unsigned long long)fe_msg
-                                               ->session_reply->client_conn_id);
+                                       (unsigned long long)
+                                               fe_msg->session_reply
+                                                       ->client_conn_id);
                        }
                } else if (!fe_msg->session_reply->create) {
                        MGMTD_FE_CLIENT_DBG(
@@ -676,7 +674,7 @@ static void mgmt_fe_client_proc_msgbufs(struct event *thread)
 
        client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
        if (mgmt_msg_procbufs(&client_ctx->mstate, mgmt_fe_client_process_msg,
-                             client_ctx, mgmt_debug_fe_client))
+                             client_ctx, MGMTD_DBG_FE_CLIENT_CHECK()))
                mgmt_fe_client_register_event(client_ctx, MGMTD_FE_PROC_MSG);
 }
 
@@ -688,7 +686,7 @@ static void mgmt_fe_client_read(struct event *thread)
        client_ctx = (struct mgmt_fe_client_ctx *)EVENT_ARG(thread);
 
        rv = mgmt_msg_read(&client_ctx->mstate, client_ctx->conn_fd,
-                          mgmt_debug_fe_client);
+                          MGMTD_DBG_FE_CLIENT_CHECK());
        if (rv == MSR_DISCONNECT) {
                mgmt_fe_server_disconnect(client_ctx, true);
                return;
@@ -700,7 +698,7 @@ static void mgmt_fe_client_read(struct event *thread)
 
 static void mgmt_fe_server_connect(struct mgmt_fe_client_ctx *client_ctx)
 {
-       const char *dbgtag = mgmt_debug_fe_client ? "FE-client" : NULL;
+       const char *dbgtag = MGMTD_DBG_FE_CLIENT_CHECK() ? "FE-client" : NULL;
 
        assert(client_ctx->conn_fd == -1);
        client_ctx->conn_fd = mgmt_msg_connect(
@@ -776,6 +774,48 @@ static void mgmt_fe_client_schedule_conn_retry(
                         &client_ctx->conn_retry_tmr);
 }
 
+DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd,
+      "[no] debug mgmt client frontend",
+      NO_STR DEBUG_STR MGMTD_STR
+      "client\n"
+      "frontend\n")
+{
+       uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+       DEBUG_MODE_SET(&mgmt_dbg_fe_client, mode, !no);
+
+       return CMD_SUCCESS;
+}
+
+static void mgmt_debug_client_fe_set_all(uint32_t flags, bool set)
+{
+       DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, flags, set);
+}
+
+static int mgmt_debug_fe_client_config_write(struct vty *vty)
+{
+       if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF))
+               vty_out(vty, "debug mgmt client frontend\n");
+
+       return CMD_SUCCESS;
+}
+
+void mgmt_debug_fe_client_show_debug(struct vty *vty)
+{
+       if (MGMTD_DBG_FE_CLIENT_CHECK())
+               vty_out(vty, "debug mgmt client frontend\n");
+}
+
+static struct debug_callbacks mgmt_dbg_fe_client_cbs = {
+       .debug_set_all = mgmt_debug_client_fe_set_all};
+
+static struct cmd_node mgmt_dbg_node = {
+       .name = "mgmt client frontend",
+       .node = DEBUG_NODE,
+       .prompt = "",
+       .config_write = mgmt_debug_fe_client_config_write,
+};
+
 /*
  * Initialize library and try connecting with MGMTD.
  */
@@ -806,6 +846,14 @@ uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
        return (uintptr_t)&mgmt_fe_client_ctx;
 }
 
+void mgmt_fe_client_lib_vty_init(void)
+{
+       debug_init(&mgmt_dbg_fe_client_cbs);
+       install_node(&mgmt_dbg_node);
+       install_element(ENABLE_NODE, &debug_mgmt_client_fe_cmd);
+       install_element(CONFIG_NODE, &debug_mgmt_client_fe_cmd);
+}
+
 /*
  * Create a new Session for a Frontend Client connection.
  */
index aa3371f03c4643edb46adf7c5cdbdd79a2d24007..94867787d999826fb324ea8e30c5caed00207022 100644 (file)
@@ -133,6 +133,20 @@ struct mgmt_fe_client_params {
 extern uintptr_t mgmt_fe_client_lib_init(struct mgmt_fe_client_params *params,
                                         struct event_loop *master_thread);
 
+/*
+ * Initialize library vty (adds debug support).
+ *
+ * This call should be added to your component when enabling other vty code to
+ * enable mgmtd client debugs. When adding, one needs to also add a their
+ * component in `xref2vtysh.py` as well.
+ */
+extern void mgmt_fe_client_lib_vty_init(void);
+
+/*
+ * Print enabled debugging commands.
+ */
+extern void mgmt_debug_fe_client_show_debug(struct vty *vty);
+
 /*
  * Create a new Session for a Frontend Client connection.
  *
index 307cf0fb496abf840e571d2087351424e47c2d11..775f6ff92f9494352f3dbe3c97f374887b2df392 100644 (file)
@@ -167,7 +167,7 @@ struct nb_node *nb_node_find(const char *path)
         * Use libyang to find the schema node associated to the path and get
         * the northbound node from there (snode private pointer).
         */
-       snode = lys_find_path(ly_native_ctx, NULL, path, 0);
+       snode = yang_find_snode(ly_native_ctx, path, 0);
        if (!snode)
                return NULL;
 
@@ -2129,8 +2129,8 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
         * all YANG lists (if any).
         */
 
-       LY_ERR err = lyd_new_path(NULL, ly_native_ctx, xpath, NULL,
-                                 LYD_NEW_PATH_UPDATE, &dnode);
+       LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
+                                  LYD_NEW_PATH_UPDATE, NULL, &dnode);
        if (err || !dnode) {
                const char *errmsg =
                        err ? ly_errmsg(ly_native_ctx) : "node not found";
index 5cf5f93b437c51ed3161423c951a5f3a684056b8..c5582fc21c2338edb3036e28a00cf75ed32d14cf 100644 (file)
@@ -1434,6 +1434,7 @@ DEFPY (show_yang_operational_data,
        struct lyd_node *dnode;
        char *strp;
        uint32_t print_options = LYD_PRINT_WITHSIBLINGS;
+       int ret;
 
        if (xml)
                format = LYD_XML;
@@ -1454,10 +1455,15 @@ DEFPY (show_yang_operational_data,
 
        /* Obtain data. */
        dnode = yang_dnode_new(ly_ctx, false);
-       if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
-                                dnode)
-           != NB_OK) {
-               vty_out(vty, "%% Failed to fetch operational data.\n");
+       ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
+                                  dnode);
+       if (ret != NB_OK) {
+               if (format == LYD_JSON)
+                       vty_out(vty, "{}\n");
+               else {
+                       /* embed ly_last_errmsg() when we get newer libyang */
+                       vty_out(vty, "<!-- Not found -->\n");
+               }
                yang_dnode_free(dnode);
                return CMD_WARNING;
        }
index 86105d2e77b7ad9850cbbe8db333eee892dd356f..7fd4af83562f99ba743aeeb05de6db60e9b44033 100644 (file)
@@ -357,7 +357,7 @@ static int frr_sr_state_data_iter_cb(const struct lysc_node *snode,
        ly_errno = 0;
        ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value,
                                0, &dnode);
-       if (!dnode && ly_errno) {
+       if (ly_errno) {
                flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
                          __func__);
                yang_data_free(data);
index a6aae08a6a563567c7b83cd287d59ada2e8c8a74..b8cad910f423e825b65add7138c78839380a2f44 100644 (file)
@@ -1399,7 +1399,7 @@ bool ipv4_unicast_valid(const struct in_addr *addr)
        if (IPV4_CLASS_D(ip))
                return false;
 
-       if (IPV4_CLASS_E(ip)) {
+       if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_E(ip)) {
                if (cmd_allow_reserved_ranges_get())
                        return true;
                else
index 9c5728370615dd947c9b45a914efc46aaaba99f6..90b792b33c61905ef7c1ebf4a8c9b70421a4caff 100644 (file)
@@ -499,11 +499,8 @@ extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
 /* NOTE: This routine expects the address argument in network byte order. */
 static inline bool ipv4_martian(const struct in_addr *addr)
 {
-       in_addr_t ip = ntohl(addr->s_addr);
-
-       if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) {
+       if (!ipv4_unicast_valid(addr))
                return true;
-       }
        return false;
 }
 
@@ -601,6 +598,14 @@ static inline bool ipv6_mcast_ssm(const struct in6_addr *addr)
        return (bits & 0xfff0ffff) == 0xff300000;
 }
 
+static inline bool ipv6_mcast_reserved(const struct in6_addr *addr)
+{
+       uint32_t bits = ntohl(addr->s6_addr32[0]);
+
+       /* ffx2::/16 */
+       return (bits & 0xff0fffff) == 0xff020000;
+}
+
 static inline uint8_t ipv4_mcast_scope(const struct in_addr *addr)
 {
        uint32_t bits = ntohl(addr->s_addr);
index 16da81fa746255f5834b38712ae4ab28a3083d0e..39455841e33b1a33537172af842f4021f09496da 100644 (file)
@@ -394,6 +394,40 @@ void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index,
 {
        rmap_match_set_hook.no_set_metric = func;
 }
+/* set min-metric */
+void route_map_set_min_metric_hook(int (*func)(struct route_map_index *index,
+                                              const char *command,
+                                              const char *arg, char *errmsg,
+                                              size_t errmsg_len))
+{
+       rmap_match_set_hook.set_min_metric = func;
+}
+
+/* no set min-metric */
+void route_map_no_set_min_metric_hook(int (*func)(struct route_map_index *index,
+                                                 const char *command,
+                                                 const char *arg, char *errmsg,
+                                                 size_t errmsg_len))
+{
+       rmap_match_set_hook.no_set_min_metric = func;
+}
+/* set max-metric */
+void route_map_set_max_metric_hook(int (*func)(struct route_map_index *index,
+                                              const char *command,
+                                              const char *arg, char *errmsg,
+                                              size_t errmsg_len))
+{
+       rmap_match_set_hook.set_max_metric = func;
+}
+
+/* no set max-metric */
+void route_map_no_set_max_metric_hook(int (*func)(struct route_map_index *index,
+                                                 const char *command,
+                                                 const char *arg, char *errmsg,
+                                                 size_t errmsg_len))
+{
+       rmap_match_set_hook.no_set_max_metric = func;
+}
 
 /* set tag */
 void route_map_set_tag_hook(int (*func)(struct route_map_index *index,
@@ -669,7 +703,7 @@ static struct route_map *route_map_add(const char *name)
        if (!map->ipv6_prefix_table)
                map->ipv6_prefix_table = route_table_init();
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Add route-map %s", name);
        return map;
 }
@@ -689,7 +723,7 @@ static void route_map_free_map(struct route_map *map)
        while ((index = map->head) != NULL)
                route_map_index_delete(index, 0);
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Deleting route-map %s", map->name);
 
        list = &route_map_master;
@@ -706,6 +740,9 @@ static void route_map_free_map(struct route_map *map)
        else
                list->head = map->next;
 
+       route_table_finish(map->ipv4_prefix_table);
+       route_table_finish(map->ipv6_prefix_table);
+
        hash_release(route_map_master_hash, map);
        XFREE(MTYPE_ROUTE_MAP_NAME, map->name);
        XFREE(MTYPE_ROUTE_MAP, map);
@@ -1120,7 +1157,7 @@ void route_map_index_delete(struct route_map_index *index, int notify)
 
        QOBJ_UNREG(index);
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Deleting route-map %s sequence %d",
                           index->map->name, index->pref);
 
@@ -1231,7 +1268,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref)
                route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED);
        }
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Route-map %s add sequence %d, type: %s",
                           map->name, pref, route_map_type_str(type));
 
@@ -1811,10 +1848,8 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
         * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if
         * the AF doesn't line up with the LPM trees, skip the optimization.
         */
-       if (map->optimization_disabled ||
-           (prefix->family == AF_INET && !map->ipv4_prefix_table) ||
-           (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+       if (map->optimization_disabled) {
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d",
                                map->name, prefix, prefix->family);
@@ -1826,9 +1861,6 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
        else
                table = map->ipv6_prefix_table;
 
-       if (!table)
-               return NULL;
-
        do {
                candidate_rmap_list =
                        route_map_get_index_list(&rn, prefix, table);
@@ -1914,19 +1946,10 @@ static void route_map_pfx_table_add_default(afi_t afi,
        p.family = afi2family(afi);
        p.prefixlen = 0;
 
-       if (p.family == AF_INET) {
-               table = index->map->ipv4_prefix_table;
-               if (!table)
-                       index->map->ipv4_prefix_table = route_table_init();
-
+       if (p.family == AF_INET)
                table = index->map->ipv4_prefix_table;
-       } else {
-               table = index->map->ipv6_prefix_table;
-               if (!table)
-                       index->map->ipv6_prefix_table = route_table_init();
-
+       else
                table = index->map->ipv6_prefix_table;
-       }
 
        /* Add default route to table */
        rn = route_node_get(table, &p);
@@ -2317,8 +2340,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event,
                                     struct route_map_index *index, afi_t afi,
                                     const char *plist_name)
 {
-       struct route_map *rmap = NULL;
-
        if (!index)
                return;
 
@@ -2332,19 +2353,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event,
                route_map_pfx_table_del_default(AFI_IP, index);
                route_map_pfx_table_del_default(AFI_IP6, index);
 
-               if ((index->map->head == NULL) && (index->map->tail == NULL)) {
-                       rmap = index->map;
-
-                       if (rmap->ipv4_prefix_table) {
-                               route_table_finish(rmap->ipv4_prefix_table);
-                               rmap->ipv4_prefix_table = NULL;
-                       }
-
-                       if (rmap->ipv6_prefix_table) {
-                               route_table_finish(rmap->ipv6_prefix_table);
-                               rmap->ipv6_prefix_table = NULL;
-                       }
-               }
                return;
        }
 
@@ -2569,12 +2577,14 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
         */
        if (prefix->family == AF_EVPN) {
                if (evpn_prefix2prefix(prefix, &conv) != 0) {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup",
                                        prefix);
                } else {
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+                       if (unlikely(CHECK_FLAG(rmap_debug,
+                                               DEBUG_ROUTEMAP_DETAIL)))
                                zlog_debug(
                                        "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup",
                                        prefix, &conv);
@@ -2586,13 +2596,13 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
        index = route_map_get_index(map, prefix, match_object, &match_ret);
        if (index) {
                index->applied++;
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug(
                                "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s",
                                map->name, index->pref, prefix,
                                route_map_cmd_result_str(match_ret));
        } else {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug(
                                "No best match sequence for pfx: %pFX in route-map: %s, result: %s",
                                prefix, map->name,
@@ -2615,7 +2625,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
                        /* Apply this index. */
                        match_ret = route_map_apply_match(&index->match_list,
                                                          prefix, match_object);
-                       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) {
+                       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) {
                                zlog_debug(
                                        "Route-map: %s, sequence: %d, prefix: %pFX, result: %s",
                                        map->name, index->pref, prefix,
@@ -2728,7 +2738,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map,
        }
 
 route_map_apply_end:
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Route-map: %s, prefix: %pFX, result: %s",
                           (map ? map->name : "null"), prefix,
                           route_map_result_str(ret));
@@ -2783,7 +2793,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg)
        tmp_dep_data.rname = arg;
        dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data);
        if (dep_data) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug("Clearing reference for %s to %s count: %d",
                                   dep->dep_name, tmp_dep_data.rname,
                                   dep_data->refcnt);
@@ -2803,7 +2813,7 @@ static void route_map_clear_all_references(char *rmap_name)
 {
        int i;
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Clearing references for %s", rmap_name);
 
        for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) {
@@ -2879,7 +2889,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
        case RMAP_EVENT_LLIST_ADDED:
        case RMAP_EVENT_CALL_ADDED:
        case RMAP_EVENT_FILTER_ADDED:
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug("Adding dependency for filter %s in route-map %s",
                                   dep_name, rmap_name);
                dep = (struct route_map_dep *)hash_get(
@@ -2908,7 +2918,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
        case RMAP_EVENT_LLIST_DELETED:
        case RMAP_EVENT_CALL_DELETED:
        case RMAP_EVENT_FILTER_DELETED:
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug("Deleting dependency for filter %s in route-map %s",
                                   dep_name, rmap_name);
                dep = (struct route_map_dep *)hash_get(dephash, dname, NULL);
@@ -3034,7 +3044,7 @@ static void route_map_process_dependency(struct hash_bucket *bucket, void *data)
        dep_data = bucket->data;
        rmap_name = dep_data->rname;
 
-       if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+       if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                zlog_debug("Notifying %s of dependency", rmap_name);
        if (route_map_master.event_hook)
                (*route_map_master.event_hook)(rmap_name);
@@ -3082,7 +3092,7 @@ void route_map_notify_dependencies(const char *affected_name,
                if (!dep->this_hash)
                        dep->this_hash = upd8_hash;
 
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)))
                        zlog_debug("Filter %s updated", dep->dep_name);
                hash_iterate(dep->dep_rmap_hash, route_map_process_dependency,
                             (void *)event);
index 2197a49e76c9ebdcbd1949a86a9a0f20e814e307..9b2e18b4a780a65cefa9fcd279ddb85f45c8a5d8 100644 (file)
@@ -312,6 +312,8 @@ DECLARE_QOBJ_TYPE(route_map);
        (strmatch(A, "frr-route-map:ipv6-next-hop"))
 #define IS_SET_METRIC(A)                                                       \
        (strmatch(A, "frr-route-map:set-metric"))
+#define IS_SET_MIN_METRIC(A) (strmatch(A, "frr-route-map:set-min-metric"))
+#define IS_SET_MAX_METRIC(A) (strmatch(A, "frr-route-map:set-max-metric"))
 #define IS_SET_TAG(A) (strmatch(A, "frr-route-map:set-tag"))
 #define IS_SET_SR_TE_COLOR(A)                                                  \
        (strmatch(A, "frr-route-map:set-sr-te-color"))
@@ -352,6 +354,8 @@ DECLARE_QOBJ_TYPE(route_map);
        (strmatch(A, "frr-bgp-route-map:set-extcommunity-none"))
 #define IS_SET_EXTCOMMUNITY_RT(A)                                              \
        (strmatch(A, "frr-bgp-route-map:set-extcommunity-rt"))
+#define IS_SET_EXTCOMMUNITY_NT(A)                                              \
+       (strmatch(A, "frr-bgp-route-map:set-extcommunity-nt"))
 #define IS_SET_EXTCOMMUNITY_SOO(A)                                             \
        (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo"))
 #define IS_SET_EXTCOMMUNITY_LB(A)                                              \
@@ -684,6 +688,22 @@ extern void route_map_no_set_metric_hook(
        int (*func)(struct route_map_index *index,
                    const char *command, const char *arg,
                    char *errmsg, size_t errmsg_len));
+/* set metric */
+extern void route_map_set_max_metric_hook(
+       int (*func)(struct route_map_index *index, const char *command,
+                   const char *arg, char *errmsg, size_t errmsg_len));
+/* no set metric */
+extern void route_map_no_set_max_metric_hook(
+       int (*func)(struct route_map_index *index, const char *command,
+                   const char *arg, char *errmsg, size_t errmsg_len));
+/* set metric */
+extern void route_map_set_min_metric_hook(
+       int (*func)(struct route_map_index *index, const char *command,
+                   const char *arg, char *errmsg, size_t errmsg_len));
+/* no set metric */
+extern void route_map_no_set_min_metric_hook(
+       int (*func)(struct route_map_index *index, const char *command,
+                   const char *arg, char *errmsg, size_t errmsg_len));
 /* set tag */
 extern void route_map_set_tag_hook(int (*func)(struct route_map_index *index,
                                               const char *command,
@@ -920,6 +940,25 @@ struct route_map_match_set_hooks {
        int (*no_set_metric)(struct route_map_index *index,
                             const char *command, const char *arg,
                             char *errmsg, size_t errmsg_len);
+       /* set min-metric */
+       int (*set_min_metric)(struct route_map_index *index,
+                             const char *command, const char *arg,
+                             char *errmsg, size_t errmsg_len);
+
+       /* no set min-metric */
+       int (*no_set_min_metric)(struct route_map_index *index,
+                                const char *command, const char *arg,
+                                char *errmsg, size_t errmsg_len);
+
+       /* set max-metric */
+       int (*set_max_metric)(struct route_map_index *index,
+                             const char *command, const char *arg,
+                             char *errmsg, size_t errmsg_len);
+
+       /* no set max-metric */
+       int (*no_set_max_metric)(struct route_map_index *index,
+                                const char *command, const char *arg,
+                                char *errmsg, size_t errmsg_len);
 
        /* set tag */
        int (*set_tag)(struct route_map_index *index,
index 4345b74bc0c1227d8b777cbbabee71fde76dbe1a..419086c4c6bb92d03fc3f004cb548ca406bf50ec 100644 (file)
@@ -890,6 +890,76 @@ DEFPY_YANG(
        return nb_cli_apply_changes(vty, NULL);
 }
 
+DEFPY_YANG(set_min_metric, set_min_metric_cmd,
+          "set min-metric <(0-4294967295)$metric>",
+          SET_STR
+          "Minimum metric value for destination routing protocol\n"
+          "Minimum metric value\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-route-map:set-min-metric']";
+       char xpath_value[XPATH_MAXLEN];
+       char value[64];
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-set-action/min-metric", xpath);
+       snprintf(value, sizeof(value), "%s", metric_str);
+
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_set_min_metric, no_set_min_metric_cmd,
+          "no set min-metric [(0-4294967295)]",
+          NO_STR SET_STR
+          "Minimum metric value for destination routing protocol\n"
+          "Minumum metric value\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-route-map:set-min-metric']";
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(set_max_metric, set_max_metric_cmd,
+          "set max-metric <(0-4294967295)$metric>",
+          SET_STR
+          "Maximum metric value for destination routing protocol\n"
+          "Miximum metric value\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-route-map:set-max-metric']";
+       char xpath_value[XPATH_MAXLEN];
+       char value[64];
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-set-action/max-metric", xpath);
+       snprintf(value, sizeof(value), "%s", metric_str);
+
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_set_max_metric, no_set_max_metric_cmd,
+          "no set max-metric [(0-4294967295)]",
+          NO_STR SET_STR
+          "Maximum Metric value for destination routing protocol\n"
+          "Maximum metric value\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-route-map:set-max-metric']";
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
 DEFPY_YANG(
        set_tag, set_tag_cmd,
        "set tag (1-4294967295)$tag",
@@ -1011,6 +1081,14 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
                                yang_dnode_get_string(
                                        dnode, "./rmap-set-action/value"));
                }
+       } else if (IS_SET_MIN_METRIC(action)) {
+               vty_out(vty, " set min-metric %s\n",
+                       yang_dnode_get_string(dnode,
+                                             "./rmap-set-action/min-metric"));
+       } else if (IS_SET_MAX_METRIC(action)) {
+               vty_out(vty, " set max-metric %s\n",
+                       yang_dnode_get_string(dnode,
+                                             "./rmap-set-action/max-metric"));
        } else if (IS_SET_TAG(action)) {
                vty_out(vty, " set tag %s\n",
                        yang_dnode_get_string(dnode, "./rmap-set-action/tag"));
@@ -1140,6 +1218,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode,
                        yang_dnode_get_string(
                                dnode,
                                "./rmap-set-action/frr-bgp-route-map:extcommunity-rt"));
+       } else if (IS_SET_EXTCOMMUNITY_NT(action)) {
+               vty_out(vty, " set extcommunity nt %s\n",
+                       yang_dnode_get_string(
+                               dnode,
+                               "./rmap-set-action/frr-bgp-route-map:extcommunity-nt"));
        } else if (IS_SET_EXTCOMMUNITY_SOO(action)) {
                vty_out(vty, " set extcommunity soo %s\n",
                        yang_dnode_get_string(
@@ -1551,6 +1634,12 @@ void route_map_cli_init(void)
        install_element(RMAP_NODE, &set_metric_cmd);
        install_element(RMAP_NODE, &no_set_metric_cmd);
 
+       install_element(RMAP_NODE, &set_min_metric_cmd);
+       install_element(RMAP_NODE, &no_set_min_metric_cmd);
+
+       install_element(RMAP_NODE, &set_max_metric_cmd);
+       install_element(RMAP_NODE, &no_set_max_metric_cmd);
+
        install_element(RMAP_NODE, &set_tag_cmd);
        install_element(RMAP_NODE, &no_set_tag_cmd);
 
index ab127dd0fcbf4e718136d0aa0e9c7bd1868f56aa..465985099429c3e45eff7b9d5ed9db5abbc3a7d1 100644 (file)
@@ -1027,6 +1027,110 @@ lib_route_map_entry_set_action_value_destroy(struct nb_cb_destroy_args *args)
        return lib_route_map_entry_set_destroy(args);
 }
 
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/set-action/min-metric
+ */
+static int set_action_min_metric_modify(enum nb_event event,
+                                       const struct lyd_node *dnode,
+                                       union nb_resource *resource,
+                                       const char *value, char *errmsg,
+                                       size_t errmsg_len)
+{
+       struct routemap_hook_context *rhc;
+       int rv;
+
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       /* Check for hook function. */
+       if (rmap_match_set_hook.set_min_metric == NULL)
+               return NB_OK;
+
+       /* Add configuration. */
+       rhc = nb_running_get_entry(dnode, NULL, true);
+
+       /* Set destroy information. */
+       rhc->rhc_shook = rmap_match_set_hook.no_set_min_metric;
+       rhc->rhc_rule = "min-metric";
+
+       rv = rmap_match_set_hook.set_min_metric(rhc->rhc_rmi, "min-metric",
+                                               value, errmsg, errmsg_len);
+       if (rv != CMD_SUCCESS) {
+               rhc->rhc_shook = NULL;
+               return NB_ERR_INCONSISTENCY;
+       }
+
+       return NB_OK;
+}
+
+static int
+lib_route_map_entry_set_action_min_metric_modify(struct nb_cb_modify_args *args)
+{
+       const char *min_metric = yang_dnode_get_string(args->dnode, NULL);
+
+       return set_action_min_metric_modify(args->event, args->dnode,
+                                           args->resource, min_metric,
+                                           args->errmsg, args->errmsg_len);
+}
+
+static int lib_route_map_entry_set_action_min_metric_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return lib_route_map_entry_set_destroy(args);
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/set-action/max-metric
+ */
+static int set_action_max_metric_modify(enum nb_event event,
+                                       const struct lyd_node *dnode,
+                                       union nb_resource *resource,
+                                       const char *value, char *errmsg,
+                                       size_t errmsg_len)
+{
+       struct routemap_hook_context *rhc;
+       int rv;
+
+       if (event != NB_EV_APPLY)
+               return NB_OK;
+
+       /* Check for hook function. */
+       if (rmap_match_set_hook.set_max_metric == NULL)
+               return NB_OK;
+
+       /* Add configuration. */
+       rhc = nb_running_get_entry(dnode, NULL, true);
+
+       /* Set destroy information. */
+       rhc->rhc_shook = rmap_match_set_hook.no_set_max_metric;
+       rhc->rhc_rule = "max-metric";
+
+       rv = rmap_match_set_hook.set_max_metric(rhc->rhc_rmi, "max-metric",
+                                               value, errmsg, errmsg_len);
+       if (rv != CMD_SUCCESS) {
+               rhc->rhc_shook = NULL;
+               return NB_ERR_INCONSISTENCY;
+       }
+
+       return NB_OK;
+}
+
+static int
+lib_route_map_entry_set_action_max_metric_modify(struct nb_cb_modify_args *args)
+{
+       const char *max_metric = yang_dnode_get_string(args->dnode, NULL);
+
+       return set_action_max_metric_modify(args->event, args->dnode,
+                                           args->resource, max_metric,
+                                           args->errmsg, args->errmsg_len);
+}
+
+static int lib_route_map_entry_set_action_max_metric_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       return lib_route_map_entry_set_destroy(args);
+}
+
 /*
  * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric
  */
@@ -1368,6 +1472,20 @@ const struct frr_yang_module_info frr_route_map_info = {
                                .destroy = lib_route_map_entry_set_action_value_destroy,
                        }
                },
+               {
+                       .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/min-metric",
+                       .cbs = {
+                               .modify = lib_route_map_entry_set_action_min_metric_modify,
+                               .destroy = lib_route_map_entry_set_action_min_metric_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/max-metric",
+                       .cbs = {
+                               .modify = lib_route_map_entry_set_action_max_metric_modify,
+                               .destroy = lib_route_map_entry_set_action_max_metric_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-metric",
                        .cbs = {
diff --git a/lib/segment_routing.c b/lib/segment_routing.c
new file mode 100644 (file)
index 0000000..f45a64d
--- /dev/null
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * segment_routing.c: Segment-Routing Library
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#include "segment_routing.h"
+
+const char *sr_algorithm_string(uint8_t algo)
+{
+       switch (algo) {
+       case SR_ALGORITHM_SPF:
+               return "SPF";
+       case SR_ALGORITHM_STRICT_SPF:
+               return "Strict SPF";
+       case SR_ALGORITHM_UNSET:
+               return "Unset";
+       default:
+               return algo >= SR_ALGORITHM_FLEX_MIN ? "Flex-Algo" : "Unknown";
+       }
+}
diff --git a/lib/segment_routing.h b/lib/segment_routing.h
new file mode 100644 (file)
index 0000000..5e71466
--- /dev/null
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*********************************************************************
+ * Copyright 2022 Hiroki Shirokura, LINE Corporation
+ * Copyright 2022 Masakazu Asama
+ * Copyright 2022 6WIND S.A.
+ *
+ * segment_routing.h: Segment-Routing Library
+ *
+ * Authors
+ * -------
+ * Hiroki Shirokura
+ * Masakazu Asama
+ * Louis Scalbert
+ */
+
+#ifndef _FRR_SR_H
+#define _FRR_SR_H
+
+#include <zebra.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * IGP Algorithm Types
+ * https://www.iana.org/assignments/igp-parameters/igp-parameters.xhtml
+ */
+#define SR_ALGORITHM_SPF 0     /* RFC8665 */
+#define SR_ALGORITHM_STRICT_SPF 1 /* RFC8665 */
+#define SR_ALGORITHM_UNSET 127    /* FRRouting defined */
+#define SR_ALGORITHM_FLEX_MIN 128 /* RFC9350 Flex-Algorithm */
+#define SR_ALGORITHM_FLEX_MAX 255 /* RFC9350 Flex-Algorithm */
+#define SR_ALGORITHM_COUNT 256
+
+const char *sr_algorithm_string(uint8_t algo);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_SR_H */
index 469fac2446f53396e0c0baa3094fe65796a4c727..c046c3c43c8a6b0932146f97eb47fc8998c0728a 100644 (file)
@@ -33,13 +33,12 @@ lib_libfrr_la_SOURCES = \
        lib/filter.c \
        lib/filter_cli.c \
        lib/filter_nb.c \
+       lib/flex_algo.c \
        lib/frrcu.c \
        lib/frrlua.c \
        lib/frrscript.c \
        lib/frr_pthread.c \
        lib/frrstr.c \
-       lib/getopt.c \
-       lib/getopt1.c \
        lib/grammar_sandbox.c \
        lib/graph.c \
        lib/hash.c \
@@ -49,6 +48,7 @@ lib_libfrr_la_SOURCES = \
        lib/if_rmap.c \
        lib/imsg-buffer.c \
        lib/imsg.c \
+       lib/iso.c \
        lib/jhash.c \
        lib/json.c \
        lib/keychain.c \
@@ -100,6 +100,7 @@ lib_libfrr_la_SOURCES = \
        lib/sockopt.c \
        lib/sockunion.c \
        lib/spf_backoff.c \
+       lib/segment_routing.c \
        lib/srcdest_table.c \
        lib/stream.c \
        lib/strformat.c \
@@ -137,6 +138,7 @@ lib_libfrr_la_SOURCES = \
 nodist_lib_libfrr_la_SOURCES = \
        yang/frr-affinity-map.yang.c \
        yang/frr-filter.yang.c \
+       yang/frr-if-rmap.yang.c \
        yang/frr-interface.yang.c \
        yang/frr-route-map.yang.c \
        yang/frr-route-types.yang.c \
@@ -175,7 +177,10 @@ clippy_scan += \
        lib/affinitymap_cli.c \
        lib/if.c \
        lib/filter_cli.c \
+       lib/if_rmap.c \
        lib/log_vty.c \
+       lib/mgmt_be_client.c \
+       lib/mgmt_fe_client.c \
        lib/nexthop_group.c \
        lib/northbound_cli.c \
        lib/plist.c \
@@ -210,6 +215,7 @@ pkginclude_HEADERS += \
        lib/distribute.h \
        lib/ferr.h \
        lib/filter.h \
+       lib/flex_algo.h \
        lib/freebsd-queue.h \
        lib/frrlua.h \
        lib/frrscript.h \
@@ -217,7 +223,6 @@ pkginclude_HEADERS += \
        lib/frratomic.h \
        lib/frrcu.h \
        lib/frrstr.h \
-       lib/getopt.h \
        lib/graph.h \
        lib/hash.h \
        lib/hook.h \
@@ -227,6 +232,7 @@ pkginclude_HEADERS += \
        lib/if_rmap.h \
        lib/imsg.h \
        lib/ipaddr.h \
+       lib/iso.h \
        lib/jhash.h \
        lib/json.h \
        lib/keychain.h \
@@ -282,6 +288,7 @@ pkginclude_HEADERS += \
        lib/sockopt.h \
        lib/sockunion.h \
        lib/spf_backoff.h \
+       lib/segment_routing.h \
        lib/srcdest_table.h \
        lib/srte.h \
        lib/stream.h \
index 30f1ab74c6dd755ac08c15b9f195eca7d3879f35..9b36d5ebfa8b04ce224f1b59cbae78f456f2651e 100644 (file)
@@ -7,6 +7,7 @@
 #include <zebra.h>
 #include <stdio.h>
 
+#include "lib/json.h"
 #include "printfrr.h"
 #include "memory.h"
 #include "termtable.h"
@@ -485,3 +486,45 @@ char *ttable_dump(struct ttable *tt, const char *newline)
 
        return buf;
 }
+
+/* Crude conversion from ttable to json array.
+ * Assume that the first row has column headings.
+ *
+ * Formats are:
+ *   d int32
+ *   f double
+ *   l int64
+ *   s string (default)
+ */
+json_object *ttable_json(struct ttable *tt, const char *const formats)
+{
+       struct ttable_cell *row; /* iteration pointers */
+       json_object *json = NULL;
+
+       json = json_object_new_array();
+
+       for (int i = 1; i < tt->nrows; i++) {
+               json_object *jobj;
+               json_object *val;
+
+               row = tt->table[i];
+               jobj = json_object_new_object();
+               json_object_array_add(json, jobj);
+               for (int j = 0; j < tt->ncols; j++) {
+                       switch (formats[j]) {
+                       case 'd':
+                       case 'l':
+                               val = json_object_new_int64(atol(row[j].text));
+                               break;
+                       case 'f':
+                               val = json_object_new_double(atof(row[j].text));
+                               break;
+                       default:
+                               val = json_object_new_string(row[j].text);
+                       }
+                       json_object_object_add(jobj, tt->table[0][j].text, val);
+               }
+       }
+
+       return json;
+}
index 3aa8caee89bd726888b09a735957c95a30818832..7258682bd80a680f047095ace134434443b62d56 100644 (file)
@@ -8,6 +8,7 @@
 #define _TERMTABLE_H_
 
 #include <zebra.h>
+#include "lib/json.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -277,6 +278,17 @@ void ttable_rowseps(struct ttable *tt, unsigned int row,
  */
 char *ttable_dump(struct ttable *tt, const char *newline);
 
+/**
+ * Convert a table to a JSON array of objects.
+ *
+ * Caller must free the returned json_object structure.
+ *
+ * @param tt the table to convert
+ * @param formats an array of characters indicating what JSON type should be
+ * used.
+ */
+json_object *ttable_json(struct ttable *tt, const char *const formats);
+
 #ifdef __cplusplus
 }
 #endif
index 0da35d0f8c185ef5c94f0ed3826505996a817b93..c0774479852da261af67d70572da58b55f2c77fa 100644 (file)
@@ -85,6 +85,15 @@ void typesafe_hash_grow(struct thash_head *head)
        uint32_t newsize = head->count, i, j;
        uint8_t newshift, delta;
 
+       /* note hash_grow is called after head->count++, so newsize is
+        * guaranteed to be >= 1.  So the minimum argument to builtin_ctz
+        * below is 2, which returns 1, and that makes newshift >= 2.
+        *
+        * Calling hash_grow with a zero head->count would result in a
+        * malformed hash table that has tabshift == 1.
+        */
+       assert(head->count > 0);
+
        hash_consistency_check(head);
 
        newsize |= newsize >> 1;
index 1e3f932565622b00906e31a7df17e6849ad6d7b4..8eb59c33b709040f065c5741751bf21e9eb78ceb 100644 (file)
@@ -20,6 +20,9 @@ extern "C" {
 #define frr_each(prefix, head, item)                                           \
        for (item = prefix##_first(head); item;                                \
                        item = prefix##_next(head, item))
+#define frr_each_const(prefix, head, item)                                     \
+       for (item = prefix##_const_first(head); item;                          \
+            item = prefix##_const_next(head, item))
 #define frr_each_safe(prefix, head, item)                                      \
        for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe =            \
                        prefix##_next_safe(head,                               \
@@ -780,6 +783,12 @@ struct thash_head {
        struct thash_item **entries;
        uint32_t count;
 
+       /* tabshift can be 0 if the hash table is empty and entries is NULL.
+        * otherwise it will always be 2 or larger because it contains
+        * the shift value *plus 1*.  This is a trick to make HASH_SIZE return
+        * the correct value (with the >> 1) for tabshift == 0, without needing
+        * a conditional branch.
+        */
        uint8_t tabshift;
        uint8_t minshift, maxshift;
 };
@@ -788,8 +797,11 @@ struct thash_head {
        ((1U << (tabshift)) >> 1)
 #define HASH_SIZE(head) \
        _HASH_SIZE((head).tabshift)
-#define _HASH_KEY(tabshift, val) \
-       ((val) >> (33 - (tabshift)))
+#define _HASH_KEY(tabshift, val)                                               \
+       ({                                                                     \
+               assume((tabshift) >= 2 && (tabshift) <= 33);                   \
+               (val) >> (33 - (tabshift));                                    \
+       })
 #define HASH_KEY(head, val) \
        _HASH_KEY((head).tabshift, val)
 #define HASH_GROW_THRESHOLD(head) \
@@ -936,6 +948,8 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h)              \
 macro_pure bool prefix ## _member(const struct prefix##_head *h,               \
                                  const type *item)                            \
 {                                                                              \
+       if (!h->hh.tabshift)                                                   \
+               return NULL;                                                   \
        uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \
        const struct thash_item *hitem = h->hh.entries[hbits];                 \
        while (hitem && hitem->hashval < hval)                                 \
index d0a667778812c22822528d2ad30f318c7bec3b47..d6a0dba20613c687d08928cb7fb40227712d8f6d 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -73,6 +73,13 @@ static bool mgmt_candidate_ds_wr_locked;
 static uint64_t mgmt_client_id_next;
 static uint64_t mgmt_last_req_id = UINT64_MAX;
 
+static bool vty_debug;
+#define VTY_DBG(fmt, ...)                                                      \
+       do {                                                                   \
+               if (vty_debug)                                                 \
+                       zlog_debug(fmt, ##__VA_ARGS__);                        \
+       } while (0)
+
 PREDECL_DLIST(vtyservs);
 
 struct vty_serv {
@@ -118,8 +125,8 @@ static int no_password_check = 0;
 /* Integrated configuration file path */
 static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
 
-static bool do_log_commands;
-static bool do_log_commands_perm;
+bool vty_log_commands;
+static bool vty_log_commands_perm;
 
 void vty_mgmt_resume_response(struct vty *vty, bool success)
 {
@@ -501,7 +508,7 @@ static int vty_command(struct vty *vty, char *buf)
        /*
         * Log non empty command lines
         */
-       if (do_log_commands &&
+       if (vty_log_commands &&
            strncmp(buf, "echo PING", strlen("echo PING")) != 0)
                cp = buf;
        if (cp != NULL) {
@@ -2400,7 +2407,7 @@ static void vty_timeout(struct event *thread)
 }
 
 /* Read up configuration file from file_name. */
-static void vty_read_file(struct nb_config *config, FILE *confp)
+void vty_read_file(struct nb_config *config, FILE *confp)
 {
        int ret;
        struct vty *vty;
@@ -3153,15 +3160,15 @@ DEFPY (log_commands,
        "Log all commands\n")
 {
        if (no) {
-               if (do_log_commands_perm) {
+               if (vty_log_commands_perm) {
                        vty_out(vty,
                                "Daemon started with permanent logging turned on for commands, ignoring\n");
                        return CMD_WARNING;
                }
 
-               do_log_commands = false;
+               vty_log_commands = false;
        } else
-               do_log_commands = true;
+               vty_log_commands = true;
 
        return CMD_SUCCESS;
 }
@@ -3189,7 +3196,7 @@ static int vty_config_write(struct vty *vty)
 
        vty_endframe(vty, "exit\n");
 
-       if (do_log_commands)
+       if (vty_log_commands)
                vty_out(vty, "log commands\n");
 
        vty_out(vty, "!\n");
@@ -3273,9 +3280,9 @@ void vty_init_vtysh(void)
 static void vty_mgmt_server_connected(uintptr_t lib_hndl, uintptr_t usr_data,
                                      bool connected)
 {
-       zlog_err("%sGot %sconnected %s MGMTD Frontend Server",
-                !connected ? "ERROR: " : "", !connected ? "dis: " : "",
-                !connected ? "from" : "to");
+       VTY_DBG("%sGot %sconnected %s MGMTD Frontend Server",
+               !connected ? "ERROR: " : "", !connected ? "dis: " : "",
+               !connected ? "from" : "to");
 
        mgmt_fe_connected = connected;
 
@@ -3295,15 +3302,13 @@ static void vty_mgmt_session_created(uintptr_t lib_hndl, uintptr_t usr_data,
        vty = (struct vty *)session_ctx;
 
        if (!success) {
-               zlog_err("%s session for client %llu failed!",
-                        create ? "Creating" : "Destroying",
-                        (unsigned long long)client_id);
+               zlog_err("%s session for client %" PRIu64 " failed!",
+                        create ? "Creating" : "Destroying", client_id);
                return;
        }
 
-       zlog_err("%s session for client %llu successfully!",
-                create ? "Created" : "Destroyed",
-                (unsigned long long)client_id);
+       VTY_DBG("%s session for client %" PRIu64 " successfully",
+               create ? "Created" : "Destroyed", client_id);
        if (create)
                vty->mgmt_session_id = session_id;
 }
@@ -3320,13 +3325,13 @@ static void vty_mgmt_ds_lock_notified(uintptr_t lib_hndl, uintptr_t usr_data,
        vty = (struct vty *)session_ctx;
 
        if (!success) {
-               zlog_err("%socking for DS %u failed! Err: '%s'",
+               zlog_err("%socking for DS %u failed, Err: '%s'",
                         lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
-               vty_out(vty, "ERROR: %socking for DS %u failed! Err: '%s'\n",
+               vty_out(vty, "ERROR: %socking for DS %u failed, Err: '%s'\n",
                        lock_ds ? "L" : "Unl", ds_id, errmsg_if_any);
        } else {
-               zlog_err("%socked DS %u successfully!", lock_ds ? "L" : "Unl",
-                        ds_id);
+               VTY_DBG("%socked DS %u successfully", lock_ds ? "L" : "Unl",
+                       ds_id);
        }
 
        vty_mgmt_resume_response(vty, success);
@@ -3342,17 +3347,15 @@ static void vty_mgmt_set_config_result_notified(
        vty = (struct vty *)session_ctx;
 
        if (!success) {
-               zlog_err(
-                       "SET_CONFIG request for client 0x%llx failed! Error: '%s'",
-                       (unsigned long long)client_id,
-                       errmsg_if_any ? errmsg_if_any : "Unknown");
-               vty_out(vty, "ERROR: SET_CONFIG request failed! Error: %s\n",
+               zlog_err("SET_CONFIG request for client 0x%" PRIx64
+                        " failed, Error: '%s'",
+                        client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+               vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
                        errmsg_if_any ? errmsg_if_any : "Unknown");
        } else {
-               zlog_err(
-                       "SET_CONFIG request for client 0x%llx req-id %llu was successfull!",
-                       (unsigned long long)client_id,
-                       (unsigned long long)req_id);
+               VTY_DBG("SET_CONFIG request for client 0x%" PRIx64
+                       " req-id %" PRIu64 " was successfull",
+                       client_id, req_id);
        }
 
        vty_mgmt_resume_response(vty, success);
@@ -3369,17 +3372,15 @@ static void vty_mgmt_commit_config_result_notified(
        vty = (struct vty *)session_ctx;
 
        if (!success) {
-               zlog_err(
-                       "COMMIT_CONFIG request for client 0x%llx failed! Error: '%s'",
-                       (unsigned long long)client_id,
-                       errmsg_if_any ? errmsg_if_any : "Unknown");
-               vty_out(vty, "ERROR: COMMIT_CONFIG request failed! Error: %s\n",
+               zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64
+                        " failed, Error: '%s'",
+                        client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+               vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
                        errmsg_if_any ? errmsg_if_any : "Unknown");
        } else {
-               zlog_err(
-                       "COMMIT_CONFIG request for client 0x%llx req-id %llu was successfull!",
-                       (unsigned long long)client_id,
-                       (unsigned long long)req_id);
+               VTY_DBG("COMMIT_CONFIG request for client 0x%" PRIx64
+                       " req-id %" PRIu64 " was successfull",
+                       client_id, req_id);
                if (errmsg_if_any)
                        vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
        }
@@ -3399,19 +3400,18 @@ static enum mgmt_result vty_mgmt_get_data_result_notified(
        vty = (struct vty *)session_ctx;
 
        if (!success) {
-               zlog_err(
-                       "GET_DATA request for client 0x%llx failed! Error: '%s'",
-                       (unsigned long long)client_id,
-                       errmsg_if_any ? errmsg_if_any : "Unknown");
-               vty_out(vty, "ERROR: GET_DATA request failed! Error: %s\n",
+               zlog_err("GET_DATA request for client 0x%" PRIx64
+                        " failed, Error: '%s'",
+                        client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
+               vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
                        errmsg_if_any ? errmsg_if_any : "Unknown");
                vty_mgmt_resume_response(vty, success);
                return MGMTD_INTERNAL_ERROR;
        }
 
-       zlog_debug(
-               "GET_DATA request for client 0x%llx req-id %llu was successfull!",
-               (unsigned long long)client_id, (unsigned long long)req_id);
+       VTY_DBG("GET_DATA request succeeded, client 0x%" PRIx64
+               " req-id %" PRIu64,
+               client_id, req_id);
 
        if (req_id != mgmt_last_req_id) {
                mgmt_last_req_id = req_id;
@@ -3468,11 +3468,9 @@ int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
                ret = mgmt_fe_lock_ds(mgmt_lib_hndl, vty->mgmt_session_id,
                                      vty->mgmt_req_id, ds_id, lock);
                if (ret != MGMTD_SUCCESS) {
-                       zlog_err(
-                               "Failed to send %sLOCK-DS-REQ to MGMTD for req-id %llu.",
-                               lock ? "" : "UN",
-                               (unsigned long long)vty->mgmt_req_id);
-                       vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!",
+                       zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
+                                lock ? "" : "UN", vty->mgmt_req_id);
+                       vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
                                lock ? "" : "UN");
                        return -1;
                }
@@ -3549,6 +3547,7 @@ int vty_mgmt_send_config_data(struct vty *vty)
                                   MGMTD_DS_RUNNING) != MGMTD_SUCCESS) {
                        zlog_err("Failed to send %d Config Xpaths to MGMTD!!",
                                 (int)indx);
+                       vty_out(vty, "Failed to send SETCFG-REQ to MGMTD!\n");
                        return -1;
                }
 
@@ -3569,10 +3568,9 @@ int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
                        MGMTD_DS_CANDIDATE, MGMTD_DS_RUNNING, validate_only,
                        abort);
                if (ret != MGMTD_SUCCESS) {
-                       zlog_err(
-                               "Failed to send COMMIT-REQ to MGMTD for req-id %llu.",
-                               (unsigned long long)vty->mgmt_req_id);
-                       vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!");
+                       zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
+                                vty->mgmt_req_id);
+                       vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
                        return -1;
                }
 
@@ -3608,9 +3606,11 @@ int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
                                      num_req);
 
        if (ret != MGMTD_SUCCESS) {
-               zlog_err("Failed to send GET-CONFIG to MGMTD for req-id %llu.",
-                        (unsigned long long)vty->mgmt_req_id);
-               vty_out(vty, "Failed to send GET-CONFIG to MGMTD!");
+               zlog_err(
+                       "Failed to send GET-CONFIG to MGMTD for req-id %" PRIu64
+                       ".",
+                       vty->mgmt_req_id);
+               vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
                return -1;
        }
 
@@ -3643,9 +3643,10 @@ int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
                               vty->mgmt_req_id, datastore, getreq, num_req);
 
        if (ret != MGMTD_SUCCESS) {
-               zlog_err("Failed to send GET-DATA to MGMTD for req-id %llu.",
-                        (unsigned long long)vty->mgmt_req_id);
-               vty_out(vty, "Failed to send GET-DATA to MGMTD!");
+               zlog_err("Failed to send GET-DATA to MGMTD for req-id %" PRIu64
+                        ".",
+                        vty->mgmt_req_id);
+               vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
                return -1;
        }
 
@@ -3676,8 +3677,8 @@ void vty_init(struct event_loop *master_thread, bool do_command_logging)
        install_element(CONFIG_NODE, &log_commands_cmd);
 
        if (do_command_logging) {
-               do_log_commands = true;
-               do_log_commands_perm = true;
+               vty_log_commands = true;
+               vty_log_commands_perm = true;
        }
 
        install_element(ENABLE_NODE, &terminal_monitor_cmd);
index 66d3355329d7982fdb4ce9445b7ee268f41c50cd..560748d91d828469235cf2e4435b7d065de3ddcb 100644 (file)
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -335,6 +335,7 @@ struct vty_arg {
 #endif
 
 extern struct nb_config *vty_mgmt_candidate_config;
+extern bool vty_log_commands;
 
 /* Prototypes. */
 extern void vty_init(struct event_loop *m, bool do_command_logging);
@@ -369,6 +370,7 @@ extern void vty_pass_fd(struct vty *vty, int fd);
 
 extern bool vty_read_config(struct nb_config *config, const char *config_file,
                            char *config_default_dir);
+extern void vty_read_file(struct nb_config *config, FILE *confp);
 extern void vty_time_print(struct vty *, int);
 extern void vty_serv_sock(const char *, unsigned short, const char *);
 extern void vty_close(struct vty *);
index 70a3251ab39db4280c8c3a904fac85d7febee71b..4dd8654217183cfcdec1d41abf4c14cb689f2d5c 100644 (file)
@@ -250,6 +250,23 @@ void yang_snode_get_path(const struct lysc_node *snode,
        }
 }
 
+struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath,
+                                 uint32_t options)
+{
+       struct lysc_node *snode;
+       struct ly_set *set;
+       LY_ERR err;
+
+       err = lys_find_xpath(ly_native_ctx, NULL, xpath, options, &set);
+       if (err || !set->count)
+               return NULL;
+
+       snode = set->snodes[0];
+       ly_set_free(set, NULL);
+
+       return snode;
+}
+
 struct lysc_node *yang_snode_real_parent(const struct lysc_node *snode)
 {
        struct lysc_node *parent = snode->parent;
index 654c246f0dc99f47e3e0110ad091d628199a832b..37369c09bf81b735603358ed8d95936f7c1a8145 100644 (file)
@@ -210,6 +210,27 @@ extern void yang_snode_get_path(const struct lysc_node *snode,
                                enum yang_path_type type, char *xpath,
                                size_t xpath_len);
 
+
+/*
+ * Find libyang schema node for the given xpath. Uses `lys_find_xpath`,
+ * returning only the first of a set of nodes -- normally there should only
+ * be one.
+ *
+ * ly_ctx
+ *    libyang context to operate on.
+ *
+ * xpath
+ *    XPath expression (absolute or relative) to find the schema node for.
+ *
+ * options
+ *    Libyang findxpathoptions value (see lys_find_xpath).
+ *
+ * Returns:
+ *    The libyang schema node if found, or NULL if not found.
+ */
+extern struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx,
+                                        const char *xpath, uint32_t options);
+
 /*
  * Find first parent schema node which is a presence-container or a list
  * (non-presence containers are ignored).
index de668230abe63edd868f564d40a541d719bdf811..eae7577a0d1c2c905261a9c211c73deb283f08dc 100644 (file)
@@ -235,8 +235,8 @@ struct yang_translator *yang_translator_load(const char *path)
                xpath_custom =
                        yang_dnode_get_string(set->dnodes[i], "./custom");
 
-               snode_custom = lys_find_path(translator->ly_ctx, NULL,
-                                            xpath_custom, 0);
+               snode_custom =
+                       yang_find_snode(translator->ly_ctx, xpath_custom, 0);
                if (!snode_custom) {
                        flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
                                  "%s: unknown data path: %s", __func__,
@@ -247,8 +247,7 @@ struct yang_translator *yang_translator_load(const char *path)
 
                xpath_native =
                        yang_dnode_get_string(set->dnodes[i], "./native");
-               snode_native =
-                       lys_find_path(ly_native_ctx, NULL, xpath_native, 0);
+               snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0);
                if (!snode_native) {
                        flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
                                  "%s: unknown data path: %s", __func__,
@@ -315,7 +314,7 @@ yang_translate_xpath(const struct yang_translator *translator, int dir,
        else
                ly_ctx = ly_native_ctx;
 
-       snode = lys_find_path(ly_ctx, NULL, xpath, 0);
+       snode = yang_find_snode(ly_ctx, xpath, 0);
        if (!snode) {
                flog_warn(EC_LIB_YANG_TRANSLATION_ERROR,
                          "%s: unknown data path: %s", __func__, xpath);
index 509c4dd8560cbfa41dcd9cc603fdbc13f612935e..dc049a374a9e47ad19ec19e917f61af27d45c64e 100644 (file)
@@ -89,7 +89,7 @@ static const char *yang_get_default_value(const char *xpath)
        const struct lysc_node *snode;
        const char *value;
 
-       snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
+       snode = yang_find_snode(ly_native_ctx, xpath, 0);
        if (snode == NULL) {
                flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
                         "%s: unknown data path: %s", __func__, xpath);
@@ -206,7 +206,7 @@ int yang_str2enum(const char *xpath, const char *value)
        const struct lysc_type_enum *type;
        const struct lysc_type_bitenum_item *enums;
 
-       snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
+       snode = yang_find_snode(ly_native_ctx, xpath, 0);
        if (snode == NULL) {
                flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
                         "%s: unknown data path: %s", __func__, xpath);
@@ -241,7 +241,7 @@ struct yang_data *yang_data_new_enum(const char *xpath, int value)
        const struct lysc_type_enum *type;
        const struct lysc_type_bitenum_item *enums;
 
-       snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
+       snode = yang_find_snode(ly_native_ctx, xpath, 0);
        if (snode == NULL) {
                flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
                         "%s: unknown data path: %s", __func__, xpath);
index 95093a56f5d9e9aaa022762c14ed256372441555..8526cbfaa1cea424f956fa2d36b581cb0f318232 100644 (file)
@@ -427,13 +427,18 @@ enum zclient_send_status zclient_send_vrf_label(struct zclient *zclient,
 }
 
 enum zclient_send_status zclient_send_localsid(struct zclient *zclient,
-               const struct in6_addr *sid, ifindex_t oif,
+               const struct in6_addr *sid, vrf_id_t vrf_id,
                enum seg6local_action_t action,
                const struct seg6local_context *context)
 {
        struct prefix_ipv6 p = {};
        struct zapi_route api = {};
        struct zapi_nexthop *znh;
+       struct interface *ifp;
+
+       ifp = if_get_vrf_loopback(vrf_id);
+       if (ifp == NULL)
+               return ZCLIENT_SEND_FAILURE;
 
        p.family = AF_INET6;
        p.prefixlen = IPV6_MAX_BITLEN;
@@ -456,7 +461,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient,
        memset(znh, 0, sizeof(*znh));
 
        znh->type = NEXTHOP_TYPE_IFINDEX;
-       znh->ifindex = oif;
+       znh->ifindex = ifp->ifindex;
        SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL);
        znh->seg6local_action = action;
        memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context));
@@ -4294,6 +4299,8 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api)
 
        memset(api, 0, sizeof(*api));
 
+       api->safi = SAFI_UNICAST;
+
        STREAM_GETL(s, api->cap);
        switch (api->cap) {
        case ZEBRA_CLIENT_GR_CAPABILITIES:
index 367e5b14740bdcd5f0866a6af8d0dd34f7c820b2..e43393fd70e741431078f2e743e17bb025acd386 100644 (file)
@@ -901,7 +901,7 @@ zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
 
 extern enum zclient_send_status
 zclient_send_localsid(struct zclient *zclient, const struct in6_addr *sid,
-                     ifindex_t oif, enum seg6local_action_t action,
+                     vrf_id_t vrf_id, enum seg6local_action_t action,
                      const struct seg6local_context *context);
 
 extern void zclient_send_reg_requests(struct zclient *, vrf_id_t);
index a5c0eb34a309a7442ec0923013c5d328b7aea250..7a65a19f5545d643f20eb6ff0598a61a859842d8 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <zebra.h>
+#include "debug.h"
 #include "mgmtd/mgmt.h"
 #include "mgmtd/mgmt_be_server.h"
 #include "mgmtd/mgmt_be_adapter.h"
 #include "mgmtd/mgmt_history.h"
 #include "mgmtd/mgmt_memory.h"
 
-bool mgmt_debug_be;
-bool mgmt_debug_fe;
-bool mgmt_debug_ds;
-bool mgmt_debug_txn;
+struct debug mgmt_debug_be = {.desc = "Management backend adapater"};
+struct debug mgmt_debug_ds = {.desc = "Management datastore"};
+struct debug mgmt_debug_fe = {.desc = "Management frontend adapater"};
+struct debug mgmt_debug_txn = {.desc = "Management transaction"};
 
 /* MGMTD process wide configuration.  */
 static struct mgmt_master mgmt_master;
index 4d186c176b1d31ee9443277d18a1b15d80ebd156..603296bb38f163191982c34f15a839554aa84be3 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef _FRR_MGMTD_H
 #define _FRR_MGMTD_H
 
+#include "debug.h"
 #include "vrf.h"
 #include "defaults.h"
 #include "stream.h"
 #define MGMTD_SOCKET_BUF_SIZE 65535
 #define MGMTD_MAX_COMMIT_LIST 10
 
-extern bool mgmt_debug_be;
-extern bool mgmt_debug_fe;
-extern bool mgmt_debug_ds;
-extern bool mgmt_debug_txn;
+extern struct debug mgmt_debug_be;
+extern struct debug mgmt_debug_ds;
+extern struct debug mgmt_debug_fe;
+extern struct debug mgmt_debug_txn;
+
+#define MGMT_DEBUG_BE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_DS_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_ds, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_FE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL)
+#define MGMT_DEBUG_TXN_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_tx, DEBUG_MODE_ALL)
 
 struct mgmt_txn_ctx;
 
@@ -62,6 +68,8 @@ struct mgmt_master {
 };
 
 extern struct mgmt_master *mm;
+extern char const *const mgmt_daemons[];
+extern uint mgmt_daemons_count;
 
 /* Inline functions */
 static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b)
@@ -100,16 +108,4 @@ extern void mgmt_master_init(struct event_loop *master, const int buffer_size);
 extern void mgmt_init(void);
 extern void mgmt_vty_init(void);
 
-static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
-                                           size_t sz)
-{
-       struct tm tm;
-       size_t n;
-
-       localtime_r((const time_t *)&tv->tv_sec, &tm);
-       n = strftime(buf, sz, "%Y-%m-%dT%H:%M:%S", &tm);
-       snprintf(&buf[n], sz - n, ",%06u000", (unsigned int)tv->tv_usec);
-       return buf;
-}
-
 #endif /* _FRR_MGMTD_H */
index af43cf3eae3bca04e5d9eb5ac16f15068882498e..9d210b8716c3ca0c227543e0bd1f32ef401fae8f 100644 (file)
 #include "mgmt_be_client.h"
 #include "mgmtd/mgmt_be_adapter.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_ADAPTER_DBG(fmt, ...)                                        \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_ADAPTER_ERR(fmt, ...)                                        \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_BE_ADAPTER_DBG(fmt, ...)                                        \
-       do {                                                                  \
-               if (mgmt_debug_be)                                            \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);      \
-       } while (0)
+#define MGMTD_BE_ADAPTER_DBG(fmt, ...)                                         \
+       DEBUGD(&mgmt_debug_be, "%s:" fmt, __func__, ##__VA_ARGS__)
 #define MGMTD_BE_ADAPTER_ERR(fmt, ...)                                        \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 #define FOREACH_ADAPTER_IN_LIST(adapter)                                       \
        frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter))
@@ -80,23 +70,23 @@ static const struct mgmt_be_xpath_map_reg xpath_static_map_reg[] = {
         .be_clients =
                 (enum mgmt_be_client_id[]){
 #if HAVE_STATICD
-                MGMTD_BE_CLIENT_ID_STATICD,
+                        MGMTD_BE_CLIENT_ID_STATICD,
 #endif
                         MGMTD_BE_CLIENT_ID_MAX}},
        {.xpath_regexp = "/frr-interface:lib/*",
         .be_clients =
                 (enum mgmt_be_client_id[]){
 #if HAVE_STATICD
-                MGMTD_BE_CLIENT_ID_STATICD,
+                        MGMTD_BE_CLIENT_ID_STATICD,
 #endif
                         MGMTD_BE_CLIENT_ID_MAX}},
        {.xpath_regexp =
-                "/frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/*",
+               "/frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/*",
 
         .be_clients =
                 (enum mgmt_be_client_id[]){
 #if HAVE_STATICD
-                MGMTD_BE_CLIENT_ID_STATICD,
+                        MGMTD_BE_CLIENT_ID_STATICD,
 #endif
                         MGMTD_BE_CLIENT_ID_MAX}},
 };
@@ -347,8 +337,8 @@ mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter)
        struct mgmt_be_client_adapter *old;
 
        FOREACH_ADAPTER_IN_LIST (old) {
-               if (old != adapter
-                   && !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
+               if (old != adapter &&
+                   !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
                        /*
                         * We have a Zombie lingering around
                         */
@@ -521,7 +511,7 @@ static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter,
                &adapter->mstate, be_msg,
                mgmtd__be_message__get_packed_size(be_msg),
                (size_t(*)(void *, void *))mgmtd__be_message__pack,
-               mgmt_debug_be);
+               MGMT_DEBUG_BE_CHECK());
        mgmt_be_adapter_sched_msg_write(adapter);
        return rv;
 }
@@ -619,7 +609,7 @@ static void mgmt_be_adapter_proc_msgbufs(struct event *thread)
        struct mgmt_be_client_adapter *adapter = EVENT_ARG(thread);
 
        if (mgmt_msg_procbufs(&adapter->mstate, mgmt_be_adapter_process_msg,
-                             adapter, mgmt_debug_be))
+                             adapter, MGMT_DEBUG_BE_CHECK()))
                mgmt_be_adapter_register_event(adapter, MGMTD_BE_PROC_MSG);
 }
 
@@ -630,7 +620,8 @@ static void mgmt_be_adapter_read(struct event *thread)
 
        adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread);
 
-       rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd, mgmt_debug_be);
+       rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd,
+                          MGMT_DEBUG_BE_CHECK());
        if (rv == MSR_DISCONNECT) {
                mgmt_be_adapter_disconnect(adapter);
                return;
@@ -645,7 +636,8 @@ static void mgmt_be_adapter_write(struct event *thread)
        struct mgmt_be_client_adapter *adapter = EVENT_ARG(thread);
        enum mgmt_msg_wsched rv;
 
-       rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd, mgmt_debug_be);
+       rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd,
+                           MGMT_DEBUG_BE_CHECK());
        if (rv == MSW_SCHED_STREAM)
                mgmt_be_adapter_register_event(adapter, MGMTD_BE_CONN_WRITE);
        else if (rv == MSW_DISCONNECT)
index 0fa7ddd6d619547cf8e639449f1736d3556cc627..029e032febe95e148700cbe0e8ad87e1188287ba 100644 (file)
 #include "mgmtd/mgmt_be_server.h"
 #include "mgmtd/mgmt_be_adapter.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_BE_SRVR_DBG(fmt, ...)                                         \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_BE_SRVR_ERR(fmt, ...)                                         \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
 #define MGMTD_BE_SRVR_DBG(fmt, ...)                                            \
-       do {                                                                   \
-               if (mgmt_debug_be)                                             \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);       \
-       } while (0)
+       DEBUGD(&mgmt_debug_be, "%s:" fmt, __func__, ##__VA_ARGS__)
 #define MGMTD_BE_SRVR_ERR(fmt, ...)                                         \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 static int mgmt_be_listen_fd = -1;
 static struct event_loop *mgmt_be_listen_tm;
@@ -119,7 +109,7 @@ static void mgmt_be_server_start(const char *hostname)
        return;
 
 mgmt_be_server_start_failed:
-       if (sock)
+       if (sock > 0)
                close(sock);
 
        mgmt_be_listen_fd = -1;
index 1724afb1829a7617518802fdfd265c24406a4f43..b5eaf7bff62dfe8d60f6d293accabd94d9d780e6 100644 (file)
 #include "mgmtd/mgmt_txn.h"
 #include "libyang/libyang.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
 #define MGMTD_DS_DBG(fmt, ...)                                                 \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_DS_ERR(fmt, ...)                                                 \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_DS_DBG(fmt, ...)                                                 \
-       do {                                                                   \
-               if (mgmt_debug_ds)                                             \
-                       zlog_err("%s: " fmt, __func__, ##__VA_ARGS__);         \
-       } while (0)
+       DEBUGD(&mgmt_debug_ds, "%s:" fmt, __func__, ##__VA_ARGS__)
 #define MGMTD_DS_ERR(fmt, ...)                                                 \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 struct mgmt_ds_ctx {
        Mgmtd__DatastoreId ds_id;
@@ -87,7 +77,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src,
                                           struct mgmt_ds_ctx *dst)
 {
        struct lyd_node *dst_dnode, *src_dnode;
-       struct ly_out *out;
 
        if (!src || !dst)
                return -1;
@@ -117,13 +106,6 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src,
                nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
        }
 
-       if (dst->ds_id == MGMTD_DS_RUNNING) {
-               if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
-                   == LY_SUCCESS)
-                       mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out);
-               ly_out_free(out, NULL, 0);
-       }
-
        /* TODO: Update the versions if nb_config present */
 
        return 0;
@@ -134,7 +116,6 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src,
 {
        int ret;
        struct lyd_node **dst_dnode, *src_dnode;
-       struct ly_out *out;
 
        if (!src || !dst)
                return -1;
@@ -159,13 +140,6 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src,
                nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
        }
 
-       if (dst->ds_id == MGMTD_DS_RUNNING) {
-               if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
-                   == LY_SUCCESS)
-                       mgmt_ds_dump_in_memory(dst, "", LYD_JSON, out);
-               ly_out_free(out, NULL, 0);
-       }
-
        return 0;
 }
 
@@ -190,6 +164,7 @@ static int mgmt_ds_load_cfg_from_file(const char *filepath,
 void mgmt_ds_reset_candidate(void)
 {
        struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode;
+
        if (dnode)
                yang_dnode_free(dnode);
 
@@ -200,8 +175,6 @@ void mgmt_ds_reset_candidate(void)
 
 int mgmt_ds_init(struct mgmt_master *mm)
 {
-       struct lyd_node *root;
-
        if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds)
                assert(!"MGMTD: Call ds_init only once!");
 
@@ -209,12 +182,6 @@ int mgmt_ds_init(struct mgmt_master *mm)
        if (!running_config)
                assert(!"MGMTD: Call ds_init after frr_init only!");
 
-       if (mgmt_ds_load_cfg_from_file(MGMTD_STARTUP_DS_FILE_PATH, &root)
-           == 0) {
-               nb_config_free(running_config);
-               running_config = nb_config_new(root);
-       }
-
        running.root.cfg_root = running_config;
        running.config_ds = true;
        running.ds_id = MGMTD_DS_RUNNING;
@@ -523,12 +490,12 @@ int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath)
                 */
                return NB_ERR_NOT_FOUND;
        /* destroy dependant */
-       if (nb_node->dep_cbs.get_dependant_xpath) {
+       if (nb_node && nb_node->dep_cbs.get_dependant_xpath) {
                nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
 
                dep_dnode = yang_dnode_get(
                        ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode
-                                          : ds_ctx->root.dnode_root,
+                                         : ds_ctx->root.dnode_root,
                        dep_xpath);
                if (dep_dnode)
                        lyd_free_tree(dep_dnode);
index 89a2ea94259f796202ce3e1a6179e57c06714f3a..2a32eb641a9eefb6f7e8685ebd482da10b209f6b 100644 (file)
 #define MGMTD_DS_NAME_CANDIDATE "candidate"
 #define MGMTD_DS_NAME_OPERATIONAL "operational"
 
-#define MGMTD_STARTUP_DS_FILE_PATH DAEMON_DB_DIR "/frr_startup.json"
-
 #define FOREACH_MGMTD_DS_ID(id)                                                \
        for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++)
 
 #define MGMTD_MAX_COMMIT_LIST 10
-#define MGMTD_MD5_HASH_LEN 16
-#define MGMTD_MD5_HASH_STR_HEX_LEN 33
 
 #define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json"
 #define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat"
-#define MGMTD_COMMIT_TIME_STR_LEN 100
 
 extern struct nb_config *running_config;
 
index 5826b17de7a6a1125661268d60e14cfaa89894b4..262741b665e9bfe28c8f9c332772858aead1e010 100644 (file)
 #include "mgmtd/mgmt_memory.h"
 #include "mgmtd/mgmt_fe_adapter.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_ADAPTER_DBG(fmt, ...)                                       \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_ADAPTER_ERR(fmt, ...)                                       \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_FE_ADAPTER_DBG(fmt, ...)                                       \
-       do {                                                                 \
-               if (mgmt_debug_fe)                                           \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);     \
-       } while (0)
+#define MGMTD_FE_ADAPTER_DBG(fmt, ...)                                         \
+       DEBUGD(&mgmt_debug_fe, "%s:" fmt, __func__, ##__VA_ARGS__)
 #define MGMTD_FE_ADAPTER_ERR(fmt, ...)                                       \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 #define FOREACH_ADAPTER_IN_LIST(adapter)                                       \
        frr_each_safe (mgmt_fe_adapters, &mgmt_fe_adapters, (adapter))
@@ -399,7 +389,7 @@ mgmt_fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter,
                &adapter->mstate, fe_msg,
                mgmtd__fe_message__get_packed_size(fe_msg),
                (size_t(*)(void *, void *))mgmtd__fe_message__pack,
-               mgmt_debug_fe);
+               MGMT_DEBUG_FE_CHECK());
        mgmt_fe_adapter_sched_msg_write(adapter);
        return rv;
 }
@@ -495,7 +485,8 @@ static int mgmt_fe_send_setcfg_reply(struct mgmt_fe_session_ctx *session,
 
        if (implicit_commit) {
                if (mm->perf_stats_en)
-                       gettimeofday(&session->adapter->cmt_stats.last_end, NULL);
+                       gettimeofday(&session->adapter->cmt_stats.last_end,
+                                    NULL);
                mgmt_fe_session_compute_commit_timers(
                        &session->adapter->cmt_stats);
        }
@@ -715,8 +706,8 @@ mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter)
        struct mgmt_fe_client_adapter *old;
 
        FOREACH_ADAPTER_IN_LIST (old) {
-               if (old != adapter
-                   && !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
+               if (old != adapter &&
+                   !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
                        /*
                         * We have a Zombie lingering around
                         */
@@ -1109,8 +1100,8 @@ mgmt_fe_session_handle_getdata_req_msg(struct mgmt_fe_session_ctx *session,
                                                  MGMTD_TXN_TYPE_SHOW);
                if (session->txn_id == MGMTD_SESSION_ID_NONE) {
                        mgmt_fe_send_getdata_reply(
-                               session, getdata_req->ds_id, getdata_req->req_id,
-                               false, NULL,
+                               session, getdata_req->ds_id,
+                               getdata_req->req_id, false, NULL,
                                "Failed to create a Show transaction!");
                        goto mgmt_fe_sess_handle_getdata_req_failed;
                }
@@ -1438,7 +1429,7 @@ static void mgmt_fe_adapter_proc_msgbufs(struct event *thread)
        struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
 
        if (mgmt_msg_procbufs(&adapter->mstate, mgmt_fe_adapter_process_msg,
-                             adapter, mgmt_debug_fe))
+                             adapter, MGMT_DEBUG_FE_CHECK()))
                mgmt_fe_adapter_register_event(adapter, MGMTD_FE_PROC_MSG);
 }
 
@@ -1447,7 +1438,8 @@ static void mgmt_fe_adapter_read(struct event *thread)
        struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
        enum mgmt_msg_rsched rv;
 
-       rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe);
+       rv = mgmt_msg_read(&adapter->mstate, adapter->conn_fd,
+                          MGMT_DEBUG_FE_CHECK());
        if (rv == MSR_DISCONNECT) {
                mgmt_fe_adapter_disconnect(adapter);
                return;
@@ -1462,7 +1454,8 @@ static void mgmt_fe_adapter_write(struct event *thread)
        struct mgmt_fe_client_adapter *adapter = EVENT_ARG(thread);
        enum mgmt_msg_wsched rv;
 
-       rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd, mgmt_debug_fe);
+       rv = mgmt_msg_write(&adapter->mstate, adapter->conn_fd,
+                           MGMT_DEBUG_FE_CHECK());
        if (rv == MSW_SCHED_STREAM)
                mgmt_fe_adapter_register_event(adapter, MGMTD_FE_CONN_WRITE);
        else if (rv == MSW_DISCONNECT)
@@ -1721,7 +1714,7 @@ static void
 mgmt_fe_adapter_cmt_stats_write(struct vty *vty,
                                    struct mgmt_fe_client_adapter *adapter)
 {
-       char buf[100] = {0};
+       char buf[MGMT_LONG_TIME_MAX_LEN];
 
        if (!mm->perf_stats_en)
                return;
@@ -1780,8 +1773,8 @@ mgmt_fe_adapter_cmt_stats_write(struct vty *vty,
                                        sizeof(buf)));
                        vty_out(vty, "        Apply-Config Start: \t\t%s\n",
                                mgmt_realtime_to_string(
-                                       &adapter->cmt_stats.apply_cfg_start, buf,
-                                       sizeof(buf)));
+                                       &adapter->cmt_stats.apply_cfg_start,
+                                       buf, sizeof(buf)));
                        vty_out(vty, "        Apply-Config End: \t\t%s\n",
                                mgmt_realtime_to_string(
                                        &adapter->cmt_stats.apply_cfg_end, buf,
@@ -1802,7 +1795,7 @@ static void
 mgmt_fe_adapter_setcfg_stats_write(struct vty *vty,
                                       struct mgmt_fe_client_adapter *adapter)
 {
-       char buf[100] = {0};
+       char buf[MGMT_LONG_TIME_MAX_LEN];
 
        if (!mm->perf_stats_en)
                return;
@@ -1818,8 +1811,9 @@ mgmt_fe_adapter_setcfg_stats_write(struct vty *vty,
                        adapter->setcfg_stats.avg_tm);
                vty_out(vty, "    Last-Set-Cfg-Details:\n");
                vty_out(vty, "      Set-Cfg Start: \t\t\t%s\n",
-                       mgmt_realtime_to_string(&adapter->setcfg_stats.last_start,
-                                               buf, sizeof(buf)));
+                       mgmt_realtime_to_string(
+                               &adapter->setcfg_stats.last_start, buf,
+                               sizeof(buf)));
                vty_out(vty, "      Set-Cfg End: \t\t\t%s\n",
                        mgmt_realtime_to_string(&adapter->setcfg_stats.last_end,
                                                buf, sizeof(buf)));
@@ -1894,9 +1888,11 @@ void mgmt_fe_adapter_reset_perf_stats(struct vty *vty)
        struct mgmt_fe_session_ctx *session;
 
        FOREACH_ADAPTER_IN_LIST (adapter) {
-               memset(&adapter->setcfg_stats, 0, sizeof(adapter->setcfg_stats));
+               memset(&adapter->setcfg_stats, 0,
+                      sizeof(adapter->setcfg_stats));
                FOREACH_SESSION_IN_LIST (adapter, session) {
-                       memset(&adapter->cmt_stats, 0, sizeof(adapter->cmt_stats));
+                       memset(&adapter->cmt_stats, 0,
+                              sizeof(adapter->cmt_stats));
                }
        }
 }
index 6097c23aac0cd5035bbaad6e9c0dc4ee4f4ff663..e737e00352f36475671c9315ff7fa9f8a9a075e9 100644 (file)
 #include "mgmtd/mgmt_fe_server.h"
 #include "mgmtd/mgmt_fe_adapter.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_FE_SRVR_DBG(fmt, ...)                                        \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_FE_SRVR_ERR(fmt, ...)                                        \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
 #define MGMTD_FE_SRVR_DBG(fmt, ...)                                            \
-       do {                                                                   \
-               if (mgmt_debug_fe)                                             \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);       \
-       } while (0)
+       DEBUGD(&mgmt_debug_fe, "%s:" fmt, __func__, ##__VA_ARGS__)
 #define MGMTD_FE_SRVR_ERR(fmt, ...)                                        \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 static int mgmt_fe_listen_fd = -1;
 static struct event_loop *mgmt_fe_listen_tm;
@@ -119,7 +109,7 @@ static void mgmt_fe_server_start(const char *hostname)
        return;
 
 mgmt_fe_server_start_failed:
-       if (sock)
+       if (sock > 0)
                close(sock);
 
        mgmt_fe_listen_fd = -1;
index 6f9f2dd63f4e1f6f3fb2702823fb4338eeeb9f0e..a49718a490bdb47e8313ef99b96ec4c627eb07e3 100644 (file)
@@ -18,8 +18,8 @@
 struct mgmt_cmt_info_t {
        struct mgmt_cmt_infos_item cmts;
 
-       char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN];
-       char time_str[MGMTD_COMMIT_TIME_STR_LEN];
+       char cmtid_str[MGMT_SHORT_TIME_MAX_LEN];
+       char time_str[MGMT_LONG_TIME_MAX_LEN];
        char cmt_json_file[PATH_MAX];
 };
 
@@ -33,7 +33,7 @@ DECLARE_DLIST(mgmt_cmt_infos, struct mgmt_cmt_info_t, cmts);
  * The only instance of VTY session that has triggered an ongoing
  * config rollback operation.
  */
-static struct vty *rollback_vty = NULL;
+static struct vty *rollback_vty;
 
 static bool mgmt_history_record_exists(char *file_path)
 {
@@ -54,36 +54,30 @@ static void mgmt_history_remove_file(char *name)
                zlog_err("Old commit info deletion failed");
 }
 
-static void mgmt_history_hash(const char *input_str, char *hash)
+static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void)
 {
-       int i;
-       unsigned char digest[MGMTD_MD5_HASH_LEN];
-       MD5_CTX ctx;
-
-       memset(&ctx, 0, sizeof(ctx));
-       MD5Init(&ctx);
-       MD5Update(&ctx, input_str, strlen(input_str));
-       MD5Final(digest, &ctx);
-
-       for (i = 0; i < MGMTD_MD5_HASH_LEN; i++)
-               snprintf(&hash[i * 2], MGMTD_MD5_HASH_STR_HEX_LEN, "%02x",
-                        (unsigned int)digest[i]);
+       struct mgmt_cmt_info_t *new;
+       struct timespec tv;
+       struct tm tm;
+
+       new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
+
+       clock_gettime(CLOCK_REALTIME, &tv);
+       localtime_r(&tv.tv_sec, &tm);
+
+       mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str));
+       mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str));
+       snprintf(new->cmt_json_file, sizeof(new->cmt_json_file),
+                MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
+
+       return new;
 }
 
 static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void)
 {
-       struct mgmt_cmt_info_t *new;
+       struct mgmt_cmt_info_t *new = mgmt_history_new_cmt_info();
        struct mgmt_cmt_info_t *cmt_info;
        struct mgmt_cmt_info_t *last_cmt_info = NULL;
-       struct timeval cmt_recd_tv;
-
-       new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
-       gettimeofday(&cmt_recd_tv, NULL);
-       mgmt_realtime_to_string(&cmt_recd_tv, new->time_str,
-                               sizeof(new->time_str));
-       mgmt_history_hash(new->time_str, new->cmtid_str);
-       snprintf(new->cmt_json_file, sizeof(new->cmt_json_file),
-                MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
 
        if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) {
                FOREACH_CMT_REC (mm, cmt_info)
@@ -100,13 +94,13 @@ static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void)
        return new;
 }
 
-static struct mgmt_cmt_info_t *mgmt_history_find_cmt_record(const char *cmtid_str)
+static struct mgmt_cmt_info_t *
+mgmt_history_find_cmt_record(const char *cmtid_str)
 {
        struct mgmt_cmt_info_t *cmt_info;
 
        FOREACH_CMT_REC (mm, cmt_info) {
-               if (strncmp(cmt_info->cmtid_str, cmtid_str,
-                           MGMTD_MD5_HASH_STR_HEX_LEN) == 0)
+               if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0)
                        return cmt_info;
        }
 
@@ -129,7 +123,8 @@ static bool mgmt_history_read_cmt_record_index(void)
 
        while ((fread(&cmt_info, sizeof(cmt_info), 1, fp)) > 0) {
                if (cnt < MGMTD_MAX_COMMIT_LIST) {
-                       if (!mgmt_history_record_exists(cmt_info.cmt_json_file)) {
+                       if (!mgmt_history_record_exists(
+                                   cmt_info.cmt_json_file)) {
                                zlog_err(
                                        "Commit record present in index_file, but commit file %s missing",
                                        cmt_info.cmt_json_file);
@@ -280,9 +275,9 @@ int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str)
        }
 
        FOREACH_CMT_REC (mm, cmt_info) {
-               if (strncmp(cmt_info->cmtid_str, cmtid_str,
-                           MGMTD_MD5_HASH_STR_HEX_LEN) == 0) {
-                       ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false);
+               if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) {
+                       ret = mgmt_history_rollback_to_cmt(vty, cmt_info,
+                                                          false);
                        return ret;
                }
 
@@ -321,7 +316,8 @@ int mgmt_history_rollback_n(struct vty *vty, int num_cmts)
 
        FOREACH_CMT_REC (mm, cmt_info) {
                if (cnt == num_cmts) {
-                       ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false);
+                       ret = mgmt_history_rollback_to_cmt(vty, cmt_info,
+                                                          false);
                        return ret;
                }
 
@@ -345,9 +341,9 @@ void show_mgmt_cmt_history(struct vty *vty)
        int slno = 0;
 
        vty_out(vty, "Last 10 commit history:\n");
-       vty_out(vty, "  Sl.No\tCommit-ID(HEX)\t\t\t  Commit-Record-Time\n");
+       vty_out(vty, "Slot Commit-ID               Commit-Record-Time\n");
        FOREACH_CMT_REC (mm, cmt_info) {
-               vty_out(vty, "  %d\t%s  %s\n", slno, cmt_info->cmtid_str,
+               vty_out(vty, "%4d %23s %s\n", slno, cmt_info->cmtid_str,
                        cmt_info->time_str);
                slno++;
        }
@@ -356,6 +352,7 @@ void show_mgmt_cmt_history(struct vty *vty)
 void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx)
 {
        struct mgmt_cmt_info_t *cmt_info = mgmt_history_create_cmt_rec();
+
        mgmt_ds_dump_ds_to_file(cmt_info->cmt_json_file, ds_ctx);
        mgmt_history_dump_cmt_record_index();
 }
index 29a1d7738ea9a6357b2166123a82d1c398618338..d3f7958952a527f31d04924f3c48e430e2ba3115 100644 (file)
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
 * Copyright (C) 2021  Vmware, Inc.
 *                   Pushpasis Sarkar <spushpasis@vmware.com>
 * Copyright (c) 2023, LabN Consulting, L.L.C.
 *
 */
+ * Copyright (C) 2021  Vmware, Inc.
                   Pushpasis Sarkar <spushpasis@vmware.com>
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
 #ifndef _FRR_MGMTD_HISTORY_H_
 #define _FRR_MGMTD_HISTORY_H_
 
@@ -54,4 +54,42 @@ extern void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx);
 extern void mgmt_history_destroy(void);
 extern void mgmt_history_init(void);
 
+/*
+ * 012345678901234567890123456789
+ * 2023-12-31T12:12:12,012345678
+ * 20231231121212012345678
+ */
+#define MGMT_LONG_TIME_FMT "%Y-%m-%dT%H:%M:%S"
+#define MGMT_LONG_TIME_MAX_LEN 30
+#define MGMT_SHORT_TIME_FMT "%Y%m%d%H%M%S"
+#define MGMT_SHORT_TIME_MAX_LEN 24
+
+static inline const char *
+mgmt_time_to_string(struct timespec *tv, bool long_fmt, char *buffer, size_t sz)
+{
+       struct tm tm;
+       size_t n;
+
+       localtime_r(&tv->tv_sec, &tm);
+
+       if (long_fmt) {
+               n = strftime(buffer, sz, MGMT_LONG_TIME_FMT, &tm);
+               snprintf(&buffer[n], sz - n, ",%09lu", tv->tv_nsec);
+       } else {
+               n = strftime(buffer, sz, MGMT_SHORT_TIME_FMT, &tm);
+               snprintf(&buffer[n], sz - n, "%09lu", tv->tv_nsec);
+       }
+
+       return buffer;
+}
+
+static inline const char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
+                                                 size_t sz)
+{
+       struct timespec ts = {.tv_sec = tv->tv_sec,
+                             .tv_nsec = tv->tv_usec * 1000};
+
+       return mgmt_time_to_string(&ts, true, buf, sz);
+}
+
 #endif /* _FRR_MGMTD_HISTORY_H_ */
index 7d176059f57646550fd5c80a1287177861a14521..08c999260d462760afd3094887f2f1a513c52fce 100644 (file)
 #include "routing_nb.h"
 
 
+char const *const mgmt_daemons[] = {
+#ifdef HAVE_STATICD
+       "staticd",
+#endif
+};
+uint mgmt_daemons_count = array_size(mgmt_daemons);
+
 /* mgmt options, we use GNU getopt library. */
 static const struct option longopts[] = {
        {"skip_runas", no_argument, NULL, 'S'},
index 5fa8aabfd615f4f159859ec65065f4d4f93670bf..ad4a4e31f414c2b6767c144387f5c9e2aa01dc47 100644 (file)
 #include "mgmtd/mgmt_memory.h"
 #include "mgmtd/mgmt_txn.h"
 
-#ifdef REDIRECT_DEBUG_TO_STDERR
-#define MGMTD_TXN_DBG(fmt, ...)                                               \
-       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
-#define MGMTD_TXN_ERR(fmt, ...)                                               \
-       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
-#else /* REDIRECT_DEBUG_TO_STDERR */
-#define MGMTD_TXN_DBG(fmt, ...)                                               \
-       do {                                                                   \
-               if (mgmt_debug_txn)                                           \
-                       zlog_err("%s: " fmt, __func__, ##__VA_ARGS__);         \
-       } while (0)
-#define MGMTD_TXN_ERR(fmt, ...)                                               \
+#define MGMTD_TXN_DBG(fmt, ...)                                                \
+       DEBUGD(&mgmt_debug_txn, "%s:" fmt, __func__, ##__VA_ARGS__)
+#define MGMTD_TXN_ERR(fmt, ...)                                                \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
-#endif /* REDIRECT_DEBUG_TO_STDERR */
 
 #define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__)
 #define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__)
@@ -1258,6 +1248,7 @@ static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn)
        char err_buf[1024] = {0};
        nb_ctx.client = NB_CLIENT_MGMTD_SERVER;
        nb_ctx.user = (void *)txn;
+
        ret = nb_candidate_validate_yang(nb_config, false, err_buf,
                                         sizeof(err_buf) - 1);
        if (ret != NB_OK) {
@@ -1787,27 +1778,10 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn,
                               struct mgmt_txn_req *txn_req,
                               struct mgmt_ds_ctx *ds_ctx)
 {
-       struct mgmt_txn_reqs_head *req_list = NULL;
-       struct mgmt_txn_reqs_head *pending_list = NULL;
        int indx;
        struct mgmt_get_data_req *get_data;
        struct mgmt_get_data_reply *get_reply;
 
-       switch (txn_req->req_event) {
-       case MGMTD_TXN_PROC_GETCFG:
-               req_list = &txn->get_cfg_reqs;
-               break;
-       case MGMTD_TXN_PROC_GETDATA:
-               req_list = &txn->get_data_reqs;
-               break;
-       case MGMTD_TXN_PROC_SETCFG:
-       case MGMTD_TXN_PROC_COMMITCFG:
-       case MGMTD_TXN_COMMITCFG_TIMEOUT:
-       case MGMTD_TXN_CLEANUP:
-               assert(!"Wrong txn request type!");
-               break;
-       }
-
        get_data = txn_req->req.get_data;
 
        if (!get_data->reply) {
@@ -1852,24 +1826,11 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn,
 
 mgmt_txn_get_config_failed:
 
-       if (pending_list) {
-               /*
-                * Move the transaction to corresponding pending list.
-                */
-               if (req_list)
-                       mgmt_txn_reqs_del(req_list, txn_req);
-               txn_req->pending_be_proc = true;
-               mgmt_txn_reqs_add_tail(pending_list, txn_req);
-               MGMTD_TXN_DBG(
-                       "Moved Req: %p for Txn: %p from Req-List to Pending-List",
-                       txn_req, txn_req->txn);
-       } else {
-               /*
-                * Delete the txn request. It will also remove it from request
-                * list.
-                */
-               mgmt_txn_req_free(&txn_req);
-       }
+       /*
+        * Delete the txn request. It will also remove it from request
+        * list.
+        */
+       mgmt_txn_req_free(&txn_req);
 
        return 0;
 }
index 79fa54a791d2e6f07d4c68b5eb258843f5161914..7d133d522f87c6d68e663045b96e7e48712c8e91 100644 (file)
@@ -10,6 +10,8 @@
 
 #include "command.h"
 #include "json.h"
+#include "northbound_cli.h"
+
 #include "mgmtd/mgmt.h"
 #include "mgmtd/mgmt_be_server.h"
 #include "mgmtd/mgmt_be_adapter.h"
@@ -378,7 +380,7 @@ DEFPY(mgmt_rollback,
        return CMD_SUCCESS;
 }
 
-static int config_write_mgmt_debug(struct vty *vty);
+int config_write_mgmt_debug(struct vty *vty);
 static struct cmd_node debug_node = {
        .name = "debug",
        .node = DEBUG_NODE,
@@ -386,24 +388,25 @@ static struct cmd_node debug_node = {
        .config_write = config_write_mgmt_debug,
 };
 
-static int config_write_mgmt_debug(struct vty *vty)
+static int write_mgmt_debug_helper(struct vty *vty, bool config)
 {
-       int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn;
-       if (!n)
-               return 0;
-       if (n == 4) {
-               vty_out(vty, "debug mgmt all\n");
+       uint32_t mode = config ? DEBUG_MODE_CONF : DEBUG_MODE_ALL;
+       bool be = DEBUG_MODE_CHECK(&mgmt_debug_be, mode);
+       bool ds = DEBUG_MODE_CHECK(&mgmt_debug_ds, mode);
+       bool fe = DEBUG_MODE_CHECK(&mgmt_debug_fe, mode);
+       bool txn = DEBUG_MODE_CHECK(&mgmt_debug_txn, mode);
+
+       if (!(be || ds || fe || txn))
                return 0;
-       }
 
        vty_out(vty, "debug mgmt");
-       if (mgmt_debug_be)
+       if (be)
                vty_out(vty, " backend");
-       if (mgmt_debug_ds)
+       if (ds)
                vty_out(vty, " datastore");
-       if (mgmt_debug_fe)
+       if (fe)
                vty_out(vty, " frontend");
-       if (mgmt_debug_txn)
+       if (txn)
                vty_out(vty, " transaction");
 
        vty_out(vty, "\n");
@@ -411,34 +414,72 @@ static int config_write_mgmt_debug(struct vty *vty)
        return 0;
 }
 
-DEFPY(debug_mgmt,
-      debug_mgmt_cmd,
-      "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>",
-      NO_STR
-      DEBUG_STR
-      MGMTD_STR
-      "All debug\n"
-      "Back-end debug\n"
+int config_write_mgmt_debug(struct vty *vty)
+{
+       return write_mgmt_debug_helper(vty, true);
+}
+
+DEFPY_NOSH(show_debugging_mgmt, show_debugging_mgmt_cmd,
+          "show debugging [mgmt]", SHOW_STR DEBUG_STR "MGMT Information\n")
+{
+       vty_out(vty, "MGMT debugging status:\n");
+
+       write_mgmt_debug_helper(vty, false);
+
+       cmd_show_lib_debugs(vty);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(debug_mgmt, debug_mgmt_cmd,
+      "[no$no] debug mgmt {backend$be|datastore$ds|frontend$fe|transaction$txn}",
+      NO_STR DEBUG_STR MGMTD_STR
+      "Backend debug\n"
       "Datastore debug\n"
-      "Front-end debug\n"
+      "Frontend debug\n"
       "Transaction debug\n")
 {
-       bool set = !no;
-       if (all)
-               be = fe = ds = txn = set ? all : NULL;
+       uint32_t mode = DEBUG_NODE2MODE(vty->node);
 
        if (be)
-               mgmt_debug_be = set;
+               DEBUG_MODE_SET(&mgmt_debug_be, mode, !no);
        if (ds)
-               mgmt_debug_ds = set;
+               DEBUG_MODE_SET(&mgmt_debug_ds, mode, !no);
        if (fe)
-               mgmt_debug_fe = set;
+               DEBUG_MODE_SET(&mgmt_debug_fe, mode, !no);
        if (txn)
-               mgmt_debug_txn = set;
+               DEBUG_MODE_SET(&mgmt_debug_txn, mode, !no);
 
        return CMD_SUCCESS;
 }
 
+/*
+ * Analog of `frr_config_read_in()`, instead of our config file though we loop
+ * over all daemons that have transitioned to mgmtd, loading their configs
+ */
+static int mgmt_config_pre_hook(struct event_loop *loop)
+{
+       FILE *confp;
+       char *p;
+
+       for (uint i = 0; i < mgmt_daemons_count; i++) {
+               p = asprintfrr(MTYPE_TMP, "%s/%s.conf", frr_sysconfdir,
+                              mgmt_daemons[i]);
+               confp = fopen(p, "r");
+               if (confp == NULL) {
+                       if (errno != ENOENT)
+                               zlog_err("%s: couldn't read config file %s: %s",
+                                        __func__, p, safe_strerror(errno));
+               } else {
+                       zlog_info("mgmtd: reading daemon config from %s", p);
+                       vty_read_file(vty_shared_candidate_config, confp);
+                       fclose(confp);
+               }
+               XFREE(MTYPE_TMP, p);
+       }
+       return 0;
+}
+
 void mgmt_vty_init(void)
 {
        /*
@@ -452,6 +493,8 @@ void mgmt_vty_init(void)
        static_vty_init();
 #endif
 
+       hook_register(frr_config_pre, mgmt_config_pre_hook);
+
        install_node(&debug_node);
 
        install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd);
@@ -479,6 +522,9 @@ void mgmt_vty_init(void)
        install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd);
        install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd);
 
+       install_element(ENABLE_NODE, &show_debugging_mgmt_cmd);
+
+       mgmt_fe_client_lib_vty_init();
        /*
         * TODO: Register and handlers for auto-completion here.
         */
diff --git a/mgmtd/mgmt_vty.c.safe b/mgmtd/mgmt_vty.c.safe
deleted file mode 100644 (file)
index c43485c..0000000
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * MGMTD VTY Interface
- * Copyright (C) 2021  Vmware, Inc.
- *                    Pushpasis Sarkar <spushpasis@vmware.com>
- *
- * 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 "command.h"
-#include "json.h"
-#include "mgmtd/mgmt.h"
-#include "mgmtd/mgmt_be_server.h"
-#include "mgmtd/mgmt_be_adapter.h"
-#include "mgmtd/mgmt_fe_server.h"
-#include "mgmtd/mgmt_fe_adapter.h"
-#include "mgmtd/mgmt_ds.h"
-#include "mgmtd/mgmt_history.h"
-
-#include "mgmtd/mgmt_vty_clippy.c"
-
-DEFPY(show_mgmt_be_adapter,
-      show_mgmt_be_adapter_cmd,
-      "show mgmt backend-adapter all",
-      SHOW_STR
-      MGMTD_STR
-      MGMTD_BE_ADAPTER_STR
-      "Display all Backend Adapters\n")
-{
-       mgmt_be_adapter_status_write(vty);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_be_xpath_reg,
-      show_mgmt_be_xpath_reg_cmd,
-      "show mgmt backend-yang-xpath-registry",
-      SHOW_STR
-      MGMTD_STR
-      "Backend Adapter YANG Xpath Registry\n")
-{
-       mgmt_be_xpath_register_write(vty);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_fe_adapter,
-      show_mgmt_fe_adapter_cmd,
-      "show mgmt frontend-adapter all",
-      SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR "Display all Frontend Adapters\n")
-{
-       mgmt_fe_adapter_status_write(vty, false);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_fe_adapter_detail, show_mgmt_fe_adapter_detail_cmd,
-      "show mgmt frontend-adapter all detail",
-      SHOW_STR MGMTD_STR MGMTD_FE_ADAPTER_STR
-      "Display all Frontend Adapters\n"
-      "Details of commit stats\n")
-{
-       mgmt_fe_adapter_status_write(vty, true);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY_HIDDEN(mgmt_performance_measurement,
-            mgmt_performance_measurement_cmd,
-            "[no] mgmt performance-measurement",
-            NO_STR
-            MGMTD_STR
-            "Enable performance measurement\n")
-{
-       if (no)
-               mgmt_fe_adapter_perf_measurement(vty, false);
-       else
-               mgmt_fe_adapter_perf_measurement(vty, true);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_reset_performance_stats,
-      mgmt_reset_performance_stats_cmd,
-      "mgmt reset-statistics",
-      MGMTD_STR
-      "Reset the Performance measurement statistics\n")
-{
-       mgmt_fe_adapter_reset_perf_stats(vty);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_txn,
-      show_mgmt_txn_cmd,
-      "show mgmt transaction all",
-      SHOW_STR
-      MGMTD_STR
-      MGMTD_TXN_STR
-      "Display all Transactions\n")
-{
-       mgmt_txn_status_write(vty);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_ds,
-      show_mgmt_ds_cmd,
-      "show mgmt datastore [all|candidate|operational|running]$dsname",
-      SHOW_STR
-      MGMTD_STR
-      MGMTD_DS_STR
-      "All datastores (default)\n"
-      "Candidate datastore\n"
-      "Operational datastore\n"
-      "Running datastore\n")
-{
-       struct mgmt_ds_ctx *ds_ctx;
-
-       if (!dsname || dsname[0] == 'a') {
-               mgmt_ds_status_write(vty);
-               return CMD_SUCCESS;
-       }
-       ds_ctx = mgmt_ds_get_ctx_by_id(mm, mgmt_ds_name2id(dsname));
-       if (!ds_ctx) {
-               vty_out(vty, "ERROR: Could not access %s datastore!\n", dsname);
-               return CMD_ERR_NO_MATCH;
-       }
-       mgmt_ds_status_write_one(vty, ds_ctx);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_commit,
-      mgmt_commit_cmd,
-      "mgmt commit <check|apply|abort>$type",
-      MGMTD_STR
-      "Commit action\n"
-      "Validate the set of config commands\n"
-      "Validate and apply the set of config commands\n"
-      "Abort and drop the set of config commands recently added\n")
-{
-       bool validate_only = type[0] == 'c';
-       bool abort = type[1] == 'b';
-
-       if (vty_mgmt_send_commit_config(vty, validate_only, abort) != 0)
-               return CMD_WARNING_CONFIG_FAILED;
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd,
-      "mgmt set-config WORD$path VALUE",
-      MGMTD_STR
-      "Set configuration data\n"
-      "XPath expression specifying the YANG data path\n"
-      "Value of the data to set\n")
-{
-       strlcpy(vty->cfg_changes[0].xpath, path,
-               sizeof(vty->cfg_changes[0].xpath));
-       vty->cfg_changes[0].value = value;
-       vty->cfg_changes[0].operation = NB_OP_CREATE;
-       vty->num_cfg_changes = 1;
-
-       vty->no_implicit_commit = true;
-       vty_mgmt_send_config_data(vty);
-       vty->no_implicit_commit = false;
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd,
-      "mgmt delete-config WORD$path",
-      MGMTD_STR
-      "Delete configuration data\n"
-      "XPath expression specifying the YANG data path\n")
-{
-
-       strlcpy(vty->cfg_changes[0].xpath, path,
-               sizeof(vty->cfg_changes[0].xpath));
-       vty->cfg_changes[0].value = NULL;
-       vty->cfg_changes[0].operation = NB_OP_DESTROY;
-       vty->num_cfg_changes = 1;
-
-       vty->no_implicit_commit = true;
-       vty_mgmt_send_config_data(vty);
-       vty->no_implicit_commit = false;
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
-      "show mgmt get-config [candidate|operational|running]$dsname WORD$path",
-      SHOW_STR MGMTD_STR
-      "Get configuration data from a specific configuration datastore\n"
-      "Candidate datastore (default)\n"
-      "Operational datastore\n"
-      "Running datastore\n"
-      "XPath expression specifying the YANG data path\n")
-{
-       const char *xpath_list[VTY_MAXCFGCHANGES] = {0};
-       Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE;
-
-       if (dsname)
-               datastore = mgmt_ds_name2id(dsname);
-
-       xpath_list[0] = path;
-       vty_mgmt_send_get_config(vty, datastore, xpath_list, 1);
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd,
-      "show mgmt get-data [candidate|operational|running]$dsname WORD$path",
-      SHOW_STR MGMTD_STR
-      "Get data from a specific datastore\n"
-      "Candidate datastore\n"
-      "Operational datastore (default)\n"
-      "Running datastore\n"
-      "XPath expression specifying the YANG data path\n")
-{
-       const char *xpath_list[VTY_MAXCFGCHANGES] = {0};
-       Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL;
-
-       if (dsname)
-               datastore = mgmt_ds_name2id(dsname);
-
-       xpath_list[0] = path;
-       vty_mgmt_send_get_data(vty, datastore, xpath_list, 1);
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_dump_data,
-      show_mgmt_dump_data_cmd,
-      "show mgmt datastore-contents [candidate|operational|running]$dsname [xpath WORD$path] [file WORD$filepath] <json|xml>$fmt",
-      SHOW_STR
-      MGMTD_STR
-      "Get Datastore contents from a specific datastore\n"
-      "Candidate datastore (default)\n"
-      "Operational datastore\n"
-      "Running datastore\n"
-      "XPath expression specifying the YANG data path\n"
-      "XPath string\n"
-      "Dump the contents to a file\n"
-      "Full path of the file\n"
-      "json output\n"
-      "xml output\n")
-{
-       struct mgmt_ds_ctx *ds_ctx;
-       Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE;
-       LYD_FORMAT format = fmt[0] == 'j' ? LYD_JSON : LYD_XML;
-       FILE *f = NULL;
-
-       if (datastore)
-               datastore = mgmt_ds_name2id(dsname);
-
-       ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore);
-       if (!ds_ctx) {
-               vty_out(vty, "ERROR: Could not access datastore!\n");
-               return CMD_ERR_NO_MATCH;
-       }
-
-       if (filepath) {
-               f = fopen(filepath, "w");
-               if (!f) {
-                       vty_out(vty,
-                               "Could not open file pointed by filepath %s\n",
-                               filepath);
-                       return CMD_SUCCESS;
-               }
-       }
-
-       mgmt_ds_dump_tree(vty, ds_ctx, path, f, format);
-
-       if (f)
-               fclose(f);
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_map_xpath,
-      show_mgmt_map_xpath_cmd,
-      "show mgmt yang-xpath-subscription WORD$path",
-      SHOW_STR
-      MGMTD_STR
-      "Get YANG Backend Subscription\n"
-      "XPath expression specifying the YANG data path\n")
-{
-       mgmt_be_xpath_subscr_info_write(vty, path);
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_load_config,
-      mgmt_load_config_cmd,
-      "mgmt load-config WORD$filepath <merge|replace>$type",
-      MGMTD_STR
-      "Load configuration onto Candidate Datastore\n"
-      "Full path of the file\n"
-      "Merge configuration with contents of Candidate Datastore\n"
-      "Replace the existing contents of Candidate datastore\n")
-{
-       bool merge = type[0] == 'm' ? true : false;
-       struct mgmt_ds_ctx *ds_ctx;
-       int ret;
-
-       if (access(filepath, F_OK) == -1) {
-               vty_out(vty, "ERROR: File %s : %s\n", filepath,
-                       strerror(errno));
-               return CMD_ERR_NO_FILE;
-       }
-
-       ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE);
-       if (!ds_ctx) {
-               vty_out(vty, "ERROR: Could not access Candidate datastore!\n");
-               return CMD_ERR_NO_MATCH;
-       }
-
-       ret = mgmt_ds_load_config_from_file(ds_ctx, filepath, merge);
-       if (ret != 0)
-               vty_out(vty, "Error with parsing the file with error code %d\n",
-                       ret);
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_save_config,
-      mgmt_save_config_cmd,
-      "mgmt save-config <candidate|running>$dsname WORD$filepath",
-      MGMTD_STR
-      "Save configuration from datastore\n"
-      "Candidate datastore\n"
-      "Running datastore\n"
-      "Full path of the file\n")
-{
-       Mgmtd__DatastoreId datastore = mgmt_ds_name2id(dsname);
-       struct mgmt_ds_ctx *ds_ctx;
-       FILE *f;
-
-       ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore);
-       if (!ds_ctx) {
-               vty_out(vty, "ERROR: Could not access the '%s' datastore!\n",
-                       dsname);
-               return CMD_ERR_NO_MATCH;
-       }
-
-       if (!filepath) {
-               vty_out(vty, "ERROR: No file path mentioned!\n");
-               return CMD_ERR_NO_MATCH;
-       }
-
-       f = fopen(filepath, "w");
-       if (!f) {
-               vty_out(vty, "Could not open file pointed by filepath %s\n",
-                       filepath);
-               return CMD_SUCCESS;
-       }
-
-       mgmt_ds_dump_tree(vty, ds_ctx, "/", f, LYD_JSON);
-
-       fclose(f);
-
-       return CMD_SUCCESS;
-}
-
-DEFPY(show_mgmt_cmt_hist,
-      show_mgmt_cmt_hist_cmd,
-      "show mgmt commit-history",
-      SHOW_STR
-      MGMTD_STR
-      "Show commit history\n")
-{
-       show_mgmt_cmt_history(vty);
-       return CMD_SUCCESS;
-}
-
-DEFPY(mgmt_rollback,
-      mgmt_rollback_cmd,
-      "mgmt rollback <commit-id WORD$commit | last [(1-10)]$last>",
-      MGMTD_STR
-      "Rollback commits\n"
-      "Rollback to commit ID\n"
-      "Commit-ID\n"
-      "Rollbak n commits\n"
-      "Number of commits\n")
-{
-       if (commit)
-               mgmt_history_rollback_by_id(vty, commit);
-       else
-               mgmt_history_rollback_n(vty, last);
-
-       return CMD_SUCCESS;
-}
-
-static int config_write_mgmt_debug(struct vty *vty);
-static struct cmd_node debug_node = {
-       .name = "debug",
-       .node = DEBUG_NODE,
-       .prompt = "",
-       .config_write = config_write_mgmt_debug,
-};
-
-static int config_write_mgmt_debug(struct vty *vty)
-{
-       int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn;
-       if (!n)
-               return 0;
-       if (n == 4) {
-               vty_out(vty, "debug mgmt all\n");
-               return 0;
-       }
-
-       vty_out(vty, "debug mgmt");
-       if (mgmt_debug_be)
-               vty_out(vty, " backend");
-       if (mgmt_debug_ds)
-               vty_out(vty, " datastore");
-       if (mgmt_debug_fe)
-               vty_out(vty, " frontend");
-       if (mgmt_debug_txn)
-               vty_out(vty, " transaction");
-
-       vty_out(vty, "\n");
-
-       return 0;
-}
-
-DEFPY(debug_mgmt,
-      debug_mgmt_cmd,
-      "[no$no] debug mgmt <all$all|{backend$be|datastore$ds|frontend$fe|transaction$txn}>",
-      NO_STR
-      DEBUG_STR
-      MGMTD_STR
-      "All debug\n"
-      "Back-end debug\n"
-      "Datastore debug\n"
-      "Front-end debug\n"
-      "Transaction debug\n")
-{
-       bool set = !no;
-       if (all)
-               be = fe = ds = txn = set ? all : NULL;
-
-       if (be)
-               mgmt_debug_be = set;
-       if (ds)
-               mgmt_debug_ds = set;
-       if (fe)
-               mgmt_debug_fe = set;
-       if (txn)
-               mgmt_debug_txn = set;
-
-       return CMD_SUCCESS;
-}
-
-void mgmt_vty_init(void)
-{
-       /*
-        * Initialize command handling from VTYSH connection.
-        * Call command initialization routines defined by
-        * backend components that are moved to new MGMTD infra
-        * here one by one.
-        */
-#if HAVE_STATICD
-       extern void static_vty_init(void);
-       static_vty_init();
-#endif
-
-       install_node(&debug_node);
-
-       install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd);
-       install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd);
-       install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd);
-       install_element(VIEW_NODE, &show_mgmt_fe_adapter_detail_cmd);
-       install_element(VIEW_NODE, &show_mgmt_txn_cmd);
-       install_element(VIEW_NODE, &show_mgmt_ds_cmd);
-       install_element(VIEW_NODE, &show_mgmt_get_config_cmd);
-       install_element(VIEW_NODE, &show_mgmt_get_data_cmd);
-       install_element(VIEW_NODE, &show_mgmt_dump_data_cmd);
-       install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd);
-       install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd);
-
-       install_element(CONFIG_NODE, &mgmt_commit_cmd);
-       install_element(CONFIG_NODE, &mgmt_set_config_data_cmd);
-       install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd);
-       install_element(CONFIG_NODE, &mgmt_load_config_cmd);
-       install_element(CONFIG_NODE, &mgmt_save_config_cmd);
-       install_element(CONFIG_NODE, &mgmt_rollback_cmd);
-
-       install_element(VIEW_NODE, &debug_mgmt_cmd);
-       install_element(CONFIG_NODE, &debug_mgmt_cmd);
-
-       /* Enable view */
-       install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd);
-       install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd);
-
-       /*
-        * TODO: Register and handlers for auto-completion here.
-        */
-}
index e9428fa90a0e9fbb1aab5ce8236fa45b0cccdce9..f2c7022ad409298087020d3c2f8a30d503313116 100644 (file)
@@ -1,13 +1,5 @@
 #include "log.h"
 
-#if defined(__GNUC__) && (__GNUC__ >= 3)
-#define likely(_x) __builtin_expect(!!(_x), 1)
-#define unlikely(_x) __builtin_expect(!!(_x), 0)
-#else
-#define likely(_x) !!(_x)
-#define unlikely(_x) !!(_x)
-#endif
-
 #define NHRP_DEBUG_COMMON      (1 << 0)
 #define NHRP_DEBUG_KERNEL      (1 << 1)
 #define NHRP_DEBUG_IF          (1 << 2)
index b2af0da4291d9714a183bb4eb25137267751c216..acd3b7df97f68d0c77851e2bcd2f4e3c77d69aa2 100644 (file)
@@ -420,6 +420,7 @@ void nhrp_nhs_terminate(void)
                                       &nifp->afi[afi].nhslist_head, nhs)
                                nhrp_nhs_free(nifp, afi, nhs);
                }
+               nhrp_peer_interface_del(ifp);
        }
 }
 
index d313510cfa31d281f5cd460f5aa62c8dd6b1a39b..216d78c1cc4a3e60efc489261c7427edb4677406 100644 (file)
@@ -937,12 +937,22 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6,
                        (ospf6->ospf6_helper_cfg.strict_lsa_check)
                                ? "Enabled"
                                : "Disabled");
+
+#if CONFDATE > 20240401
+               CPP_NOTICE("Remove deprecated json key: restartSupoort")
+#endif
                json_object_string_add(
                        json, "restartSupoort",
                        (ospf6->ospf6_helper_cfg.only_planned_restart)
                                ? "Planned Restart only"
                                : "Planned and Unplanned Restarts");
 
+               json_object_string_add(
+                       json, "restartSupport",
+                       (ospf6->ospf6_helper_cfg.only_planned_restart)
+                               ? "Planned Restart only"
+                               : "Planned and Unplanned Restarts");
+
                json_object_int_add(
                        json, "supportedGracePeriod",
                        ospf6->ospf6_helper_cfg.supported_grace_time);
index 3cc0d5e96386ab61867c3a3c8f445c403a3a2c50..0990b14307f2254156c406ae36cf118d29d440f9 100644 (file)
@@ -310,7 +310,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v,
 static int ospf6_spf_install(struct ospf6_vertex *v,
                             struct ospf6_route_table *result_table)
 {
-       struct ospf6_route *route, *parent_route;
+       struct ospf6_route *route;
        struct ospf6_vertex *prev;
 
        if (IS_OSPF6_DEBUG_SPF(PROCESS))
@@ -330,7 +330,12 @@ static int ospf6_spf_install(struct ospf6_vertex *v,
                        zlog_debug(
                                "  another path found to route %pFX lsa %s, merge",
                                &route->prefix, v->lsa->name);
-               ospf6_spf_merge_nexthops_to_route(route, v);
+
+               /* merging the parent's nexthop information to the child's
+                * if the parent is not the root of the tree.
+                */
+               if (!ospf6_merge_parents_nh_to_child(v, route, result_table))
+                       ospf6_spf_merge_nexthops_to_route(route, v);
 
                prev = (struct ospf6_vertex *)route->route_option;
                assert(prev->hops <= v->hops);
@@ -396,13 +401,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v,
         * installed,
         * its parent's route's nexthops have already been installed.
         */
-       if (v->parent && v->parent->hops) {
-               parent_route =
-                       ospf6_route_lookup(&v->parent->vertex_id, result_table);
-               if (parent_route) {
-                       ospf6_route_merge_nexthops(route, parent_route);
-               }
-       }
+       ospf6_merge_parents_nh_to_child(v, route, result_table);
 
        if (v->parent)
                listnode_add_sort(v->parent->child_list, v);
@@ -1275,3 +1274,20 @@ void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6)
        event_add_timer(master, ospf6_ase_calculate_timer, ospf6,
                        OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc);
 }
+
+bool ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v,
+                                    struct ospf6_route *route,
+                                    struct ospf6_route_table *result_table)
+{
+       struct ospf6_route *parent_route;
+
+       if (v->parent && v->parent->hops) {
+               parent_route =
+                       ospf6_route_lookup(&v->parent->vertex_id, result_table);
+               if (parent_route) {
+                       ospf6_route_merge_nexthops(route, parent_route);
+                       return true;
+               }
+       }
+       return false;
+}
index 55ca3ec4fe9ea5a37b647be05a2fb02b418b6ce2..c2fd5d9ef1003b09080560a0267c45ab3bd7afe1 100644 (file)
@@ -152,4 +152,8 @@ extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area);
 extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6);
 extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
                                     struct ospf6_area *area);
+extern bool
+ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v,
+                               struct ospf6_route *route,
+                               struct ospf6_route_table *result_table);
 #endif /* OSPF6_SPF_H */
index e14586c5ee031048f7f989240a6942b97cd4c4a6..ded520889f4f9f03419e5acae54674d9560f2ff1 100644 (file)
@@ -54,6 +54,7 @@ static void ospf_area_range_free(struct ospf_area_range *range)
 }
 
 static void ospf_area_range_add(struct ospf_area *area,
+                               struct route_table *ranges,
                                struct ospf_area_range *range)
 {
        struct route_node *rn;
@@ -64,7 +65,7 @@ static void ospf_area_range_add(struct ospf_area *area,
        p.prefix = range->addr;
        apply_mask_ipv4(&p);
 
-       rn = route_node_get(area->ranges, (struct prefix *)&p);
+       rn = route_node_get(ranges, (struct prefix *)&p);
        if (rn->info)
                route_unlock_node(rn);
        else
@@ -75,10 +76,12 @@ static void ospf_area_range_delete(struct ospf_area *area,
                                   struct route_node *rn)
 {
        struct ospf_area_range *range = rn->info;
+       bool nssa = CHECK_FLAG(range->flags, OSPF_AREA_RANGE_NSSA);
 
-       if (range->specifics != 0)
+       if (ospf_area_range_active(range) &&
+           CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE))
                ospf_delete_discard_route(area->ospf, area->ospf->new_table,
-                                         (struct prefix_ipv4 *)&rn->p);
+                                         (struct prefix_ipv4 *)&rn->p, nssa);
 
        ospf_area_range_free(range);
        rn->info = NULL;
@@ -87,11 +90,12 @@ static void ospf_area_range_delete(struct ospf_area *area,
 }
 
 struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *area,
+                                              struct route_table *ranges,
                                               struct prefix_ipv4 *p)
 {
        struct route_node *rn;
 
-       rn = route_node_lookup(area->ranges, (struct prefix *)p);
+       rn = route_node_lookup(ranges, (struct prefix *)p);
        if (rn) {
                route_unlock_node(rn);
                return rn->info;
@@ -133,11 +137,12 @@ struct ospf_area_range *ospf_area_range_lookup_next(struct ospf_area *area,
 }
 
 static struct ospf_area_range *ospf_area_range_match(struct ospf_area *area,
+                                                    struct route_table *ranges,
                                                     struct prefix_ipv4 *p)
 {
        struct route_node *node;
 
-       node = route_node_match(area->ranges, (struct prefix *)p);
+       node = route_node_match(ranges, (struct prefix *)p);
        if (node) {
                route_unlock_node(node);
                return node->info;
@@ -153,7 +158,7 @@ struct ospf_area_range *ospf_area_range_match_any(struct ospf *ospf,
        struct listnode *node;
 
        for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
-               if ((range = ospf_area_range_match(area, p)))
+               if ((range = ospf_area_range_match(area, area->ranges, p)))
                        return range;
 
        return NULL;
@@ -169,17 +174,13 @@ static int ospf_area_actively_attached(struct ospf_area *area)
        return area->act_ints;
 }
 
-int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id,
-                       struct prefix_ipv4 *p, int advertise)
+int ospf_area_range_set(struct ospf *ospf, struct ospf_area *area,
+                       struct route_table *ranges, struct prefix_ipv4 *p,
+                       int advertise, bool nssa)
 {
-       struct ospf_area *area;
        struct ospf_area_range *range;
 
-       area = ospf_area_get(ospf, area_id);
-       if (area == NULL)
-               return 0;
-
-       range = ospf_area_range_lookup(area, p);
+       range = ospf_area_range_lookup(area, ranges, p);
        if (range != NULL) {
                if (!CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE))
                        range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC;
@@ -190,7 +191,7 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id,
                        ospf_schedule_abr_task(ospf);
        } else {
                range = ospf_area_range_new(p);
-               ospf_area_range_add(area, range);
+               ospf_area_range_add(area, ranges, range);
                ospf_schedule_abr_task(ospf);
        }
 
@@ -201,20 +202,19 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id,
                range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC;
        }
 
+       if (nssa)
+               SET_FLAG(range->flags, OSPF_AREA_RANGE_NSSA);
+
        return 1;
 }
 
-int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id,
-                            struct prefix_ipv4 *p, uint32_t cost)
+int ospf_area_range_cost_set(struct ospf *ospf, struct ospf_area *area,
+                            struct route_table *ranges, struct prefix_ipv4 *p,
+                            uint32_t cost)
 {
-       struct ospf_area *area;
        struct ospf_area_range *range;
 
-       area = ospf_area_get(ospf, area_id);
-       if (area == NULL)
-               return 0;
-
-       range = ospf_area_range_lookup(area, p);
+       range = ospf_area_range_lookup(area, ranges, p);
        if (range == NULL)
                return 0;
 
@@ -227,17 +227,12 @@ int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id,
        return 1;
 }
 
-int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id,
-                         struct prefix_ipv4 *p)
+int ospf_area_range_unset(struct ospf *ospf, struct ospf_area *area,
+                         struct route_table *ranges, struct prefix_ipv4 *p)
 {
-       struct ospf_area *area;
        struct route_node *rn;
 
-       area = ospf_area_lookup_by_area_id(ospf, area_id);
-       if (area == NULL)
-               return 0;
-
-       rn = route_node_lookup(area->ranges, (struct prefix *)p);
+       rn = route_node_lookup(ranges, (struct prefix *)p);
        if (rn == NULL)
                return 0;
 
@@ -249,14 +244,12 @@ int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id,
        return 1;
 }
 
-int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id,
+int ospf_area_range_substitute_set(struct ospf *ospf, struct ospf_area *area,
                                   struct prefix_ipv4 *p, struct prefix_ipv4 *s)
 {
-       struct ospf_area *area;
        struct ospf_area_range *range;
 
-       area = ospf_area_get(ospf, area_id);
-       range = ospf_area_range_lookup(area, p);
+       range = ospf_area_range_lookup(area, area->ranges, p);
 
        if (range != NULL) {
                if (!CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE)
@@ -264,7 +257,7 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id,
                        ospf_schedule_abr_task(ospf);
        } else {
                range = ospf_area_range_new(p);
-               ospf_area_range_add(area, range);
+               ospf_area_range_add(area, area->ranges, range);
                ospf_schedule_abr_task(ospf);
        }
 
@@ -276,17 +269,12 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id,
        return 1;
 }
 
-int ospf_area_range_substitute_unset(struct ospf *ospf, struct in_addr area_id,
+int ospf_area_range_substitute_unset(struct ospf *ospf, struct ospf_area *area,
                                     struct prefix_ipv4 *p)
 {
-       struct ospf_area *area;
        struct ospf_area_range *range;
 
-       area = ospf_area_lookup_by_area_id(ospf, area_id);
-       if (area == NULL)
-               return 0;
-
-       range = ospf_area_range_lookup(area, p);
+       range = ospf_area_range_lookup(area, area->ranges, p);
        if (range == NULL)
                return 0;
 
@@ -538,8 +526,7 @@ void ospf_check_abr_status(struct ospf *ospf)
 }
 
 static void ospf_abr_update_aggregate(struct ospf_area_range *range,
-                                     struct ospf_route * or,
-                                     struct ospf_area *area)
+                                     uint32_t cost, struct ospf_area *area)
 {
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: Start", __func__);
@@ -557,20 +544,18 @@ static void ospf_abr_update_aggregate(struct ospf_area_range *range,
 
                range->cost = range->cost_config;
        } else {
-               if (range->specifics == 0) {
+               if (!ospf_area_range_active(range)) {
                        if (IS_DEBUG_OSPF_EVENT)
-                               zlog_debug("%s: use or->cost %d", __func__,
-                                          or->cost);
+                               zlog_debug("%s: use cost %d", __func__, cost);
 
-                       range->cost = or->cost; /* 1st time get 1st cost */
+                       range->cost = cost; /* 1st time get 1st cost */
                }
 
-               if (or->cost > range->cost) {
+               if (cost > range->cost) {
                        if (IS_DEBUG_OSPF_EVENT)
-                               zlog_debug("%s: update to %d", __func__,
-                                               or->cost);
+                               zlog_debug("%s: update to %d", __func__, cost);
 
-                       range->cost = or->cost;
+                       range->cost = cost;
                }
        }
 
@@ -605,6 +590,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
        struct ospf_lsa *old = NULL, *new = NULL;
        struct as_external_lsa *ext7;
        struct prefix_ipv4 p;
+       struct ospf_area_range *range;
 
        if (!CHECK_FLAG(lsa->data->options, OSPF_OPTION_NP)) {
                if (IS_DEBUG_OSPF_NSSA)
@@ -646,6 +632,18 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
                return 1;
        }
 
+       range = ospf_area_range_match(area, area->nssa_ranges, &p);
+       if (range) {
+               if (IS_DEBUG_OSPF_NSSA)
+                       zlog_debug("Suppressed by range %pI4/%u of area %pI4",
+                                  &range->addr, range->masklen,
+                                  &area->area_id);
+
+               ospf_abr_update_aggregate(range, GET_METRIC(ext7->e[0].metric),
+                                         area);
+               return 1;
+       }
+
        if (old && CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) {
                if (IS_DEBUG_OSPF_NSSA)
                        zlog_debug(
@@ -675,17 +673,27 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
                }
        }
 
-       /* Area where Aggregate testing will be inserted, just like summary
-          advertisements */
-       /* ospf_abr_check_nssa_range (p_arg, lsa-> cost, lsa -> area); */
-
        return 0;
 }
 
-static void ospf_abr_translate_nssa_range(struct prefix_ipv4 *p, uint32_t cost)
+static void ospf_abr_translate_nssa_range(struct ospf *ospf,
+                                         struct prefix_ipv4 *p, uint32_t cost)
 {
-       /* The Type-7 is created from the aggregated prefix and forwarded
-          for lsa installation and flooding... to be added... */
+       struct external_info ei = {};
+       struct ospf_lsa *lsa;
+
+       prefix_copy(&ei.p, p);
+       ei.type = ZEBRA_ROUTE_OSPF;
+       ei.route_map_set.metric = cost;
+       ei.route_map_set.metric_type = -1;
+
+       lsa = ospf_external_info_find_lsa(ospf, p);
+       if (lsa)
+               lsa = ospf_external_lsa_refresh(ospf, lsa, &ei,
+                                               LSA_REFRESH_FORCE, true);
+       else
+               lsa = ospf_external_lsa_originate(ospf, &ei);
+       SET_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT);
 }
 
 void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost,
@@ -892,9 +900,11 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p,
                                zlog_debug(
                                        "%s: this is intra-area route to %pFX",
                                        __func__, p);
-                       if ((range = ospf_area_range_match(or_area, p))
-                           && !ospf_area_is_transit(area))
-                               ospf_abr_update_aggregate(range, or, area);
+                       if ((range = ospf_area_range_match(
+                                    or_area, or_area->ranges, p)) &&
+                           !ospf_area_is_transit(area))
+                               ospf_abr_update_aggregate(range, or->cost,
+                                                         area);
                        else
                                ospf_abr_announce_network_to_area(p, or->cost,
                                                                  area);
@@ -1345,7 +1355,7 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf)
                zlog_debug("%s: Stop", __func__);
 }
 
-static void ospf_abr_prepare_aggregates(struct ospf *ospf)
+static void ospf_abr_prepare_aggregates(struct ospf *ospf, bool nssa)
 {
        struct listnode *node;
        struct route_node *rn;
@@ -1356,7 +1366,14 @@ static void ospf_abr_prepare_aggregates(struct ospf *ospf)
                zlog_debug("%s: Start", __func__);
 
        for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
-               for (rn = route_top(area->ranges); rn; rn = route_next(rn))
+               struct route_table *ranges;
+
+               if (nssa)
+                       ranges = area->nssa_ranges;
+               else
+                       ranges = area->ranges;
+
+               for (rn = route_top(ranges); rn; rn = route_next(rn))
                        if ((range = rn->info) != NULL) {
                                range->cost = 0;
                                range->specifics = 0;
@@ -1409,7 +1426,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf)
                                        p.prefixlen = range->subst_masklen;
                                }
 
-                               if (range->specifics) {
+                               if (ospf_area_range_active(range)) {
                                        if (IS_DEBUG_OSPF_EVENT)
                                                zlog_debug("%s: active range",
                                                           __func__);
@@ -1452,13 +1469,11 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf)
                zlog_debug("%s: Stop", __func__);
 }
 
-static void
-ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
+static void ospf_abr_send_nssa_aggregates(struct ospf *ospf)
 {
-       struct listnode *node;  /*, n; */
-       struct ospf_area *area; /*, *ar; */
+       struct listnode *node;
+       struct ospf_area *area;
        struct route_node *rn;
-       struct ospf_area_range *range;
        struct prefix_ipv4 p;
 
        if (IS_DEBUG_OSPF_NSSA)
@@ -1472,20 +1487,13 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
                        zlog_debug("%s: looking at area %pI4", __func__,
                                   &area->area_id);
 
-               for (rn = route_top(area->ranges); rn; rn = route_next(rn)) {
-                       if (rn->info == NULL)
-                               continue;
+               for (rn = route_top(area->nssa_ranges); rn;
+                    rn = route_next(rn)) {
+                       struct ospf_area_range *range;
 
                        range = rn->info;
-
-                       if (!CHECK_FLAG(range->flags,
-                                       OSPF_AREA_RANGE_ADVERTISE)) {
-                               if (IS_DEBUG_OSPF_NSSA)
-                                       zlog_debug(
-                                               "%s: discarding suppress-ranges",
-                                               __func__);
+                       if (!range)
                                continue;
-                       }
 
                        p.family = AF_INET;
                        p.prefix = range->addr;
@@ -1495,14 +1503,9 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
                                zlog_debug("%s: this is range: %pFX", __func__,
                                           &p);
 
-                       if (CHECK_FLAG(range->flags,
-                                      OSPF_AREA_RANGE_SUBSTITUTE)) {
-                               p.family = AF_INET;
-                               p.prefix = range->subst_addr;
-                               p.prefixlen = range->subst_masklen;
-                       }
-
-                       if (range->specifics) {
+                       if (ospf_area_range_active(range)
+                           && CHECK_FLAG(range->flags,
+                                         OSPF_AREA_RANGE_ADVERTISE)) {
                                if (IS_DEBUG_OSPF_NSSA)
                                        zlog_debug("%s: active range",
                                                   __func__);
@@ -1512,7 +1515,8 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */
                                 *  translate, Install (as Type-5), Approve, and
                                 * Flood
                                 */
-                               ospf_abr_translate_nssa_range(&p, range->cost);
+                               ospf_abr_translate_nssa_range(ospf, &p,
+                                                             range->cost);
                        }
                } /* all area ranges*/
        }        /* all areas */
@@ -1807,6 +1811,82 @@ static void ospf_abr_announce_non_dna_routers(struct event *thread)
        OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__);
 }
 
+static void ospf_abr_nssa_type7_default_create(struct ospf *ospf,
+                                              struct ospf_area *area,
+                                              struct ospf_lsa *lsa)
+{
+       struct external_info ei;
+
+       if (IS_DEBUG_OSPF_NSSA)
+               zlog_debug(
+                       "Announcing Type-7 default route into NSSA area %pI4",
+                       &area->area_id);
+
+       /* Prepare the extrenal_info for aggregator */
+       memset(&ei, 0, sizeof(struct external_info));
+       ei.p.family = AF_INET;
+       ei.p.prefixlen = 0;
+       ei.tag = 0;
+       ei.type = 0;
+       ei.instance = ospf->instance;
+
+       /* Compute default route type and metric. */
+       if (area->nssa_default_originate.metric_value != -1)
+               ei.route_map_set.metric =
+                       area->nssa_default_originate.metric_value;
+       else
+               ei.route_map_set.metric = DEFAULT_DEFAULT_ALWAYS_METRIC;
+       if (area->nssa_default_originate.metric_type != -1)
+               ei.route_map_set.metric_type =
+                       area->nssa_default_originate.metric_type;
+       else
+               ei.route_map_set.metric_type = DEFAULT_METRIC_TYPE;
+
+       if (!lsa)
+               ospf_nssa_lsa_originate(area, &ei);
+       else
+               ospf_nssa_lsa_refresh(area, lsa, &ei);
+}
+
+static void ospf_abr_nssa_type7_default_delete(struct ospf *ospf,
+                                              struct ospf_area *area,
+                                              struct ospf_lsa *lsa)
+{
+       if (lsa && !CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) {
+               if (IS_DEBUG_OSPF_NSSA)
+                       zlog_debug(
+                               "Withdrawing Type-7 default route from area %pI4",
+                               &area->area_id);
+
+               ospf_ls_retransmit_delete_nbr_area(area, lsa);
+               ospf_refresher_unregister_lsa(ospf, lsa);
+               ospf_lsa_flush_area(lsa, area);
+       }
+}
+
+/* NSSA Type-7 default route. */
+void ospf_abr_nssa_type7_defaults(struct ospf *ospf)
+{
+       struct ospf_area *area;
+       struct listnode *node;
+
+       for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
+               struct in_addr id = {};
+               struct ospf_lsa *lsa;
+
+               lsa = ospf_lsdb_lookup_by_id(area->lsdb, OSPF_AS_NSSA_LSA, id,
+                                            area->ospf->router_id);
+               if (area->external_routing == OSPF_AREA_NSSA
+                   && area->nssa_default_originate.enabled
+                   && (IS_OSPF_ABR(ospf)
+                       || (IS_OSPF_ASBR(ospf)
+                           && ospf->nssa_default_import_check.status)))
+                       ospf_abr_nssa_type7_default_create(ospf, area, lsa);
+               else
+                       ospf_abr_nssa_type7_default_delete(ospf, area, lsa);
+       }
+}
+
 static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf,
                                                       struct ospf_lsa *lsa)
 {
@@ -1874,30 +1954,39 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf)
                zlog_debug("%s: Stop", __func__);
 }
 
-static void ospf_abr_manage_discard_routes(struct ospf *ospf)
+static void ospf_abr_manage_discard_routes(struct ospf *ospf, bool nssa)
 {
        struct listnode *node, *nnode;
        struct route_node *rn;
        struct ospf_area *area;
-       struct ospf_area_range *range;
 
-       for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area))
-               for (rn = route_top(area->ranges); rn; rn = route_next(rn))
-                       if ((range = rn->info) != NULL)
-                               if (CHECK_FLAG(range->flags,
-                                              OSPF_AREA_RANGE_ADVERTISE)) {
-                                       if (range->specifics)
-                                               ospf_add_discard_route(
-                                                       ospf, ospf->new_table,
-                                                       area,
-                                                       (struct prefix_ipv4
-                                                                *)&rn->p);
-                                       else
-                                               ospf_delete_discard_route(
-                                                       ospf, ospf->new_table,
-                                                       (struct prefix_ipv4
-                                                                *)&rn->p);
-                               }
+       for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
+               struct route_table *ranges;
+
+               if (nssa)
+                       ranges = area->nssa_ranges;
+               else
+                       ranges = area->ranges;
+
+               for (rn = route_top(ranges); rn; rn = route_next(rn)) {
+                       struct ospf_area_range *range;
+
+                       range = rn->info;
+                       if (!range)
+                               continue;
+
+                       if (ospf_area_range_active(range)
+                           && CHECK_FLAG(range->flags,
+                                         OSPF_AREA_RANGE_ADVERTISE))
+                               ospf_add_discard_route(
+                                       ospf, ospf->new_table, area,
+                                       (struct prefix_ipv4 *)&rn->p, nssa);
+                       else
+                               ospf_delete_discard_route(
+                                       ospf, ospf->new_table,
+                                       (struct prefix_ipv4 *)&rn->p, nssa);
+               }
+       }
 }
 
 /* This is the function taking care about ABR NSSA, i.e.  NSSA
@@ -1925,7 +2014,7 @@ static void ospf_abr_manage_discard_routes(struct ospf *ospf)
    For External Calculations, any NSSA areas use the Type-7 AREA-LSDB,
    any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */
 
-static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
+void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
 {
        if (ospf->gr_info.restart_in_progress)
                return;
@@ -1952,7 +2041,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
        /* RESET all Ranges in every Area, same as summaries */
        if (IS_DEBUG_OSPF_NSSA)
                zlog_debug("%s: NSSA initialize aggregates", __func__);
-       ospf_abr_prepare_aggregates(ospf); /*TURNED OFF just for now */
+       ospf_abr_prepare_aggregates(ospf, true);
 
        /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or
         *  Aggregate as Type-7
@@ -1983,7 +2072,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
                zlog_debug("%s: remove unapproved translates", __func__);
        ospf_abr_remove_unapproved_translates(ospf);
 
-       ospf_abr_manage_discard_routes(ospf); /* same as normal...discard */
+       ospf_abr_manage_discard_routes(ospf, true);
 
        if (IS_DEBUG_OSPF_NSSA)
                zlog_debug("%s: Stop", __func__);
@@ -2012,7 +2101,7 @@ void ospf_abr_task(struct ospf *ospf)
 
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: prepare aggregates", __func__);
-       ospf_abr_prepare_aggregates(ospf);
+       ospf_abr_prepare_aggregates(ospf, false);
 
        if (IS_OSPF_ABR(ospf)) {
                if (IS_DEBUG_OSPF_EVENT)
@@ -2031,6 +2120,11 @@ void ospf_abr_task(struct ospf *ospf)
                        zlog_debug("%s: announce stub defaults", __func__);
                ospf_abr_announce_stub_defaults(ospf);
 
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug("%s: announce NSSA Type-7 defaults",
+                                  __func__);
+               ospf_abr_nssa_type7_defaults(ospf);
+
                if (ospf->fr_configured) {
                        OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT,
                                       "%s(): announce non-DNArouters",
@@ -2050,7 +2144,7 @@ void ospf_abr_task(struct ospf *ospf)
                zlog_debug("%s: remove unapproved summaries", __func__);
        ospf_abr_remove_unapproved_summaries(ospf);
 
-       ospf_abr_manage_discard_routes(ospf);
+       ospf_abr_manage_discard_routes(ospf, false);
 
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: Stop", __func__);
index 19d444b125cf06aa2321b10faa10c4c2731f8675..cc2b2b05481f5333bacd78b40c4cdbcc86ec5e12 100644 (file)
@@ -16,6 +16,7 @@
 
 #define OSPF_AREA_RANGE_ADVERTISE      (1 << 0)
 #define OSPF_AREA_RANGE_SUBSTITUTE     (1 << 1)
+#define OSPF_AREA_RANGE_NSSA           (1 << 2)
 
 /* Area range. */
 struct ospf_area_range {
@@ -44,23 +45,23 @@ struct ospf_area_range {
 
 /* Prototypes. */
 extern struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *,
+                                                     struct route_table *,
                                                      struct prefix_ipv4 *);
-
-extern struct ospf_area_range *ospf_some_area_range_match(struct prefix_ipv4 *);
-
 extern struct ospf_area_range *
 ospf_area_range_lookup_next(struct ospf_area *, struct in_addr *, int);
 
-extern int ospf_area_range_set(struct ospf *, struct in_addr,
-                              struct prefix_ipv4 *, int);
-extern int ospf_area_range_cost_set(struct ospf *, struct in_addr,
-                                   struct prefix_ipv4 *, uint32_t);
-extern int ospf_area_range_unset(struct ospf *, struct in_addr,
-                                struct prefix_ipv4 *);
-extern int ospf_area_range_substitute_set(struct ospf *, struct in_addr,
+extern int ospf_area_range_set(struct ospf *, struct ospf_area *,
+                              struct route_table *, struct prefix_ipv4 *, int,
+                              bool);
+extern int ospf_area_range_cost_set(struct ospf *, struct ospf_area *,
+                                   struct route_table *, struct prefix_ipv4 *,
+                                   uint32_t);
+extern int ospf_area_range_unset(struct ospf *, struct ospf_area *,
+                                struct route_table *, struct prefix_ipv4 *);
+extern int ospf_area_range_substitute_set(struct ospf *, struct ospf_area *,
                                          struct prefix_ipv4 *,
                                          struct prefix_ipv4 *);
-extern int ospf_area_range_substitute_unset(struct ospf *, struct in_addr,
+extern int ospf_area_range_substitute_unset(struct ospf *, struct ospf_area *,
                                            struct prefix_ipv4 *);
 extern struct ospf_area_range *ospf_area_range_match_any(struct ospf *,
                                                         struct prefix_ipv4 *);
@@ -69,10 +70,12 @@ extern int ospf_act_bb_connection(struct ospf *);
 
 extern void ospf_check_abr_status(struct ospf *);
 extern void ospf_abr_task(struct ospf *);
+extern void ospf_abr_nssa_task(struct ospf *ospf);
 extern void ospf_schedule_abr_task(struct ospf *);
 
 extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t,
                                              struct ospf_area *);
+extern void ospf_abr_nssa_type7_defaults(struct ospf *ospf);
 extern void ospf_abr_nssa_check_status(struct ospf *ospf);
 extern void ospf_abr_generate_indication_lsa(struct ospf *ospf,
                                             const struct ospf_area *area);
index 7befcc1086fbc51cd50f82e2b4be60e67176e8df..1b68f6e02236e72eb83ee146fbb12ea93f917c87 100644 (file)
@@ -94,7 +94,7 @@ int ospf_route_map_set_compare(struct route_map_set_values *values1,
 struct external_info *
 ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance,
                       struct prefix_ipv4 p, ifindex_t ifindex,
-                      struct in_addr nexthop, route_tag_t tag)
+                      struct in_addr nexthop, route_tag_t tag, uint32_t metric)
 {
        struct external_info *new;
        struct route_node *rn;
@@ -131,6 +131,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance,
        new->tag = tag;
        new->orig_tag = tag;
        new->aggr_route = NULL;
+       new->metric = metric;
+       new->min_metric = 0;
+       new->max_metric = OSPF_LS_INFINITY;
 
        /* we don't unlock rn from the get() because we're attaching the info */
        if (rn)
@@ -138,9 +141,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance,
 
        if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
                zlog_debug(
-                       "Redistribute[%s][%u]: %pFX external info created, with NH %pI4",
+                       "Redistribute[%s][%u]: %pFX external info created, with NH %pI4, metric:%u",
                        ospf_redist_string(type), ospf->vrf_id, &p,
-                       &nexthop.s_addr);
+                       &nexthop.s_addr, metric);
        }
        return new;
 }
index 6b427c5fd20e84afa50b0cc9c0c49bebc05e6b38..dfb9d965c54b1ad3a95cd9da3150501d7c9123d1 100644 (file)
@@ -36,6 +36,10 @@ struct external_info {
        /* Actual tag received from zebra*/
        route_tag_t orig_tag;
 
+       uint32_t metric;
+       uint32_t min_metric;
+       uint32_t max_metric;
+
        struct route_map_set_values route_map_set;
 #define ROUTEMAP_METRIC(E) (E)->route_map_set.metric
 #define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type
@@ -99,11 +103,10 @@ extern struct external_info *ospf_external_info_new(struct ospf *, uint8_t,
 extern void ospf_reset_route_map_set_values(struct route_map_set_values *);
 extern int ospf_route_map_set_compare(struct route_map_set_values *,
                                      struct route_map_set_values *);
-extern struct external_info *ospf_external_info_add(struct ospf *, uint8_t,
-                                                   unsigned short,
-                                                   struct prefix_ipv4,
-                                                   ifindex_t, struct in_addr,
-                                                   route_tag_t);
+extern struct external_info *
+ospf_external_info_add(struct ospf *, uint8_t, unsigned short,
+                      struct prefix_ipv4, ifindex_t, struct in_addr,
+                      route_tag_t, uint32_t metric);
 extern void ospf_external_info_delete(struct ospf *, uint8_t, unsigned short,
                                      struct prefix_ipv4);
 extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t,
index 80390af505cdc4f9dc2df3eb5af587091bebca03..cc2110d43331d208a090a2d3a829af817b6fe47a 100644 (file)
@@ -159,7 +159,9 @@ ospf_ase_calculate_new_route(struct ospf_lsa *lsa,
 
        if (!IS_EXTERNAL_METRIC(al->e[0].tos)) {
                if (IS_DEBUG_OSPF(lsa, LSA))
-                       zlog_debug("Route[External]: type-1 created.");
+                       zlog_debug(
+                               "Route[External]: type-1 created, asbr cost:%d  metric:%d.",
+                               asbr_route->cost, metric);
                new->path_type = OSPF_PATH_TYPE1_EXTERNAL;
                new->cost = asbr_route->cost + metric; /* X + Y */
        } else {
index 649ba70e02445835dd08fae31fc2b8a208e9a556..5742ece1f7d0ede078a80665e130c37588c25d7f 100644 (file)
@@ -651,6 +651,8 @@ int ospf_if_new_hook(struct interface *ifp)
 
        ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info));
 
+       IF_OSPF_IF_INFO(ifp)->oii_fd = -1;
+
        IF_OIFS(ifp) = route_table_init();
        IF_OIFS_PARAMS(ifp) = route_table_init();
 
@@ -691,6 +693,8 @@ static int ospf_if_delete_hook(struct interface *ifp)
 {
        int rc = 0;
        struct route_node *rn;
+       struct ospf_if_info *oii;
+
        rc = ospf_opaque_del_if(ifp);
 
        /*
@@ -707,6 +711,13 @@ static int ospf_if_delete_hook(struct interface *ifp)
        route_table_finish(IF_OIFS(ifp));
        route_table_finish(IF_OIFS_PARAMS(ifp));
 
+       /* Close per-interface socket */
+       oii = ifp->info;
+       if (oii && oii->oii_fd > 0) {
+               close(oii->oii_fd);
+               oii->oii_fd = -1;
+       }
+
        XFREE(MTYPE_OSPF_IF_INFO, ifp->info);
 
        return rc;
@@ -1367,6 +1378,16 @@ static int ospf_ifp_up(struct interface *ifp)
        struct ospf_interface *oi;
        struct route_node *rn;
        struct ospf_if_info *oii = ifp->info;
+       struct ospf *ospf;
+
+       if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
+               zlog_debug("Zebra: Interface[%s] state change to up.",
+                          ifp->name);
+
+       /* Open per-intf write socket if configured */
+       ospf = ifp->vrf->info;
+       if (ospf && ospf->intf_socket_enabled)
+               ospf_ifp_sock_init(ifp);
 
        ospf_if_recalculate_output_cost(ifp);
 
@@ -1384,10 +1405,6 @@ static int ospf_ifp_up(struct interface *ifp)
                return 0;
        }
 
-       if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
-               zlog_debug("Zebra: Interface[%s] state change to up.",
-                          ifp->name);
-
        for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
                if ((oi = rn->info) == NULL)
                        continue;
@@ -1416,6 +1433,9 @@ static int ospf_ifp_down(struct interface *ifp)
                ospf_if_down(oi);
        }
 
+       /* Close per-interface write socket if configured */
+       ospf_ifp_sock_close(ifp);
+
        return 0;
 }
 
index 8625a72ac13209ea460539482a2704f701d72388..649df437a4f162d4c3d90bf7db9f1f15cd778355 100644 (file)
@@ -121,6 +121,9 @@ struct ospf_if_info {
                membership_counts[MEMBER_MAX]; /* multicast group refcnts */
 
        uint32_t curr_mtu;
+
+       /* Per-interface write socket, configured via 'ospf' object */
+       int oii_fd;
 };
 
 struct ospf_interface;
index 82f7b96fd508049cfdf8b67011ffa7bc0c60b716..452a3ba3747e597b504905ca2c610853dc5190c4 100644 (file)
@@ -1654,9 +1654,6 @@ struct in_addr ospf_get_nssa_ip(struct ospf_area *area)
        if (best_default.s_addr != INADDR_ANY)
                return best_default;
 
-       if (best_default.s_addr != INADDR_ANY)
-               return best_default;
-
        return fwd;
 }
 
@@ -1868,8 +1865,7 @@ static struct ospf_lsa *ospf_external_lsa_new(struct ospf *ospf,
 }
 
 /* As Type-7 */
-static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa,
-                                   struct external_info *ei)
+static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa)
 {
        struct ospf_lsa *new;
        struct as_external_lsa *extlsa;
@@ -1979,6 +1975,9 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf,
        ei.nexthop = ext->header.adv_router;
        ei.route_map_set.metric = -1;
        ei.route_map_set.metric_type = -1;
+       ei.metric = DEFAULT_DEFAULT_METRIC;
+       ei.max_metric = OSPF_LS_INFINITY;
+       ei.min_metric = 0;
        ei.tag = 0;
        ei.instance = 0;
 
@@ -2017,7 +2016,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
                                                struct ospf_lsa *type7,
                                                struct ospf_lsa *type5)
 {
-       struct ospf_lsa *new;
+       struct ospf_lsa *new, *translated_lsa;
        struct as_external_lsa *extnew;
 
        if (ospf->gr_info.restart_in_progress) {
@@ -2031,7 +2030,8 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
         * the OSPF_LSA_LOCAL_XLT flag, must originate by hand
         */
 
-       if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) {
+       if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) ==
+           NULL) {
                if (IS_DEBUG_OSPF_NSSA)
                        zlog_debug(
                                "%s: Could not translate Type-7, Id %pI4, to Type-5",
@@ -2039,16 +2039,17 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
                return NULL;
        }
 
-       extnew = (struct as_external_lsa *)new->data;
+       extnew = (struct as_external_lsa *)translated_lsa->data;
 
        /* Update LSA sequence number from translated Type-5 LSA */
        if (type5)
-               new->data->ls_seqnum = lsa_seqnum_increment(type5);
+               translated_lsa->data->ls_seqnum = lsa_seqnum_increment(type5);
 
-       if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) {
+       if ((new = ospf_lsa_install(ospf, NULL, translated_lsa)) == NULL) {
                flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
                          "%s: Could not install LSA id %pI4", __func__,
                          &type7->data->id);
+               ospf_lsa_free(translated_lsa);
                return NULL;
        }
 
@@ -2071,7 +2072,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
                                              struct ospf_lsa *type7,
                                              struct ospf_lsa *type5)
 {
-       struct ospf_lsa *new = NULL;
+       struct ospf_lsa *new = NULL, *translated_lsa = NULL;
        struct as_external_lsa *extold = NULL;
        uint32_t ls_seqnum = 0;
 
@@ -2147,7 +2148,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
        ospf_ls_retransmit_delete_nbr_as(ospf, type5);
 
        /* create new translated LSA */
-       if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) {
+       if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) ==
+           NULL) {
                if (IS_DEBUG_OSPF_NSSA)
                        zlog_debug(
                                "%s: Could not translate Type-7 for %pI4 to Type-5",
@@ -2157,13 +2159,14 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
 
        if (type7->area->suppress_fa == 1) {
                if (extold->e[0].fwd_addr.s_addr == 0)
-                       new->data->ls_seqnum = htonl(ls_seqnum + 1);
+                       translated_lsa->data->ls_seqnum = htonl(ls_seqnum + 1);
        }
 
-       if (!(new = ospf_lsa_install(ospf, NULL, new))) {
+       if (!(new = ospf_lsa_install(ospf, NULL, translated_lsa))) {
                flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
                          "%s: Could not install translated LSA, Id %pI4",
                          __func__, &type7->data->id);
+               ospf_lsa_free(translated_lsa);
                return NULL;
        }
 
@@ -2253,7 +2256,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
            /* stay away from translated LSAs! */
            !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)))
                ospf_install_flood_nssa(
-                       ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */
+                       ospf, new); /* Install/Flood Type-7 to all NSSAs */
 
        /* Debug logging. */
        if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
@@ -2266,6 +2269,100 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
        return new;
 }
 
+/* Originate an NSSA-LSA, install and flood. */
+struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area,
+                                        struct external_info *ei)
+{
+       struct ospf *ospf = area->ospf;
+       struct ospf_lsa *new;
+
+       if (ospf->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+                       zlog_debug(
+                               "LSA[Type7]: Graceful Restart in progress, don't originate");
+               return NULL;
+       }
+
+       if (ospf->router_id.s_addr == INADDR_ANY) {
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug(
+                               "LSA[Type7:%pI4]: deferring NSSA-LSA origination, router ID is zero",
+                               &ei->p.prefix);
+               return NULL;
+       }
+
+       /* Create new NSSA-LSA instance. */
+       if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) {
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug(
+                               "LSA[Type7:%pI4]: Could not originate NSSA-LSA",
+                               &ei->p.prefix);
+               return NULL;
+       }
+       new->data->type = OSPF_AS_NSSA_LSA;
+       new->area = area;
+
+       /* Install newly created LSA into Type-7 LSDB. */
+       ospf_lsa_install(ospf, NULL, new);
+
+       /* Update LSA origination count. */
+       ospf->lsa_originate_count++;
+
+       /* Flooding new LSA */
+       ospf_flood_through_area(area, NULL, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug("LSA[Type%d:%pI4]: Originate NSSA-LSA %p",
+                          new->data->type, &new->data->id, (void *)new);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
+/* Refresh NSSA-LSA. */
+struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area,
+                                      struct ospf_lsa *lsa,
+                                      struct external_info *ei)
+{
+       struct ospf *ospf = area->ospf;
+       struct ospf_lsa *new;
+
+       /* Delete LSA from neighbor retransmit-list. */
+       ospf_ls_retransmit_delete_nbr_as(ospf, lsa);
+
+       /* Unregister AS-external-LSA from refresh-list. */
+       ospf_refresher_unregister_lsa(ospf, lsa);
+
+       /* Create new NSSA-LSA instance. */
+       if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) {
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug(
+                               "LSA[Type7:%pI4]: Could not originate NSSA-LSA",
+                               &ei->p.prefix);
+               return NULL;
+       }
+       new->data->type = OSPF_AS_NSSA_LSA;
+       new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+       new->area = area;
+
+       /* Install newly created LSA into Type-7 LSDB. */
+       ospf_lsa_install(ospf, NULL, new);
+
+       /* Flooding new LSA */
+       ospf_flood_through_area(area, NULL, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug("LSA[Type%d:%pI4]: NSSA-LSA refresh",
+                          new->data->type, &new->data->id);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
 static struct external_info *ospf_default_external_info(struct ospf *ospf)
 {
        int type;
@@ -2611,8 +2708,8 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf,
 
        /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */
        if (ospf->anyNSSA && !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)))
-               ospf_install_flood_nssa(ospf, new,
-                                       ei); /* Install/Flood per new rules */
+               ospf_install_flood_nssa(ospf,
+                                       new); /* Install/Flood per new rules */
 
        /* Register self-originated LSA to refresh queue.
         * Translated LSAs should not be registered, but refreshed upon
index 8ab293f4dbdda16100d92835896f9c6531d65485..d5ca0694ccb204bd890e1593c1586cb9cd24dfd3 100644 (file)
@@ -278,6 +278,11 @@ extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *);
 
 extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *,
                                                    struct external_info *);
+extern struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area,
+                                               struct external_info *ei);
+extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area,
+                                             struct ospf_lsa *lsa,
+                                             struct external_info *ei);
 extern void ospf_external_lsa_rid_change(struct ospf *ospf);
 extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *,
                                        uint32_t, struct in_addr,
index d3f30ce1ee81c93ec10ad522afbb39dc44a3b4ff..aff8ed05c723542ed1118ce70c20799695ace683 100644 (file)
@@ -15,6 +15,7 @@
 #include "sockopt.h"
 #include "privs.h"
 #include "lib_errors.h"
+#include "lib/table.h"
 
 #include "ospfd/ospfd.h"
 #include "ospfd/ospf_network.h"
@@ -111,7 +112,7 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p,
                         "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s",
                         top->fd, &p->u.prefix4, ifindex,
                         safe_strerror(errno));
-       else
+       else if (IS_DEBUG_OSPF_EVENT)
                zlog_debug(
                        "interface %pI4 [%u] leave AllDRouters Multicast group.",
                        &p->u.prefix4, ifindex);
@@ -119,62 +120,60 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p,
        return ret;
 }
 
-int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
+int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex)
 {
        uint8_t val;
        int ret, len;
 
        /* Prevent receiving self-origined multicast packets. */
-       ret = setsockopt_ipv4_multicast_loop(top->fd, 0);
+       ret = setsockopt_ipv4_multicast_loop(fd, 0);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s",
-                        top->fd, safe_strerror(errno));
+                        fd, safe_strerror(errno));
 
        /* Explicitly set multicast ttl to 1 -- endo. */
        val = 1;
        len = sizeof(val);
-       ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val,
-                        len);
+       ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s",
-                        top->fd, safe_strerror(errno));
+                        fd, safe_strerror(errno));
 #ifndef GNU_LINUX
        /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send
         * packet out of ifindex. Below would be used Non Linux system.
         */
-       ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex);
+       ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex);
        if (ret < 0)
                flog_err(EC_LIB_SOCKET,
                         "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s",
-                        top->fd, &p->u.prefix4, ifindex,
+                        fd, &p->u.prefix4, ifindex,
                         safe_strerror(errno));
 #endif
 
        return ret;
 }
 
-int ospf_sock_init(struct ospf *ospf)
+/*
+ * Helper to open and set up a socket; returns the new fd on success,
+ * -1 on error.
+ */
+static int sock_init_common(vrf_id_t vrf_id, const char *name, int *pfd)
 {
        int ospf_sock;
        int ret, hincl = 1;
-       int bufsize = (8 * 1024 * 1024);
-
-       /* silently ignore. already done */
-       if (ospf->fd > 0)
-               return -1;
 
-       if (ospf->vrf_id == VRF_UNKNOWN) {
+       if (vrf_id == VRF_UNKNOWN) {
                /* silently return since VRF is not ready */
                return -1;
        }
+
        frr_with_privs(&ospfd_privs) {
                ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,
-                                      ospf->vrf_id, ospf->name);
+                                      vrf_id, name);
                if (ospf_sock < 0) {
-                       flog_err(EC_LIB_SOCKET,
-                                "ospf_read_sock_init: socket: %s",
+                       flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__,
                                 safe_strerror(errno));
                        return -1;
                }
@@ -213,9 +212,102 @@ int ospf_sock_init(struct ospf *ospf)
                                 ospf_sock);
        }
 
-       setsockopt_so_sendbuf(ospf_sock, bufsize);
-       setsockopt_so_recvbuf(ospf_sock, bufsize);
+       *pfd = ospf_sock;
 
-       ospf->fd = ospf_sock;
        return ret;
 }
+
+/*
+ * Update a socket bufsize(s), based on its ospf instance
+ */
+void ospf_sock_bufsize_update(const struct ospf *ospf, int sock,
+                             enum ospf_sock_type_e type)
+{
+       int bufsize;
+
+       if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_RECV) {
+               bufsize = ospf->recv_sock_bufsize;
+               setsockopt_so_recvbuf(sock, bufsize);
+       }
+
+       if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_SEND) {
+               bufsize = ospf->send_sock_bufsize;
+               setsockopt_so_sendbuf(sock, bufsize);
+       }
+}
+
+int ospf_sock_init(struct ospf *ospf)
+{
+       int ret;
+
+       /* silently ignore. already done */
+       if (ospf->fd > 0)
+               return -1;
+
+       ret = sock_init_common(ospf->vrf_id, ospf->name, &(ospf->fd));
+
+       if (ret >= 0) /* Update socket buffer sizes */
+               ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH);
+
+       return ret;
+}
+
+/*
+ * Open per-interface write socket
+ */
+int ospf_ifp_sock_init(struct interface *ifp)
+{
+       struct ospf_if_info *oii;
+       struct ospf_interface *oi;
+       struct ospf *ospf;
+       struct route_node *rn;
+       int ret;
+
+       oii = IF_OSPF_IF_INFO(ifp);
+       if (oii == NULL)
+               return -1;
+
+       if (oii->oii_fd > 0)
+               return 0;
+
+       rn = route_top(IF_OIFS(ifp));
+       if (rn && rn->info) {
+               oi = rn->info;
+               ospf = oi->ospf;
+       } else
+               return -1;
+
+       ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, &oii->oii_fd);
+
+       if (ret >= 0) /* Update socket buffer sizes */
+               ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_BOTH);
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name,
+                          oii, oii->oii_fd);
+
+       return ret;
+}
+
+/*
+ * Close per-interface write socket
+ */
+int ospf_ifp_sock_close(struct interface *ifp)
+{
+       struct ospf_if_info *oii;
+
+       oii = IF_OSPF_IF_INFO(ifp);
+       if (oii == NULL)
+               return 0;
+
+       if (oii->oii_fd > 0) {
+               if (IS_DEBUG_OSPF_EVENT)
+                       zlog_debug("%s: ifp %s, oii %p, fd %d", __func__,
+                                  ifp->name, oii, oii->oii_fd);
+
+               close(oii->oii_fd);
+               oii->oii_fd = -1;
+       }
+
+       return 0;
+}
index 33fd8980bff20366716be1a3fd495f5358977574..b810bad50bef3e359ba906a2d912fea4a987f2c3 100644 (file)
@@ -13,7 +13,20 @@ extern int ospf_if_drop_allspfrouters(struct ospf *, struct prefix *,
                                      ifindex_t);
 extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t);
 extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
-extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t);
+extern int ospf_if_ipmulticast(int fd, struct prefix *, ifindex_t);
 extern int ospf_sock_init(struct ospf *ospf);
+/* Open, close per-interface write socket */
+int ospf_ifp_sock_init(struct interface *ifp);
+int ospf_ifp_sock_close(struct interface *ifp);
+
+enum ospf_sock_type_e {
+       OSPF_SOCK_NONE = 0,
+       OSPF_SOCK_RECV,
+       OSPF_SOCK_SEND,
+       OSPF_SOCK_BOTH
+};
+
+void ospf_sock_bufsize_update(const struct ospf *ospf, int sock,
+                             enum ospf_sock_type_e type);
 
 #endif /* _ZEBRA_OSPF_NETWORK_H */
index 6f66ee10a1ebbd722ffe31ef32a274b02ed73c43..c2b40af1c4706161b93bfd35a39466c2786de044 100644 (file)
@@ -544,7 +544,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
                listnode_add(new->area->opaque_lsa_self, oipt);
                break;
        case OSPF_OPAQUE_AS_LSA:
-               top = ospf_lookup_by_vrf_id(new->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if (new->area != NULL && (top = new->area->ospf) == NULL) {
                        free_opaque_info_per_type(oipt, true);
                        oipt = NULL;
@@ -652,7 +652,7 @@ lookup_opaque_info_by_type(struct ospf_lsa *lsa)
                                "Type-10 Opaque-LSA: Reference to AREA is missing?");
                break;
        case OSPF_OPAQUE_AS_LSA:
-               top = ospf_lookup_by_vrf_id(lsa->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) {
                        flog_warn(
                                EC_OSPF_LSA,
@@ -758,6 +758,13 @@ DEFUN (capability_opaque,
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
 
+       /* Check that OSPF is using default VRF */
+       if (ospf->vrf_id != VRF_DEFAULT) {
+               vty_out(vty,
+                       "OSPF Opaque LSA is only supported in default VRF\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        /* Turn on the "master switch" of opaque-lsa capability. */
        if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) {
                if (IS_DEBUG_OSPF_EVENT)
@@ -1588,7 +1595,7 @@ struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc)
                }
                break;
        case OSPF_OPAQUE_AS_LSA:
-               top = ospf_lookup_by_vrf_id(lsa->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) {
                        /* Above conditions must have passed. */
                        flog_warn(EC_OSPF_LSA, "%s: Something wrong?",
@@ -1615,7 +1622,7 @@ struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa)
        struct ospf_opaque_functab *functab;
        struct ospf_lsa *new = NULL;
 
-       ospf = ospf_lookup_by_vrf_id(lsa->vrf_id);
+       ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
 
        if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL
            || functab->lsa_refresher == NULL) {
@@ -1752,7 +1759,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
 
        /* Generate a dummy lsa to be passed for a lookup function. */
        lsa = pseudo_lsa(oi, area, lsa_type, opaque_type);
-       lsa->vrf_id = top->vrf_id;
+       lsa->vrf_id = VRF_DEFAULT;
 
        if ((oipt = lookup_opaque_info_by_type(lsa)) == NULL) {
                struct ospf_opaque_functab *functab;
@@ -1987,7 +1994,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0)
                ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa);
                break;
        case OSPF_OPAQUE_AS_LSA:
-               top = ospf_lookup_by_vrf_id(lsa0->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL))
                        top = lsa0->area->ospf;
                ospf_ls_retransmit_delete_nbr_as(top, lsa);
@@ -2037,7 +2044,7 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0)
        struct ospf_lsa *lsa;
        struct ospf *top;
 
-       top = ospf_lookup_by_vrf_id(lsa0->vrf_id);
+       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
 
        if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL
            || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) {
index 5f7d49e0bbc4afc1efd714fd2ef54ea91d7a88f3..552acfd6d32be08f5c5424f5614b928828c8afd0 100644 (file)
@@ -618,7 +618,7 @@ static void ospf_write(struct event *thread)
        struct msghdr msg;
        struct iovec iov[2];
        uint8_t type;
-       int ret;
+       int ret, fd;
        int flags = 0;
        struct listnode *node;
 #ifdef WANT_OSPF_WRITE_FRAGMENT
@@ -633,11 +633,12 @@ static void ospf_write(struct event *thread)
        struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf;
        struct in_pktinfo *pi;
 #endif
+       fd = ospf->fd;
 
-       if (ospf->fd < 0 || ospf->oi_running == 0) {
+       if (fd < 0 || ospf->oi_running == 0) {
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug("%s failed to send, fd %d, instance %u",
-                                  __func__, ospf->fd, ospf->oi_running);
+                                  __func__, fd, ospf->oi_running);
                return;
        }
 
@@ -657,6 +658,15 @@ static void ospf_write(struct event *thread)
                /* convenience - max OSPF data per packet */
                maxdatasize = oi->ifp->mtu - sizeof(struct ip);
 #endif /* WANT_OSPF_WRITE_FRAGMENT */
+
+               /* Reset socket fd to use. */
+               fd = ospf->fd;
+
+               /* Check for per-interface socket */
+               if (ospf->intf_socket_enabled &&
+                   (IF_OSPF_IF_INFO(oi->ifp))->oii_fd > 0)
+                       fd = (IF_OSPF_IF_INFO(oi->ifp))->oii_fd;
+
                /* Get one packet from queue. */
                op = ospf_fifo_head(oi->obuf);
                assert(op);
@@ -664,8 +674,7 @@ static void ospf_write(struct event *thread)
 
                if (op->dst.s_addr == htonl(OSPF_ALLSPFROUTERS)
                    || op->dst.s_addr == htonl(OSPF_ALLDROUTERS))
-                       ospf_if_ipmulticast(ospf, oi->address,
-                                           oi->ifp->ifindex);
+                       ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex);
 
                /* Rewrite the md5 signature & update the seq */
                ospf_make_md5_digest(oi, op);
@@ -760,13 +769,13 @@ static void ospf_write(struct event *thread)
 
 #ifdef WANT_OSPF_WRITE_FRAGMENT
                if (op->length > maxdatasize)
-                       ospf_write_frags(ospf->fd, op, &iph, &msg, maxdatasize,
+                       ospf_write_frags(fd, op, &iph, &msg, maxdatasize,
                                         oi->ifp->mtu, flags, type);
 #endif /* WANT_OSPF_WRITE_FRAGMENT */
 
                /* send final fragment (could be first) */
                sockopt_iphdrincl_swab_htosys(&iph);
-               ret = sendmsg(ospf->fd, &msg, flags);
+               ret = sendmsg(fd, &msg, flags);
                sockopt_iphdrincl_swab_systoh(&iph);
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug(
index 0179f9ee0bea7ee41ffc55cec96870e786b1c27d..725443f490786a71b1a7898b0b6f269853f4a8ec 100644 (file)
@@ -798,13 +798,10 @@ static struct ospf_lsa *ospf_router_info_lsa_new(struct ospf_area *area)
        /* Now, create an OSPF LSA instance. */
        new = ospf_lsa_new_and_data(length);
 
+       /* Routing Information is only supported for default VRF */
+       new->vrf_id = VRF_DEFAULT;
        new->area = area;
 
-       if (new->area && new->area->ospf)
-               new->vrf_id = new->area->ospf->vrf_id;
-       else
-               new->vrf_id = VRF_DEFAULT;
-
        SET_FLAG(new->flags, OSPF_LSA_SELF);
        memcpy(new->data, lsah, length);
        stream_free(s);
@@ -817,7 +814,6 @@ static int ospf_router_info_lsa_originate_as(void *arg)
        struct ospf_lsa *new;
        struct ospf *top;
        int rc = -1;
-       vrf_id_t vrf_id = VRF_DEFAULT;
 
        /* Sanity Check */
        if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) {
@@ -830,13 +826,12 @@ static int ospf_router_info_lsa_originate_as(void *arg)
 
        /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
        new = ospf_router_info_lsa_new(NULL);
-       new->vrf_id = VRF_DEFAULT;
        top = (struct ospf *)arg;
 
        /* Check ospf info */
        if (top == NULL) {
                zlog_debug("RI (%s): ospf instance not found for vrf id %u",
-                          __func__, vrf_id);
+                          __func__, VRF_DEFAULT);
                ospf_lsa_unlock(&new);
                return rc;
        }
@@ -874,7 +869,6 @@ static int ospf_router_info_lsa_originate_area(void *arg)
        struct ospf *top;
        struct ospf_ri_area_info *ai = NULL;
        int rc = -1;
-       vrf_id_t vrf_id = VRF_DEFAULT;
 
        /* Sanity Check */
        if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
@@ -893,19 +887,18 @@ static int ospf_router_info_lsa_originate_area(void *arg)
                        __func__);
                return rc;
        }
-       if (ai->area->ospf) {
-               vrf_id = ai->area->ospf->vrf_id;
+
+       if (ai->area->ospf)
                top = ai->area->ospf;
-       } else {
-               top = ospf_lookup_by_vrf_id(vrf_id);
-       }
+       else
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+
        new = ospf_router_info_lsa_new(ai->area);
-       new->vrf_id = vrf_id;
 
        /* Check ospf info */
        if (top == NULL) {
                zlog_debug("RI (%s): ospf instance not found for vrf id %u",
-                          __func__, vrf_id);
+                          __func__, VRF_DEFAULT);
                ospf_lsa_unlock(&new);
                return rc;
        }
@@ -1039,10 +1032,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa)
                /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
                new = ospf_router_info_lsa_new(ai->area);
                new->data->ls_seqnum = lsa_seqnum_increment(lsa);
-               new->vrf_id = lsa->vrf_id;
                /* Install this LSA into LSDB. */
                /* Given "lsa" will be freed in the next function. */
-               top = ospf_lookup_by_vrf_id(lsa->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
                        flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
                                  "RI (%s): ospf_lsa_install() ?", __func__);
@@ -1062,10 +1054,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa)
                /* Create new Opaque-LSA/ROUTER INFORMATION instance. */
                new = ospf_router_info_lsa_new(NULL);
                new->data->ls_seqnum = lsa_seqnum_increment(lsa);
-               new->vrf_id = lsa->vrf_id;
                /* Install this LSA into LSDB. */
                /* Given "lsa" will be freed in the next function. */
-               top = ospf_lookup_by_vrf_id(lsa->vrf_id);
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
                if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
                        flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
                                  "RI (%s): ospf_lsa_install() ?", __func__);
@@ -1676,10 +1667,18 @@ DEFUN (router_info,
 {
        int idx_mode = 1;
        uint8_t scope;
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
 
        if (OspfRI.enabled)
                return CMD_SUCCESS;
 
+       /* Check that the OSPF is using default VRF */
+       if (ospf->vrf_id != VRF_DEFAULT) {
+               vty_out(vty,
+                       "Router Information is only supported in default VRF\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        /* Check and get Area value if present */
        if (strncmp(argv[idx_mode]->arg, "as", 2) == 0)
                scope = OSPF_OPAQUE_AS_LSA;
index 5f18bff1cf79996de3b9523a3e982fc669cc0e5d..75868056adb31c51aa6d8def2ed3acfc670eb91e 100644 (file)
@@ -1008,7 +1008,8 @@ void ospf_prune_unreachable_routers(struct route_table *rtrs)
 }
 
 int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
-                          struct ospf_area *area, struct prefix_ipv4 *p)
+                          struct ospf_area *area, struct prefix_ipv4 *p,
+                          bool nssa)
 {
        struct route_node *rn;
        struct ospf_route * or, *new_or;
@@ -1027,7 +1028,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
 
                or = rn->info;
 
-               if (or->path_type == OSPF_PATH_INTRA_AREA) {
+               if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) {
                        if (IS_DEBUG_OSPF_EVENT)
                                zlog_debug("%s: an intra-area route exists",
                                           __func__);
@@ -1054,7 +1055,10 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
        new_or->cost = 0;
        new_or->u.std.area_id = area->area_id;
        new_or->u.std.external_routing = area->external_routing;
-       new_or->path_type = OSPF_PATH_INTER_AREA;
+       if (nssa)
+               new_or->path_type = OSPF_PATH_TYPE2_EXTERNAL;
+       else
+               new_or->path_type = OSPF_PATH_INTER_AREA;
        rn->info = new_or;
 
        ospf_zebra_add_discard(ospf, p);
@@ -1063,7 +1067,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
 }
 
 void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt,
-                              struct prefix_ipv4 *p)
+                              struct prefix_ipv4 *p, bool nssa)
 {
        struct route_node *rn;
        struct ospf_route * or ;
@@ -1081,7 +1085,7 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt,
 
        or = rn->info;
 
-       if (or->path_type == OSPF_PATH_INTRA_AREA) {
+       if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) {
                if (IS_DEBUG_OSPF_EVENT)
                        zlog_debug("%s: an intra-area route exists", __func__);
                return;
index 2582067aece7af3f28cad144257da447464268df..7639a0049e1263eed1c454d23f4fc9e88a423977 100644 (file)
@@ -152,9 +152,10 @@ extern void ospf_route_subst_nexthops(struct ospf_route *, struct list *);
 extern void ospf_prune_unreachable_networks(struct route_table *);
 extern void ospf_prune_unreachable_routers(struct route_table *);
 extern int ospf_add_discard_route(struct ospf *, struct route_table *,
-                                 struct ospf_area *, struct prefix_ipv4 *);
+                                 struct ospf_area *, struct prefix_ipv4 *,
+                                 bool);
 extern void ospf_delete_discard_route(struct ospf *, struct route_table *,
-                                     struct prefix_ipv4 *);
+                                     struct prefix_ipv4 *, bool);
 extern int ospf_route_match_same(struct route_table *, struct prefix_ipv4 *,
                                 struct ospf_route *);
 
index 3087008819275c7a97fa0040d083fc9c56a7dc4f..d2f639031be6a07f375895d30e5c34f48c9ea0b6 100644 (file)
@@ -120,7 +120,7 @@ route_match_ip_nexthop(void *rule, const struct prefix *prefix, void *object)
 
        alist = access_list_lookup(AFI_IP, (char *)rule);
        if (alist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -168,7 +168,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
 
        plist = prefix_list_lookup(AFI_IP, (char *)rule);
        if (plist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -245,7 +245,7 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object)
 
        alist = access_list_lookup(AFI_IP, (char *)rule);
        if (alist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -286,7 +286,7 @@ route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
 
        plist = prefix_list_lookup(AFI_IP, (char *)rule);
        if (plist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -398,17 +398,19 @@ route_set_metric(void *rule, const struct prefix *prefix, void *object)
        if (!metric->used)
                return RMAP_OKAY;
 
-       ei->route_map_set.metric = DEFAULT_DEFAULT_METRIC;
+       ROUTEMAP_METRIC(ei) = ei->metric;
 
        if (metric->type == metric_increment)
-               ei->route_map_set.metric += metric->metric;
+               ROUTEMAP_METRIC(ei) += metric->metric;
        else if (metric->type == metric_decrement)
-               ei->route_map_set.metric -= metric->metric;
+               ROUTEMAP_METRIC(ei) -= metric->metric;
        else if (metric->type == metric_absolute)
-               ei->route_map_set.metric = metric->metric;
+               ROUTEMAP_METRIC(ei) = metric->metric;
 
-       if (ei->route_map_set.metric > OSPF_LS_INFINITY)
-               ei->route_map_set.metric = OSPF_LS_INFINITY;
+       if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric)
+               ROUTEMAP_METRIC(ei) = ei->min_metric;
+       if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric)
+               ROUTEMAP_METRIC(ei) = ei->max_metric;
 
        return RMAP_OKAY;
 }
@@ -462,6 +464,115 @@ static const struct route_map_rule_cmd route_set_metric_cmd = {
        route_set_metric_free,
 };
 
+/* `set min-metric METRIC' */
+/* Set min-metric to attribute. */
+static enum route_map_cmd_result_t
+route_set_min_metric(void *rule, const struct prefix *prefix, void *object)
+{
+       uint32_t *min_metric;
+       struct external_info *ei;
+
+       /* Fetch routemap's rule information. */
+       min_metric = rule;
+       ei = object;
+
+       ei->min_metric = *min_metric;
+
+       if (ei->min_metric > OSPF_LS_INFINITY)
+               ei->min_metric = OSPF_LS_INFINITY;
+
+       if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric)
+               ROUTEMAP_METRIC(ei) = ei->min_metric;
+
+       return RMAP_OKAY;
+}
+
+/* set min-metric compilation. */
+static void *route_set_min_metric_compile(const char *arg)
+{
+
+       uint32_t *min_metric;
+
+       min_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t));
+
+       *min_metric = strtoul(arg, NULL, 10);
+
+       if (*min_metric)
+               return min_metric;
+
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, min_metric);
+       return NULL;
+}
+
+/* Free route map's compiled `set min-metric' value. */
+static void route_set_min_metric_free(void *rule)
+{
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+static const struct route_map_rule_cmd route_set_min_metric_cmd = {
+       "min-metric",
+       route_set_min_metric,
+       route_set_min_metric_compile,
+       route_set_min_metric_free,
+};
+
+
+/* `set max-metric METRIC' */
+/* Set max-metric to attribute. */
+static enum route_map_cmd_result_t
+route_set_max_metric(void *rule, const struct prefix *prefix, void *object)
+{
+       uint32_t *max_metric;
+       struct external_info *ei;
+
+       /* Fetch routemap's rule information. */
+       max_metric = rule;
+       ei = object;
+
+       ei->max_metric = *max_metric;
+
+       if (ei->max_metric > OSPF_LS_INFINITY)
+               ei->max_metric = OSPF_LS_INFINITY;
+
+       if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric)
+               ROUTEMAP_METRIC(ei) = ei->max_metric;
+
+       return RMAP_OKAY;
+}
+
+/* set max-metric compilation. */
+static void *route_set_max_metric_compile(const char *arg)
+{
+
+       uint32_t *max_metric;
+
+       max_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t));
+
+       *max_metric = strtoul(arg, NULL, 10);
+
+       if (*max_metric)
+               return max_metric;
+
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, max_metric);
+       return NULL;
+}
+
+/* Free route map's compiled `set max-metric' value. */
+static void route_set_max_metric_free(void *rule)
+{
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set metric rule structure. */
+static const struct route_map_rule_cmd route_set_max_metric_cmd = {
+       "max-metric",
+       route_set_max_metric,
+       route_set_max_metric_compile,
+       route_set_max_metric_free,
+};
+
 /* `set metric-type TYPE' */
 /* Set metric-type to attribute. */
 static enum route_map_cmd_result_t
@@ -475,7 +586,7 @@ route_set_metric_type(void *rule, const struct prefix *prefix, void *object)
        ei = object;
 
        /* Set metric out value. */
-       ei->route_map_set.metric_type = *metric_type;
+       ROUTEMAP_METRIC_TYPE(ei) = *metric_type;
 
        return RMAP_OKAY;
 }
@@ -582,9 +693,6 @@ void ospf_route_map_init(void)
        route_map_delete_hook(ospf_route_map_update);
        route_map_event_hook(ospf_route_map_event);
 
-       route_map_set_metric_hook(generic_set_add);
-       route_map_no_set_metric_hook(generic_set_delete);
-
        route_map_match_ip_next_hop_hook(generic_match_add);
        route_map_no_match_ip_next_hop_hook(generic_match_delete);
 
@@ -609,6 +717,12 @@ void ospf_route_map_init(void)
        route_map_set_metric_hook(generic_set_add);
        route_map_no_set_metric_hook(generic_set_delete);
 
+       route_map_set_min_metric_hook(generic_set_add);
+       route_map_no_set_min_metric_hook(generic_set_delete);
+
+       route_map_set_max_metric_hook(generic_set_add);
+       route_map_no_set_max_metric_hook(generic_set_delete);
+
        route_map_set_tag_hook(generic_set_add);
        route_map_no_set_tag_hook(generic_set_delete);
 
@@ -621,6 +735,8 @@ void ospf_route_map_init(void)
        route_map_install_match(&route_match_tag_cmd);
 
        route_map_install_set(&route_set_metric_cmd);
+       route_map_install_set(&route_set_min_metric_cmd);
+       route_map_install_set(&route_set_max_metric_cmd);
        route_map_install_set(&route_set_metric_type_cmd);
        route_map_install_set(&route_set_tag_cmd);
 
index 2982cc7d6f2114e2130fbe67980b2b695600a773..fcc43e7311c2bae2ac7522bfa4ff91522543bfab 100644 (file)
@@ -1112,7 +1112,7 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v,
                oid2in_addr(offset, IN_ADDR_SIZE, range_net);
                p.prefix = *range_net;
 
-               return ospf_area_range_lookup(area, &p);
+               return ospf_area_range_lookup(area, area->ranges, &p);
        } else {
                /* Set OID offset for Area ID. */
                offset = name + v->namelen;
index dc9dd34303644c96085220d26fd6e475abe311a7..3cf39e5fb54203e0dd19f787db7feb58444fe44e 100644 (file)
@@ -1207,10 +1207,9 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf,
        /* Now, create an OSPF LSA instance. */
        new = ospf_lsa_new_and_data(length);
 
-       new->vrf_id = ospf->vrf_id;
-       if (area && area->ospf)
-               new->vrf_id = area->ospf->vrf_id;
        new->area = area;
+       new->vrf_id = VRF_DEFAULT;
+
        SET_FLAG(new->flags, OSPF_LSA_SELF);
        memcpy(new->data, lsah, length);
        stream_free(s);
@@ -1329,7 +1328,6 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top,
                          __func__);
                return rc;
        }
-       new->vrf_id = top->vrf_id;
 
        /* Install this LSA into LSDB. */
        if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
@@ -1482,7 +1480,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
                ospf_opaque_lsa_flush_schedule(lsa);
                return NULL;
        }
-       top = ospf_lookup_by_vrf_id(lsa->vrf_id);
+       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
        /* Create new Opaque-LSA/MPLS-TE instance. */
        new = ospf_mpls_te_lsa_new(top, area, lp);
        if (new == NULL) {
@@ -1667,12 +1665,13 @@ static struct ls_vertex *get_vertex(struct ls_ted *ted, struct ospf_lsa *lsa)
 static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv,
                                struct in_addr link_id)
 {
-       uint64_t key;
+       struct ls_edge_key key;
        struct ls_edge *edge;
        struct ls_attributes *attr;
 
        /* Search Edge that corresponds to the Link ID */
-       key = ((uint64_t)ntohl(link_id.s_addr)) & 0xffffffff;
+       key.family = AF_INET;
+       IPV4_ADDR_COPY(&key.k.addr, &link_id);
        edge = ls_find_edge_by_key(ted, key);
 
        /* Create new one if not exist */
@@ -1781,7 +1780,7 @@ static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex,
  * @param metric       Standard metric attached to this Edge
  */
 static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex,
-                                 struct prefix p, uint8_t metric)
+                                 struct prefix *p, uint8_t metric)
 {
        struct ls_subnet *subnet;
        struct ls_prefix *ls_pref;
@@ -1840,7 +1839,7 @@ static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr)
        p.family = AF_INET;
        p.prefixlen = IPV4_MAX_BITLEN;
        p.u.prefix4 = addr;
-       subnet = ls_find_subnet(ted, p);
+       subnet = ls_find_subnet(ted, &p);
 
        /* Remove subnet if found */
        if (subnet) {
@@ -1933,7 +1932,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
                        p.prefixlen = IPV4_MAX_BITLEN;
                        p.u.prefix4 = rl->link[i].link_data;
                        metric = ntohs(rl->link[i].metric);
-                       ospf_te_update_subnet(ted, vertex, p, metric);
+                       ospf_te_update_subnet(ted, vertex, &p, metric);
                        break;
                case LSA_LINK_TYPE_STUB:
                        /* Keep only /32 prefix */
@@ -1942,7 +1941,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
                                p.family = AF_INET;
                                p.u.prefix4 = rl->link[i].link_id;
                                metric = ntohs(rl->link[i].metric);
-                               ospf_te_update_subnet(ted, vertex, p, metric);
+                               ospf_te_update_subnet(ted, vertex, &p, metric);
                        }
                        break;
                default:
@@ -2074,12 +2073,12 @@ static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge)
        p.family = AF_INET;
        p.prefixlen = IPV4_MAX_BITLEN;
        p.u.prefix4 = attr->standard.local;
-       ospf_te_update_subnet(ted, edge->source, p, attr->standard.te_metric);
+       ospf_te_update_subnet(ted, edge->source, &p, attr->standard.te_metric);
 
        p.family = AF_INET;
        p.prefixlen = IPV4_MAX_BITLEN;
        p.u.prefix4 = attr->standard.remote_addr;
-       ospf_te_update_subnet(ted, vertex, p, attr->standard.te_metric);
+       ospf_te_update_subnet(ted, vertex, &p, attr->standard.te_metric);
 
        /* Connect Edge to the remote Vertex */
        if (edge->destination == NULL) {
@@ -2352,7 +2351,7 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa)
        struct ls_attributes *attr;
        struct tlv_header *tlvh;
        struct in_addr addr;
-       uint64_t key = 0;
+       struct ls_edge_key key = {.family = AF_UNSPEC};
        uint16_t len, sum;
        uint8_t lsa_id;
 
@@ -2368,12 +2367,13 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa)
        for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
                if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) {
                        memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE);
-                       key = ((uint64_t)ntohl(addr.s_addr)) & 0xffffffff;
+                       key.family = AF_INET;
+                       IPV4_ADDR_COPY(&key.k.addr, &addr);
                        break;
                }
                sum += TLV_SIZE(tlvh);
        }
-       if (key == 0)
+       if (key.family == AF_UNSPEC)
                return 0;
 
        /* Search Edge that corresponds to the Link ID */
@@ -2625,14 +2625,14 @@ static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa)
        pref.family = AF_INET;
        pref.prefixlen = ext->pref_length;
        pref.u.prefix4 = ext->address;
-       subnet = ls_find_subnet(ted, pref);
+       subnet = ls_find_subnet(ted, &pref);
 
        /* Create new Link State Prefix if not found */
        if (!subnet) {
                lnid.origin = OSPFv2;
                lnid.id.ip.addr = lsa->data->adv_router;
                lnid.id.ip.area_id = lsa->area->area_id;
-               ls_pref = ls_prefix_new(lnid, pref);
+               ls_pref = ls_prefix_new(lnid, &pref);
                /* and add it to the TED */
                subnet = ls_subnet_add(ted, ls_pref);
        }
@@ -2698,7 +2698,7 @@ static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa)
        pref.family = AF_INET;
        pref.prefixlen = ext->pref_length;
        pref.u.prefix4 = ext->address;
-       subnet = ls_find_subnet(ted, pref);
+       subnet = ls_find_subnet(ted, &pref);
 
        /* Check if there is a corresponding subnet */
        if (!subnet)
@@ -2864,11 +2864,12 @@ static int ospf_te_delete_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa)
        struct ls_edge *edge;
        struct ls_attributes *atr;
        struct ext_tlv_link *ext;
-       uint64_t key;
+       struct ls_edge_key key;
 
        /* Search for corresponding Edge from Link State Data Base */
        ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data);
-       key = ((uint64_t)ntohl(ext->link_data.s_addr)) & 0xffffffff;
+       key.family = AF_INET;
+       IPV4_ADDR_COPY(&key.k.addr, &ext->link_data);
        edge = ls_find_edge_by_key(ted, key);
 
        /* Check if there is a corresponding Edge */
@@ -3847,6 +3848,12 @@ DEFUN (ospf_mpls_te_on,
        if (OspfMplsTE.enabled)
                return CMD_SUCCESS;
 
+       /* Check that the OSPF is using default VRF */
+       if (ospf->vrf_id != VRF_DEFAULT) {
+               vty_out(vty, "MPLS TE is only supported in default VRF\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        ote_debug("MPLS-TE: OFF -> ON");
 
        OspfMplsTE.enabled = true;
@@ -4229,12 +4236,10 @@ static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp)
 
 DEFUN (show_ip_ospf_mpls_te_link,
        show_ip_ospf_mpls_te_link_cmd,
-       "show ip ospf [vrf <NAME|all>] mpls-te interface [INTERFACE]",
+       "show ip ospf mpls-te interface [INTERFACE]",
        SHOW_STR
        IP_STR
        OSPF_STR
-       VRF_CMD_HELP_STR
-       "All VRFs\n"
        "MPLS-TE information\n"
        "Interface information\n"
        "Interface name\n")
@@ -4242,43 +4247,18 @@ DEFUN (show_ip_ospf_mpls_te_link,
        struct vrf *vrf;
        int idx_interface = 0;
        struct interface *ifp = NULL;
-       struct listnode *node;
-       char *vrf_name = NULL;
-       bool all_vrf = false;
-       int inst = 0;
-       int idx_vrf = 0;
        struct ospf *ospf = NULL;
 
-       if (argv_find(argv, argc, "vrf", &idx_vrf)) {
-               vrf_name = argv[idx_vrf + 1]->arg;
-               all_vrf = strmatch(vrf_name, "all");
-       }
        argv_find(argv, argc, "INTERFACE", &idx_interface);
-       /* vrf input is provided could be all or specific vrf*/
-       if (vrf_name) {
-               if (all_vrf) {
-                       for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
-                               if (!ospf->oi_running)
-                                       continue;
-                               vrf = vrf_lookup_by_id(ospf->vrf_id);
-                               FOR_ALL_INTERFACES (vrf, ifp)
-                                       show_mpls_te_link_sub(vty, ifp);
-                       }
-                       return CMD_SUCCESS;
-               }
-               ospf = ospf_lookup_by_inst_name(inst, vrf_name);
-       } else
-               ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
        if (ospf == NULL || !ospf->oi_running)
                return CMD_SUCCESS;
 
-       vrf = vrf_lookup_by_id(ospf->vrf_id);
+       vrf = vrf_lookup_by_id(VRF_DEFAULT);
        if (!vrf)
                return CMD_SUCCESS;
        if (idx_interface) {
-               ifp = if_lookup_by_name(
-                                       argv[idx_interface]->arg,
-                                       ospf->vrf_id);
+               ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT);
                if (ifp == NULL) {
                        vty_out(vty, "No such interface name in vrf %s\n",
                                vrf->name);
@@ -4321,6 +4301,7 @@ DEFUN (show_ip_ospf_mpls_te_db,
        struct ls_edge *edge;
        struct ls_subnet *subnet;
        uint64_t key;
+       struct ls_edge_key ekey;
        bool verbose = false;
        bool uj = use_json(argc, argv);
        json_object *json = NULL;
@@ -4374,8 +4355,9 @@ DEFUN (show_ip_ospf_mpls_te_db,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Edge from the Link State Database */
-                       key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
-                       edge = ls_find_edge_by_key(OspfMplsTE.ted, key);
+                       ekey.family = AF_INET;
+                       IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr);
+                       edge = ls_find_edge_by_key(OspfMplsTE.ted, ekey);
                        if (!edge) {
                                vty_out(vty, "No edge found for ID %pI4\n",
                                        &ip_addr);
@@ -4398,7 +4380,7 @@ DEFUN (show_ip_ospf_mpls_te_db,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Subnet from the Link State Database */
-                       subnet = ls_find_subnet(OspfMplsTE.ted, pref);
+                       subnet = ls_find_subnet(OspfMplsTE.ted, &pref);
                        if (!subnet) {
                                vty_out(vty, "No subnet found for ID %pFX\n",
                                        &pref);
index 8c0afd8527e45ff811c341c2d7e3adbc0a47287a..1b4d26ef3d07f8f72d2558a497006f9ae750a8ba 100644 (file)
 #include "ospfd/ospf_spf.h"
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_zebra.h"
-/*#include "ospfd/ospf_routemap.h" */
 #include "ospfd/ospf_vty.h"
 #include "ospfd/ospf_dump.h"
 #include "ospfd/ospf_bfd.h"
 #include "ospfd/ospf_ldp_sync.h"
-
+#include "ospfd/ospf_network.h"
 
 FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
        { .val_bool = true, .match_profile = "datacenter", },
@@ -611,6 +610,7 @@ DEFUN (ospf_area_range,
        "Advertised metric for this range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct ospf_area *area;
        int idx_ipv4_number = 1;
        int idx_ipv4_prefixlen = 3;
        int idx_cost = 6;
@@ -622,12 +622,14 @@ DEFUN (ospf_area_range,
        VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg);
        str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
 
-       ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE);
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
+
+       ospf_area_range_set(ospf, area, area->ranges, &p,
+                           OSPF_AREA_RANGE_ADVERTISE, false);
        if (argc > 5) {
                cost = strtoul(argv[idx_cost]->arg, NULL, 10);
-               ospf_area_range_cost_set(ospf, area_id, &p, cost);
+               ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost);
        }
 
        return CMD_SUCCESS;
@@ -647,6 +649,7 @@ DEFUN (ospf_area_range_cost,
        "Network prefix to be announced instead of range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct ospf_area *area;
        int idx_ipv4_number = 1;
        int idx_ipv4_prefixlen = 3;
        int idx = 4;
@@ -658,19 +661,20 @@ DEFUN (ospf_area_range_cost,
        VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg);
        str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
 
-       ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE);
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
 
+       ospf_area_range_set(ospf, area, area->ranges, &p,
+                           OSPF_AREA_RANGE_ADVERTISE, false);
        if (argv_find(argv, argc, "cost", &idx)) {
                cost = strtoul(argv[idx + 1]->arg, NULL, 10);
-               ospf_area_range_cost_set(ospf, area_id, &p, cost);
+               ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost);
        }
 
        idx = 4;
        if (argv_find(argv, argc, "substitute", &idx)) {
                str2prefix_ipv4(argv[idx + 1]->arg, &s);
-               ospf_area_range_substitute_set(ospf, area_id, &p, &s);
+               ospf_area_range_substitute_set(ospf, area, &p, &s);
        }
 
        return CMD_SUCCESS;
@@ -687,6 +691,7 @@ DEFUN (ospf_area_range_not_advertise,
        "DoNotAdvertise this range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct ospf_area *area;
        int idx_ipv4_number = 1;
        int idx_ipv4_prefixlen = 3;
        struct prefix_ipv4 p;
@@ -696,10 +701,11 @@ DEFUN (ospf_area_range_not_advertise,
        VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg);
        str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
 
-       ospf_area_range_set(ospf, area_id, &p, 0);
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
-       ospf_area_range_substitute_unset(ospf, area_id, &p);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
+
+       ospf_area_range_set(ospf, area, area->ranges, &p, 0, false);
+       ospf_area_range_substitute_unset(ospf, area, &p);
 
        return CMD_SUCCESS;
 }
@@ -721,6 +727,7 @@ DEFUN (no_ospf_area_range,
        "DoNotAdvertise this range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct ospf_area *area;
        int idx_ipv4_number = 2;
        int idx_ipv4_prefixlen = 4;
        struct prefix_ipv4 p;
@@ -730,7 +737,10 @@ DEFUN (no_ospf_area_range,
        VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg);
        str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
 
-       ospf_area_range_unset(ospf, area_id, &p);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
+
+       ospf_area_range_unset(ospf, area, area->ranges, &p);
 
        return CMD_SUCCESS;
 }
@@ -748,6 +758,7 @@ DEFUN (no_ospf_area_range_substitute,
        "Network prefix to be announced instead of range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct ospf_area *area;
        int idx_ipv4_number = 2;
        int idx_ipv4_prefixlen = 4;
        int idx_ipv4_prefixlen_2 = 6;
@@ -759,7 +770,10 @@ DEFUN (no_ospf_area_range_substitute,
        str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p);
        str2prefix_ipv4(argv[idx_ipv4_prefixlen_2]->arg, &s);
 
-       ospf_area_range_substitute_unset(ospf, area_id, &p);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
+
+       ospf_area_range_substitute_unset(ospf, area, &p);
 
        return CMD_SUCCESS;
 }
@@ -1433,15 +1447,35 @@ DEFUN (no_ospf_area_stub_no_summary,
        return CMD_SUCCESS;
 }
 
-static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc,
-                                     struct cmd_token **argv, int cfg_nosum,
-                                     int nosum)
+DEFPY (ospf_area_nssa,
+       ospf_area_nssa_cmd,
+       "area <A.B.C.D|(0-4294967295)>$area_str nssa\
+         [{\
+          <translate-candidate|translate-never|translate-always>$translator_role\
+          |default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\
+          |no-summary$no_summary\
+          |suppress-fa$suppress_fa\
+        }]",
+       "OSPF area parameters\n"
+       "OSPF area ID in IP address format\n"
+       "OSPF area ID as a decimal value\n"
+       "Configure OSPF area as nssa\n"
+       "Configure NSSA-ABR for translate election (default)\n"
+       "Configure NSSA-ABR to never translate\n"
+       "Configure NSSA-ABR to always translate\n"
+       "Originate Type 7 default into NSSA area\n"
+       "OSPF default metric\n"
+       "OSPF metric\n"
+       "OSPF metric type for default routes\n"
+       "Set OSPF External Type 1/2 metrics\n"
+       "Do not inject inter-area routes into nssa\n"
+       "Suppress forwarding address\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
        struct in_addr area_id;
        int ret, format;
 
-       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, argv[1]->arg);
+       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str);
 
        ret = ospf_area_nssa_set(ospf, area_id);
        ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
@@ -1452,14 +1486,14 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc,
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       if (argc > 3) {
-               if (strncmp(argv[3]->text, "translate-c", 11) == 0)
+       if (translator_role) {
+               if (strncmp(translator_role, "translate-c", 11) == 0)
                        ospf_area_nssa_translator_role_set(
                                ospf, area_id, OSPF_NSSA_ROLE_CANDIDATE);
-               else if (strncmp(argv[3]->text, "translate-n", 11) == 0)
+               else if (strncmp(translator_role, "translate-n", 11) == 0)
                        ospf_area_nssa_translator_role_set(
                                ospf, area_id, OSPF_NSSA_ROLE_NEVER);
-               else if (strncmp(argv[3]->text, "translate-a", 11) == 0)
+               else if (strncmp(translator_role, "translate-a", 11) == 0)
                        ospf_area_nssa_translator_role_set(
                                ospf, area_id, OSPF_NSSA_ROLE_ALWAYS);
        } else {
@@ -1467,12 +1501,27 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc,
                                                   OSPF_NSSA_ROLE_CANDIDATE);
        }
 
-       if (cfg_nosum) {
-               if (nosum)
-                       ospf_area_no_summary_set(ospf, area_id);
-               else
-                       ospf_area_no_summary_unset(ospf, area_id);
-       }
+       if (dflt_originate) {
+               int metric_type = DEFAULT_METRIC_TYPE;
+
+               if (mval_str == NULL)
+                       mval = -1;
+               if (mtype_str)
+                       (void)str2metric_type(mtype_str, &metric_type);
+               ospf_area_nssa_default_originate_set(ospf, area_id, mval,
+                                                    metric_type);
+       } else
+               ospf_area_nssa_default_originate_unset(ospf, area_id);
+
+       if (no_summary)
+               ospf_area_nssa_no_summary_set(ospf, area_id);
+       else
+               ospf_area_no_summary_unset(ospf, area_id);
+
+       if (suppress_fa)
+               ospf_area_nssa_suppress_fa_set(ospf, area_id);
+       else
+               ospf_area_nssa_suppress_fa_unset(ospf, area_id);
 
        /* Flush the external LSA for the specified area */
        ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA);
@@ -1482,168 +1531,125 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc,
        return CMD_SUCCESS;
 }
 
-
-DEFUN (ospf_area_nssa_translate,
-       ospf_area_nssa_translate_cmd,
-       "area <A.B.C.D|(0-4294967295)> nssa <translate-candidate|translate-never|translate-always>",
+DEFPY (no_ospf_area_nssa,
+       no_ospf_area_nssa_cmd,
+       "no area <A.B.C.D|(0-4294967295)>$area_str nssa\
+         [{\
+          <translate-candidate|translate-never|translate-always>\
+          |default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\
+          |no-summary\
+          |suppress-fa\
+        }]",
+       NO_STR
        "OSPF area parameters\n"
        "OSPF area ID in IP address format\n"
        "OSPF area ID as a decimal value\n"
        "Configure OSPF area as nssa\n"
        "Configure NSSA-ABR for translate election (default)\n"
        "Configure NSSA-ABR to never translate\n"
-       "Configure NSSA-ABR to always translate\n")
-{
-       return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0);
-}
-
-DEFUN (ospf_area_nssa,
-       ospf_area_nssa_cmd,
-       "area <A.B.C.D|(0-4294967295)> nssa",
-       "OSPF area parameters\n"
-       "OSPF area ID in IP address format\n"
-       "OSPF area ID as a decimal value\n"
-       "Configure OSPF area as nssa\n")
-{
-       return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0);
-}
-
-DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd,
-      "area <A.B.C.D|(0-4294967295)> nssa suppress-fa",
-      "OSPF area parameters\n"
-      "OSPF area ID in IP address format\n"
-      "OSPF area ID as a decimal value\n"
-      "Configure OSPF area as nssa\n"
-      "Suppress forwarding address\n")
+       "Configure NSSA-ABR to always translate\n"
+       "Originate Type 7 default into NSSA area\n"
+       "OSPF default metric\n"
+       "OSPF metric\n"
+       "OSPF metric type for default routes\n"
+       "Set OSPF External Type 1/2 metrics\n"
+       "Do not inject inter-area routes into nssa\n"
+       "Suppress forwarding address\n")
 {
-       int idx_ipv4_number = 1;
-       struct in_addr area_id;
-       int format;
-
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
-       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format,
-                                  argv[idx_ipv4_number]->arg);
-
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
-       ospf_area_nssa_suppress_fa_set(ospf, area_id);
-
-       ospf_schedule_abr_task(ospf);
-
-       return CMD_SUCCESS;
-}
-
-DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd,
-      "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa",
-      NO_STR
-      "OSPF area parameters\n"
-      "OSPF area ID in IP address format\n"
-      "OSPF area ID as a decimal value\n"
-      "Configure OSPF area as nssa\n"
-      "Suppress forwarding address\n")
-{
-       int idx_ipv4_number = 2;
        struct in_addr area_id;
        int format;
 
-       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str);
 
-       VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format,
-                                  argv[idx_ipv4_number]->arg);
-
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
+       /* Flush the NSSA LSA for the specified area */
+       ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA);
+       ospf_area_no_summary_unset(ospf, area_id);
+       ospf_area_nssa_default_originate_unset(ospf, area_id);
        ospf_area_nssa_suppress_fa_unset(ospf, area_id);
+       ospf_area_nssa_unset(ospf, area_id);
 
        ospf_schedule_abr_task(ospf);
 
        return CMD_SUCCESS;
 }
 
-DEFUN (ospf_area_nssa_no_summary,
-       ospf_area_nssa_no_summary_cmd,
-       "area <A.B.C.D|(0-4294967295)> nssa no-summary",
+DEFPY (ospf_area_nssa_range,
+       ospf_area_nssa_range_cmd,
+       "area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise$not_adv|cost (0-16777215)$cost>]",
        "OSPF area parameters\n"
        "OSPF area ID in IP address format\n"
        "OSPF area ID as a decimal value\n"
        "Configure OSPF area as nssa\n"
-       "Do not inject inter-area routes into nssa\n")
+       "Configured address range\n"
+       "Specify IPv4 prefix\n"
+       "Do not advertise\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
 {
-       int idx_ipv4_number = 1;
-       struct in_addr area_id;
-       int format;
-
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
-       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format,
-                                  argv[idx_ipv4_number]->arg);
-
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
-       ospf_area_nssa_no_summary_set(ospf, area_id);
-
-       ospf_schedule_abr_task(ospf);
-
-       return CMD_SUCCESS;
-}
-
-DEFUN (no_ospf_area_nssa_no_summary,
-       no_ospf_area_nssa_no_summary_cmd,
-       "no area <A.B.C.D|(0-4294967295)> nssa no-summary",
-       NO_STR
-       "OSPF area parameters\n"
-       "OSPF area ID in IP address format\n"
-       "OSPF area ID as a decimal value\n"
-       "Configure OSPF area as nssa\n"
-       "Do not inject inter-area routes into nssa\n")
-{
-       int idx_ipv4_number = 2;
+       struct ospf_area *area;
        struct in_addr area_id;
        int format;
+       int advertise = 0;
 
-       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       VTY_GET_OSPF_AREA_ID(area_id, format, area_str);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
 
-       VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format,
-                                  argv[idx_ipv4_number]->arg);
+       if (area->external_routing != OSPF_AREA_NSSA) {
+               vty_out(vty, "%% First configure %s as an NSSA area\n",
+                       area_str);
+               return CMD_WARNING;
+       }
 
-       ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
-                                    format);
-       ospf_area_no_summary_unset(ospf, area_id);
+       if (!not_adv)
+               advertise = OSPF_AREA_RANGE_ADVERTISE;
 
-       ospf_schedule_abr_task(ospf);
+       ospf_area_range_set(ospf, area, area->nssa_ranges,
+                           (struct prefix_ipv4 *)prefix, advertise, true);
+       if (cost_str)
+               ospf_area_range_cost_set(ospf, area, area->nssa_ranges,
+                                        (struct prefix_ipv4 *)prefix, cost);
 
        return CMD_SUCCESS;
 }
 
-DEFUN (no_ospf_area_nssa,
-       no_ospf_area_nssa_cmd,
-       "no area <A.B.C.D|(0-4294967295)> nssa [<translate-candidate|translate-never|translate-always>]",
+DEFPY (no_ospf_area_nssa_range,
+       no_ospf_area_nssa_range_cmd,
+       "no area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise|cost (0-16777215)>]",
        NO_STR
        "OSPF area parameters\n"
        "OSPF area ID in IP address format\n"
        "OSPF area ID as a decimal value\n"
        "Configure OSPF area as nssa\n"
-       "Configure NSSA-ABR for translate election (default)\n"
-       "Configure NSSA-ABR to never translate\n"
-       "Configure NSSA-ABR to always translate\n")
+       "Configured address range\n"
+       "Specify IPv4 prefix\n"
+       "Do not advertise\n"
+       "User specified metric for this range\n"
+       "Advertised metric for this range\n")
 {
        VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
-       int idx_ipv4_number = 2;
+       struct ospf_area *area;
        struct in_addr area_id;
        int format;
 
-       VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format,
-                                  argv[idx_ipv4_number]->arg);
+       VTY_GET_OSPF_AREA_ID(area_id, format, area_str);
+       area = ospf_area_get(ospf, area_id);
+       ospf_area_display_format_set(ospf, area, format);
 
-       /* Flush the NSSA LSA for the specified area */
-       ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA);
-       ospf_area_nssa_unset(ospf, area_id, argc);
+       if (area->external_routing != OSPF_AREA_NSSA) {
+               vty_out(vty, "%% First configure %s as an NSSA area\n",
+                       area_str);
+               return CMD_WARNING;
+       }
 
-       ospf_schedule_abr_task(ospf);
+       ospf_area_range_unset(ospf, area, area->nssa_ranges,
+                             (struct prefix_ipv4 *)prefix);
 
        return CMD_SUCCESS;
 }
 
-
 DEFUN (ospf_area_default_cost,
        ospf_area_default_cost_cmd,
        "area <A.B.C.D|(0-4294967295)> default-cost (0-16777215)",
@@ -3396,6 +3402,23 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf,
        /* show LDP-Sync status */
        ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0);
 
+       /* Socket buffer sizes */
+       if (json) {
+               if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE)
+                       json_object_int_add(json_vrf, "recvSockBufsize",
+                                           ospf->recv_sock_bufsize);
+               if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE)
+                       json_object_int_add(json_vrf, "sendSockBufsize",
+                                           ospf->send_sock_bufsize);
+       } else {
+               if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE)
+                       vty_out(vty, " Receive socket bufsize: %u\n",
+                               ospf->recv_sock_bufsize);
+               if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE)
+                       vty_out(vty, " Send socket bufsize: %u\n",
+                               ospf->send_sock_bufsize);
+       }
+
        /* Show each area status. */
        for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area))
                show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0);
@@ -3973,16 +3996,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
                /* Interface name is specified. */
                ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
                if (ifp == NULL) {
-                       if (use_json)
+                       if (use_json) {
                                json_object_boolean_true_add(json_vrf,
                                                             "noSuchIface");
-                       else
+                               json_object_free(json_interface);
+                       } else
                                vty_out(vty, "No such interface name\n");
                } else {
-                       if (use_json) {
+                       if (use_json)
                                json_interface_sub = json_object_new_object();
-                               json_interface = json_object_new_object();
-                       }
 
                        show_ip_ospf_interface_sub(
                                vty, ospf, ifp, json_interface_sub, use_json);
@@ -4407,15 +4429,10 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty,
                json_neighbor = json_object_new_object();
 
                ospf_nbr_ism_state_message(nbr, msgbuf, sizeof(msgbuf));
-#if CONFDATE > 20230321
-               CPP_NOTICE(
-                       "Remove show_ip_ospf_neighbor_sub() JSON keys: priority, state, deadTimeMsecs, address, retransmitCounter, requestCounter, dbSummaryCounter")
-#endif
-               json_object_int_add(json_neighbor, "priority", nbr->priority);
-               json_object_string_add(json_neighbor, "state", msgbuf);
+               json_object_string_add(json_neighbor, "nbrState", msgbuf);
+
                json_object_int_add(json_neighbor, "nbrPriority",
                                    nbr->priority);
-               json_object_string_add(json_neighbor, "nbrState", msgbuf);
 
                json_object_string_add(
                        json_neighbor, "converged",
@@ -4432,8 +4449,6 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty,
                                     1000LL;
                        json_object_int_add(json_neighbor, "upTimeInMsec",
                                            time_val);
-                       json_object_int_add(json_neighbor, "deadTimeMsecs",
-                                           time_store);
                        json_object_int_add(json_neighbor,
                                            "routerDeadIntervalTimerDueMsec",
                                            time_store);
@@ -4452,24 +4467,16 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty,
                                               "routerDeadIntervalTimerDueMsec",
                                               "inactive");
                }
-               json_object_string_addf(json_neighbor, "address", "%pI4",
-                                       &nbr->src);
                json_object_string_addf(json_neighbor, "ifaceAddress", "%pI4",
                                        &nbr->src);
                json_object_string_add(json_neighbor, "ifaceName",
                                       IF_NAME(nbr->oi));
-               json_object_int_add(json_neighbor, "retransmitCounter",
-                                   ospf_ls_retransmit_count(nbr));
                json_object_int_add(json_neighbor,
                                    "linkStateRetransmissionListCounter",
                                    ospf_ls_retransmit_count(nbr));
-               json_object_int_add(json_neighbor, "requestCounter",
-                                   ospf_ls_request_count(nbr));
                json_object_int_add(json_neighbor,
                                    "linkStateRequestListCounter",
                                    ospf_ls_request_count(nbr));
-               json_object_int_add(json_neighbor, "dbSummaryCounter",
-                                   ospf_db_summary_count(nbr));
                json_object_int_add(json_neighbor, "databaseSummaryListCounter",
                                    ospf_db_summary_count(nbr));
 
@@ -5065,6 +5072,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
        json_object *json_neigh = NULL, *json_neigh_array = NULL;
        char neigh_str[INET_ADDRSTRLEN] = {0};
        char neigh_state[16] = {0};
+       struct ospf_neighbor *nbr_dr, *nbr_bdr;
 
        if (use_json) {
                if (prev_nbr &&
@@ -5192,19 +5200,38 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty,
                }
        }
 
-       /* Show Designated Rotuer ID. */
-       if (use_json)
-               json_object_string_addf(json_neigh, "routerDesignatedId",
-                                       "%pI4", &nbr->d_router);
-       else
-               vty_out(vty, "    DR is %pI4,", &nbr->d_router);
+       /* Show Designated Router ID. */
+       if (DR(oi).s_addr == INADDR_ANY) {
+               if (!use_json)
+                       vty_out(vty,
+                               "  No designated router on this network\n");
+       } else {
+               nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi));
+               if (nbr_dr) {
+                       if (use_json)
+                               json_object_string_addf(
+                                       json_neigh, "routerDesignatedId",
+                                       "%pI4", &nbr_dr->router_id);
+                       else
+                               vty_out(vty, "    DR is %pI4,",
+                                       &nbr_dr->router_id);
+               }
+       }
 
-       /* Show Backup Designated Rotuer ID. */
-       if (use_json)
-               json_object_string_addf(json_neigh, "routerDesignatedBackupId",
-                                       "%pI4", &nbr->bd_router);
-       else
-               vty_out(vty, " BDR is %pI4\n", &nbr->bd_router);
+       /* Show Backup Designated Router ID. */
+       nbr_bdr = ospf_nbr_lookup_by_addr(oi->nbrs, &BDR(oi));
+       if (nbr_bdr == NULL) {
+               if (!use_json)
+                       vty_out(vty,
+                               "  No backup designated router on this network\n");
+       } else {
+               if (use_json)
+                       json_object_string_addf(json_neigh,
+                                               "routerDesignatedBackupId",
+                                               "%pI4", &nbr_bdr->router_id);
+               else
+                       vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id);
+       }
 
        /* Show options. */
        if (use_json) {
@@ -10053,12 +10080,21 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf,
                json_object_string_add(json_vrf, "strictLsaCheck",
                                       (ospf->strict_lsa_check) ? "Enabled"
                                                                : "Disabled");
+#if CONFDATE > 20240401
+               CPP_NOTICE("Remove deprecated json key: restartSupoort")
+#endif
                json_object_string_add(
                        json_vrf, "restartSupoort",
                        (ospf->only_planned_restart)
                                ? "Planned Restart only"
                                : "Planned and Unplanned Restarts");
 
+               json_object_string_add(
+                       json_vrf, "restartSupport",
+                       (ospf->only_planned_restart)
+                               ? "Planned Restart only"
+                               : "Planned and Unplanned Restarts");
+
                json_object_int_add(json_vrf, "supportedGracePeriod",
                                    ospf->supported_grace_time);
 
@@ -11981,29 +12017,62 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf)
                                        vty_out(vty, " no-summary\n");
                                vty_out(vty, "\n");
                        } else if (area->external_routing == OSPF_AREA_NSSA) {
+                               vty_out(vty, " area %s nssa", buf);
+
                                switch (area->NSSATranslatorRole) {
                                case OSPF_NSSA_ROLE_NEVER:
-                                       vty_out(vty,
-                                               " area %s nssa translate-never\n",
-                                               buf);
+                                       vty_out(vty, " translate-never");
                                        break;
                                case OSPF_NSSA_ROLE_ALWAYS:
-                                       vty_out(vty,
-                                               " area %s nssa translate-always\n",
-                                               buf);
+                                       vty_out(vty, " translate-always");
                                        break;
                                case OSPF_NSSA_ROLE_CANDIDATE:
-                                       vty_out(vty, " area %s nssa \n", buf);
                                        break;
                                }
-                               if (area->no_summary)
+
+                               if (area->nssa_default_originate.enabled) {
                                        vty_out(vty,
-                                               " area %s nssa no-summary\n",
-                                               buf);
+                                               " default-information-originate");
+                                       if (area->nssa_default_originate
+                                                   .metric_value != -1)
+                                               vty_out(vty, " metric %d",
+                                                       area->nssa_default_originate
+                                                               .metric_value);
+                                       if (area->nssa_default_originate
+                                                   .metric_type !=
+                                           DEFAULT_METRIC_TYPE)
+                                               vty_out(vty, " metric-type 1");
+                               }
+
+                               if (area->no_summary)
+                                       vty_out(vty, " no-summary");
                                if (area->suppress_fa)
-                                       vty_out(vty,
-                                               " area %s nssa suppress-fa\n",
-                                               buf);
+                                       vty_out(vty, " suppress-fa");
+                               vty_out(vty, "\n");
+
+                               for (rn1 = route_top(area->nssa_ranges); rn1;
+                                    rn1 = route_next(rn1)) {
+                                       struct ospf_area_range *range;
+
+                                       range = rn1->info;
+                                       if (!range)
+                                               continue;
+
+                                       vty_out(vty, " area %s nssa range %pFX",
+                                               buf, &rn1->p);
+
+                                       if (range->cost_config !=
+                                           OSPF_AREA_RANGE_COST_UNSPEC)
+                                               vty_out(vty, " cost %u",
+                                                       range->cost_config);
+
+                                       if (!CHECK_FLAG(
+                                                   range->flags,
+                                                   OSPF_AREA_RANGE_ADVERTISE))
+                                               vty_out(vty, " not-advertise");
+
+                                       vty_out(vty, "\n");
+                               }
                        }
 
                        if (area->default_cost != 1)
@@ -12434,6 +12503,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
        if (ospf->fr_configured)
                vty_out(vty, " flood-reduction\n");
 
+       if (!ospf->intf_socket_enabled)
+               vty_out(vty, " no socket-per-interface\n");
+
        /* Redistribute information print. */
        config_write_ospf_redistribute(vty, ospf);
 
@@ -12490,6 +12562,22 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
        /* LDP-Sync print */
        ospf_ldp_sync_write_config(vty, ospf);
 
+       /* Socket buffer sizes */
+       if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) {
+               if (ospf->send_sock_bufsize == ospf->recv_sock_bufsize)
+                       vty_out(vty, " socket buffer all %u\n",
+                               ospf->recv_sock_bufsize);
+               else
+                       vty_out(vty, " socket buffer recv %u\n",
+                               ospf->recv_sock_bufsize);
+       }
+
+       if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE &&
+           ospf->send_sock_bufsize != ospf->recv_sock_bufsize)
+               vty_out(vty, " socket buffer send %u\n",
+                       ospf->send_sock_bufsize);
+
+
        vty_out(vty, "exit\n");
 
        write++;
@@ -12946,6 +13034,71 @@ DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd,
        return CMD_SUCCESS;
 }
 
+DEFPY(ospf_socket_bufsizes,
+      ospf_socket_bufsizes_cmd,
+      "[no] socket buffer <send$send_val | recv$recv_val | all$all_val> \
+         ![(1-4000000000)$bufsize]",
+      NO_STR
+      "Socket parameters\n"
+      "Buffer size configuration\n"
+      "Send buffer size\n"
+      "Receive buffer size\n"
+      "Both send and receive buffer sizes\n"
+      "Buffer size, in bytes\n")
+{
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       uint32_t recvsz, sendsz;
+
+       if (no)
+               bufsize = OSPF_DEFAULT_SOCK_BUFSIZE;
+
+       if (all_val) {
+               recvsz = bufsize;
+               sendsz = bufsize;
+       } else if (send_val) {
+               sendsz = bufsize;
+               recvsz = ospf->recv_sock_bufsize;
+       } else if (recv_val) {
+               recvsz = bufsize;
+               sendsz = ospf->send_sock_bufsize;
+       } else
+               return CMD_SUCCESS;
+
+       /* React to a change by modifying existing sockets */
+       ospf_update_bufsize(ospf, recvsz, sendsz);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (per_intf_socket,
+       per_intf_socket_cmd,
+       "[no] socket-per-interface",
+       NO_STR
+       "Use write socket per interface\n")
+{
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+       struct listnode *node;
+       struct ospf_interface *oi;
+
+       if (no) {
+               if (ospf->intf_socket_enabled) {
+                       ospf->intf_socket_enabled = false;
+
+                       /* Iterate and close any sockets */
+                       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
+                               ospf_ifp_sock_close(oi->ifp);
+               }
+       } else if (!ospf->intf_socket_enabled) {
+               ospf->intf_socket_enabled = true;
+
+               /* Iterate and open sockets */
+               for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
+                       ospf_ifp_sock_init(oi->ifp);
+       }
+
+       return CMD_SUCCESS;
+}
+
 void ospf_vty_clear_init(void)
 {
        install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd);
@@ -13029,12 +13182,9 @@ void ospf_vty_init(void)
 
        /* "area nssa" commands. */
        install_element(OSPF_NODE, &ospf_area_nssa_cmd);
-       install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd);
-       install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd);
-       install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd);
-       install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd);
-       install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd);
        install_element(OSPF_NODE, &no_ospf_area_nssa_cmd);
+       install_element(OSPF_NODE, &ospf_area_nssa_range_cmd);
+       install_element(OSPF_NODE, &no_ospf_area_nssa_range_cmd);
 
        install_element(OSPF_NODE, &ospf_area_default_cost_cmd);
        install_element(OSPF_NODE, &no_ospf_area_default_cost_cmd);
@@ -13112,6 +13262,9 @@ void ospf_vty_init(void)
        install_element(OSPF_NODE, &flood_reduction_area_cmd);
        install_element(OSPF_NODE, &no_flood_reduction_area_cmd);
 
+       install_element(OSPF_NODE, &ospf_socket_bufsizes_cmd);
+       install_element(OSPF_NODE, &per_intf_socket_cmd);
+
        /* Init interface related vty commands. */
        ospf_vty_if_init();
 
index 3e02d3c33e1dcfa5179bdd769fa7b34a2bf9ced3..0b770a8364787c8679326798e9f2e3981cceb6ba 100644 (file)
@@ -20,6 +20,7 @@
 #include "log.h"
 #include "route_opaque.h"
 #include "lib/bfd.h"
+#include "lib/lib_errors.h"
 #include "nexthop.h"
 
 #include "ospfd/ospfd.h"
@@ -955,8 +956,8 @@ int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype,
                type_str = "always";
                ospf->redistribute++;
                ospf_external_add(ospf, DEFAULT_ROUTE, 0);
-               ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop,
-                                      0);
+               ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, 0,
+                                      DEFAULT_DEFAULT_METRIC);
                break;
        }
 
@@ -1302,9 +1303,11 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
                rt_type = DEFAULT_ROUTE;
 
        if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE))
-               zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %pFX",
-                          __func__, zserv_command_string(cmd),
-                          zebra_route_string(api.type), vrf_id, &api.prefix);
+               zlog_debug(
+                       "%s: cmd %s from client %s: vrf_id %d, p %pFX, metric %d",
+                       __func__, zserv_command_string(cmd),
+                       zebra_route_string(api.type), vrf_id, &api.prefix,
+                       api.metric);
 
        if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) {
                /* XXX|HACK|TODO|FIXME:
@@ -1331,7 +1334,8 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
                                                          p);
 
                ei = ospf_external_info_add(ospf, rt_type, api.instance, p,
-                                           ifindex, nexthop, api.tag);
+                                           ifindex, nexthop, api.tag,
+                                           api.metric);
                if (ei == NULL) {
                        /* Nothing has changed, so nothing to do; return */
                        return 0;
@@ -1470,6 +1474,61 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
        return 0;
 }
 
+void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg)
+{
+       struct prefix prefix = {};
+       int command;
+
+       if (zclient->sock < 0) {
+               if (IS_DEBUG_OSPF(zebra, ZEBRA))
+                       zlog_debug("  Not connected to Zebra");
+               return;
+       }
+
+       prefix.family = AF_INET;
+       prefix.prefixlen = 0;
+
+       if (unreg)
+               command = ZEBRA_NEXTHOP_UNREGISTER;
+       else
+               command = ZEBRA_NEXTHOP_REGISTER;
+
+       if (IS_DEBUG_OSPF(zebra, ZEBRA))
+               zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__,
+                          zserv_command_string(command), &prefix,
+                          ospf->vrf_id);
+
+       if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false,
+                            true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE)
+               flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed",
+                        __func__);
+}
+
+static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS)
+{
+       struct ospf *ospf;
+       struct zapi_route nhr;
+       struct prefix matched;
+
+       ospf = ospf_lookup_by_vrf_id(vrf_id);
+       if (ospf == NULL || !IS_OSPF_ASBR(ospf))
+               return 0;
+
+       if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
+               zlog_err("%s[%u]: Failure to decode route", __func__,
+                        ospf->vrf_id);
+               return -1;
+       }
+
+       if (matched.family != AF_INET || matched.prefixlen != 0 ||
+           nhr.type == ZEBRA_ROUTE_OSPF)
+               return 0;
+
+       ospf->nssa_default_import_check.status = !!nhr.nexthop_num;
+       ospf_abr_nssa_type7_defaults(ospf);
+
+       return 0;
+}
 
 int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name)
 {
@@ -2132,6 +2191,7 @@ static zclient_handler *const ospf_handlers[] = {
 
        [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route,
        [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route,
+       [ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update,
 
        [ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler,
 
index 711b1e7a61e3c32c954924ff1c9f802653dc7320..86a5678fc4fa89917f3c794ad7b181d91394ec81 100644 (file)
@@ -69,6 +69,7 @@ extern int ospf_redistribute_set(struct ospf *, struct ospf_redist *, int,
                                 unsigned short, int, int);
 extern int ospf_redistribute_unset(struct ospf *, int, unsigned short);
 extern int ospf_redistribute_default_set(struct ospf *, int, int, int);
+extern void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg);
 extern int ospf_distribute_list_out_set(struct ospf *, int, const char *);
 extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *);
 extern void ospf_routemap_set(struct ospf_redist *, const char *);
index 112ddfedb7c4440820f1f0d50017fe9ac48bc9a5..7e83714c0ae18d99b5b113ad6d5739876d93e427 100644 (file)
@@ -419,6 +419,10 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name)
        QOBJ_REG(new, ospf);
 
        new->fd = -1;
+       new->intf_socket_enabled = true;
+
+       new->recv_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE;
+       new->send_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE;
 
        return new;
 }
@@ -963,6 +967,7 @@ struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id)
 
        new->oiflist = list_new();
        new->ranges = route_table_init();
+       new->nssa_ranges = route_table_init();
 
        if (area_id.s_addr == OSPF_AREA_BACKBONE)
                ospf->backbone = new;
@@ -1006,6 +1011,7 @@ static void ospf_area_free(struct ospf_area *area)
        ospf_lsa_unlock(&area->router_lsa_self);
 
        route_table_finish(area->ranges);
+       route_table_finish(area->nssa_ranges);
        list_delete(&area->oiflist);
 
        if (EXPORT_NAME(area))
@@ -1029,13 +1035,14 @@ void ospf_area_check_free(struct ospf *ospf, struct in_addr area_id)
        struct ospf_area *area;
 
        area = ospf_area_lookup_by_area_id(ospf, area_id);
-       if (area && listcount(area->oiflist) == 0 && area->ranges->top == NULL
-           && !ospf_vl_count(ospf, area)
-           && area->shortcut_configured == OSPF_SHORTCUT_DEFAULT
-           && area->external_routing == OSPF_AREA_DEFAULT
-           && area->no_summary == 0 && area->default_cost == 1
-           && EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL
-           && area->auth_type == OSPF_AUTH_NULL) {
+       if (area && listcount(area->oiflist) == 0 &&
+           area->ranges->top == NULL && area->nssa_ranges->top == NULL &&
+           !ospf_vl_count(ospf, area) &&
+           area->shortcut_configured == OSPF_SHORTCUT_DEFAULT &&
+           area->external_routing == OSPF_AREA_DEFAULT &&
+           area->no_summary == 0 && area->default_cost == 1 &&
+           EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL &&
+           area->auth_type == OSPF_AUTH_NULL) {
                listnode_delete(ospf->areas, area);
                ospf_area_free(area);
        }
@@ -1701,7 +1708,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id)
        return 1;
 }
 
-int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc)
+int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id)
 {
        struct ospf_area *area;
 
@@ -1709,22 +1716,14 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc)
        if (area == NULL)
                return 0;
 
-       /* argc < 5 -> 'no area x nssa' */
-       if (argc < 5 && area->external_routing == OSPF_AREA_NSSA) {
-               ospf->anyNSSA--;
-               /* set NSSA area defaults */
-               area->no_summary = 0;
-               area->suppress_fa = 0;
-               area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
-               area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
-               area->NSSATranslatorStabilityInterval =
-                       OSPF_NSSA_TRANS_STABLE_DEFAULT;
-               ospf_area_type_set(area, OSPF_AREA_DEFAULT);
-       } else {
-               ospf_area_nssa_translator_role_set(ospf, area_id,
-                                                  OSPF_NSSA_ROLE_CANDIDATE);
-       }
-
+       ospf->anyNSSA--;
+       /* set NSSA area defaults */
+       area->no_summary = 0;
+       area->suppress_fa = 0;
+       area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
+       area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
+       area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT;
+       ospf_area_type_set(area, OSPF_AREA_DEFAULT);
        ospf_area_check_free(ospf, area_id);
 
        return 1;
@@ -1782,6 +1781,51 @@ int ospf_area_nssa_translator_role_set(struct ospf *ospf,
        return 1;
 }
 
+void ospf_area_nssa_default_originate_set(struct ospf *ospf,
+                                         struct in_addr area_id, int metric,
+                                         int metric_type)
+{
+       struct ospf_area *area;
+
+       area = ospf_area_lookup_by_area_id(ospf, area_id);
+       if (area == NULL)
+               return;
+
+       if (!area->nssa_default_originate.enabled) {
+               area->nssa_default_originate.enabled = true;
+               if (++ospf->nssa_default_import_check.refcnt == 1) {
+                       ospf->nssa_default_import_check.status = false;
+                       ospf_zebra_import_default_route(ospf, false);
+               }
+       }
+
+       area->nssa_default_originate.metric_value = metric;
+       area->nssa_default_originate.metric_type = metric_type;
+}
+
+void ospf_area_nssa_default_originate_unset(struct ospf *ospf,
+                                           struct in_addr area_id)
+{
+       struct ospf_area *area;
+
+       area = ospf_area_lookup_by_area_id(ospf, area_id);
+       if (area == NULL)
+               return;
+
+       if (area->nssa_default_originate.enabled) {
+               area->nssa_default_originate.enabled = false;
+               if (--ospf->nssa_default_import_check.refcnt == 0) {
+                       ospf->nssa_default_import_check.status = false;
+                       ospf_zebra_import_default_route(ospf, true);
+               }
+               area->nssa_default_originate.metric_value = -1;
+               area->nssa_default_originate.metric_type = -1;
+
+               if (!IS_OSPF_ABR(ospf))
+                       ospf_abr_nssa_type7_defaults(ospf);
+       }
+}
+
 int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area,
                              const char *list_name)
 {
@@ -2140,6 +2184,32 @@ int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, struct in_addr addr)
        return 1;
 }
 
+/*
+ * Update socket bufsize(s), usually after config change
+ */
+void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize,
+                        uint32_t sendsize)
+{
+       enum ospf_sock_type_e type = OSPF_SOCK_NONE;
+
+       /* Figure out whether there's been a change */
+       if (recvsize != ospf->recv_sock_bufsize) {
+               type = OSPF_SOCK_RECV;
+               ospf->recv_sock_bufsize = recvsize;
+
+               if (sendsize != ospf->send_sock_bufsize) {
+                       type = OSPF_SOCK_BOTH;
+                       ospf->send_sock_bufsize = sendsize;
+               }
+       } else if (sendsize != ospf->send_sock_bufsize) {
+               type = OSPF_SOCK_SEND;
+               ospf->send_sock_bufsize = sendsize;
+       }
+
+       if (type != OSPF_SOCK_NONE)
+               ospf_sock_bufsize_update(ospf, ospf->fd, type);
+}
+
 void ospf_master_init(struct event_loop *master)
 {
        memset(&ospf_master, 0, sizeof(ospf_master));
index 6727d802a65faddb932a24ead0a38c790b479894..1f8d1a32e6b7a2ff2d514aaaa75b3599d9dd2dd0 100644 (file)
@@ -67,6 +67,9 @@
 #define OSPF_LS_REFRESH_SHIFT       (60 * 15)
 #define OSPF_LS_REFRESH_JITTER      60
 
+/* Default socket buffer size */
+#define OSPF_DEFAULT_SOCK_BUFSIZE   (8 * 1024 * 1024)
+
 struct ospf_external {
        unsigned short instance;
        struct route_table *external_info;
@@ -302,6 +305,18 @@ struct ospf {
 
        int default_metric; /* Default metric for redistribute. */
 
+       /* NSSA default-information-originate */
+       struct {
+               /* # of NSSA areas requesting default information */
+               uint16_t refcnt;
+
+               /*
+                * Whether a default route known through non-OSPF protocol is
+                * present in the RIB.
+                */
+               bool status;
+       } nssa_default_import_check;
+
 #define OSPF_LSA_REFRESHER_GRANULARITY 10
 #define OSPF_LSA_REFRESHER_SLOTS                                               \
        ((OSPF_LS_REFRESH_TIME + OSPF_LS_REFRESH_SHIFT)                        \
@@ -412,6 +427,13 @@ struct ospf {
        /* Flood Reduction configuration state */
        bool fr_configured;
 
+       /* Socket buffer sizes */
+       uint32_t recv_sock_bufsize;
+       uint32_t send_sock_bufsize;
+
+       /* Per-interface write socket */
+       bool intf_socket_enabled;
+
        QOBJ_FIELDS;
 };
 DECLARE_QOBJ_TYPE(ospf);
@@ -517,6 +539,7 @@ struct ospf_area {
 #define OSPF_TRANSIT_FALSE      0
 #define OSPF_TRANSIT_TRUE       1
        struct route_table *ranges; /* Configured Area Ranges. */
+       struct route_table *nssa_ranges; /* Configured NSSA Area Ranges. */
 
        /* RFC3137 stub router state flags for area */
        uint8_t stub_router_state;
@@ -561,6 +584,13 @@ struct ospf_area {
 #define PREFIX_LIST_OUT(A)  (A)->plist_out.list
 #define PREFIX_NAME_OUT(A)  (A)->plist_out.name
 
+       /* NSSA default-information-originate */
+       struct {
+               bool enabled;
+               int metric_type;
+               int metric_value;
+       } nssa_default_originate;
+
        /* Shortest Path Tree. */
        struct vertex *spf;
        struct list *spf_vertex_list;
@@ -697,14 +727,18 @@ extern int ospf_area_no_summary_set(struct ospf *ospf, struct in_addr area_id);
 extern int ospf_area_no_summary_unset(struct ospf *ospf,
                                      struct in_addr area_id);
 extern int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id);
-extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id,
-                               int argc);
+extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id);
 extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf,
                                          struct in_addr area_id);
 extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf,
                                            struct in_addr area_id);
 extern int ospf_area_nssa_translator_role_set(struct ospf *ospf,
                                              struct in_addr area_id, int role);
+extern void ospf_area_nssa_default_originate_set(struct ospf *ospf,
+                                                struct in_addr area_id,
+                                                int metric, int metric_type);
+extern void ospf_area_nssa_default_originate_unset(struct ospf *ospf,
+                                                  struct in_addr area_id);
 extern int ospf_area_export_list_set(struct ospf *ospf,
                                     struct ospf_area *area_id,
                                     const char *list_name);
@@ -769,6 +803,9 @@ int ospf_area_nssa_no_summary_set(struct ospf *ospf, struct in_addr area_id);
 const char *ospf_get_name(const struct ospf *ospf);
 extern struct ospf_interface *add_ospf_interface(struct connected *co,
                                                 struct ospf_area *area);
+/* Update socket bufsize(s), after config change */
+void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize,
+                        uint32_t sendsize);
 
 extern int p_spaces_compare_func(const struct p_space *a,
                                 const struct p_space *b);
index d8ddd8cdc88f7d08a50ae8049f64dfb1747f2aab..df23f9312744f1da4b720db783a6ff063649fefc 100644 (file)
@@ -206,7 +206,7 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote)
 {
        uint32_t sid = MPLS_LABEL_NONE;
        struct ls_edge *edge;
-       uint64_t key;
+       struct ls_edge_key key;
 
        if (!path_ted_is_initialized())
                return MPLS_LABEL_NONE;
@@ -218,7 +218,8 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote)
        case IPADDR_V4:
                /* We have local and remote ip */
                /* so check all attributes in ted */
-               key = ((uint64_t)ntohl(local->ip._v4_addr.s_addr)) & 0xffffffff;
+               key.family = AF_INET;
+               IPV4_ADDR_COPY(&key.k.addr, &local->ip._v4_addr);
                edge = ls_find_edge_by_key(ted_state_g.ted, key);
                if (edge) {
                        if (edge->attributes->standard.remote.s_addr
@@ -232,8 +233,8 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote)
                }
                break;
        case IPADDR_V6:
-               key = (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[2]) << 32 |
-                     (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[3]);
+               key.family = AF_INET6;
+               IPV6_ADDR_COPY(&key.k.addr6, &local->ip._v6_addr);
                edge = ls_find_edge_by_key(ted_state_g.ted, key);
                if (edge) {
                        if ((0 == memcmp(&edge->attributes->standard.remote6,
@@ -268,7 +269,7 @@ uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo)
        switch (prefix->family) {
        case AF_INET:
        case AF_INET6:
-               subnet = ls_find_subnet(ted_state_g.ted, *prefix);
+               subnet = ls_find_subnet(ted_state_g.ted, prefix);
                if (subnet) {
                        if ((CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR))
                            && (subnet->ls_pref->sr.algo == algo))
@@ -298,7 +299,7 @@ uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id)
        switch (prefix->family) {
        case AF_INET:
        case AF_INET6:
-               subnet = ls_find_subnet(ted_state_g.ted, *prefix);
+               subnet = ls_find_subnet(ted_state_g.ted, prefix);
                if (subnet && subnet->vertex
                    && subnet->vertex->outgoing_edges) {
                        /* from the vertex linked in subnet */
index 9394e4c15a39ae187a58c38f08b9a307e74ad44f..94e8b874f7799e407a4d5c6bb88933061cebd925 100644 (file)
@@ -539,11 +539,8 @@ DEFPY (interface_ipv6_mld_join,
        "Source address\n")
 {
        char xpath[XPATH_MAXLEN];
-       struct ipaddr group_addr = {0};
 
-       (void)str2ipaddr(group_str, &group_addr);
-
-       if (!IN6_IS_ADDR_MULTICAST(&group_addr)) {
+       if (!IN6_IS_ADDR_MULTICAST(&group)) {
                vty_out(vty, "Invalid Multicast Address\n");
                return CMD_WARNING_CONFIG_FAILED;
        }
index 1d84a9941b8a083913a7a163627e0c4684f454f8..aa39be63dbbb00e0ffb50b12695214849eef58c1 100644 (file)
@@ -99,7 +99,7 @@ static inline uint8_t in6_multicast_scope(const pim_addr *addr)
        return addr->s6_addr[1] & 0xf;
 }
 
-static inline bool in6_multicast_nofwd(const pim_addr *addr)
+bool in6_multicast_nofwd(const pim_addr *addr)
 {
        return in6_multicast_scope(addr) <= IPV6_MULTICAST_SCOPE_LINK;
 }
@@ -182,13 +182,11 @@ DECLARE_HASH(gm_gsq_pends, struct gm_gsq_pending, itm, gm_gsq_pending_cmp,
  * interface -> (S,G)
  */
 
-static int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b)
+int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b)
 {
        return pim_sgaddr_cmp(a->sgaddr, b->sgaddr);
 }
 
-DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp);
-
 static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp,
                                pim_addr src)
 {
@@ -346,7 +344,8 @@ static const char *const gm_states[] = {
 };
 /* clang-format on */
 
-CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported");
+/* TODO: S,G entries in EXCLUDE (i.e. prune) unsupported" */
+
 /* tib_sg_gm_prune() below is an "un-join", it doesn't prune S,G when *,G is
  * joined.  Whether we actually want/need to support this is a separate
  * question - it is almost never used.  In fact this is exactly what RFC5790
@@ -596,7 +595,7 @@ static void gm_sg_expiry_cancel(struct gm_sg *sg)
  * everything else is thrown into pkt for creation of state in pass 2
  */
 static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
-                              struct mld_v2_rec_hdr *rechdr)
+                              struct mld_v2_rec_hdr *rechdr, size_t n_src)
 {
        /* NB: pkt->subscriber can be NULL here if the subscriber was not
         * previously seen!
@@ -605,7 +604,6 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
        struct gm_sg *grp;
        struct gm_packet_sg *old_grp = NULL;
        struct gm_packet_sg *item;
-       size_t n_src = ntohs(rechdr->n_src);
        size_t j;
        bool is_excl = false;
 
@@ -648,7 +646,7 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
                         */
                        gm_packet_sg_drop(old_grp);
                        gm_sg_update(grp, false);
-                       CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
                }
                break;
 
@@ -796,7 +794,8 @@ static void gm_handle_v2_pass2_excl(struct gm_packet_state *pkt, size_t offs)
        gm_sg_update(sg_grp, false);
 }
 
-CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state");
+/* TODO: QRV/QQIC are not copied from queries to local state" */
+
 /* on receiving a query, we need to update our robustness/query interval to
  * match, so we correctly process group/source specific queries after last
  * member leaves
@@ -818,13 +817,24 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp,
                return;
        }
 
-       /* errors after this may at least partially process the packet */
-       gm_ifp->stats.rx_new_report++;
-
        hdr = (struct mld_v2_report_hdr *)data;
        data += sizeof(*hdr);
        len -= sizeof(*hdr);
 
+       n_records = ntohs(hdr->n_records);
+       if (n_records > len / sizeof(struct mld_v2_rec_hdr)) {
+               /* note this is only an upper bound, records with source lists
+                * are larger.  This is mostly here to make coverity happy.
+                */
+               zlog_warn(log_pkt_src(
+                       "malformed MLDv2 report (infeasible record count)"));
+               gm_ifp->stats.rx_drop_malformed++;
+               return;
+       }
+
+       /* errors after this may at least partially process the packet */
+       gm_ifp->stats.rx_new_report++;
+
        /* can't have more *,G and S,G items than there is space for ipv6
         * addresses, so just use this to allocate temporary buffer
         */
@@ -835,8 +845,6 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp,
        pkt->iface = gm_ifp;
        pkt->subscriber = gm_subscriber_findref(gm_ifp, pkt_src->sin6_addr);
 
-       n_records = ntohs(hdr->n_records);
-
        /* validate & remove state in v2_pass1() */
        for (i = 0; i < n_records; i++) {
                struct mld_v2_rec_hdr *rechdr;
@@ -874,7 +882,7 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp,
                data += record_size;
                len -= record_size;
 
-               gm_handle_v2_pass1(pkt, rechdr);
+               gm_handle_v2_pass1(pkt, rechdr, n_src);
        }
 
        if (!pkt->n_active) {
@@ -943,7 +951,8 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp,
 
        item = gm_packet_sg_setup(pkt, grp, true, false);
        item->n_exclude = 0;
-       CPP_NOTICE("set v1-seen timer on grp here");
+
+/* TODO "set v1-seen timer on grp here" */
 
        /* } */
 
@@ -1006,7 +1015,9 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp,
                if (old_grp) {
                        gm_packet_sg_drop(old_grp);
                        gm_sg_update(grp, false);
-                       CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
+
                }
        }
 
@@ -1498,6 +1509,15 @@ static void gm_handle_query(struct gm_if *gm_ifp,
                gm_handle_q_group(gm_ifp, &timers, hdr->grp);
                gm_ifp->stats.rx_query_new_group++;
        } else {
+               /* this is checked above:
+                * if (len >= sizeof(struct mld_v2_query_hdr)) {
+                *   size_t src_space = ntohs(hdr->n_src) * sizeof(pim_addr);
+                *   if (len < sizeof(struct mld_v2_query_hdr) + src_space) {
+                */
+               assume(ntohs(hdr->n_src) <=
+                      (len - sizeof(struct mld_v2_query_hdr)) /
+                              sizeof(pim_addr));
+
                gm_handle_q_groupsrc(gm_ifp, &timers, hdr->grp, hdr->srcs,
                                     ntohs(hdr->n_src));
                gm_ifp->stats.rx_query_new_groupsrc++;
@@ -2256,6 +2276,7 @@ void gm_ifp_update(struct interface *ifp)
        if (!pim_ifp->mld) {
                changed = true;
                gm_start(ifp);
+               assume(pim_ifp->mld != NULL);
        }
 
        gm_ifp = pim_ifp->mld;
@@ -2391,6 +2412,8 @@ static void gm_show_if_one(struct vty *vty, struct interface *ifp,
        struct gm_if *gm_ifp = pim_ifp->mld;
        bool querier;
 
+       assume(js_if || tt);
+
        querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
 
        if (js_if) {
@@ -2473,6 +2496,19 @@ static void gm_show_if_vrf(struct vty *vty, struct vrf *vrf, const char *ifname,
 
                if (js) {
                        js_if = json_object_new_object();
+                       /*
+                        * If we have js as true and detail as false
+                        * and if Coverity thinks that js_if is NULL
+                        * because of a failed call to new then
+                        * when we call gm_show_if_one below
+                        * the tt can be deref'ed and as such
+                        * FRR will crash.  But since we know
+                        * that json_object_new_object never fails
+                        * then let's tell Coverity that this assumption
+                        * is true.  I'm not worried about fast path
+                        * here at all.
+                        */
+                       assert(js_if);
                        json_object_object_add(js_vrf, ifp->name, js_if);
                }
 
index d3f1d39257f0dd1b59b394c5ec2ab5e035c24ef4..7634fb2ec4537c5dd888bffa996ccdf978f2fba4 100644 (file)
@@ -113,6 +113,8 @@ struct gm_sg {
         */
        struct gm_packet_sg *most_recent;
 };
+int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b);
+DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp);
 
 /* host tracking entry.  addr will be one of:
  *
@@ -352,5 +354,6 @@ static inline void gm_ifp_teardown(struct interface *ifp)
 #endif
 
 extern void gm_cli_init(void);
+bool in6_multicast_nofwd(const pim_addr *addr);
 
 #endif /* PIM6_MLD_H */
index cc473d1f809bdc14977c047531537cd92c3993e3..94c63bbccc78faf09765f5159e15ee3228d13b57 100644 (file)
@@ -28,6 +28,7 @@ typedef struct in_addr pim_addr;
 #define PIM_MROUTE_DBG  "mroute"
 #define PIMREG          "pimreg"
 #define GM              "IGMP"
+#define IPPROTO_GM      IPPROTO_IGMP
 
 #define PIM_ADDR_FUNCNAME(name) ipv4_##name
 
@@ -57,6 +58,7 @@ typedef struct in6_addr pim_addr;
 #define PIM_MROUTE_DBG  "mroute6"
 #define PIMREG          "pim6reg"
 #define GM              "MLD"
+#define IPPROTO_GM      IPPROTO_ICMPV6
 
 #define PIM_ADDR_FUNCNAME(name) ipv6_##name
 
index b36e006311aeb857d92894d6000484fb0bb24857..df9161943d378544722032c4d1e60c7777a9471f 100644 (file)
@@ -156,7 +156,6 @@ static void pim_on_bs_timer(struct event *t)
 
        pim_nht_bsr_del(scope->pim, scope->current_bsr);
        /* Reset scope zone data */
-       scope->accept_nofwd_bsm = false;
        scope->state = ACCEPT_ANY;
        scope->current_bsr = PIMADDR_ANY;
        scope->current_bsr_prio = 0;
@@ -1364,6 +1363,10 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf,
                }
        }
 
+       /* BSM packet is seen, so resetting accept_nofwd_bsm to false */
+       if (pim->global_scope.accept_nofwd_bsm)
+               pim->global_scope.accept_nofwd_bsm = false;
+
        if (!pim_addr_cmp(sg->grp, qpim_all_pim_routers_addr)) {
                /* Multicast BSMs are only accepted if source interface & IP
                 * match RPF towards the BSR's IP address, or they have
index 8c75b0a5b59def844a3eeb07a9185c7bd95e312a..2e90cf9053379c341c90374908fbdb3705c55c50 100644 (file)
@@ -67,7 +67,7 @@ static struct cmd_node debug_node = {
 };
 
 static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[],
-                                     const int argc, int *idx)
+                                     const int argc, int *idx, bool uj)
 {
        struct vrf *vrf;
 
@@ -76,9 +76,13 @@ static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[],
        else
                vrf = vrf_lookup_by_id(VRF_DEFAULT);
 
-       if (!vrf)
-               vty_out(vty, "Specified VRF: %s does not exist\n",
-                       argv[*idx]->arg);
+       if (!vrf) {
+               if (uj)
+                       vty_json_empty(vty);
+               else
+                       vty_out(vty, "Specified VRF: %s does not exist\n",
+                               argv[*idx]->arg);
+       }
 
        return vrf;
 }
@@ -822,19 +826,172 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
        }
 }
 
-static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj)
+static void igmp_source_json_helper(struct gm_source *src,
+                                   json_object *json_sources, char *source_str,
+                                   char *mmss, char *uptime)
+{
+       json_object *json_source = NULL;
+
+       json_source = json_object_new_object();
+       if (!json_source)
+               return;
+
+       json_object_string_add(json_source, "source", source_str);
+       json_object_string_add(json_source, "timer", mmss);
+       json_object_boolean_add(json_source, "forwarded",
+                               IGMP_SOURCE_TEST_FORWARDING(src->source_flags));
+       json_object_string_add(json_source, "uptime", uptime);
+       json_object_array_add(json_sources, json_source);
+}
+
+static void igmp_group_print(struct interface *ifp, struct vty *vty, bool uj,
+                            json_object *json, struct gm_group *grp,
+                            time_t now, bool detail)
 {
-       struct interface *ifp;
-       time_t now;
-       json_object *json = NULL;
        json_object *json_iface = NULL;
        json_object *json_group = NULL;
        json_object *json_groups = NULL;
+       char group_str[INET_ADDRSTRLEN];
+       char hhmmss[PIM_TIME_STRLEN];
+       char uptime[PIM_TIME_STRLEN];
+
+       pim_inet4_dump("<group?>", grp->group_addr, group_str,
+                      sizeof(group_str));
+       pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer);
+       pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation);
+
+       if (uj) {
+               json_object_object_get_ex(json, ifp->name, &json_iface);
+               if (!json_iface) {
+                       json_iface = json_object_new_object();
+                       if (!json_iface)
+                               return;
+                       json_object_pim_ifp_add(json_iface, ifp);
+                       json_object_object_add(json, ifp->name, json_iface);
+                       json_groups = json_object_new_array();
+                       if (!json_groups)
+                               return;
+                       json_object_object_add(json_iface, "groups",
+                                              json_groups);
+               }
+
+               json_object_object_get_ex(json_iface, "groups", &json_groups);
+               if (json_groups) {
+                       json_group = json_object_new_object();
+                       if (!json_group)
+                               return;
+
+                       json_object_string_add(json_group, "group", group_str);
+                       if (grp->igmp_version == IGMP_DEFAULT_VERSION)
+                               json_object_string_add(
+                                       json_group, "mode",
+                                       grp->group_filtermode_isexcl
+                                               ? "EXCLUDE"
+                                               : "INCLUDE");
+
+                       json_object_string_add(json_group, "timer", hhmmss);
+                       json_object_int_add(
+                               json_group, "sourcesCount",
+                               grp->group_source_list
+                                       ? listcount(grp->group_source_list)
+                                       : 0);
+                       json_object_int_add(json_group, "version",
+                                           grp->igmp_version);
+                       json_object_string_add(json_group, "uptime", uptime);
+                       json_object_array_add(json_groups, json_group);
+
+                       if (detail) {
+                               struct listnode *srcnode;
+                               struct gm_source *src;
+                               json_object *json_sources = NULL;
+
+                               json_sources = json_object_new_array();
+                               if (!json_sources)
+                                       return;
+
+                               json_object_object_add(json_group, "sources",
+                                                      json_sources);
+
+                               for (ALL_LIST_ELEMENTS_RO(
+                                            grp->group_source_list, srcnode,
+                                            src)) {
+                                       char source_str[INET_ADDRSTRLEN];
+                                       char mmss[PIM_TIME_STRLEN];
+                                       char src_uptime[PIM_TIME_STRLEN];
+
+                                       pim_inet4_dump(
+                                               "<source?>", src->source_addr,
+                                               source_str, sizeof(source_str));
+                                       pim_time_timer_to_mmss(
+                                               mmss, sizeof(mmss),
+                                               src->t_source_timer);
+                                       pim_time_uptime(
+                                               src_uptime, sizeof(src_uptime),
+                                               now - src->source_creation);
+
+                                       igmp_source_json_helper(
+                                               src, json_sources, source_str,
+                                               mmss, src_uptime);
+                               }
+                       }
+               }
+       } else {
+               if (detail) {
+                       struct listnode *srcnode;
+                       struct gm_source *src;
+
+                       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+                                                 srcnode, src)) {
+                               char source_str[INET_ADDRSTRLEN];
+
+                               pim_inet4_dump("<source?>", src->source_addr,
+                                              source_str, sizeof(source_str));
+
+                               vty_out(vty,
+                                       "%-16s %-15s %4s %8s %-15s %d %8s\n",
+                                       ifp->name, group_str,
+                                       grp->igmp_version == 3
+                                               ? (grp->group_filtermode_isexcl
+                                                          ? "EXCL"
+                                                          : "INCL")
+                                               : "----",
+                                       hhmmss, source_str, grp->igmp_version,
+                                       uptime);
+                       }
+                       return;
+               }
+
+               vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", ifp->name,
+                       group_str,
+                       grp->igmp_version == 3
+                               ? (grp->group_filtermode_isexcl ? "EXCL"
+                                                               : "INCL")
+                               : "----",
+                       hhmmss,
+                       grp->group_source_list
+                               ? listcount(grp->group_source_list)
+                               : 0,
+                       grp->igmp_version, uptime);
+       }
+}
+
+static void igmp_show_groups_interface_single(struct pim_instance *pim,
+                                             struct vty *vty, bool uj,
+                                             const char *ifname,
+                                             const char *grp_str, bool detail)
+{
+       struct interface *ifp;
+       time_t now;
+       json_object *json = NULL;
+       struct pim_interface *pim_ifp = NULL;
+       struct gm_group *grp;
 
        now = pim_time_monotonic_sec();
 
        if (uj) {
                json = json_object_new_object();
+               if (!json)
+                       return;
                json_object_int_add(json, "totalGroups", pim->gm_group_count);
                json_object_int_add(json, "watermarkLimit",
                                    pim->gm_watermark_limit);
@@ -843,8 +1000,87 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj)
                vty_out(vty, "Watermark warn limit(%s): %u\n",
                        pim->gm_watermark_limit ? "Set" : "Not Set",
                        pim->gm_watermark_limit);
-               vty_out(vty,
-                       "Interface        Group           Mode Timer    Srcs V Uptime  \n");
+
+               if (!detail)
+                       vty_out(vty,
+                               "Interface        Group           Mode Timer    Srcs V Uptime\n");
+               else
+                       vty_out(vty,
+                               "Interface        Group           Mode Timer    Source          V Uptime\n");
+       }
+
+       ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id);
+       if (!ifp) {
+               if (uj)
+                       vty_json(vty, json);
+               return;
+       }
+
+       pim_ifp = ifp->info;
+       if (!pim_ifp) {
+               if (uj)
+                       vty_json(vty, json);
+               return;
+       }
+
+       if (grp_str) {
+               struct in_addr group_addr;
+               struct gm_sock *igmp;
+
+               if (inet_pton(AF_INET, grp_str, &group_addr) == 1) {
+                       igmp = pim_igmp_sock_lookup_ifaddr(
+                               pim_ifp->gm_socket_list,
+                               pim_ifp->primary_address);
+                       if (igmp) {
+                               grp = find_group_by_addr(igmp, group_addr);
+                               if (grp)
+                                       igmp_group_print(ifp, vty, uj, json,
+                                                        grp, now, detail);
+                       }
+               }
+       } else {
+               struct listnode *grpnode;
+
+               /* scan igmp groups */
+               for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, grp))
+                       igmp_group_print(ifp, vty, uj, json, grp, now, detail);
+       }
+
+       if (uj) {
+               if (detail)
+                       vty_json_no_pretty(vty, json);
+               else
+                       vty_json(vty, json);
+       }
+}
+
+static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj,
+                            const char *grp_str, bool detail)
+{
+       struct interface *ifp;
+       time_t now;
+       json_object *json = NULL;
+
+       now = pim_time_monotonic_sec();
+
+       if (uj) {
+               json = json_object_new_object();
+               if (!json)
+                       return;
+               json_object_int_add(json, "totalGroups", pim->gm_group_count);
+               json_object_int_add(json, "watermarkLimit",
+                                   pim->gm_watermark_limit);
+       } else {
+               vty_out(vty, "Total IGMP groups: %u\n", pim->gm_group_count);
+               vty_out(vty, "Watermark warn limit(%s): %u\n",
+                       pim->gm_watermark_limit ? "Set" : "Not Set",
+                       pim->gm_watermark_limit);
+               if (!detail)
+                       vty_out(vty,
+                               "Interface        Group           Mode Timer    Srcs V Uptime\n");
+               else
+                       vty_out(vty,
+                               "Interface        Group           Mode Timer    Source          V Uptime\n");
        }
 
        /* scan interfaces */
@@ -856,78 +1092,38 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj)
                if (!pim_ifp)
                        continue;
 
-               /* scan igmp groups */
-               for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
-                                         grp)) {
-                       char group_str[INET_ADDRSTRLEN];
-                       char hhmmss[10];
-                       char uptime[10];
-
-                       pim_inet4_dump("<group?>", grp->group_addr, group_str,
-                                      sizeof(group_str));
-                       pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss),
-                                                grp->t_group_timer);
-                       pim_time_uptime(uptime, sizeof(uptime),
-                                       now - grp->group_creation);
-
-                       if (uj) {
-                               json_object_object_get_ex(json, ifp->name,
-                                                         &json_iface);
-
-                               if (!json_iface) {
-                                       json_iface = json_object_new_object();
-                                       json_object_pim_ifp_add(json_iface,
-                                                               ifp);
-                                       json_object_object_add(json, ifp->name,
-                                                              json_iface);
-                                       json_groups = json_object_new_array();
-                                       json_object_object_add(json_iface,
-                                                              "groups",
-                                                              json_groups);
+               if (grp_str) {
+                       struct in_addr group_addr;
+                       struct gm_sock *igmp;
+
+                       if (inet_pton(AF_INET, grp_str, &group_addr) == 1) {
+                               igmp = pim_igmp_sock_lookup_ifaddr(
+                                       pim_ifp->gm_socket_list,
+                                       pim_ifp->primary_address);
+                               if (igmp) {
+                                       grp = find_group_by_addr(igmp,
+                                                                group_addr);
+                                       if (grp)
+                                               igmp_group_print(ifp, vty, uj,
+                                                                json, grp, now,
+                                                                detail);
                                }
-
-                               json_group = json_object_new_object();
-                               json_object_string_add(json_group, "group",
-                                                      group_str);
-
-                               if (grp->igmp_version == 3)
-                                       json_object_string_add(
-                                               json_group, "mode",
-                                               grp->group_filtermode_isexcl
-                                                       ? "EXCLUDE"
-                                                       : "INCLUDE");
-
-                               json_object_string_add(json_group, "timer",
-                                                      hhmmss);
-                               json_object_int_add(
-                                       json_group, "sourcesCount",
-                                       grp->group_source_list ? listcount(
-                                               grp->group_source_list)
-                                                              : 0);
-                               json_object_int_add(json_group, "version",
-                                                   grp->igmp_version);
-                               json_object_string_add(json_group, "uptime",
-                                                      uptime);
-                               json_object_array_add(json_groups, json_group);
-                       } else {
-                               vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n",
-                                       ifp->name, group_str,
-                                       grp->igmp_version == 3
-                                               ? (grp->group_filtermode_isexcl
-                                                          ? "EXCL"
-                                                          : "INCL")
-                                               : "----",
-                                       hhmmss,
-                                       grp->group_source_list ? listcount(
-                                               grp->group_source_list)
-                                                              : 0,
-                                       grp->igmp_version, uptime);
                        }
-               } /* scan igmp groups */
-       }         /* scan interfaces */
+               } else {
+                       /* scan igmp groups */
+                       for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list,
+                                                 grpnode, grp))
+                               igmp_group_print(ifp, vty, uj, json, grp, now,
+                                                detail);
+               }
+       } /* scan interfaces */
 
-       if (uj)
-               vty_json(vty, json);
+       if (uj) {
+               if (detail)
+                       vty_json_no_pretty(vty, json);
+               else
+                       vty_json(vty, json);
+       }
 }
 
 static void igmp_show_group_retransmission(struct pim_instance *pim,
@@ -981,24 +1177,175 @@ static void igmp_show_group_retransmission(struct pim_instance *pim,
        }         /* scan interfaces */
 }
 
+static void igmp_sources_print(struct interface *ifp, char *group_str,
+                              struct gm_source *src, time_t now,
+                              json_object *json, struct vty *vty, bool uj)
+{
+       json_object *json_iface = NULL;
+       json_object *json_group = NULL;
+       json_object *json_sources = NULL;
+       char source_str[INET_ADDRSTRLEN];
+       char mmss[PIM_TIME_STRLEN];
+       char uptime[PIM_TIME_STRLEN];
+
+       pim_inet4_dump("<source?>", src->source_addr, source_str,
+                      sizeof(source_str));
+       pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer);
+       pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation);
+
+       if (uj) {
+               json_object_object_get_ex(json, ifp->name, &json_iface);
+               if (!json_iface) {
+                       json_iface = json_object_new_object();
+                       if (!json_iface)
+                               return;
+                       json_object_string_add(json_iface, "name", ifp->name);
+                       json_object_object_add(json, ifp->name, json_iface);
+               }
+
+               json_object_object_get_ex(json_iface, group_str, &json_group);
+               if (!json_group) {
+                       json_group = json_object_new_object();
+                       if (!json_group)
+                               return;
+                       json_object_string_add(json_group, "group", group_str);
+                       json_object_object_add(json_iface, group_str,
+                                              json_group);
+                       json_sources = json_object_new_array();
+                       if (!json_sources)
+                               return;
+                       json_object_object_add(json_group, "sources",
+                                              json_sources);
+               }
+
+               json_object_object_get_ex(json_group, "sources", &json_sources);
+               if (json_sources)
+                       igmp_source_json_helper(src, json_sources, source_str,
+                                               mmss, uptime);
+       } else {
+               vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n", ifp->name,
+                       group_str, source_str, mmss,
+                       IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y"
+                                                                      : "N",
+                       uptime);
+       }
+}
+
+static void igmp_show_sources_interface_single(struct pim_instance *pim,
+                                              struct vty *vty, bool uj,
+                                              const char *ifname,
+                                              const char *grp_str)
+{
+       struct interface *ifp;
+       time_t now;
+       json_object *json = NULL;
+       struct pim_interface *pim_ifp;
+       struct gm_group *grp;
+
+       now = pim_time_monotonic_sec();
+
+       if (uj) {
+               json = json_object_new_object();
+               if (!json)
+                       return;
+       } else {
+               vty_out(vty,
+                       "Interface        Group           Source          Timer Fwd Uptime  \n");
+       }
+
+       ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id);
+       if (!ifp) {
+               if (uj)
+                       vty_json(vty, json);
+               return;
+       }
+
+       pim_ifp = ifp->info;
+       if (!pim_ifp) {
+               if (uj)
+                       vty_json(vty, json);
+               return;
+       }
+
+       if (grp_str) {
+               struct in_addr group_addr;
+               struct gm_sock *igmp;
+               struct listnode *srcnode;
+               struct gm_source *src;
+               char group_str[INET_ADDRSTRLEN];
+               int res;
+
+               res = inet_pton(AF_INET, grp_str, &group_addr);
+               if (res <= 0) {
+                       if (uj)
+                               vty_json(vty, json);
+                       return;
+               }
+
+               igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list,
+                                                  pim_ifp->primary_address);
+               if (!igmp) {
+                       if (uj)
+                               vty_json(vty, json);
+                       return;
+               }
+
+               grp = find_group_by_addr(igmp, group_addr);
+               if (!grp) {
+                       if (uj)
+                               vty_json(vty, json);
+                       return;
+               }
+               pim_inet4_dump("<group?>", grp->group_addr, group_str,
+                              sizeof(group_str));
+
+               /* scan group sources */
+               for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src))
+                       igmp_sources_print(ifp, group_str, src, now, json, vty,
+                                          uj);
+       } else {
+               struct listnode *grpnode;
+
+               /* scan igmp groups */
+               for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
+                                         grp)) {
+                       char group_str[INET_ADDRSTRLEN];
+                       struct listnode *srcnode;
+                       struct gm_source *src;
+
+                       pim_inet4_dump("<group?>", grp->group_addr, group_str,
+                                      sizeof(group_str));
+
+                       /* scan group sources */
+                       for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+                                                 srcnode, src))
+                               igmp_sources_print(ifp, group_str, src, now,
+                                                  json, vty, uj);
+
+               } /* scan igmp groups */
+       }
+
+       if (uj)
+               vty_json(vty, json);
+}
+
 static void igmp_show_sources(struct pim_instance *pim, struct vty *vty,
                              bool uj)
 {
        struct interface *ifp;
        time_t now;
        json_object *json = NULL;
-       json_object *json_iface = NULL;
-       json_object *json_group = NULL;
-       json_object *json_source = NULL;
-       json_object *json_sources = NULL;
 
        now = pim_time_monotonic_sec();
 
-       if (uj)
+       if (uj) {
                json = json_object_new_object();
-       else
+               if (!json)
+                       return;
+       } else {
                vty_out(vty,
-                       "Interface        Group           Source          Timer Fwd Uptime  \n");
+                       "Interface        Group           Source          Timer Fwd Uptime\n");
+       }
 
        /* scan interfaces */
        FOR_ALL_INTERFACES (pim->vrf, ifp) {
@@ -1021,82 +1368,12 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty,
 
                        /* scan group sources */
                        for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
-                                                 srcnode, src)) {
-                               char source_str[INET_ADDRSTRLEN];
-                               char mmss[10];
-                               char uptime[10];
-
-                               pim_inet4_dump("<source?>", src->source_addr,
-                                              source_str, sizeof(source_str));
-
-                               pim_time_timer_to_mmss(mmss, sizeof(mmss),
-                                                      src->t_source_timer);
-
-                               pim_time_uptime(uptime, sizeof(uptime),
-                                               now - src->source_creation);
-
-                               if (uj) {
-                                       json_object_object_get_ex(
-                                               json, ifp->name, &json_iface);
-                                       if (!json_iface) {
-                                               json_iface =
-                                                       json_object_new_object();
-                                               json_object_string_add(
-                                                       json_iface, "name",
-                                                       ifp->name);
-                                               json_object_object_add(
-                                                       json, ifp->name,
-                                                       json_iface);
-                                       }
-                                       json_object_object_get_ex(json_iface,
-                                                                 group_str,
-                                                                 &json_group);
-
-                                       if (!json_group) {
-                                               json_group =
-                                                       json_object_new_object();
-                                               json_object_string_add(
-                                                       json_group, "group",
-                                                       group_str);
-                                               json_object_object_add(
-                                                       json_iface, group_str,
-                                                       json_group);
-                                               json_sources =
-                                                       json_object_new_array();
-                                               json_object_object_add(
-                                                       json_group, "sources",
-                                                       json_sources);
-                                       }
-                                       json_source = json_object_new_object();
-                                       json_object_string_add(json_source,
-                                                              "source",
-                                                              source_str);
-                                       json_object_string_add(json_source,
-                                                              "timer", mmss);
-                                       json_object_boolean_add(
-                                               json_source, "forwarded",
-                                               IGMP_SOURCE_TEST_FORWARDING(
-                                                       src->source_flags));
-                                       json_object_string_add(
-                                               json_source, "uptime", uptime);
-                                       json_object_array_add(json_sources,
-                                                             json_source);
-
-                               } else {
-                                       vty_out(vty,
-                                               "%-16s %-15s %-15s %5s %3s %8s\n",
-                                               ifp->name, group_str,
-                                               source_str, mmss,
-                                               IGMP_SOURCE_TEST_FORWARDING(
-                                                       src->source_flags)
-                                                       ? "Y"
-                                                       : "N",
-                                               uptime);
-                               }
-
-                       } /* scan group sources */
+                                                 srcnode, src))
+                               igmp_sources_print(ifp, group_str, src, now,
+                                                  json, vty, uj);
                }        /* scan igmp groups */
        }                 /* scan interfaces */
+
        if (uj)
                vty_json(vty, json);
 }
@@ -1227,7 +1504,7 @@ DEFUN (clear_ip_interfaces,
        VRF_CMD_HELP_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1247,7 +1524,7 @@ DEFUN (clear_ip_igmp_interfaces,
        "Reset IGMP interfaces\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1355,7 +1632,7 @@ DEFUN (clear_ip_pim_bsr_db,
        "Reset pim bsr data\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1378,8 +1655,8 @@ DEFUN (show_ip_igmp_interface,
        JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1444,8 +1721,8 @@ DEFUN (show_ip_igmp_join,
        JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1487,39 +1764,47 @@ DEFUN (show_ip_igmp_join_vrf_all,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_igmp_groups,
-       show_ip_igmp_groups_cmd,
-       "show ip igmp [vrf NAME] groups [json]",
-       SHOW_STR
-       IP_STR
-       IGMP_STR
-       VRF_CMD_HELP_STR
-       IGMP_GROUP_STR
-       JSON_STR)
+DEFPY(show_ip_igmp_groups,
+      show_ip_igmp_groups_cmd,
+      "show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail$detail] [json$json]",
+      SHOW_STR
+      IP_STR
+      IGMP_STR
+      VRF_CMD_HELP_STR
+      IGMP_GROUP_STR
+      "Interface name\n"
+      "Group address\n"
+      "Detailed Information\n"
+      JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
-       bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);
 
        if (!vrf)
                return CMD_WARNING;
 
-       igmp_show_groups(vrf->info, vty, uj);
+       if (ifname)
+               igmp_show_groups_interface_single(vrf->info, vty, !!json,
+                                                 ifname, grp_str, !!detail);
+       else
+               igmp_show_groups(vrf->info, vty, !!json, NULL, !!detail);
 
        return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_igmp_groups_vrf_all,
-       show_ip_igmp_groups_vrf_all_cmd,
-       "show ip igmp vrf all groups [json]",
-       SHOW_STR
-       IP_STR
-       IGMP_STR
-       VRF_CMD_HELP_STR
-       IGMP_GROUP_STR
-       JSON_STR)
+DEFPY(show_ip_igmp_groups_vrf_all,
+      show_ip_igmp_groups_vrf_all_cmd,
+      "show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]",
+      SHOW_STR
+      IP_STR
+      IGMP_STR
+      VRF_CMD_HELP_STR
+      IGMP_GROUP_STR
+      "Group address\n"
+      "Detailed Information\n"
+      JSON_STR)
 {
-       bool uj = use_json(argc, argv);
+       bool uj = !!json;
        struct vrf *vrf;
        bool first = true;
 
@@ -1533,7 +1818,7 @@ DEFUN (show_ip_igmp_groups_vrf_all,
                        first = false;
                } else
                        vty_out(vty, "VRF: %s\n", vrf->name);
-               igmp_show_groups(vrf->info, vty, uj);
+               igmp_show_groups(vrf->info, vty, uj, grp_str, !!detail);
        }
        if (uj)
                vty_out(vty, "}\n");
@@ -1552,7 +1837,7 @@ DEFUN (show_ip_igmp_groups_retransmissions,
        "IGMP group retransmissions\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1562,23 +1847,29 @@ DEFUN (show_ip_igmp_groups_retransmissions,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_ip_igmp_sources,
-       show_ip_igmp_sources_cmd,
-       "show ip igmp [vrf NAME] sources [json]",
-       SHOW_STR
-       IP_STR
-       IGMP_STR
-       VRF_CMD_HELP_STR
-       IGMP_SOURCE_STR
-       JSON_STR)
+DEFPY(show_ip_igmp_sources,
+      show_ip_igmp_sources_cmd,
+      "show ip igmp [vrf NAME$vrf_name] sources [INTERFACE$ifname [GROUP$grp_str]] [json$json]",
+      SHOW_STR
+      IP_STR
+      IGMP_STR
+      VRF_CMD_HELP_STR
+      IGMP_SOURCE_STR
+      "Interface name\n"
+      "Group address\n"
+      JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json);
 
        if (!vrf)
                return CMD_WARNING;
 
-       igmp_show_sources(vrf->info, vty, use_json(argc, argv));
+       if (ifname)
+               igmp_show_sources_interface_single(vrf->info, vty, !!json,
+                                                  ifname, grp_str);
+       else
+               igmp_show_sources(vrf->info, vty, !!json);
 
        return CMD_SUCCESS;
 }
@@ -1594,7 +1885,7 @@ DEFUN (show_ip_igmp_sources_retransmissions,
        "IGMP source retransmissions\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1617,8 +1908,8 @@ DEFUN (show_ip_igmp_statistics,
        JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1746,7 +2037,7 @@ DEFUN (show_ip_pim_assert,
        "PIM interface assert\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1766,7 +2057,7 @@ DEFUN (show_ip_pim_assert_internal,
        "PIM interface internal assert state\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1786,7 +2077,7 @@ DEFUN (show_ip_pim_assert_metric,
        "PIM interface assert metric\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -1806,7 +2097,7 @@ DEFUN (show_ip_pim_assert_winner_metric,
        "PIM interface assert winner metric\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -2125,8 +2416,8 @@ DEFUN(show_ip_pim_mlag_up, show_ip_pim_mlag_up_cmd,
        const char *src_or_group = NULL;
        const char *group = NULL;
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf || !vrf->info) {
                vty_out(vty, "%s: VRF or Info missing\n", __func__);
@@ -2598,7 +2889,7 @@ DEFUN (show_ip_rib,
        "Unicast address\n")
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
        struct in_addr addr;
        const char *addr_str;
        struct pim_nexthop nexthop;
@@ -2686,7 +2977,7 @@ DEFUN (show_ip_ssmpingd,
        VRF_CMD_HELP_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false);
 
        if (!vrf)
                return CMD_WARNING;
@@ -3141,8 +3432,8 @@ DEFUN (show_ip_pim_ssm_range,
        JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -3192,8 +3483,8 @@ DEFUN (show_ip_pim_group_type,
        JSON_STR)
 {
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
        bool uj = use_json(argc, argv);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -5086,7 +5377,7 @@ DEFUN (show_ip_msdp_mesh_group,
        bool uj = use_json(argc, argv);
        int idx = 2;
        struct pim_msdp_mg *mg;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
        struct pim_instance *pim;
        struct json_object *json = NULL;
 
@@ -5322,7 +5613,7 @@ DEFUN (show_ip_msdp_peer_detail,
 {
        bool uj = use_json(argc, argv);
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -5562,7 +5853,7 @@ DEFUN (show_ip_msdp_sa_detail,
 {
        bool uj = use_json(argc, argv);
        int idx = 2;
-       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -5677,7 +5968,7 @@ DEFUN (show_ip_msdp_sa_sg,
        struct vrf *vrf;
        int idx = 2;
 
-       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -5947,7 +6238,7 @@ DEFUN (show_ip_pim_vxlan_sg,
        struct vrf *vrf;
        int idx = 2;
 
-       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
@@ -6007,7 +6298,7 @@ DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work,
        struct vrf *vrf;
        int idx = 2;
 
-       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+       vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj);
 
        if (!vrf)
                return CMD_WARNING;
index fb6693491ba5dc7acb6ee520d01c86ccbb94f986..d39d77cd2fde34fc1332c03468362328bb7295b7 100644 (file)
@@ -60,4 +60,5 @@
 
 void pim_cmd_init(void);
 
+#define PIM_TIME_STRLEN 10
 #endif /* PIM_CMD_H */
index d284912d1d26d8d0ec6000a5c00025a636226254..b053e422ec7f7a34f510505be16a6c368ff0c441 100644 (file)
 
 #if PIM_IPV == 4
 static void pim_if_igmp_join_del_all(struct interface *ifp);
-static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
-                         struct in_addr group_addr, struct in_addr source_addr,
-                         struct pim_interface *pim_ifp);
 #endif
+static int gm_join_sock(const char *ifname, ifindex_t ifindex,
+                       pim_addr group_addr, pim_addr source_addr,
+                       struct pim_interface *pim_ifp);
 
 void pim_if_init(struct pim_instance *pim)
 {
@@ -560,7 +560,7 @@ void pim_if_addr_add(struct connected *ifc)
                                /* Close socket and reopen with Source and Group
                                 */
                                close(ij->sock_fd);
-                               join_fd = igmp_join_sock(
+                               join_fd = gm_join_sock(
                                        ifp->name, ifp->ifindex, ij->group_addr,
                                        ij->source_addr, pim_ifp);
                                if (join_fd < 0) {
@@ -573,7 +573,7 @@ void pim_if_addr_add(struct connected *ifc)
                                                "<src?>", ij->source_addr,
                                                source_str, sizeof(source_str));
                                        zlog_warn(
-                                               "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
+                                               "%s: gm_join_sock() failure for IGMP group %s source %s on interface %s",
                                                __func__, group_str, source_str,
                                                ifp->name);
                                        /* warning only */
@@ -982,6 +982,12 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
                zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__,
                          ifp->ifindex, ifp->name);
                return -2;
+       } else if ((ifp->ifindex == 0) &&
+                  ((strncmp(ifp->name, "pimreg", 6)) &&
+                   (strncmp(ifp->name, "pim6reg", 7)))) {
+               zlog_warn("%s: ifindex=%d == 0 on interface %s", __func__,
+                         ifp->ifindex, ifp->name);
+               return -2;
        }
 
        ifaddr = pim_ifp->primary_address;
@@ -1208,15 +1214,13 @@ long pim_if_t_suppressed_msec(struct interface *ifp)
        return t_suppressed_msec;
 }
 
-#if PIM_IPV == 4
-static void igmp_join_free(struct gm_join *ij)
+static void gm_join_free(struct gm_join *ij)
 {
        XFREE(MTYPE_PIM_IGMP_JOIN, ij);
 }
 
-static struct gm_join *igmp_join_find(struct list *join_list,
-                                     struct in_addr group_addr,
-                                     struct in_addr source_addr)
+static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr,
+                                   pim_addr source_addr)
 {
        struct listnode *node;
        struct gm_join *ij;
@@ -1224,38 +1228,33 @@ static struct gm_join *igmp_join_find(struct list *join_list,
        assert(join_list);
 
        for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) {
-               if ((group_addr.s_addr == ij->group_addr.s_addr)
-                   && (source_addr.s_addr == ij->source_addr.s_addr))
+               if ((!pim_addr_cmp(group_addr, ij->group_addr)) &&
+                   (!pim_addr_cmp(source_addr, ij->source_addr)))
                        return ij;
        }
 
        return 0;
 }
 
-static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
-                         struct in_addr group_addr, struct in_addr source_addr,
-                         struct pim_interface *pim_ifp)
+static int gm_join_sock(const char *ifname, ifindex_t ifindex,
+                       pim_addr group_addr, pim_addr source_addr,
+                       struct pim_interface *pim_ifp)
 {
        int join_fd;
 
        pim_ifp->igmp_ifstat_joins_sent++;
 
-       join_fd = pim_socket_raw(IPPROTO_IGMP);
+       join_fd = pim_socket_raw(IPPROTO_GM);
        if (join_fd < 0) {
                pim_ifp->igmp_ifstat_joins_failed++;
                return -1;
        }
 
-       if (pim_igmp_join_source(join_fd, ifindex, group_addr, source_addr)) {
-               char group_str[INET_ADDRSTRLEN];
-               char source_str[INET_ADDRSTRLEN];
-               pim_inet4_dump("<grp?>", group_addr, group_str,
-                              sizeof(group_str));
-               pim_inet4_dump("<src?>", source_addr, source_str,
-                              sizeof(source_str));
+       if (pim_gm_join_source(join_fd, ifindex, group_addr, source_addr)) {
                zlog_warn(
-                       "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
-                       __func__, join_fd, group_str, source_str, ifindex,
+                       "%s: setsockopt(fd=%d) failure for " GM
+                       " group %pPAs source %pPAs ifindex %d on interface %s: errno=%d: %s",
+                       __func__, join_fd, &group_addr, &source_addr, ifindex,
                        ifname, errno, safe_strerror(errno));
 
                pim_ifp->igmp_ifstat_joins_failed++;
@@ -1267,10 +1266,8 @@ static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
        return join_fd;
 }
 
-#if PIM_IPV == 4
-static struct gm_join *igmp_join_new(struct interface *ifp,
-                                    struct in_addr group_addr,
-                                    struct in_addr source_addr)
+static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr,
+                                  pim_addr source_addr)
 {
        struct pim_interface *pim_ifp;
        struct gm_join *ij;
@@ -1279,19 +1276,12 @@ static struct gm_join *igmp_join_new(struct interface *ifp,
        pim_ifp = ifp->info;
        assert(pim_ifp);
 
-       join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr,
-                                source_addr, pim_ifp);
+       join_fd = gm_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr,
+                              pim_ifp);
        if (join_fd < 0) {
-               char group_str[INET_ADDRSTRLEN];
-               char source_str[INET_ADDRSTRLEN];
-
-               pim_inet4_dump("<grp?>", group_addr, group_str,
-                              sizeof(group_str));
-               pim_inet4_dump("<src?>", source_addr, source_str,
-                              sizeof(source_str));
-               zlog_warn(
-                       "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s",
-                       __func__, group_str, source_str, ifp->name);
+               zlog_warn("%s: gm_join_sock() failure for " GM
+                         " group %pPAs source %pPAs on interface %s",
+                         __func__, &group_addr, &source_addr, ifp->name);
                return 0;
        }
 
@@ -1306,11 +1296,9 @@ static struct gm_join *igmp_join_new(struct interface *ifp,
 
        return ij;
 }
-#endif /* PIM_IPV == 4 */
 
-#if PIM_IPV == 4
-ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr,
-                           struct in_addr source_addr)
+ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
+                         pim_addr source_addr)
 {
        struct pim_interface *pim_ifp;
        struct gm_join *ij;
@@ -1323,37 +1311,32 @@ ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr,
 
        if (!pim_ifp->gm_join_list) {
                pim_ifp->gm_join_list = list_new();
-               pim_ifp->gm_join_list->del = (void (*)(void *))igmp_join_free;
+               pim_ifp->gm_join_list->del = (void (*)(void *))gm_join_free;
        }
 
-       ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr);
+       ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr);
 
-       /* This interface has already been configured to join this IGMP group
+       /* This interface has already been configured to join this IGMP/MLD
+        * group
         */
        if (ij) {
                return ferr_ok();
        }
 
-       (void)igmp_join_new(ifp, group_addr, source_addr);
+       (void)gm_join_new(ifp, group_addr, source_addr);
 
        if (PIM_DEBUG_GM_EVENTS) {
-               char group_str[INET_ADDRSTRLEN];
-               char source_str[INET_ADDRSTRLEN];
-               pim_inet4_dump("<grp?>", group_addr, group_str,
-                              sizeof(group_str));
-               pim_inet4_dump("<src?>", source_addr, source_str,
-                              sizeof(source_str));
                zlog_debug(
-                       "%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s",
-                       __func__, source_str, group_str, ifp->name);
+                       "%s: issued static " GM
+                       " join for channel (S,G)=(%pPA,%pPA) on interface %s",
+                       __func__, &source_addr, &group_addr, ifp->name);
        }
 
        return ferr_ok();
 }
-#endif /* PIM_IPV == 4 */
 
-int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr,
-                        struct in_addr source_addr)
+int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
+                      pim_addr source_addr)
 {
        struct pim_interface *pim_ifp;
        struct gm_join *ij;
@@ -1366,40 +1349,29 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr,
        }
 
        if (!pim_ifp->gm_join_list) {
-               zlog_warn("%s: no IGMP join on interface %s", __func__,
+               zlog_warn("%s: no " GM " join on interface %s", __func__,
                          ifp->name);
                return -2;
        }
 
-       ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr);
+       ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr);
        if (!ij) {
-               char group_str[INET_ADDRSTRLEN];
-               char source_str[INET_ADDRSTRLEN];
-               pim_inet4_dump("<grp?>", group_addr, group_str,
-                              sizeof(group_str));
-               pim_inet4_dump("<src?>", source_addr, source_str,
-                              sizeof(source_str));
-               zlog_warn(
-                       "%s: could not find IGMP group %s source %s on interface %s",
-                       __func__, group_str, source_str, ifp->name);
+               zlog_warn("%s: could not find " GM
+                         " group %pPAs source %pPAs on interface %s",
+                         __func__, &group_addr, &source_addr, ifp->name);
                return -3;
        }
 
        if (close(ij->sock_fd)) {
-               char group_str[INET_ADDRSTRLEN];
-               char source_str[INET_ADDRSTRLEN];
-               pim_inet4_dump("<grp?>", group_addr, group_str,
-                              sizeof(group_str));
-               pim_inet4_dump("<src?>", source_addr, source_str,
-                              sizeof(source_str));
                zlog_warn(
-                       "%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s",
-                       __func__, ij->sock_fd, group_str, source_str, ifp->name,
-                       errno, safe_strerror(errno));
+                       "%s: failure closing sock_fd=%d for " GM
+                       " group %pPAs source %pPAs on interface %s: errno=%d: %s",
+                       __func__, ij->sock_fd, &group_addr, &source_addr,
+                       ifp->name, errno, safe_strerror(errno));
                /* warning only */
        }
        listnode_delete(pim_ifp->gm_join_list, ij);
-       igmp_join_free(ij);
+       gm_join_free(ij);
        if (listcount(pim_ifp->gm_join_list) < 1) {
                list_delete(&pim_ifp->gm_join_list);
                pim_ifp->gm_join_list = 0;
@@ -1408,6 +1380,7 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr,
        return 0;
 }
 
+#if PIM_IPV == 4
 __attribute__((unused))
 static void pim_if_igmp_join_del_all(struct interface *ifp)
 {
@@ -1427,21 +1400,9 @@ static void pim_if_igmp_join_del_all(struct interface *ifp)
                return;
 
        for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij))
-               pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr);
+               pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr);
 }
-#else /* PIM_IPV != 4 */
-ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr,
-                           struct in_addr source_addr)
-{
-       return ferr_ok();
-}
-
-int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr,
-                        struct in_addr source_addr)
-{
-       return 0;
-}
-#endif /* PIM_IPV != 4 */
+#endif /* PIM_IPV == 4 */
 
 /*
   RFC 4601
index 0aff56d558d2aebaf56f42225f5e4a2c31e9ec9b..973840a75385e326876e7532152410134b8fd098 100644 (file)
@@ -217,10 +217,10 @@ int pim_if_t_override_msec(struct interface *ifp);
 
 pim_addr pim_find_primary_addr(struct interface *ifp);
 
-ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr,
-                           struct in_addr source_addr);
-int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr,
-                        struct in_addr source_addr);
+ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr,
+                         pim_addr source_addr);
+int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr,
+                      pim_addr source_addr);
 
 void pim_if_update_could_assert(struct interface *ifp);
 
index d6d22be3e4c73565dcc11f1dfb3ec98751cd0970..0e9498c50c784badbe1f3c53cc93cea5e57ec066 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef PIM_IGMP_JOIN_H
 #define PIM_IGMP_JOIN_H
 
+#include "pim_addr.h"
+
 /* required headers #include'd by caller */
 
 #ifndef SOL_IP
@@ -26,35 +28,64 @@ struct group_source_req {
 };
 #endif
 
-static inline int pim_igmp_join_source(int fd, ifindex_t ifindex,
-                                      struct in_addr group_addr,
-                                      struct in_addr source_addr)
+#if PIM_IPV == 4
+static inline int pim_gm_join_source(int fd, ifindex_t ifindex,
+                                    pim_addr group_addr, pim_addr source_addr)
 {
        struct group_source_req req;
-       struct sockaddr_in group;
-       struct sockaddr_in source;
+       struct sockaddr_in group = {};
+       struct sockaddr_in source = {};
 
        memset(&req, 0, sizeof(req));
-       memset(&group, 0, sizeof(group));
-       group.sin_family = AF_INET;
+
+       group.sin_family = PIM_AF;
        group.sin_addr = group_addr;
        group.sin_port = htons(0);
-       memcpy(&req.gsr_group, &group, sizeof(struct sockaddr_in));
+       memcpy(&req.gsr_group, &group, sizeof(group));
 
-       memset(&source, 0, sizeof(source));
-       source.sin_family = AF_INET;
+       source.sin_family = PIM_AF;
        source.sin_addr = source_addr;
        source.sin_port = htons(0);
-       memcpy(&req.gsr_source, &source, sizeof(struct sockaddr_in));
+       memcpy(&req.gsr_source, &source, sizeof(source));
 
        req.gsr_interface = ifindex;
 
-       if (source_addr.s_addr == INADDR_ANY)
+       if (pim_addr_is_any(source_addr))
                return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req,
                                  sizeof(req));
        else
                return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req,
                                  sizeof(req));
 }
+#else  /* PIM_IPV != 4*/
+static inline int pim_gm_join_source(int fd, ifindex_t ifindex,
+                                    pim_addr group_addr, pim_addr source_addr)
+{
+       struct group_source_req req;
+       struct sockaddr_in6 group = {};
+       struct sockaddr_in6 source = {};
+
+       memset(&req, 0, sizeof(req));
+
+       group.sin6_family = PIM_AF;
+       group.sin6_addr = group_addr;
+       group.sin6_port = htons(0);
+       memcpy(&req.gsr_group, &group, sizeof(group));
+
+       source.sin6_family = PIM_AF;
+       source.sin6_addr = source_addr;
+       source.sin6_port = htons(0);
+       memcpy(&req.gsr_source, &source, sizeof(source));
+
+       req.gsr_interface = ifindex;
+
+       if (pim_addr_is_any(source_addr))
+               return setsockopt(fd, SOL_IPV6, MCAST_JOIN_GROUP, &req,
+                                 sizeof(req));
+       else
+               return setsockopt(fd, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP, &req,
+                                 sizeof(req));
+}
+#endif /* PIM_IPV != 4*/
 
 #endif /* PIM_IGMP_JOIN_H */
index 4d5da31827659383bff8df8f5e42e09624d43f34..b64fcdeb87237a14aadcda71c5c9544f13ae3543 100644 (file)
@@ -46,7 +46,7 @@ int pim_mroute_set(struct pim_instance *pim, int enable)
        /*
         * We need to create the VRF table for the pim mroute_socket
         */
-       if (pim->vrf->vrf_id != VRF_DEFAULT) {
+       if (enable && pim->vrf->vrf_id != VRF_DEFAULT) {
                frr_with_privs (&pimd_privs) {
 
                        data = pim->vrf->data.l.table_id;
index fa6f664149570a4182d02dd64849101d11d9289d..3c4ab1d4cc5fa1be87ac83e7a1b549923a762089 100644 (file)
@@ -71,12 +71,19 @@ static void pim_if_membership_clear(struct interface *ifp)
 static void pim_if_membership_refresh(struct interface *ifp)
 {
        struct pim_interface *pim_ifp;
+#if PIM_IPV == 4
        struct listnode *grpnode;
        struct gm_group *grp;
-
+#else
+       struct gm_if *gm_ifp;
+       struct gm_sg *sg, *sg_start;
+#endif
 
        pim_ifp = ifp->info;
        assert(pim_ifp);
+#if PIM_IPV == 6
+       gm_ifp = pim_ifp->mld;
+#endif
 
        if (!pim_ifp->pim_enable)
                return;
@@ -90,6 +97,7 @@ static void pim_if_membership_refresh(struct interface *ifp)
 
        pim_ifchannel_membership_clear(ifp);
 
+#if PIM_IPV == 4
        /*
         * Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on
         * the interface
@@ -116,6 +124,16 @@ static void pim_if_membership_refresh(struct interface *ifp)
 
                } /* scan group sources */
        }        /* scan igmp groups */
+#else
+       sg_start = gm_sgs_first(gm_ifp->sgs);
+
+       frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) {
+               if (!in6_multicast_nofwd(&sg->sgaddr.grp)) {
+                       pim_ifchannel_local_membership_add(
+                               ifp, &sg->sgaddr, false /*is_vxlan*/);
+               }
+       }
+#endif
 
        /*
         * Finally delete every PIM (S,G) entry lacking all state info
@@ -2880,10 +2898,9 @@ int lib_interface_gmp_address_family_robustness_variable_modify(
 int lib_interface_gmp_address_family_static_group_create(
        struct nb_cb_create_args *args)
 {
-#if PIM_IPV == 4
        struct interface *ifp;
-       struct ipaddr source_addr;
-       struct ipaddr group_addr;
+       pim_addr source_addr;
+       pim_addr group_addr;
        int result;
        const char *ifp_name;
        const struct lyd_node *if_dnode;
@@ -2899,33 +2916,40 @@ int lib_interface_gmp_address_family_static_group_create(
                        return NB_ERR_VALIDATION;
                }
 
-               yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr");
-               if (pim_is_group_224_0_0_0_24(group_addr.ip._v4_addr)) {
+               yang_dnode_get_pimaddr(&group_addr, args->dnode,
+                                      "./group-addr");
+#if PIM_IPV == 4
+               if (pim_is_group_224_0_0_0_24(group_addr)) {
                        snprintf(
                                args->errmsg, args->errmsg_len,
                                "Groups within 224.0.0.0/24 are reserved and cannot be joined");
                        return NB_ERR_VALIDATION;
                }
+#else
+               if (ipv6_mcast_reserved(&group_addr)) {
+                       snprintf(
+                               args->errmsg, args->errmsg_len,
+                               "Groups within ffx2::/16 are reserved and cannot be joined");
+                       return NB_ERR_VALIDATION;
+               }
+#endif
                break;
        case NB_EV_PREPARE:
        case NB_EV_ABORT:
                break;
        case NB_EV_APPLY:
                ifp = nb_running_get_entry(args->dnode, NULL, true);
-               yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr");
-               yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr");
-
-               result = pim_if_igmp_join_add(ifp, group_addr.ip._v4_addr,
-                               source_addr.ip._v4_addr);
+               yang_dnode_get_pimaddr(&source_addr, args->dnode,
+                                      "./source-addr");
+               yang_dnode_get_pimaddr(&group_addr, args->dnode,
+                                      "./group-addr");
+               result = pim_if_gm_join_add(ifp, group_addr, source_addr);
                if (result) {
                        snprintf(args->errmsg, args->errmsg_len,
-                                "Failure joining IGMP group");
+                                "Failure joining " GM " group");
                        return NB_ERR_INCONSISTENCY;
                }
        }
-#else
-       /* TBD Depends on MLD data structure changes */
-#endif /* PIM_IPV == 4 */
        return NB_OK;
 }
 
@@ -2933,8 +2957,8 @@ int lib_interface_gmp_address_family_static_group_destroy(
        struct nb_cb_destroy_args *args)
 {
        struct interface *ifp;
-       struct ipaddr source_addr;
-       struct ipaddr group_addr;
+       pim_addr source_addr;
+       pim_addr group_addr;
        int result;
 
        switch (args->event) {
@@ -2944,22 +2968,17 @@ int lib_interface_gmp_address_family_static_group_destroy(
                break;
        case NB_EV_APPLY:
                ifp = nb_running_get_entry(args->dnode, NULL, true);
-               yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr");
-               yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr");
-
-               result = pim_if_igmp_join_del(ifp, group_addr.ip._v4_addr,
-                               source_addr.ip._v4_addr);
+               yang_dnode_get_pimaddr(&source_addr, args->dnode,
+                                      "./source-addr");
+               yang_dnode_get_pimaddr(&group_addr, args->dnode,
+                                      "./group-addr");
+               result = pim_if_gm_join_del(ifp, group_addr, source_addr);
 
                if (result) {
-                       char src_str[INET_ADDRSTRLEN];
-                       char grp_str[INET_ADDRSTRLEN];
-
-                       ipaddr2str(&source_addr, src_str, sizeof(src_str));
-                       ipaddr2str(&group_addr, grp_str, sizeof(grp_str));
-
                        snprintf(args->errmsg, args->errmsg_len,
-                                "%% Failure leaving IGMP group %s %s on interface %s: %d",
-                                src_str, grp_str, ifp->name, result);
+                                "%% Failure leaving " GM
+                                " group %pPAs %pPAs on interface %s: %d",
+                                &source_addr, &group_addr, ifp->name, result);
 
                        return NB_ERR_INCONSISTENCY;
                }
index 3d0ecb1bf4fc1dafa0ad79efbebbf1a9c451da2f..1cd7cce086fb6680f3ac59d78a54ee763f55732d 100644 (file)
@@ -29,6 +29,7 @@
 #include "pim_jp_agg.h"
 #include "pim_bfd.h"
 #include "pim_register.h"
+#include "pim_oil.h"
 
 static void dr_election_by_addr(struct interface *ifp)
 {
@@ -123,9 +124,10 @@ int pim_if_dr_election(struct interface *ifp)
                pim_if_update_could_assert(ifp);
                pim_if_update_assert_tracking_desired(ifp);
 
-               if (PIM_I_am_DR(pim_ifp))
+               if (PIM_I_am_DR(pim_ifp)) {
                        pim_ifp->am_i_dr = true;
-               else {
+                       pim_clear_nocache_state(pim_ifp);
+               } else {
                        if (pim_ifp->am_i_dr == true) {
                                pim_reg_del_on_couldreg_fail(ifp);
                                pim_ifp->am_i_dr = false;
index d164e7ed81caf8a65e52848d54a1f67153d631e9..5f0f2a5933dc8bc043b9a7fee011b9df21bb740c 100644 (file)
@@ -724,27 +724,20 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
                return 0;
        }
 
-       if (cmd == ZEBRA_NEXTHOP_UPDATE) {
-               rpf.rpf_addr = pim_addr_from_prefix(&match);
-               pnc = pim_nexthop_cache_find(pim, &rpf);
-               if (!pnc) {
-                       if (PIM_DEBUG_PIM_NHT)
-                               zlog_debug(
-                                       "%s: Skipping NHT update, addr %pPA is not in local cached DB.",
-                                       __func__, &rpf.rpf_addr);
-                       return 0;
-               }
-       } else {
-               /*
-                * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE
-                */
+       rpf.rpf_addr = pim_addr_from_prefix(&match);
+       pnc = pim_nexthop_cache_find(pim, &rpf);
+       if (!pnc) {
+               if (PIM_DEBUG_PIM_NHT)
+                       zlog_debug(
+                               "%s: Skipping NHT update, addr %pPA is not in local cached DB.",
+                               __func__, &rpf.rpf_addr);
                return 0;
        }
 
        pnc->last_update = pim_time_monotonic_usec();
 
        if (nhr.nexthop_num) {
-               pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
+               pnc->nexthop_num = 0;
 
                for (i = 0; i < nhr.nexthop_num; i++) {
                        nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
@@ -862,7 +855,8 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
                                nhlist_tail = nexthop;
                                nhlist_head = nexthop;
                        }
-                       // Only keep track of nexthops which are PIM enabled.
+
+                       // Keep track of all nexthops, even PIM-disabled ones.
                        pnc->nexthop_num++;
                }
                /* Reset existing pnc->nexthop before assigning new list */
index 63ddb85d001bc0682311368bcfa1bac4bb5abf69..e1ec9b34a113fbbceeddfdc886c0f3d27274a92c 100644 (file)
@@ -149,6 +149,31 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
        return c_oil;
 }
 
+
+/*
+ * Clean up mroute and channel oil created for dropping pkts from directly
+ * connected source when the interface was non DR.
+ */
+void pim_clear_nocache_state(struct pim_interface *pim_ifp)
+{
+       struct channel_oil *c_oil;
+
+       frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) {
+
+               if ((!c_oil->up) ||
+                   !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags)))
+                       continue;
+
+               if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index)
+                       continue;
+
+               EVENT_OFF(c_oil->up->t_ka_timer);
+               PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags);
+               PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags);
+               pim_upstream_del(pim_ifp->pim, c_oil->up, __func__);
+       }
+}
+
 struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
                                        const char *name)
 {
index 45b337cc720fa075589f5644ece7283d88bdd51a..dc66eaace454c1fab9e80ca1b892b7d7f7e60194 100644 (file)
@@ -182,6 +182,7 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
                                         pim_sgaddr *sg);
 struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
                                        pim_sgaddr *sg, const char *name);
+void pim_clear_nocache_state(struct pim_interface *pim_ifp);
 struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
                                        const char *name);
 
index 91628930f4f64be17375bf5fef7526a22467549e..0f6547ee2e9a9113ffe8340be0734158ca3af502 100644 (file)
@@ -335,18 +335,12 @@ static int gm_config_write(struct vty *vty, int writes,
                struct listnode *node;
                struct gm_join *ij;
                for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) {
-                       char group_str[INET_ADDRSTRLEN];
-                       char source_str[INET_ADDRSTRLEN];
-                       pim_inet4_dump("<grp?>", ij->group_addr, group_str,
-                                      sizeof(group_str));
-                       if (ij->source_addr.s_addr == INADDR_ANY) {
-                               vty_out(vty, " ip igmp join %s\n", group_str);
-                       } else {
-                               inet_ntop(AF_INET, &ij->source_addr, source_str,
-                                         sizeof(source_str));
-                               vty_out(vty, " ip igmp join %s %s\n", group_str,
-                                       source_str);
-                       }
+                       if (pim_addr_is_any(ij->source_addr))
+                               vty_out(vty, " ip igmp join %pPAs\n",
+                                       &ij->group_addr);
+                       else
+                               vty_out(vty, " ip igmp join %pPAs %pPAs\n",
+                                       &ij->group_addr, &ij->source_addr);
                        ++writes;
                }
        }
@@ -388,6 +382,21 @@ static int gm_config_write(struct vty *vty, int writes,
                vty_out(vty, " ipv6 mld last-member-query-interval %d\n",
                        pim_ifp->gm_specific_query_max_response_time_dsec);
 
+       /* IF ipv6 mld join */
+       if (pim_ifp->gm_join_list) {
+               struct listnode *node;
+               struct gm_join *ij;
+               for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) {
+                       if (pim_addr_is_any(ij->source_addr))
+                               vty_out(vty, " ipv6 mld join %pPAs\n",
+                                       &ij->group_addr);
+                       else
+                               vty_out(vty, " ipv6 mld join %pPAs %pPAs\n",
+                                       &ij->group_addr, &ij->source_addr);
+                       ++writes;
+               }
+       }
+
        return writes;
 }
 #endif
index 97b68c0a327a353a8ded004e1290cc23884c5cc8..92dcbf9d1d5b1d1aff6f9744017ffdd402b9acb0 100644 (file)
@@ -68,6 +68,8 @@ static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS)
                           vrf_id, new_vrf_id);
 
        pim = pim_get_pim_instance(new_vrf_id);
+       if (!pim)
+               return 0;
 
        if_update_to_new_vrf(ifp, new_vrf_id);
 
index fd7255cb87090f9c1d135e2484930be6a335c868..9a7901ec3fbf572230778c0ea29cec1d0f5ac007 100644 (file)
@@ -169,6 +169,7 @@ pimd_pim6d_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=6
 pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP)
 endif
 
+pimd_test_igmpv3_join_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4
 pimd_test_igmpv3_join_LDADD = lib/libfrr.la
 pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
 
index 07070900d2fe8db93d6608fcf33bf41ede5b3f8d..926e453c9db3596cc3ad62fd630f54e57e192d5f 100644 (file)
@@ -54,8 +54,8 @@ static int iface_solve_index(const char *ifname)
 
 int main(int argc, const char *argv[])
 {
-       struct in_addr group_addr;
-       struct in_addr source_addr;
+       pim_addr group_addr;
+       pim_addr source_addr;
        const char *ifname;
        const char *group;
        const char *source;
@@ -106,7 +106,7 @@ int main(int argc, const char *argv[])
                exit(1);
        }
 
-       result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr);
+       result = pim_gm_join_source(fd, ifindex, group_addr, source_addr);
        if (result) {
                fprintf(stderr,
                        "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n",
index c325979d24f38d8d9a1313d3a434f12d97d02151..6dd5c8866ed473fc399100a52acddcee49a9913e 100644 (file)
@@ -37,6 +37,8 @@ daemon_flags = {
     "lib/filter_cli.c": "VTYSH_ACL",
     "lib/if.c": "VTYSH_INTERFACE",
     "lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D",
+    "lib/mgmt_be_client.c": "VTYSH_STATICD",
+    "lib/mgmt_fe_client.c": "VTYSH_MGMTD",
     "lib/lib_vty.c": "VTYSH_ALL",
     "lib/log_vty.c": "VTYSH_ALL",
     "lib/nexthop_group.c": "VTYSH_NH_GROUP",
diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c
new file mode 100644 (file)
index 0000000..b59db11
--- /dev/null
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/bfd.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+
+DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
+
+extern struct zclient *zclient;
+
+static const char *rip_bfd_interface_profile(struct rip_interface *ri)
+{
+       struct rip *rip = ri->rip;
+
+       if (ri->bfd.profile)
+               return ri->bfd.profile;
+
+       if (rip->default_bfd_profile)
+               return rip->default_bfd_profile;
+
+       return NULL;
+}
+
+static void rip_bfd_session_change(struct bfd_session_params *bsp,
+                                  const struct bfd_session_status *bss,
+                                  void *arg)
+{
+       struct rip_peer *rp = arg;
+
+       /* BFD peer went down. */
+       if (bss->state == BFD_STATUS_DOWN &&
+           bss->previous_state == BFD_STATUS_UP) {
+               if (IS_RIP_DEBUG_EVENT)
+                       zlog_debug("%s: peer %pI4: BFD Down", __func__,
+                                  &rp->addr);
+
+               rip_peer_delete_routes(rp);
+               listnode_delete(rp->rip->peer_list, rp);
+               rip_peer_free(rp);
+               return;
+       }
+
+       /* BFD peer went up. */
+       if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN)
+               if (IS_RIP_DEBUG_EVENT)
+                       zlog_debug("%s: peer %pI4: BFD Up", __func__,
+                                  &rp->addr);
+}
+
+void rip_bfd_session_update(struct rip_peer *rp)
+{
+       struct rip_interface *ri = rp->ri;
+
+       /* BFD configuration was removed. */
+       if (ri == NULL || !ri->bfd.enabled) {
+               bfd_sess_free(&rp->bfd_session);
+               return;
+       }
+
+       /* New BFD session. */
+       if (rp->bfd_session == NULL) {
+               rp->bfd_session = bfd_sess_new(rip_bfd_session_change, rp);
+               bfd_sess_set_ipv4_addrs(rp->bfd_session, NULL, &rp->addr);
+               bfd_sess_set_interface(rp->bfd_session, ri->ifp->name);
+               bfd_sess_set_vrf(rp->bfd_session, rp->rip->vrf->vrf_id);
+       }
+
+       /* Set new configuration. */
+       bfd_sess_set_timers(rp->bfd_session, BFD_DEF_DETECT_MULT,
+                           BFD_DEF_MIN_RX, BFD_DEF_MIN_TX);
+       bfd_sess_set_profile(rp->bfd_session, rip_bfd_interface_profile(ri));
+
+       bfd_sess_install(rp->bfd_session);
+}
+
+void rip_bfd_interface_update(struct rip_interface *ri)
+{
+       struct rip *rip;
+       struct rip_peer *rp;
+       struct listnode *node;
+
+       rip = ri->rip;
+       if (!rip)
+               return;
+
+       for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, rp)) {
+               if (rp->ri != ri)
+                       continue;
+
+               rip_bfd_session_update(rp);
+       }
+}
+
+void rip_bfd_instance_update(struct rip *rip)
+{
+       struct interface *ifp;
+
+       FOR_ALL_INTERFACES (rip->vrf, ifp) {
+               struct rip_interface *ri;
+
+               ri = ifp->info;
+               if (!ri)
+                       continue;
+
+               rip_bfd_interface_update(ri);
+       }
+}
+
+void rip_bfd_init(struct event_loop *tm)
+{
+       bfd_protocol_integration_init(zclient, tm);
+}
diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h
new file mode 100644 (file)
index 0000000..7621498
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#ifndef _RIP_BFD_
+#define _RIP_BFD_
+
+#include "frrevent.h"
+
+DECLARE_MTYPE(RIP_BFD_PROFILE);
+
+struct rip;
+struct rip_interface;
+struct rip_peer;
+
+void rip_bfd_session_update(struct rip_peer *rp);
+void rip_bfd_interface_update(struct rip_interface *ri);
+void rip_bfd_instance_update(struct rip *rip);
+void rip_bfd_init(struct event_loop *tm);
+
+#endif /* _RIP_BFD_ */
index cac29c00d458e80cd9fb41b165156a8c37bda650..ac9fc4b1a818ba651e60f633bdbc5dee2bd0ddb5 100644 (file)
@@ -581,6 +581,42 @@ void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode,
        }
 }
 
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+DEFPY_YANG(rip_bfd_default_profile, rip_bfd_default_profile_cmd,
+          "bfd default-profile BFDPROF$profile",
+          "Bidirectional Forwarding Detection\n"
+          "BFD default profile\n"
+          "Profile name\n")
+{
+       nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_MODIFY,
+                             profile);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_rip_bfd_default_profile, no_rip_bfd_default_profile_cmd,
+          "no bfd default-profile [BFDPROF]",
+          NO_STR
+          "Bidirectional Forwarding Detection\n"
+          "BFD default profile\n"
+          "Profile name\n")
+{
+       nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_DESTROY,
+                             NULL);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+                                               const struct lyd_node *dnode,
+                                               bool show_defaults)
+{
+       vty_out(vty, " bfd default-profile %s\n",
+               yang_dnode_get_string(dnode, NULL));
+}
+
 /*
  * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
  */
@@ -979,6 +1015,66 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
                yang_dnode_get_string(dnode, NULL));
 }
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+DEFPY_YANG(ip_rip_bfd, ip_rip_bfd_cmd, "[no] ip rip bfd",
+          NO_STR IP_STR
+          "Routing Information Protocol\n"
+          "Enable BFD support\n")
+{
+       nb_cli_enqueue_change(vty, "./bfd-monitoring/enable", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+                               bool show_defaults)
+{
+       vty_out(vty, " ip rip bfd\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd/profile
+ */
+DEFPY_YANG(ip_rip_bfd_profile, ip_rip_bfd_profile_cmd,
+          "[no] ip rip bfd profile BFDPROF$profile",
+          NO_STR IP_STR
+          "Routing Information Protocol\n"
+          "Enable BFD support\n"
+          "Use a pre-configured profile\n"
+          "Profile name\n")
+{
+       if (no)
+               nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+                                     NB_OP_DESTROY, NULL);
+       else
+               nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+                                     NB_OP_MODIFY, profile);
+
+       return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG(no_ip_rip_bfd_profile, no_ip_rip_bfd_profile_cmd,
+          "no ip rip bfd profile",
+          NO_STR IP_STR
+          "Routing Information Protocol\n"
+          "Enable BFD support\n"
+          "Use a pre-configured profile\n")
+{
+       nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", NB_OP_DESTROY,
+                             NULL);
+       return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+                                bool show_defaults)
+{
+       vty_out(vty, " ip rip bfd profile %s\n",
+               yang_dnode_get_string(dnode, NULL));
+}
+
 /*
  * XPath: /frr-ripd:clear-rip-route
  */
@@ -1078,6 +1174,8 @@ void rip_cli_init(void)
        install_element(RIP_NODE, &no_rip_timers_cmd);
        install_element(RIP_NODE, &rip_version_cmd);
        install_element(RIP_NODE, &no_rip_version_cmd);
+       install_element(RIP_NODE, &rip_bfd_default_profile_cmd);
+       install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd);
 
        install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd);
        install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd);
@@ -1092,6 +1190,9 @@ void rip_cli_init(void)
        install_element(INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
        install_element(INTERFACE_NODE,
                        &no_ip_rip_authentication_key_chain_cmd);
+       install_element(INTERFACE_NODE, &ip_rip_bfd_cmd);
+       install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
+       install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
 
        install_element(ENABLE_NODE, &clear_ip_rip_cmd);
 }
index 0b92f174b1ff1301424972f6dbae25c0b5d09229..b58015a67d89e50f7145b22e049e2edf49109b84 100644 (file)
@@ -25,6 +25,7 @@
 #include "zebra/connected.h"
 
 #include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
 #include "ripd/rip_debug.h"
 #include "ripd/rip_interface.h"
 
@@ -457,6 +458,7 @@ static void rip_interface_reset(struct rip_interface *ri)
        ri->sent_updates = 0;
 
        ri->passive = 0;
+       XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
 
        rip_interface_clean(ri);
 }
@@ -1109,8 +1111,10 @@ void rip_interface_sync(struct interface *ifp)
        struct rip_interface *ri;
 
        ri = ifp->info;
-       if (ri)
+       if (ri) {
                ri->rip = ifp->vrf->info;
+               ri->ifp = ifp;
+       }
 }
 
 /* Called when interface structure allocated. */
index a6e4ad776bf132ea44e66937c8bd819b1f62ea88..0e26662cdb5ca9b815568666fd0f603e0255d2af 100644 (file)
 #include "if_rmap.h"
 #include "libfrr.h"
 #include "routemap.h"
+#include "bfd.h"
 
 #include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
 #include "ripd/rip_nb.h"
 #include "ripd/rip_errors.h"
 
@@ -65,6 +67,7 @@ static void sigint(void)
 {
        zlog_notice("Terminating on signal");
 
+       bfd_protocol_integration_set_shutdown(true);
        rip_vrf_terminate();
        if_rmap_terminate();
        rip_zclient_stop();
@@ -162,6 +165,7 @@ int main(int argc, char **argv)
        rip_if_init();
        rip_cli_init();
        rip_zclient_init(master);
+       rip_bfd_init(master);
 
        frr_config_fork();
        frr_run(master);
index 9947c01af56b2b060544b446223e00ff11f5b8e1..d11f1e1d34fee98241d3adf7b0613cec8e04c4de 100644 (file)
@@ -10,6 +10,7 @@
 #include "libfrr.h"
 
 #include "ripd/rip_nb.h"
+#include "lib/if_rmap.h"
 
 /* clang-format off */
 const struct frr_yang_module_info frr_ripd_info = {
@@ -165,6 +166,28 @@ const struct frr_yang_module_info frr_ripd_info = {
                                .modify = ripd_instance_redistribute_metric_modify,
                        },
                },
+               {
+                       .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map",
+                       .cbs = {
+                               .create = ripd_instance_if_route_maps_if_route_map_create,
+                               .destroy = ripd_instance_if_route_maps_if_route_map_destroy,
+                               .cli_show = cli_show_if_route_map,
+                       }
+               },
+               {
+                       .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map",
+                       .cbs = {
+                               .modify = ripd_instance_if_route_maps_if_route_map_in_route_map_modify,
+                               .destroy = ripd_instance_if_route_maps_if_route_map_in_route_map_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map",
+                       .cbs = {
+                               .modify = ripd_instance_if_route_maps_if_route_map_out_route_map_modify,
+                               .destroy = ripd_instance_if_route_maps_if_route_map_out_route_map_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-ripd:ripd/instance/static-route",
                        .cbs = {
@@ -216,6 +239,14 @@ const struct frr_yang_module_info frr_ripd_info = {
                                .modify = ripd_instance_version_send_modify,
                        },
                },
+               {
+                       .xpath = "/frr-ripd:ripd/instance/default-bfd-profile",
+                       .cbs = {
+                               .modify = ripd_instance_default_bfd_profile_modify,
+                               .destroy = ripd_instance_default_bfd_profile_destroy,
+                               .cli_show = cli_show_ripd_instance_default_bfd_profile,
+                       },
+               },
                {
                        .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon",
                        .cbs = {
@@ -279,6 +310,28 @@ const struct frr_yang_module_info frr_ripd_info = {
                                .modify = lib_interface_rip_authentication_key_chain_modify,
                        },
                },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring",
+                       .cbs = {
+                               .create = lib_interface_rip_bfd_create,
+                               .destroy = lib_interface_rip_bfd_destroy,
+                       },
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable",
+                       .cbs = {
+                               .cli_show = cli_show_ip_rip_bfd_enable,
+                               .modify = lib_interface_rip_bfd_enable_modify,
+                       },
+               },
+               {
+                       .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile",
+                       .cbs = {
+                               .cli_show = cli_show_ip_rip_bfd_profile,
+                               .modify = lib_interface_rip_bfd_profile_modify,
+                               .destroy = lib_interface_rip_bfd_profile_destroy,
+                       },
+               },
                {
                        .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor",
                        .cbs = {
@@ -337,6 +390,66 @@ const struct frr_yang_module_info frr_ripd_info = {
                                .get_elem = ripd_instance_state_routes_route_interface_get_elem,
                        },
                },
+               {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop",
+                        .cbs = {
+                                .get_next = ripd_instance_state_routes_route_nexthops_nexthop_get_next,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem,
+                        }
+                },
+                {
+                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time",
+                        .cbs = {
+                                .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem,
+                        }
+                },
                {
                        .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric",
                        .cbs = {
index 99114c99288ad6ceb7445e2c3e3fa8cb5c3d52ff..9929e0952b6febb70ab3e20a38fe40212ea35703 100644 (file)
@@ -52,6 +52,18 @@ int ripd_instance_redistribute_route_map_destroy(
        struct nb_cb_destroy_args *args);
 int ripd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args);
 int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_create(
+       struct nb_cb_create_args *args);
+int ripd_instance_if_route_maps_if_route_map_destroy(
+       struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_in_route_map_modify(
+       struct nb_cb_modify_args *args);
+int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+       struct nb_cb_destroy_args *args);
+int ripd_instance_if_route_maps_if_route_map_out_route_map_modify(
+       struct nb_cb_modify_args *args);
+int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+       struct nb_cb_destroy_args *args);
 int ripd_instance_static_route_create(struct nb_cb_create_args *args);
 int ripd_instance_static_route_destroy(struct nb_cb_destroy_args *args);
 int ripd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args);
@@ -60,6 +72,8 @@ int ripd_instance_timers_holddown_interval_modify(
 int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args);
 int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args);
 int ripd_instance_version_send_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args);
 const void *ripd_instance_state_neighbors_neighbor_get_next(
        struct nb_cb_get_next_args *args);
 int ripd_instance_state_neighbors_neighbor_get_keys(
@@ -89,6 +103,37 @@ struct yang_data *ripd_instance_state_routes_route_interface_get_elem(
        struct nb_cb_get_elem_args *args);
 struct yang_data *ripd_instance_state_routes_route_metric_get_elem(
        struct nb_cb_get_elem_args *args);
+const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next(
+       struct nb_cb_get_next_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem(
+       struct nb_cb_get_elem_args *args);
+struct yang_data *ripd_instance_state_routes_route_metric_get_elem(
+       struct nb_cb_get_elem_args *args);
 int clear_rip_route_rpc(struct nb_cb_rpc_args *args);
 int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args);
 int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args);
@@ -108,6 +153,12 @@ int lib_interface_rip_authentication_key_chain_modify(
        struct nb_cb_modify_args *args);
 int lib_interface_rip_authentication_key_chain_destroy(
        struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args);
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_enable_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args);
 
 /* Optional 'apply_finish' callbacks. */
 void ripd_instance_redistribute_apply_finish(
@@ -163,6 +214,9 @@ void cli_show_ip_rip_receive_version(struct vty *vty,
                                     bool show_defaults);
 void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode,
                                  bool show_defaults);
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+                                               const struct lyd_node *dnode,
+                                               bool show_defaults);
 void cli_show_ip_rip_authentication_scheme(struct vty *vty,
                                           const struct lyd_node *dnode,
                                           bool show_defaults);
@@ -172,6 +226,10 @@ void cli_show_ip_rip_authentication_string(struct vty *vty,
 void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
                                              const struct lyd_node *dnode,
                                              bool show_defaults);
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+                               bool show_defaults);
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+                                bool show_defaults);
 
 /* Notifications. */
 extern void ripd_notif_send_auth_type_failure(const char *ifname);
index 2277ddc2045755c55a8e6cc2f663e6c51db091ca..5c7bd0fb8623425169152882fb5eb63083c0c793 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
  * Copyright (C) 2018  NetDEF, Inc.
  *                     Renato Westphal
+ * Copyright (C) 2023 LabN Consulting, L.L.C.
  */
 
 #include <zebra.h>
@@ -13,6 +14,7 @@
 #include "prefix.h"
 #include "table.h"
 #include "command.h"
+#include "if_rmap.h"
 #include "routemap.h"
 #include "northbound.h"
 #include "libfrr.h"
@@ -21,6 +23,7 @@
 #include "ripd/rip_nb.h"
 #include "ripd/rip_debug.h"
 #include "ripd/rip_interface.h"
+#include "ripd/rip_bfd.h"
 
 /*
  * XPath: /frr-ripd:ripd/instance
@@ -523,7 +526,7 @@ int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args)
        rip = nb_running_get_entry(args->dnode, NULL, true);
        ifname = yang_dnode_get_string(args->dnode, NULL);
 
-       return rip_passive_nondefault_unset(rip, ifname);
+       return rip_passive_nondefault_set(rip, ifname);
 }
 
 int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args)
@@ -537,7 +540,7 @@ int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args)
        rip = nb_running_get_entry(args->dnode, NULL, true);
        ifname = yang_dnode_get_string(args->dnode, NULL);
 
-       return rip_passive_nondefault_set(rip, ifname);
+       return rip_passive_nondefault_unset(rip, ifname);
 }
 
 /*
@@ -680,6 +683,94 @@ int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args)
        return NB_OK;
 }
 
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_create(
+       struct nb_cb_create_args *args)
+{
+       /* if_rmap is created when first routemap is added */
+       return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct rip *rip;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       /*
+        * YANG will prune edit deletes up to the most general deleted node so
+        * we need to handle deleting any existing state underneath and not
+        * count on those more specific callbacks being called individually.
+        */
+
+       rip = nb_running_get_entry(args->dnode, NULL, true);
+       if_rmap_yang_destroy_cb(rip->if_rmap_ctx, args->dnode);
+
+       return NB_OK;
+}
+
+static void if_route_map_modify(const struct lyd_node *dnode,
+                               enum if_rmap_type type, bool delete)
+{
+       struct rip *rip = nb_running_get_entry(dnode, NULL, true);
+
+       if_rmap_yang_modify_cb(rip->if_rmap_ctx, dnode, type, delete);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_in_route_map_modify(
+       struct nb_cb_modify_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_IN, false);
+
+       return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_IN, true);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map
+ */
+int ripd_instance_if_route_maps_if_route_map_out_route_map_modify(
+       struct nb_cb_modify_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_OUT, false);
+
+       return NB_OK;
+}
+
+int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_OUT, true);
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-ripd:ripd/instance/static-route
  */
@@ -815,6 +906,40 @@ int ripd_instance_version_send_modify(struct nb_cb_modify_args *args)
        return NB_OK;
 }
 
+/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+       struct rip *rip;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       rip = nb_running_get_entry(args->dnode, NULL, true);
+       XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+       rip->default_bfd_profile =
+               XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+                       yang_dnode_get_string(args->dnode, NULL));
+       rip_bfd_instance_update(rip);
+
+       return NB_OK;
+}
+
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+       struct rip *rip;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       rip = nb_running_get_entry(args->dnode, NULL, true);
+       XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+       rip_bfd_instance_update(rip);
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
  */
@@ -980,6 +1105,104 @@ int lib_interface_rip_authentication_password_destroy(
        return NB_OK;
 }
 
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring
+ */
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args)
+{
+       struct interface *ifp;
+       struct rip_interface *ri;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(args->dnode, NULL, true);
+       ri = ifp->info;
+       ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable");
+       XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+       if (yang_dnode_exists(args->dnode, "./profile"))
+               ri->bfd.profile = XSTRDUP(
+                       MTYPE_RIP_BFD_PROFILE,
+                       yang_dnode_get_string(args->dnode, "./profile"));
+
+       rip_bfd_interface_update(ri);
+
+       return NB_OK;
+}
+
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+       struct interface *ifp;
+       struct rip_interface *ri;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(args->dnode, NULL, true);
+       ri = ifp->info;
+       ri->bfd.enabled = false;
+       XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+       rip_bfd_interface_update(ri);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args)
+{
+       struct interface *ifp;
+       struct rip_interface *ri;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(args->dnode, NULL, true);
+       ri = ifp->info;
+       ri->bfd.enabled = yang_dnode_get_bool(args->dnode, NULL);
+       rip_bfd_interface_update(ri);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile
+ */
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+       struct interface *ifp;
+       struct rip_interface *ri;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(args->dnode, NULL, true);
+       ri = ifp->info;
+       XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+       ri->bfd.profile = XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+                                 yang_dnode_get_string(args->dnode, NULL));
+       rip_bfd_interface_update(ri);
+
+       return NB_OK;
+}
+
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+       struct interface *ifp;
+       struct rip_interface *ri;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       ifp = nb_running_get_entry(args->dnode, NULL, true);
+       ri = ifp->info;
+       XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+       rip_bfd_interface_update(ri);
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
  */
index 0e2931b464ff9a025a451e61ebfdcdde5cc99b1e..fa0d382a0e095915b2228743e36e8aaa34aeca8f 100644 (file)
@@ -207,9 +207,170 @@ struct yang_data *ripd_instance_state_routes_route_prefix_get_elem(
        const struct route_node *rn = args->list_entry;
        const struct rip_info *rinfo = listnode_head(rn->info);
 
+       assert(rinfo);
        return yang_data_new_ipv4p(args->xpath, &rinfo->rp->p);
 }
 
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop
+ */
+const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next(
+       struct nb_cb_get_next_args *args)
+{
+       const struct route_node *rn = args->parent_list_entry;
+       const struct listnode *node = args->list_entry;
+
+       assert(rn);
+       if (node)
+               return listnextnode(node);
+       assert(rn->info);
+       return listhead((struct list *)rn->info);
+}
+
+static inline const struct rip_info *get_rip_info(const void *info)
+{
+       return (const struct rip_info *)listgetdata(
+               (const struct listnode *)info);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       assert(rinfo);
+       return yang_data_new_enum(args->xpath, rinfo->nh.type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       assert(rinfo);
+       return yang_data_new_enum(args->xpath, rinfo->type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       assert(rinfo);
+       return yang_data_new_enum(args->xpath, rinfo->sub_type);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       if (rinfo->nh.type != NEXTHOP_TYPE_IPV4 &&
+           rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX)
+               return NULL;
+
+       return yang_data_new_ipv4(args->xpath, &rinfo->nh.gate.ipv4);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+       const struct rip *rip = rip_info_get_instance(rinfo);
+
+       if (rinfo->nh.type != NEXTHOP_TYPE_IFINDEX &&
+           rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX)
+               return NULL;
+
+       return yang_data_new_string(
+               args->xpath,
+               ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id));
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       if (rinfo->type != ZEBRA_ROUTE_RIP || rinfo->sub_type != RIP_ROUTE_RTE)
+               return NULL;
+
+       return yang_data_new_ipv4(args->xpath, &rinfo->from);
+}
+
+/*
+ * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       return yang_data_new_uint32(args->xpath, rinfo->tag);
+}
+
+/*
+ * XPath:
+ * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+
+       if ((rinfo->type == ZEBRA_ROUTE_RIP &&
+            rinfo->sub_type == RIP_ROUTE_RTE) ||
+           rinfo->metric == RIP_METRIC_INFINITY || rinfo->external_metric == 0)
+               return NULL;
+       return yang_data_new_uint32(args->xpath, rinfo->external_metric);
+}
+
+/*
+ * XPath:
+ * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time
+ */
+struct yang_data *
+ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem(
+       struct nb_cb_get_elem_args *args)
+{
+       const struct rip_info *rinfo = get_rip_info(args->list_entry);
+       struct event *event;
+
+       if ((event = rinfo->t_timeout) == NULL)
+               event = rinfo->t_garbage_collect;
+       if (!event)
+               return NULL;
+
+       return yang_data_new_uint32(args->xpath,
+                                   event_timer_remain_second(event));
+}
+
 /*
  * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop
  */
index 9410ef380ecc723de5beafb65a415fe2ee67d175..3e8ddeccf2679b02db9f0ff84e23875f0a37b7df 100644 (file)
 #include "linklist.h"
 #include "frrevent.h"
 #include "memory.h"
+#include "table.h"
 
 #include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
 
 DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer");
 
@@ -21,8 +23,9 @@ static struct rip_peer *rip_peer_new(void)
        return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer));
 }
 
-static void rip_peer_free(struct rip_peer *peer)
+void rip_peer_free(struct rip_peer *peer)
 {
+       bfd_sess_free(&peer->bfd_session);
        EVENT_OFF(peer->t_timeout);
        XFREE(MTYPE_RIP_PEER, peer);
 }
@@ -62,7 +65,8 @@ static void rip_peer_timeout(struct event *t)
 }
 
 /* Get RIP peer.  At the same time update timeout thread. */
-static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
+static struct rip_peer *rip_peer_get(struct rip *rip, struct rip_interface *ri,
+                                    struct in_addr *addr)
 {
        struct rip_peer *peer;
 
@@ -73,7 +77,9 @@ static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
        } else {
                peer = rip_peer_new();
                peer->rip = rip;
+               peer->ri = ri;
                peer->addr = *addr;
+               rip_bfd_session_update(peer);
                listnode_add_sort(rip->peer_list, peer);
        }
 
@@ -87,24 +93,27 @@ static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
        return peer;
 }
 
-void rip_peer_update(struct rip *rip, struct sockaddr_in *from, uint8_t version)
+void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+                    struct sockaddr_in *from, uint8_t version)
 {
        struct rip_peer *peer;
-       peer = rip_peer_get(rip, &from->sin_addr);
+       peer = rip_peer_get(rip, ri, &from->sin_addr);
        peer->version = version;
 }
 
-void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+                       struct sockaddr_in *from)
 {
        struct rip_peer *peer;
-       peer = rip_peer_get(rip, &from->sin_addr);
+       peer = rip_peer_get(rip, ri, &from->sin_addr);
        peer->recv_badroutes++;
 }
 
-void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+                        struct sockaddr_in *from)
 {
        struct rip_peer *peer;
-       peer = rip_peer_get(rip, &from->sin_addr);
+       peer = rip_peer_get(rip, ri, &from->sin_addr);
        peer->recv_badpackets++;
 }
 
@@ -136,7 +145,7 @@ void rip_peer_display(struct vty *vty, struct rip *rip)
        char timebuf[RIP_UPTIME_LEN];
 
        for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) {
-               vty_out(vty, "    %-16pI4 %9d %9d %9d   %s\n",
+               vty_out(vty, "    %-17pI4 %9d %9d %9d %11s\n",
                        &peer->addr, peer->recv_badpackets,
                        peer->recv_badroutes, ZEBRA_RIP_DISTANCE_DEFAULT,
                        rip_peer_uptime(peer, timebuf, RIP_UPTIME_LEN));
@@ -155,3 +164,46 @@ void rip_peer_list_del(void *arg)
 {
        rip_peer_free(arg);
 }
+
+void rip_peer_delete_routes(const struct rip_peer *peer)
+{
+       struct route_node *route_node;
+
+       for (route_node = route_top(peer->rip->table); route_node;
+            route_node = route_next(route_node)) {
+               struct rip_info *route_entry;
+               struct listnode *listnode;
+               struct listnode *listnode_next;
+               struct list *list;
+
+               list = route_node->info;
+               if (list == NULL)
+                       continue;
+
+               for (ALL_LIST_ELEMENTS(list, listnode, listnode_next,
+                                      route_entry)) {
+                       if (!rip_route_rte(route_entry))
+                               continue;
+                       if (route_entry->from.s_addr != peer->addr.s_addr)
+                               continue;
+
+                       if (listcount(list) == 1) {
+                               EVENT_OFF(route_entry->t_timeout);
+                               EVENT_OFF(route_entry->t_garbage_collect);
+                               listnode_delete(list, route_entry);
+                               if (list_isempty(list)) {
+                                       list_delete((struct list **)&route_node
+                                                           ->info);
+                                       route_unlock_node(route_node);
+                               }
+                               rip_info_free(route_entry);
+
+                               /* Signal the output process to trigger an
+                                * update (see section 2.5). */
+                               rip_event(peer->rip, RIP_TRIGGERED_UPDATE, 0);
+                       } else
+                               rip_ecmp_delete(peer->rip, route_entry);
+                       break;
+               }
+       }
+}
index 35c4b1f1beef169f5634f450cfda38d540172ba3..698dcb982d8f5b9affdaa59a542228d71fb5ea96 100644 (file)
@@ -13,6 +13,7 @@
 #include "zclient.h"
 #include "log.h"
 #include "vrf.h"
+#include "bfd.h"
 #include "ripd/ripd.h"
 #include "ripd/rip_debug.h"
 #include "ripd/rip_interface.h"
@@ -196,6 +197,7 @@ void rip_zebra_vrf_register(struct vrf *vrf)
                           vrf->name, vrf->vrf_id);
 
        zclient_send_reg_requests(zclient, vrf->vrf_id);
+       bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf->vrf_id);
 }
 
 void rip_zebra_vrf_deregister(struct vrf *vrf)
@@ -208,11 +210,13 @@ void rip_zebra_vrf_deregister(struct vrf *vrf)
                           vrf->name, vrf->vrf_id);
 
        zclient_send_dereg_requests(zclient, vrf->vrf_id);
+       bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_DEREGISTER, vrf->vrf_id);
 }
 
 static void rip_zebra_connected(struct zclient *zclient)
 {
        zclient_send_reg_requests(zclient, VRF_DEFAULT);
+       bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
 }
 
 zclient_handler *const rip_handlers[] = {
index ae4d93b4f5853f82835077737dcb02a877bb0fab..cb8dd4945d4f7ccdb5a7f6aeb4c63c031f439afe 100644 (file)
@@ -1123,7 +1123,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
        if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
                zlog_info("response doesn't come from RIP port: %d",
                          from->sin_port);
-               rip_peer_bad_packet(rip, from);
+               rip_peer_bad_packet(rip, ri, from);
                return;
        }
 
@@ -1137,7 +1137,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                zlog_info(
                        "This datagram doesn't come from a valid neighbor: %pI4",
                        &from->sin_addr);
-               rip_peer_bad_packet(rip, from);
+               rip_peer_bad_packet(rip, ri, from);
                return;
        }
 
@@ -1147,7 +1147,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
        ; /* Alredy done in rip_read () */
 
        /* Update RIP peer. */
-       rip_peer_update(rip, from, packet->version);
+       rip_peer_update(rip, ri, from, packet->version);
 
        /* Set RTE pointer. */
        rte = packet->rte;
@@ -1171,12 +1171,18 @@ static void rip_response_process(struct rip_packet *packet, int size,
                        continue;
                }
 
+               if (packet->version == RIPv1 && rte->tag != 0) {
+                       zlog_warn("RIPv1 reserved field is nonzero: %d",
+                                 ntohs(rte->tag));
+                       continue;
+               }
+
                /* - is the destination address valid (e.g., unicast; not net 0
                   or 127) */
                if (!rip_destination_check(rte->prefix)) {
                        zlog_info(
                                "Network is net 0 or net 127 or it is not unicast network");
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1186,7 +1192,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                /* - is the metric valid (i.e., between 1 and 16, inclusive) */
                if (!(rte->metric >= 1 && rte->metric <= 16)) {
                        zlog_info("Route's metric is not in the 1-16 range.");
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1195,7 +1201,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                    && rte->nexthop.s_addr != INADDR_ANY) {
                        zlog_info("RIPv1 packet with nexthop value %pI4",
                                  &rte->nexthop);
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1326,7 +1332,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                        zlog_warn(
                                "RIPv2 address %pI4 is not mask /%d applied one",
                                &rte->prefix, ip_masklen(rte->mask));
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1500,7 +1506,7 @@ static int rip_send_packet(uint8_t *buf, int size, struct sockaddr_in *to,
        ret = sendmsg(rip->sock, &msg, 0);
 
        if (IS_RIP_DEBUG_EVENT)
-               zlog_debug("SEND to  %pI4%d", &sin.sin_addr,
+               zlog_debug("SEND to %pI4 port %d", &sin.sin_addr,
                           ntohs(sin.sin_port));
 
        if (ret < 0)
@@ -1643,7 +1649,7 @@ static void rip_request_process(struct rip_packet *packet, int size,
                return;
 
        /* RIP peer update. */
-       rip_peer_update(rip, from, packet->version);
+       rip_peer_update(rip, ri, from, packet->version);
 
        lim = ((caddr_t)packet) + size;
        rte = packet->rte;
@@ -1711,7 +1717,7 @@ static void rip_read(struct event *t)
        socklen_t fromlen;
        struct interface *ifp = NULL;
        struct connected *ifc;
-       struct rip_interface *ri;
+       struct rip_interface *ri = NULL;
        struct prefix p;
 
        /* Fetch socket then register myself. */
@@ -1743,8 +1749,10 @@ static void rip_read(struct event *t)
        /* Which interface is this packet comes from. */
        ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
                                rip->vrf->vrf_id);
-       if (ifc)
+       if (ifc) {
                ifp = ifc->ifp;
+               ri = ifp->info;
+       }
 
        /* RIP packet received */
        if (IS_RIP_DEBUG_EVENT)
@@ -1753,7 +1761,7 @@ static void rip_read(struct event *t)
                           ifp ? ifp->name : "unknown", rip->vrf_name);
 
        /* If this packet come from unknown interface, ignore it. */
-       if (ifp == NULL) {
+       if (ifp == NULL || ri == NULL) {
                zlog_info(
                        "%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
                        __func__, &from.sin_addr, ntohs(from.sin_port),
@@ -1779,13 +1787,13 @@ static void rip_read(struct event *t)
        if (len < RIP_PACKET_MINSIZ) {
                zlog_warn("packet size %d is smaller than minimum size %d", len,
                          RIP_PACKET_MINSIZ);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
        if (len > RIP_PACKET_MAXSIZ) {
                zlog_warn("packet size %d is larger than max size %d", len,
                          RIP_PACKET_MAXSIZ);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1793,7 +1801,7 @@ static void rip_read(struct event *t)
        if ((len - RIP_PACKET_MINSIZ) % 20) {
                zlog_warn("packet size %d is wrong for RIP packet alignment",
                          len);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1807,7 +1815,7 @@ static void rip_read(struct event *t)
        if (packet->version == 0) {
                zlog_info("version 0 with command %d received.",
                          packet->command);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1823,12 +1831,11 @@ static void rip_read(struct event *t)
                packet->version = RIPv2;
 
        /* Is RIP running or is this RIP neighbor ?*/
-       ri = ifp->info;
        if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
                if (IS_RIP_DEBUG_EVENT)
                        zlog_debug("RIP is not enabled on interface %s.",
                                   ifp->name);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1842,7 +1849,7 @@ static void rip_read(struct event *t)
                        zlog_debug(
                                "  packet's v%d doesn't fit to if version spec",
                                packet->version);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1857,7 +1864,7 @@ static void rip_read(struct event *t)
                                "packet RIPv%d is dropped because authentication disabled",
                                packet->version);
                ripd_notif_send_auth_type_failure(ifp->name);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1893,7 +1900,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv1 dropped because authentication enabled");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
        } else if (ri->auth_type != RIP_NO_AUTH) {
@@ -1906,7 +1913,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv2 authentication failed: no auth RTE in packet");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
 
@@ -1916,7 +1923,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv2 dropped because authentication enabled");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
 
@@ -1952,7 +1959,7 @@ static void rip_read(struct event *t)
                                zlog_debug("RIPv2 %s authentication failure",
                                           auth_desc);
                        ripd_notif_send_auth_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
        }
@@ -1971,16 +1978,16 @@ static void rip_read(struct event *t)
                zlog_info(
                        "Obsolete command %s received, please sent it to routed",
                        lookup_msg(rip_msg, packet->command, NULL));
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        case RIP_POLL_ENTRY:
                zlog_info("Obsolete command %s received",
                          lookup_msg(rip_msg, packet->command, NULL));
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        default:
                zlog_info("Unknown RIP command %d received", packet->command);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        }
 }
@@ -2836,16 +2843,11 @@ uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo)
                        if (access_list_apply(alist, &rinfo->rp->p)
                            == FILTER_DENY)
                                return 0;
-
-                       return rdistance->distance;
-               } else
-                       return rdistance->distance;
+               }
+               return rdistance->distance;
        }
 
-       if (rip->distance)
-               return rip->distance;
-
-       return 0;
+       return rip->distance;
 }
 
 static void rip_distance_show(struct vty *vty, struct rip *rip)
@@ -3209,9 +3211,6 @@ static int config_write_rip(struct vty *vty)
                /* Distribute configuration. */
                config_write_distribute(vty, rip->distribute_ctx);
 
-               /* Interface routemap configuration */
-               config_write_if_rmap(vty, rip->if_rmap_ctx);
-
                vty_out(vty, "exit\n");
 
                write = 1;
@@ -3347,6 +3346,7 @@ void rip_clean(struct rip *rip)
        route_table_finish(rip->distance_table);
 
        RB_REMOVE(rip_instance_head, &rip_instances, rip);
+       XFREE(MTYPE_TMP, rip->default_bfd_profile);
        XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
        XFREE(MTYPE_RIP, rip);
 }
index 176a3bfc376766655b4e73b4f3d3c6ca77ffb730..bba3c280697b9a080625371a8ff5c9ab52b83c60 100644 (file)
@@ -10,6 +10,7 @@
 #include "nexthop.h"
 #include "distribute.h"
 #include "memory.h"
+#include "bfd.h"
 
 /* RIP version number. */
 #define RIPv1                            1
@@ -182,6 +183,9 @@ struct rip {
                /* RIP queries. */
                long queries;
        } counters;
+
+       /* Default BFD profile to use with BFD sessions. */
+       char *default_bfd_profile;
 };
 RB_HEAD(rip_instance_head, rip);
 RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare)
@@ -265,6 +269,9 @@ struct rip_interface {
        /* Parent routing instance. */
        struct rip *rip;
 
+       /* Interface data from zebra. */
+       struct interface *ifp;
+
        /* RIP is enabled on this interface. */
        int enable_network;
        int enable_interface;
@@ -318,6 +325,12 @@ struct rip_interface {
 
        /* Passive interface. */
        int passive;
+
+       /* BFD information. */
+       struct {
+               bool enabled;
+               char *profile;
+       } bfd;
 };
 
 /* RIP peer information. */
@@ -325,6 +338,9 @@ struct rip_peer {
        /* Parent routing instance. */
        struct rip *rip;
 
+       /* Back-pointer to RIP interface. */
+       struct rip_interface *ri;
+
        /* Peer address. */
        struct in_addr addr;
 
@@ -343,6 +359,9 @@ struct rip_peer {
 
        /* Timeout thread. */
        struct event *t_timeout;
+
+       /* BFD information */
+       struct bfd_session_params *bfd_session;
 };
 
 struct rip_distance {
@@ -461,16 +480,20 @@ extern void rip_if_rmap_update_interface(struct interface *ifp);
 extern int rip_show_network_config(struct vty *vty, struct rip *rip);
 extern void rip_show_redistribute_config(struct vty *vty, struct rip *rip);
 
-extern void rip_peer_update(struct rip *rip, struct sockaddr_in *from,
-                           uint8_t version);
-extern void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from);
-extern void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from);
+extern void rip_peer_free(struct rip_peer *peer);
+extern void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+                           struct sockaddr_in *from, uint8_t version);
+extern void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+                              struct sockaddr_in *from);
+extern void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+                               struct sockaddr_in *from);
 extern void rip_peer_display(struct vty *vty, struct rip *rip);
 extern struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr);
 extern struct rip_peer *rip_peer_lookup_next(struct rip *rip,
                                             struct in_addr *addr);
 extern int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2);
 extern void rip_peer_list_del(void *arg);
+void rip_peer_delete_routes(const struct rip_peer *peer);
 
 extern void rip_info_free(struct rip_info *);
 extern struct rip *rip_info_get_instance(const struct rip_info *rinfo);
index 98cc765c91cfb559d5dd6a22a663a104c653e492..294a05e575ae09b007011b388860202d6b3a632a 100644 (file)
@@ -13,6 +13,7 @@ man8 += $(MANBUILD)/frr-ripd.8
 endif
 
 ripd_ripd_SOURCES = \
+       ripd/rip_bfd.c \
        ripd/rip_cli.c \
        ripd/rip_debug.c \
        ripd/rip_errors.c \
@@ -31,10 +32,12 @@ ripd_ripd_SOURCES = \
        # end
 
 clippy_scan += \
+       ripd/rip_bfd.c \
        ripd/rip_cli.c \
        # end
 
 noinst_HEADERS += \
+       ripd/rip_bfd.h \
        ripd/rip_debug.h \
        ripd/rip_errors.h \
        ripd/rip_interface.h \
index 63144d866b26bb1eefcf78e5e7761662917860b4..1c6d7191a3a8a13e13bcf7b2d92f8c1139aa9b19 100644 (file)
@@ -10,6 +10,7 @@
 #include "libfrr.h"
 
 #include "ripngd/ripng_nb.h"
+#include "lib/if_rmap.h"
 
 /* clang-format off */
 const struct frr_yang_module_info frr_ripngd_info = {
@@ -114,6 +115,28 @@ const struct frr_yang_module_info frr_ripngd_info = {
                                .modify = ripngd_instance_redistribute_metric_modify,
                        },
                },
+               {
+                       .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map",
+                       .cbs = {
+                               .create = ripngd_instance_if_route_maps_if_route_map_create,
+                               .destroy = ripngd_instance_if_route_maps_if_route_map_destroy,
+                               .cli_show = cli_show_if_route_map,
+                       }
+               },
+               {
+                       .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/in-route-map",
+                       .cbs = {
+                               .modify = ripngd_instance_if_route_maps_if_route_map_in_route_map_modify,
+                               .destroy = ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy,
+                       }
+               },
+               {
+                       .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map",
+                       .cbs = {
+                               .modify = ripngd_instance_if_route_maps_if_route_map_out_route_map_modify,
+                               .destroy = ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-ripngd:ripngd/instance/static-route",
                        .cbs = {
index 675cef7c92b2dcad6932949242c71d24c672fe4e..1c0e63c24197d66c96b83780fa459e6d1b91ec6c 100644 (file)
@@ -39,6 +39,18 @@ int ripngd_instance_redistribute_route_map_destroy(
 int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args);
 int ripngd_instance_redistribute_metric_destroy(
        struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_create(
+       struct nb_cb_create_args *args);
+int ripngd_instance_if_route_maps_if_route_map_destroy(
+       struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify(
+       struct nb_cb_modify_args *args);
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+       struct nb_cb_destroy_args *args);
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify(
+       struct nb_cb_modify_args *args);
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+       struct nb_cb_destroy_args *args);
 int ripngd_instance_static_route_create(struct nb_cb_create_args *args);
 int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args);
 int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args);
index 006bf79ce8d56cb87d8e9a77e30f6f3a13b721a2..30f707e061e989266a36e0a2c030a5a8ef2e30ac 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 1998 Kunihiro Ishiguro
  * Copyright (C) 2018 NetDEF, Inc.
  *                    Renato Westphal
+ * Copyright (C) 2023 LabN Consulting, L.L.C.
  */
 
 #include <zebra.h>
@@ -13,6 +14,7 @@
 #include "prefix.h"
 #include "table.h"
 #include "command.h"
+#include "if_rmap.h"
 #include "routemap.h"
 #include "agg_table.h"
 #include "northbound.h"
@@ -502,6 +504,93 @@ int ripngd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args)
        return NB_OK;
 }
 
+/*
+ * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map
+ */
+int ripngd_instance_if_route_maps_if_route_map_create(
+       struct nb_cb_create_args *args)
+{
+       /* if_rmap is created when first routemap is added */
+       return NB_OK;
+}
+
+int ripngd_instance_if_route_maps_if_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       struct ripng *ripng;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       /*
+        * YANG will prune edit deletes up to the most general deleted node so
+        * we need to handle deleting any existing state underneath and not
+        * count on those more specific callbacks being called individually.
+        */
+
+       ripng = nb_running_get_entry(args->dnode, NULL, true);
+       if_rmap_yang_destroy_cb(ripng->if_rmap_ctx, args->dnode);
+
+       return NB_OK;
+}
+
+static void if_route_map_modify(const struct lyd_node *dnode,
+                               enum if_rmap_type type, bool delete)
+{
+       struct ripng *ripng = nb_running_get_entry(dnode, NULL, true);
+
+       if_rmap_yang_modify_cb(ripng->if_rmap_ctx, dnode, type, delete);
+}
+/*
+ * XPath: /frr-ripng:ripng/instance/if-route-maps/if-route-map/in-route-map
+ */
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify(
+       struct nb_cb_modify_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_IN, false);
+
+       return NB_OK;
+}
+
+int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_IN, true);
+
+       return NB_OK;
+}
+
+/*
+ * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map
+ */
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify(
+       struct nb_cb_modify_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_OUT, false);
+
+       return NB_OK;
+}
+
+int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       if_route_map_modify(args->dnode, IF_RMAP_OUT, true);
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-ripngd:ripngd/instance/static-route
  */
index 79e8871f6a5f5dc35e0a3beceafcc987987b558c..2f6409a70d31d6ddf5cb55dadc91161bafe46a5b 100644 (file)
@@ -2231,7 +2231,6 @@ static int ripng_config_write(struct vty *vty)
                nb_cli_show_dnode_cmds(vty, dnode, false);
 
                config_write_distribute(vty, ripng->distribute_ctx);
-               config_write_if_rmap(vty, ripng->if_rmap_ctx);
 
                vty_out(vty, "exit\n");
 
index 77b562d6a61ef69023152b8e865e2ef54ad8737e..ca2212cd87136a0af224f5e7ca3aec2737d794c0 100644 (file)
@@ -985,6 +985,7 @@ DEFUN (show_sharp_ted,
        struct ls_edge *edge;
        struct ls_subnet *subnet;
        uint64_t key;
+       struct ls_edge_key ekey;
        bool verbose = false;
        bool uj = use_json(argc, argv);
        json_object *json = NULL;
@@ -1035,8 +1036,9 @@ DEFUN (show_sharp_ted,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Edge from the Link State Database */
-                       key = ((uint64_t)ip_addr.s_addr) & 0xffffffff;
-                       edge = ls_find_edge_by_key(sg.ted, key);
+                       ekey.family = AF_INET;
+                       IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr);
+                       edge = ls_find_edge_by_key(sg.ted, ekey);
                        if (!edge) {
                                vty_out(vty, "No edge found for ID %pI4\n",
                                        &ip_addr);
@@ -1059,7 +1061,7 @@ DEFUN (show_sharp_ted,
                                return CMD_WARNING_CONFIG_FAILED;
                        }
                        /* Get the Subnet from the Link State Database */
-                       subnet = ls_find_subnet(sg.ted, pref);
+                       subnet = ls_find_subnet(sg.ted, &pref);
                        if (!subnet) {
                                vty_out(vty, "No subnet found for ID %pFX\n",
                                        &pref);
@@ -1245,6 +1247,7 @@ DEFPY (show_sharp_cspf,
        }
        if (path->status != SUCCESS) {
                vty_out(vty, "Path computation failed: %d\n", path->status);
+               cpath_del(path);
                return CMD_SUCCESS;
        }
 
@@ -1260,7 +1263,7 @@ DEFPY (show_sharp_cspf,
                                &edge->attributes->standard.remote6);
        }
        vty_out(vty, "\n");
-
+       cpath_del(path);
        return CMD_SUCCESS;
 }
 
index 17ad70596968e02ff845e0dd55aa3cf97f238f72..91fb7f03b107c822e6de1b54fc7a31b606493ebf 100644 (file)
@@ -790,7 +790,7 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
                   zclient->session_id, info.type);
 
        if (info.type == LINK_STATE_UPDATE) {
-               lse = ls_stream2ted(sg.ted, s, false);
+               lse = ls_stream2ted(sg.ted, s, true);
                if (lse) {
                        zlog_debug(" |- Got %s %s from Link State Database",
                                   status2txt[lse->status],
index 09f22318ffc2d2556db18b8c1f485c0559be56e7..9809d9751ad804c70cda2667e16eeb3e88a70000 100644 (file)
@@ -161,6 +161,10 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = {
 
 #define STATIC_VTY_PORT 2616
 
+/*
+ * NOTE: .flags == FRR_NO_SPLIT_CONFIG to avoid reading split config, mgmtd will
+ * do this for us now
+ */
 FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT,
 
                .proghelp = "Implementation of STATIC.",
@@ -170,7 +174,8 @@ FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT,
 
                .privs = &static_privs, .yang_modules = staticd_yang_modules,
                .n_yang_modules = array_size(staticd_yang_modules),
-);
+
+               .flags = FRR_NO_SPLIT_CONFIG);
 
 int main(int argc, char **argv, char **envp)
 {
@@ -210,9 +215,12 @@ int main(int argc, char **argv, char **envp)
 
        routing_control_plane_protocols_register_vrf_dependency();
 
-       snprintf(backup_config_file, sizeof(backup_config_file),
-                "%s/zebra.conf", frr_sysconfdir);
-       staticd_di.backup_config_file = backup_config_file;
+       /*
+        * We set FRR_NO_SPLIT_CONFIG flag to avoid reading our config, but we
+        * still need to write one if vtysh tells us to. Setting the host
+        * config filename does this.
+        */
+       host_config_set(config_default);
 
        frr_config_fork();
        frr_run(master);
index 386b255a855464b6bc8d671f2272ef516d79758b..3e58a44aa73afcaac033991c36b2ffc1ce872379 100644 (file)
@@ -13,6 +13,7 @@
 #include "nexthop.h"
 #include "table.h"
 #include "srcdest_table.h"
+#include "mgmt_be_client.h"
 #include "mpls.h"
 #include "northbound.h"
 #include "libfrr.h"
@@ -1553,4 +1554,6 @@ void static_vty_init(void)
 
        install_element(ENABLE_NODE, &debug_staticd_cmd);
        install_element(CONFIG_NODE, &debug_staticd_cmd);
+
+       mgmt_be_client_lib_vty_init();
 }
index 54596dbdfb014d0cf80e76124249c02bc3e48c00..ae7903e0ccfc4adf7041972be539d522aa896018 100644 (file)
@@ -1042,9 +1042,9 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type)
 
        if (!parse_ret) {
                if (type == BGP_ATTR_MP_REACH_NLRI)
-                       nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0);
+                       nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false);
                else if (type == BGP_ATTR_MP_UNREACH_NLRI)
-                       nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1);
+                       nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true);
        }
        handle_result(peer, t, parse_ret, nlri_ret);
 }
index d0288f600def91839761003c2d2f17e3c14dbe98..e47456965e0e2f43734d61cc49263269fb34ab7b 100644 (file)
@@ -98,7 +98,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp,
 {
        struct prefix prefix;
        struct sr_prefix_cfg pcfg = {};
-       struct sr_prefix_cfg *pcfg_p = NULL;
+       struct sr_prefix_cfg *pcfg_p[SR_ALGORITHM_COUNT] = {NULL};
 
        if (str2prefix(prefix_str, &prefix) != 1) {
                zlog_debug("%s: invalid network: %s", __func__, prefix_str);
@@ -106,7 +106,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp,
        }
 
        if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) {
-               pcfg_p = &pcfg;
+               pcfg_p[SR_ALGORITHM_SPF] = &pcfg;
 
                pcfg.sid = *next_sid_index;
                *next_sid_index = *next_sid_index + 1;
@@ -163,31 +163,32 @@ static void lsp_add_reach(struct isis_lsp *lsp,
 static void lsp_add_router_capability(struct isis_lsp *lsp,
                                      const struct isis_test_node *tnode)
 {
-       struct isis_router_cap cap = {};
+       struct isis_router_cap *cap;
 
        if (!tnode->router_id)
                return;
 
-       if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) {
+       cap = isis_tlvs_init_router_capability(lsp->tlvs);
+
+       if (inet_pton(AF_INET, tnode->router_id, &cap->router_id) != 1) {
                zlog_debug("%s: invalid router-id: %s", __func__,
                           tnode->router_id);
                return;
        }
 
        if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) {
-               cap.srgb.flags =
+               cap->srgb.flags =
                        ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V;
-               cap.srgb.lower_bound = tnode->srgb.lower_bound
-                                              ? tnode->srgb.lower_bound
-                                              : SRGB_DFTL_LOWER_BOUND;
-               cap.srgb.range_size = tnode->srgb.range_size
-                                             ? tnode->srgb.range_size
-                                             : SRGB_DFTL_RANGE_SIZE;
-               cap.algo[0] = SR_ALGORITHM_SPF;
-               cap.algo[1] = SR_ALGORITHM_UNSET;
+               cap->srgb.lower_bound = tnode->srgb.lower_bound
+                                               ? tnode->srgb.lower_bound
+                                               : SRGB_DFTL_LOWER_BOUND;
+               cap->srgb.range_size = tnode->srgb.range_size
+                                              ? tnode->srgb.range_size
+                                              : SRGB_DFTL_RANGE_SIZE;
+               cap->algo[0] = SR_ALGORITHM_SPF;
+               cap->algo[1] = SR_ALGORITHM_UNSET;
        }
 
-       isis_tlvs_set_router_capability(lsp->tlvs, &cap);
 }
 
 static void lsp_add_mt_router_info(struct isis_lsp *lsp,
index 85ddfea5b52d9cf40f6d53eb6d71b86241388df1..6eb180b501db52343771683c48bd4fe008837138 100644 (file)
@@ -49,12 +49,13 @@ static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
        /* Run SPF. */
        spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
        spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
-                                  spf_type, F_SPFTREE_NO_ADJACENCIES);
+                                  spf_type, F_SPFTREE_NO_ADJACENCIES,
+                                  SR_ALGORITHM_SPF);
        isis_run_spf(spftree);
 
        /* Print the SPT and the corresponding routing table. */
        isis_print_spftree(vty, spftree);
-       isis_print_routes(vty, spftree, false, false);
+       isis_print_routes(vty, spftree, NULL, false, false);
 
        /* Cleanup SPF tree. */
        isis_spftree_del(spftree);
@@ -71,8 +72,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
 
        /* 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);
+       spftree_self =
+               isis_spftree_new(area, lspdb, root->sysid, level, tree,
+                                SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
        isis_run_spf(spftree_self);
 
        /* Run forward SPF on all adjacent routers. */
@@ -84,9 +86,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
        /* 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);
+       isis_print_routes(vty, spftree_self, NULL, false, false);
        vty_out(vty, "Backup:\n");
-       isis_print_routes(vty, spftree_self, false, true);
+       isis_print_routes(vty, spftree_self, NULL, false, true);
 
        /* Cleanup everything. */
        isis_spftree_del(spftree_self);
@@ -107,8 +109,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
 
        /* 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);
+       spftree_self =
+               isis_spftree_new(area, lspdb, root->sysid, level, tree,
+                                SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
        isis_run_spf(spftree_self);
 
        /* Run reverse SPF in the root node. */
@@ -162,9 +165,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
        /* 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);
+       isis_print_routes(vty, spftree_self, NULL, false, false);
        vty_out(vty, "Backup:\n");
-       isis_print_routes(vty, spftree_self, false, true);
+       isis_print_routes(vty, spftree_self, NULL, false, true);
 
        /* Cleanup everything. */
        isis_spftree_del(spftree_self);
@@ -187,8 +190,9 @@ static void test_run_ti_lfa(struct vty *vty,
 
        /* 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);
+       spftree_self =
+               isis_spftree_new(area, lspdb, root->sysid, level, tree,
+                                SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF);
        isis_run_spf(spftree_self);
 
        /* Run reverse SPF in the root node. */
@@ -224,7 +228,7 @@ static void test_run_ti_lfa(struct vty *vty,
         * Print the post-convergence SPT and the corresponding routing table.
         */
        isis_print_spftree(vty, spftree_pc);
-       isis_print_routes(vty, spftree_self, false, true);
+       isis_print_routes(vty, spftree_self, NULL, false, true);
 
        /* Cleanup everything. */
        isis_spftree_del(spftree_self);
index 8f548561863f66e7d73d00763808806dfe7051ea..4ad41fca425ee15dce93bc62454579060a7257f3 100644 (file)
@@ -25,7 +25,6 @@
 #include "lib/frr_pthread.h"
 #include "lib/frratomic.h"
 #include "lib/frrstr.h"
-#include "lib/getopt.h"
 #include "lib/graph.h"
 #include "lib/hash.h"
 #include "lib/hook.h"
index 1bc092a49e6185a941ef97346667e7b95dcd3d81..e950d0120d3697cc25e1ad955eb25d698112d3b0 100644 (file)
@@ -15,7 +15,7 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD)
 tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c
-EXTRA_DIST += tests/lib/test_frrscript.py
+EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua
 
 
 ##############################################################################
index 91528139b5d70ebe74cd3f9700394149b7d55a8f..80c4005437fa17e72d4e537f08539ad830065735 100644 (file)
@@ -171,6 +171,11 @@ static void concat(test_, TYPE)(void)
 
        ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
 
+#if !IS_ATOMIC(REALTYPE)
+       assert(!list_member(&head, &itm[0]));
+       assert(!list_member(&head, &itm[1]));
+#endif
+
 #if IS_SORTED(REALTYPE)
        prng = prng_new(0);
        k = 0;
index 6d870f355f927be3a5db22938dadf39f6c2a23e1..9ce2f2e8258fef7c78f68c0ecd7cee78d62c19ad 100644 (file)
@@ -1,9 +1,9 @@
 log file ospf6d.log
 !
-debug ospf6 lsa unknown
-debug ospf6 zebra
-debug ospf6 interface
-debug ospf6 neighbor
+!debug ospf6 lsa unknown
+!debug ospf6 zebra
+!debug ospf6 interface
+!debug ospf6 neighbor
 !
 interface r1-eth4
 !
index f7c3a4c19d29333b20a99379e43df9a84835efae..92bb99c8f24e182b05e98e9c2f621b661ce5baca 100644 (file)
@@ -288,6 +288,17 @@ def test_converge_protocols():
 
     thisDir = os.path.dirname(os.path.realpath(__file__))
 
+    # We need loopback to have a link local so it always is the
+    # "selected" router for fe80::/64 when we static compare below.
+    print("Adding link-local to loopback for stable results")
+    cmd = (
+        "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;"
+        " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;"
+        " ip address add dev lo scope link"
+        " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64"
+    )
+    net["r1"].cmd_raises(cmd)
+
     print("\n\n** Waiting for protocols convergence")
     print("******************************************\n")
 
index 6d4483acc4f7c2237df3c8810ebba002b6afe9c2..ec62d8d275a55153fad9ae129386029ee09beae5 100644 (file)
@@ -1,6 +1,6 @@
-debug bfd network
-debug bfd peer
-debug bfd zebra
+!debug bfd network
+!debug bfd peer
+!debug bfd zebra
 !
 bfd
  profile slow-tx
index 6d4483acc4f7c2237df3c8810ebba002b6afe9c2..ec62d8d275a55153fad9ae129386029ee09beae5 100644 (file)
@@ -1,6 +1,6 @@
-debug bfd network
-debug bfd peer
-debug bfd zebra
+!debug bfd network
+!debug bfd peer
+!debug bfd zebra
 !
 bfd
  profile slow-tx
index fa53a4291997cbea0e143c8445dc7331ae72b387..44f95b9bb33a8a68ce59c3e6ec2a9ab39124dae4 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65010
  no bgp ebgp-requires-policy
index cdf8898c90d776247f4e3280b83965b28e79c29c..d60fdcf7cbaf57c3e15c105df59647c24d57d1d1 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65020
  no bgp ebgp-requires-policy
index 109e0eadbb1dc103a2206824965d3fa2384fe5dd..15466b4259c28e326256cc5e297bc8cf6e8a38b1 100644 (file)
@@ -1,8 +1,8 @@
 !
-debug bgp updates
-debug bgp vpn leak-from-vrf
-debug bgp vpn leak-to-vrf
-debug bgp nht
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
 !
 router bgp 65001
  bgp router-id 10.10.10.10
index 4f0a6ab0f1b7ecbe1db6b185969d8f89cd87c0ab..ad0ee3e471c0a309a701da72a3adebf22d422e5d 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65001
  bgp router-id 10.10.10.101
index 2071c256dabed326037042a0cadffb0c42844c08..e855f75c20dee7643dd5055fcfe5802b1a39c429 100644 (file)
@@ -2,7 +2,9 @@ router bgp 65001
  no bgp ebgp-requires-policy
  neighbor 192.168.255.2 remote-as external
  neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 timers connect 1
  neighbor 192.168.255.2 bfd
+ neighbor 192.168.255.2 passive
  address-family ipv4
   redistribute connected
  exit-address-family
index 3279804e6ec5e9f7a9189fb9ac610b66a321afc8..faf2c6b39bbf6d3f3631d8bf76af9466d0bf4748 100644 (file)
@@ -2,6 +2,7 @@ router bgp 65002
  no bgp ebgp-requires-policy
  neighbor 192.168.255.1 remote-as external
  neighbor 192.168.255.1 timers 3 10
+ neighbor 192.168.255.1 timers connect 1
  neighbor 192.168.255.1 bfd
  address-family ipv4
   redistribute connected
index 0bc0306d7db75ee3e285eb59f691cf5fc83e36dc..00142981c502e1b31543e7a79e52cd47571d15b4 100644 (file)
@@ -88,13 +88,14 @@ def test_bgp_bfd_down_notification():
         expected = {
             "192.168.255.1": {
                 "lastNotificationReason": "Cease/BFD Down",
+                "lastNotificationHardReset": True,
             }
         }
         return topotest.json_cmp(output, expected)
 
     step("Initial BGP converge")
     test_func = functools.partial(_bgp_converge)
-    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
     assert result is None, "Failed to see BGP convergence on R2"
 
     step("Kill bfdd on R2")
@@ -102,7 +103,7 @@ def test_bgp_bfd_down_notification():
 
     step("Check if we received Cease/BFD Down notification message")
     test_func = functools.partial(_bgp_bfd_down_notification)
-    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
     assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2"
 
 
index 35ad2d32e6d458fb563a7a442770cf74411243cb..98a9780688883e524b20e5e58493e6789c5934e8 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65002
  no bgp ebgp-requires-policy
index 8413ef7fc37261e2c6011737bfbe93d3d9e5e8e7..107d2ad8d2049abc4ed67393e711914b6c89d3e4 100644 (file)
@@ -1,7 +1,7 @@
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
 !
 router bgp 100
  no bgp ebgp-requires-policy
index 9f6a9852de06be0d663f7d21ca1d3f08dcbc0581..fe13dfe729943e8fda188ee90ce650ddcddb82cf 100644 (file)
@@ -1,7 +1,7 @@
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
 !
 router bgp 200
  no bgp ebgp-requires-policy
index 3a018a42b30812cd2b302026113f63255d3c629e..74d5fd6e290c1e5d30270eb8cc443ebe66fe12b3 100644 (file)
@@ -1,7 +1,7 @@
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
 !
 router bgp 300
  no bgp ebgp-requires-policy
index 134f221543ca818e42914b37a9e2d9948c9e94e0..89a85e5a34bb0026a46f687b4d20cf789a475e62 100644 (file)
@@ -1,7 +1,7 @@
-debug bgp neighbor-events
-debug bgp nht
-debug bgp updates in
-debug bgp updates out
+!debug bgp neighbor-events
+!debug bgp nht
+!debug bgp updates in
+!debug bgp updates out
 !
 router bgp 400
  no bgp ebgp-requires-policy
index e2ff1df9657fbda8f24bddcceda0fe79aa1ba24d..2f76d59d4ab72018a49403c8ac2401adfc9e46d6 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp neighbor
+!debug bgp neighbor
 !
 router bgp 65001
  no bgp ebgp-requires-policy
index f8af210ed73a257c4c275c50c5e84c24ce4d7d5f..65c0c3532a9cc3189861f5f40da6232960014818 100755 (executable)
@@ -83,6 +83,7 @@ def build_topo(tgen):
     switch.add_link(tgen.gears["PE2"])
     switch.add_link(tgen.gears["host2"])
 
+
 def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
     pe = tgen.gears[pe_name]
 
@@ -100,7 +101,9 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
 
     # setup single vxlan device
     pe.run(
-        "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(tunnel_local_ip)
+        "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(
+            tunnel_local_ip
+        )
     )
     pe.run("ip link set dev vxlan0 master bridge")
     pe.run("bridge link set dev vxlan0 vlan_tunnel on")
@@ -136,10 +139,12 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf):
         pe.run("bridge vlan add dev vxlan0 vid 300")
         pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300")
 
+
 def setup_p_router(tgen, p_name):
     p1 = tgen.gears[p_name]
     p1.run("sysctl -w net.ipv4.ip_forward=1")
 
+
 def setup_module(mod):
     "Sets up the pytest environment"
 
@@ -180,7 +185,7 @@ def teardown_module(mod):
     "Teardown the pytest environment"
     tgen = get_topogen()
 
-    #tgen.mininet_cli()
+    # tgen.mininet_cli()
     # This function tears down the whole topology.
     tgen.stop_topology()
 
@@ -204,17 +209,21 @@ def check_vni_macs_present(tgen, router, vni, maclist):
             )
     return None
 
+
 def check_flood_entry_present(pe, vni, vtep):
     if not topotest.iproute2_is_fdb_get_capable():
         return None
 
-    output = pe.run("bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni))
+    output = pe.run(
+        "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)
+    )
 
     if str(vtep) not in output:
         return output
 
     return None
 
+
 def test_pe1_converge_evpn():
     "Wait for protocol convergence"
 
@@ -231,6 +240,15 @@ def test_pe1_converge_evpn():
     _, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
     assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
 
+    # Let's ensure that the hosts have actually tried talking to
+    # each other.  Otherwise under certain startup conditions
+    # they may not actually do any l2 arp'ing and as such
+    # the bridges won't know about the hosts on their networks
+    host1 = tgen.gears["host1"]
+    host1.run("ping -c 1 10.10.1.56")
+    host2 = tgen.gears["host2"]
+    host2.run("ping -c 1 10.10.1.55")
+
     test_func = partial(
         check_vni_macs_present,
         tgen,
@@ -249,11 +267,12 @@ def test_pe1_converge_evpn():
     assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep)
     assert result is None, assertmsg
 
+
 def test_pe2_converge_evpn():
     "Wait for protocol convergence"
 
     tgen = get_topogen()
-#Don't run this test if we have any failure.
+    # Don't run this test if we have any failure.
     if tgen.routers_have_failure():
         pytest.skip(tgen.errors)
 
@@ -284,6 +303,7 @@ def test_pe2_converge_evpn():
     assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep)
     assert result is None, assertmsg
 
+
 def mac_learn_test(host, local):
     "check the host MAC gets learned by the VNI"
 
@@ -389,11 +409,11 @@ def ip_learn_test(tgen, host, local, remote, ip_addr):
         if "HWaddr" in line_items[0]:
             mac = line_items[1]
             break
-    #print(host_output)
+    # print(host_output)
 
     # check we have a local association between the MAC and IP
     local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
-    #print(local_output)
+    # print(local_output)
     local_output_json = json.loads(local_output)
     mac_type = local_output_json[mac]["type"]
     assertmsg = "Failed to learn local IP address on host {}".format(host.name)
@@ -417,7 +437,7 @@ def ip_learn_test(tgen, host, local, remote, ip_addr):
         remote_output = remote.vtysh_cmd(
             "show evpn mac vni 101 mac {} json".format(mac)
         )
-        #print(remote_output)
+        # print(remote_output)
         remote_output_json = json.loads(remote_output)
         type = remote_output_json[mac]["type"]
         if not remote_output_json[mac]["neighbors"] == "none":
@@ -431,12 +451,12 @@ def ip_learn_test(tgen, host, local, remote, ip_addr):
         count += 1
         sleep(1)
 
-    #print("tries: {}".format(count))
+    # print("tries: {}".format(count))
     assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac)
     # some debug for this failure
     if not converged == True:
         log_output = remote.run("cat zebra.log")
-        #print(log_output)
+        # print(log_output)
 
     assert converged == True, assertmsg
     if remote_output_json[mac]["neighbors"]["active"]:
@@ -463,8 +483,8 @@ def test_ip_pe1_learn():
     host1 = tgen.gears["host1"]
     pe1 = tgen.gears["PE1"]
     pe2 = tgen.gears["PE2"]
-    pe2.vtysh_cmd("debug zebra vxlan")
-    pe2.vtysh_cmd("debug zebra kernel")
+    pe2.vtysh_cmd("debug zebra vxlan")
+    pe2.vtysh_cmd("debug zebra kernel")
     # lets populate that arp cache
     host1.run("ping -c1 10.10.1.1")
     ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55")
@@ -482,13 +502,14 @@ def test_ip_pe2_learn():
     host2 = tgen.gears["host2"]
     pe1 = tgen.gears["PE1"]
     pe2 = tgen.gears["PE2"]
-    pe1.vtysh_cmd("debug zebra vxlan")
-    pe1.vtysh_cmd("debug zebra kernel")
+    pe1.vtysh_cmd("debug zebra vxlan")
+    pe1.vtysh_cmd("debug zebra kernel")
     # lets populate that arp cache
     host2.run("ping -c1 10.10.1.3")
     ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56")
     # tgen.mininet_cli()
 
+
 def show_dvni_route(pe, vni, prefix, vrf):
     output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix))
 
@@ -502,6 +523,7 @@ def show_dvni_route(pe, vni, prefix, vrf):
 
     return None
 
+
 def test_dvni():
     "test Downstream VNI works as expected importing into PE1"
 
@@ -517,7 +539,7 @@ def test_dvni():
     _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
     assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix)
     assert result is None, assertmsg
-    #tgen.mininet_cli()
+    # tgen.mininet_cli()
 
 
 def test_memory_leak():
index 48b79ab5eed6f2566a93917c6c67a9db9cd088e8..288404301215c98c21437af02d1f19165255f637 100755 (executable)
@@ -164,6 +164,15 @@ def test_pe1_converge_evpn():
     _, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
     assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
 
+    # Let's ensure that the hosts have actually tried talking to
+    # each other.  Otherwise under certain startup conditions
+    # they may not actually do any l2 arp'ing and as such
+    # the bridges won't know about the hosts on their networks
+    host1 = tgen.gears["host1"]
+    host1.run("ping -c 1 10.10.1.56")
+    host2 = tgen.gears["host2"]
+    host2.run("ping -c 1 10.10.1.55")
+
     test_func = partial(
         check_vni_macs_present,
         tgen,
@@ -171,6 +180,7 @@ def test_pe1_converge_evpn():
         101,
         (("host1", "host1-eth0"), ("host2", "host2-eth0")),
     )
+
     _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
     if result:
         logger.warning("%s", result)
@@ -385,8 +395,8 @@ def test_ip_pe1_learn():
     host1 = tgen.gears["host1"]
     pe1 = tgen.gears["PE1"]
     pe2 = tgen.gears["PE2"]
-    pe2.vtysh_cmd("debug zebra vxlan")
-    pe2.vtysh_cmd("debug zebra kernel")
+    pe2.vtysh_cmd("debug zebra vxlan")
+    pe2.vtysh_cmd("debug zebra kernel")
     # lets populate that arp cache
     host1.run("ping -c1 10.10.1.1")
     ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55")
@@ -404,8 +414,8 @@ def test_ip_pe2_learn():
     host2 = tgen.gears["host2"]
     pe1 = tgen.gears["PE1"]
     pe2 = tgen.gears["PE2"]
-    pe1.vtysh_cmd("debug zebra vxlan")
-    pe1.vtysh_cmd("debug zebra kernel")
+    pe1.vtysh_cmd("debug zebra vxlan")
+    pe1.vtysh_cmd("debug zebra kernel")
     # lets populate that arp cache
     host2.run("ping -c1 10.10.1.3")
     ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56")
index 3b5f46d9346b014f1d49ab62418c50995d2366d3..caf700d82a976461000352656800ccb0a32e9384 100644 (file)
@@ -2,13 +2,13 @@
   "neighbors":{
     "192.168.0.2":[
       {
-        "priority":5,
+        "nbrPriority":5,
         "converged":"Full"
       }
     ],
     "192.168.0.3":[
       {
-        "priority":5,
+        "nbrPriority":5,
         "converged":"Full"
       }
     ]
index 47bb57cd00f75e7157382beda1858e52abd572b9..3a168ba33592a8831094034fee9c0eb713126e61 100644 (file)
@@ -2,13 +2,13 @@
   "neighbors":{
     "192.168.0.1":[
       {
-        "priority":10,
+        "nbrPriority":10,
         "converged":"Full"
       }
     ],
     "192.168.0.3":[
       {
-        "priority":5,
+        "nbrPriority":5,
         "converged":"Full"
       }
     ]
index b84974ccca64a1eb919eb6a1eedb7a1a962755b7..9f8c05949f8db6412ad1879f2c5fc634fff5a9e7 100644 (file)
@@ -2,13 +2,13 @@
   "neighbors":{
     "192.168.0.1":[
       {
-        "priority":10,
+        "nbrPriority":10,
         "converged":"Full"
       }
     ],
     "192.168.0.2":[
       {
-        "priority":10,
+        "nbrPriority":10,
         "converged":"Full"
       }
     ]
index 6388295c951f3c963644b3008aae42a4ee020558..1a8f8302ffefa5817cfb304aae709087deb47952 100644 (file)
@@ -1159,7 +1159,7 @@ def test_BGP_GR_TC_31_2_p1(request):
     reset_config_on_routers(tgen)
 
     logger.info(
-        "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized  "
+        "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Helper Mode] initialized  "
     )
 
     # Configure graceful-restart
@@ -1251,7 +1251,7 @@ def test_BGP_GR_TC_31_2_p1(request):
             tc_name, result
         )
 
-    logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode  ")
+    logger.info("[Phase 2] : R1 Goes from Disable to Restart Mode  ")
 
     # Configure graceful-restart
     input_dict = {
@@ -1356,31 +1356,7 @@ def test_BGP_GR_TC_31_2_p1(request):
         },
     }
 
-    # here the verify_graceful_restart fro the neighbor would be
-    # "NotReceived" as the latest GR config is not yet applied.
-    for addr_type in ADDR_TYPES:
-        result = verify_graceful_restart(
-            tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
-        )
-        assert result is True, "Testcase {} : Failed \n Error {}".format(
-            tc_name, result
-        )
-
-    for addr_type in ADDR_TYPES:
-        # Verifying RIB routes
-        next_hop = next_hop_per_address_family(
-            tgen, dut, peer, addr_type, NEXT_HOP_IP_2
-        )
-        input_topo = {key: topo["routers"][key] for key in ["r2"]}
-        result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
-        assert result is True, "Testcase {} : Failed \n Error {}".format(
-            tc_name, result
-        )
-
-    logger.info("[Phase 6] : R1 is about to come up now  ")
-    start_router_daemons(tgen, "r1", ["bgpd"])
-
-    logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ")
+    logger.info("[Phase 4] : R1 is UP and GR state is correct ")
 
     for addr_type in ADDR_TYPES:
         result = verify_graceful_restart(
@@ -2142,6 +2118,9 @@ def test_BGP_GR_TC_43_p1(request):
             tc_name, result
         )
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r1", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
@@ -2432,6 +2411,9 @@ def test_BGP_GR_TC_44_p1(request):
         result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
         assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r2", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
@@ -2727,6 +2709,9 @@ def test_BGP_GR_TC_45_p1(request):
         result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
         assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r1", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
index 1166cdc0efb8018d4e2b38081dfe4bc12d6e3fdf..31aaa0b8a6dfd2fd245d6422f609f65eafd96f91 100644 (file)
@@ -763,6 +763,9 @@ def test_BGP_GR_TC_46_p1(request):
             tc_name, result
         )
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r1", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
@@ -1023,6 +1026,9 @@ def test_BGP_GR_TC_47_p1(request):
             tc_name, result
         )
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r1", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
@@ -1300,6 +1306,9 @@ def test_BGP_GR_TC_48_p1(request):
             tc_name, result
         )
 
+    # restart the daemon or we get warnings in the follow-on tests
+    start_router_daemons(tgen, "r1", ["bgpd"])
+
     write_test_footer(tc_name)
 
 
index eaa6aa4c3072c7269397c08cbc54a1fad118e45b..46993c7d9a4658be8d13caa2c842121a186b63b4 100644 (file)
@@ -62,6 +62,25 @@ else:
         "pass",
         "Adding {} routes".format(num),
     )
+    luCommand(
+        "ce1",
+        'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33',
+        str(num),
+        "wait",
+        "See all sharp routes in rib on ce1",
+        wait,
+        wait_time=10,
+        )
+    luCommand(
+        "ce2",
+        'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33',
+        str(num),
+        "wait",
+        "See all sharp routes in rib on ce2",
+        wait,
+        wait_time=10,
+        )
+
     rtrs = ["ce1", "ce2", "ce3"]
     for rtr in rtrs:
         luCommand(
index 3af779c427c24b12ea254f8c2823b08806fa1121..f4bb487e4091238245e75f3620267208c7a9d12e 100644 (file)
@@ -82,53 +82,23 @@ def test_bgp_addpath_labeled_unicast():
     r3 = tgen.gears["r3"]
     r4 = tgen.gears["r4"]
 
-    def _bgp_check_advertised_routes(prefix_num):
-        output = json.loads(
-            r3.vtysh_cmd(
-                "show bgp ipv4 labeled-unicast neighbors 192.168.34.4 advertised-routes json"
-            )
-        )
+    def _bgp_check_received_routes(pfxcount):
+        output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json"))
         expected = {
-            "advertisedRoutes": {
-                "10.0.0.1/32": {
-                    "appliedStatusSymbols": {
-                        "*": True,
-                        ">": True,
-                        "=": True,
-                    }
+            "peers": {
+                "192.168.34.3": {
+                    "pfxRcd": pfxcount,
+                    "state": "Established",
                 }
-            },
-            "totalPrefixCounter": prefix_num,
+            }
         }
         return topotest.json_cmp(output, expected)
 
-    test_func = functools.partial(_bgp_check_advertised_routes, 2)
+    test_func = functools.partial(_bgp_check_received_routes, 2)
     _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
     assert (
         result is None
-    ), "Failed to advertise labeled-unicast with addpath (multipath)"
-
-    def _bgp_check_received_routes():
-        output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast json"))
-        expected = {
-            "routes": {
-                "10.0.0.1/32": [
-                    {
-                        "valid": True,
-                        "path": "65003 65001",
-                    },
-                    {
-                        "valid": True,
-                        "path": "65003 65002",
-                    },
-                ]
-            }
-        }
-        return topotest.json_cmp(output, expected)
-
-    test_func = functools.partial(_bgp_check_received_routes)
-    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
-    assert result is None, "Failed to receive labeled-unicast with addpath (multipath)"
+    ), "Failed to receive labeled-unicast with addpath (multipath=2)"
 
     step("Enable BGP session for R5")
     r3.vtysh_cmd(
@@ -139,11 +109,11 @@ def test_bgp_addpath_labeled_unicast():
         """
     )
 
-    test_func = functools.partial(_bgp_check_advertised_routes, 3)
+    test_func = functools.partial(_bgp_check_received_routes, 3)
     _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
     assert (
         result is None
-    ), "Failed to advertise labeled-unicast with addpath (multipath)"
+    ), "Failed to receive labeled-unicast with addpath (multipath=3)"
 
     step("Disable BGP session for R5")
     r3.vtysh_cmd(
@@ -154,11 +124,11 @@ def test_bgp_addpath_labeled_unicast():
         """
     )
 
-    test_func = functools.partial(_bgp_check_advertised_routes, 2)
+    test_func = functools.partial(_bgp_check_received_routes, 2)
     _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
     assert (
         result is None
-    ), "Failed to advertise labeled-unicast with addpath (multipath)"
+    ), "Failed to receive labeled-unicast with addpath (multipath=2)"
 
 
 if __name__ == "__main__":
diff --git a/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..a31439c
--- /dev/null
@@ -0,0 +1,15 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ bgp labeled-unicast explicit-null
+ neighbor 192.0.2.2 remote-as 65501
+!
+ address-family ipv4 unicast
+  no neighbor 192.0.2.2 activate
+  network 192.168.2.1/32
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+  neighbor 192.0.2.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf
new file mode 100644 (file)
index 0000000..b845748
--- /dev/null
@@ -0,0 +1,6 @@
+interface r1-eth0
+ ip address 192.0.2.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/32
+!
\ No newline at end of file
diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..41c2b9b
--- /dev/null
@@ -0,0 +1,15 @@
+router bgp 65501
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ bgp labeled-unicast explicit-null
+ neighbor 192.0.2.1 remote-as 65500
+!
+ address-family ipv4 unicast
+  no neighbor 192.0.2.1 activate
+  network 192.168.2.2/32
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+  neighbor 192.0.2.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf
new file mode 100644 (file)
index 0000000..9a63961
--- /dev/null
@@ -0,0 +1,6 @@
+interface r2-eth0
+ ip address 192.0.2.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/32
+!
diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py
new file mode 100644 (file)
index 0000000..0656e1e
--- /dev/null
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# test_bgp_explicitnull.py
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2023 by 6WIND S.A.
+#
+
+"""
+test_bgp_lu_explicitnull.py: Test BGP LU label allocation
+"""
+
+import os
+import sys
+import json
+import functools
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Basic scenario for BGP-LU. Nodes are directly connected.
+# The 192.168.2.2/32 prefix is advertised from r2 to r1
+# The explicit-null label should be used
+# The 192.168.2.1/32 prefix is advertised from r1 to r2
+# The explicit-null label should be used
+# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label
+#
+#  AS65500    BGP-LU        AS65501
+# +-----+                +-----+
+# |     |.1            .2|     |
+# |  1  +----------------+  2  + 192.168.0.2/32
+# |     |   192.0.2.0/24 |     |
+# +-----+                +-----+
+
+
+def build_topo(tgen):
+    "Build function"
+
+    # Create routers
+    tgen.add_router("r1")
+    tgen.add_router("r2")
+
+    # r1-r2
+    switch = tgen.add_switch("s1")
+    switch.add_link(tgen.gears["r1"])
+    switch.add_link(tgen.gears["r2"])
+
+    # r1
+    switch = tgen.add_switch("s2")
+    switch.add_link(tgen.gears["r1"])
+
+    # r2
+    switch = tgen.add_switch("s3")
+    switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+    "Sets up the pytest environment"
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(build_topo, mod.__name__)
+
+    # Skip if no mpls support
+    if not tgen.hasmpls:
+        logger.info("MPLS is not available, skipping test")
+        pytest.skip("MPLS is not available, skipping")
+        return
+
+    # ... and here it calls Mininet initialization functions.
+    tgen.start_topology()
+
+    # This is a sample of configuration loading.
+    router_list = tgen.routers()
+
+    # Enable mpls input for routers, so we can ping
+    sval = "net.mpls.conf.{}.input"
+    topotest.sysctl_assure(router_list["r2"], sval.format("r2-eth0"), 1)
+    topotest.sysctl_assure(router_list["r1"], sval.format("r1-eth0"), 1)
+
+    # For all registred routers, load the zebra configuration file
+    for rname, router in router_list.items():
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+        )
+
+    # After loading the configurations, this function loads configured daemons.
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+
+    # This function tears down the whole topology.
+    tgen.stop_topology()
+
+
+def check_show_ip_label_prefix_found(router, ipversion, prefix, label):
+    output = json.loads(
+        router.vtysh_cmd("show {} route {} json".format(ipversion, prefix))
+    )
+    expected = {
+        prefix: [
+            {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]}
+        ]
+    }
+    return topotest.json_cmp(output, expected)
+
+
+def test_converge_bgplu():
+    "Wait for protocol convergence"
+
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    # tgen.mininet_cli();
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+    # Check r1 gets prefix 192.168.2.2/32
+    test_func = functools.partial(
+        check_show_ip_label_prefix_found,
+        tgen.gears["r1"],
+        "ip",
+        "192.168.2.2/32",
+        "0",
+    )
+    success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+    assert success, "r1, prefix 192.168.2.2/32 from r2 not present"
+
+    # Check r2 gets prefix 192.168.2.1/32
+    test_func = functools.partial(
+        check_show_ip_label_prefix_found,
+        tgen.gears["r2"],
+        "ip",
+        "192.168.2.1/32",
+        "0",
+    )
+    success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+    assert success, "r2, prefix 192.168.2.1/32 from r1 not present"
+
+
+def test_traffic_connectivity():
+    "Wait for protocol convergence"
+
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    def _check_ping(name, dest_addr, src_addr):
+        tgen = get_topogen()
+        output = tgen.gears[name].run(
+            "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr)
+        )
+        logger.info(output)
+        if " 0% packet loss" not in output:
+            return True
+
+    logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK")
+    tgen = get_topogen()
+    func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1")
+    # tgen.mininet_cli()
+    success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+    assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails"
+
+
+def test_memory_leak():
+    "Run the memory leak test and report results."
+    tgen = get_topogen()
+    if not tgen.is_memleak_enabled():
+        pytest.skip("Memory leak test/report is disabled")
+
+    tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf
new file mode 100644 (file)
index 0000000..8698338
--- /dev/null
@@ -0,0 +1,21 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router bgp 65001
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.3 remote-as external
+ neighbor 192.168.1.4 remote-as external
+ address-family ipv4 unicast
+  network 10.10.10.10/32
+  neighbor 192.168.1.2 route-map rmap out
+  neighbor 192.168.1.3 route-map rmap out
+  neighbor 192.168.1.4 route-map rmap out
+ exit-address-family
+!
+route-map rmap permit 10
+ set extcommunity nt 192.168.1.3:0 192.168.1.4:0
+exit
diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf
new file mode 100644 (file)
index 0000000..09fda78
--- /dev/null
@@ -0,0 +1,8 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf
new file mode 100644 (file)
index 0000000..4883f1f
--- /dev/null
@@ -0,0 +1,8 @@
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router bgp 65003
+ bgp router-id 192.168.1.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf
new file mode 100644 (file)
index 0000000..f518bd1
--- /dev/null
@@ -0,0 +1,8 @@
+!
+int r4-eth0
+ ip address 192.168.1.4/24
+!
+router bgp 65004
+ bgp router-id 192.168.1.4
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py
new file mode 100644 (file)
index 0000000..23e820b
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if Node Target Extended Communities works.
+
+At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id),
+and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2,
+because this route does not have NT:192.168.1.2.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+    for routern in range(1, 5):
+        tgen.add_router("r{}".format(routern))
+
+    switch = tgen.add_switch("s1")
+    switch.add_link(tgen.gears["r1"])
+    switch.add_link(tgen.gears["r2"])
+    switch.add_link(tgen.gears["r3"])
+    switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for i, (rname, router) in enumerate(router_list.items(), 1):
+        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_bgp_node_target_extended_communities():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+    r3 = tgen.gears["r3"]
+    r4 = tgen.gears["r4"]
+
+    def _bgp_converge():
+        output = json.loads(r1.vtysh_cmd("show bgp summary json"))
+        expected = {
+            "ipv4Unicast": {
+                "peers": {
+                    "192.168.1.2": {
+                        "pfxSnt": 1,
+                        "state": "Established",
+                    },
+                    "192.168.1.3": {
+                        "pfxSnt": 1,
+                        "state": "Established",
+                    },
+                    "192.168.1.4": {
+                        "pfxSnt": 1,
+                        "state": "Established",
+                    },
+                }
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4"
+
+    def _bgp_check_route(router, exists):
+        output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+        if exists:
+            expected = {
+                "routes": {
+                    "10.10.10.10/32": [
+                        {
+                            "valid": True,
+                        }
+                    ]
+                }
+            }
+        else:
+            expected = {
+                "routes": {
+                    "10.10.10.10/32": None,
+                }
+            }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_check_route, r3, True)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+    test_func = functools.partial(_bgp_check_route, r4, True)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assert result is None, "10.10.10.10/32 is not installed, but SHOULD be"
+
+    test_func = functools.partial(_bgp_check_route, r2, False)
+    _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+    assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be"
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 77f7c5581b22accb4453e160a3ab2b7a40f69c33..733205928f3e222f6bbaa0cfb1a893d23f06d5cc 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65002
  no bgp ebgp-requires-policy
diff --git a/tests/topotests/bgp_route_map_delay_timer/__init__.py b/tests/topotests/bgp_route_map_delay_timer/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..e5325c9
--- /dev/null
@@ -0,0 +1,24 @@
+!
+!debug bgp updates
+!debug bgp neighbor
+!
+bgp route-map delay-timer 5
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+  network 10.10.10.1/32
+  network 10.10.10.2/32
+  network 10.10.10.3/32
+  aggregate-address 10.10.10.0/24 summary-only
+  neighbor 192.168.1.2 unsuppress-map r2
+ exit-address-family
+!
+ip prefix-list r1 seq 5 permit 10.10.10.1/32
+ip prefix-list r1 seq 10 permit 10.10.10.2/32
+!
+route-map r2 permit 10
+ match ip address prefix-list r1
+exit
diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf
new file mode 100644 (file)
index 0000000..b29940f
--- /dev/null
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..36653e6
--- /dev/null
@@ -0,0 +1,4 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf
new file mode 100644 (file)
index 0000000..cffe827
--- /dev/null
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py
new file mode 100644 (file)
index 0000000..15a077d
--- /dev/null
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+    topodef = {"s1": ("r1", "r2")}
+    tgen = Topogen(topodef, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for i, (rname, router) in enumerate(router_list.items(), 1):
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+        )
+
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_bgp_route_map_delay_timer():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+
+    def _bgp_converge_1():
+        output = json.loads(
+            r1.vtysh_cmd(
+                "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+            )
+        )
+        expected = {
+            "advertisedRoutes": {
+                "10.10.10.0/24": {},
+                "10.10.10.1/32": {},
+                "10.10.10.2/32": {},
+                "10.10.10.3/32": None,
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_bgp_converge_1)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+    assert result is None, "10.10.10.3/32 should not be advertised to r2"
+
+    # Set route-map delay-timer to max value and remove 10.10.10.2/32.
+    # After this, r1 MUST do not announce updates immediately, and wait
+    # 600 seconds before withdrawing 10.10.10.2/32.
+    r2.vtysh_cmd(
+        """
+        configure terminal
+            bgp route-map delay-timer 600
+            no ip prefix-list r1 seq 10 permit 10.10.10.2/32
+    """
+    )
+
+    def _bgp_converge_2():
+        output = json.loads(
+            r1.vtysh_cmd(
+                "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+            )
+        )
+        expected = {
+            "advertisedRoutes": {
+                "10.10.10.0/24": {},
+                "10.10.10.1/32": {},
+                "10.10.10.2/32": None,
+                "10.10.10.3/32": None,
+            }
+        }
+        return topotest.json_cmp(output, expected)
+
+    # We are checking `not None` here to wait count*wait time and if we have different
+    # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately.
+    test_func = functools.partial(_bgp_converge_2)
+    _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5)
+    assert (
+        result is not None
+    ), "10.10.10.2/32 advertised, but should not be advertised to r2"
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index c9ad0b1a5bd1b9535ea3b64778fd5c69d131a537..4aa11ec9d07f6736a4190af668ac343c28cda5bd 100644 (file)
@@ -1,9 +1,9 @@
 !
-debug bgp updates
-debug bgp vpn leak-from-vrf
-debug bgp vpn leak-to-vrf
-debug bgp nht
-debug route-map
+!debug bgp updates
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp nht
+!debug route-map
 !
 router bgp 65001
  bgp router-id 10.10.10.10
diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf
new file mode 100644 (file)
index 0000000..1929dfa
--- /dev/null
@@ -0,0 +1,2 @@
+router bgp 65001
+ neighbor 192.168.2.1 remote-as external
diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py
new file mode 100644 (file)
index 0000000..673efc2
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 03 2023, Trey Aspelund <taspelund@nvidia.com>
+#
+# Copyright (C) 2023 NVIDIA Corporation
+#
+# Test if the CLI parser for RT/SoO ecoms correctly
+# constrain user input to valid 4-byte ASN values.
+#
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+    tgen.add_router("pe1")
+
+
+def setup_module(mod):
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+    pe1 = tgen.gears["pe1"]
+    pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf"))
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_bgp_route_origin_parser():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    pe1 = tgen.gears["pe1"]
+
+    def _invalid_soo_accepted():
+        pe1.vtysh_cmd(
+            """
+        configure terminal
+        router bgp 65001
+         address-family ipv4 unicast
+          neighbor 192.168.2.1 soo 4294967296:65
+        """
+        )
+        run_cfg = pe1.vtysh_cmd("show run")
+        return "soo" in run_cfg
+
+    def _max_soo_accepted():
+        pe1.vtysh_cmd(
+            """
+        configure terminal
+        router bgp 65001
+         address-family ipv4 unicast
+          neighbor 192.168.2.1 soo 4294967295:65
+            """
+        )
+        run_cfg = pe1.vtysh_cmd("show run")
+        return "soo 4294967295:65" in run_cfg
+
+    def _invalid_rt_accepted():
+        pe1.vtysh_cmd(
+            """
+        configure terminal
+        router bgp 65001
+         address-family ipv4 unicast
+          rt vpn both 4294967296:65
+        """
+        )
+        run_cfg = pe1.vtysh_cmd("show run")
+        return "rt vpn" in run_cfg
+
+    def _max_rt_accepted():
+        pe1.vtysh_cmd(
+            """
+        configure terminal
+        router bgp 65001
+         address-family ipv4 unicast
+          rt vpn both 4294967295:65
+            """
+        )
+        run_cfg = pe1.vtysh_cmd("show run")
+        return "rt vpn both 4294967295:65" in run_cfg
+
+    step(
+        "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted"
+    )
+    test_func = functools.partial(_invalid_soo_accepted)
+    _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5)
+    assert result is False, "invalid 4-byte value of SoO accepted"
+
+    step("Configure max 4-byte value SoO (4294967295:65), this should be accepted")
+    test_func = functools.partial(_max_soo_accepted)
+    _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5)
+    assert result is True, "max 4-byte value of SoO not accepted"
+
+    step(
+        "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted"
+    )
+    test_func = functools.partial(_invalid_rt_accepted)
+    _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5)
+    assert result is False, "invalid 4-byte value of RT accepted"
+
+    step("Configure max 4-byte value RT (4294967295:65), this should be accepted")
+    test_func = functools.partial(_max_rt_accepted)
+    _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5)
+    assert result is True, "max 4-byte value of RT not accepted"
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 3512e66cec608905b4c65962321b6c12ae21b9af..cf0013e1b7a1439549787348a3067de06192e524 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp updates
+!debug bgp updates
 !
 router bgp 65002
  no bgp ebgp-requires-policy
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf
new file mode 100644 (file)
index 0000000..3459796
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json
new file mode 100644 (file)
index 0000000..d19e315
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:1::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:1::/64": [
+    {
+      "prefix": "2001:1::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf
new file mode 100644 (file)
index 0000000..bb5f93f
--- /dev/null
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ipv6 address 2001:1::2/64
+ ip address 192.168.1.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:1::1
+ip route 0.0.0.0/0 192.168.1.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf
new file mode 100644 (file)
index 0000000..8ed9978
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce2
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json
new file mode 100644 (file)
index 0000000..35ff14e
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:2::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:2::/64": [
+    {
+      "prefix": "2001:2::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf
new file mode 100644 (file)
index 0000000..a52b83f
--- /dev/null
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ipv6 address 2001:2::2/64
+ ip address 192.168.2.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:2::1
+ip route 0.0.0.0/0 192.168.2.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf
new file mode 100644 (file)
index 0000000..a85d970
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce3
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json
new file mode 100644 (file)
index 0000000..2f2931f
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:3::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:3::/64": [
+    {
+      "prefix": "2001:3::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf
new file mode 100644 (file)
index 0000000..beca0b1
--- /dev/null
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface eth0
+ ipv6 address 2001:3::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:3::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf
new file mode 100644 (file)
index 0000000..93fb32f
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce4
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json
new file mode 100644 (file)
index 0000000..8a98768
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:4::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:4::/64": [
+    {
+      "prefix": "2001:4::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf
new file mode 100644 (file)
index 0000000..7b21074
--- /dev/null
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce4
+!
+interface eth0
+ ipv6 address 2001:4::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:4::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf
new file mode 100644 (file)
index 0000000..2ab6f2d
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce5
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json
new file mode 100644 (file)
index 0000000..80ff52a
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:5::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:5::/64": [
+    {
+      "prefix": "2001:5::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf
new file mode 100644 (file)
index 0000000..b5ad48e
--- /dev/null
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce5
+!
+interface eth0
+ ipv6 address 2001:5::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:5::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf
new file mode 100644 (file)
index 0000000..e0b6540
--- /dev/null
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce6
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json
new file mode 100644 (file)
index 0000000..ace6136
--- /dev/null
@@ -0,0 +1,58 @@
+{
+  "::/0": [
+    {
+      "prefix": "::/0",
+      "protocol": "static",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 1,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 73,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "ip": "2001:6::1",
+          "afi": "ipv6",
+          "interfaceName": "eth0",
+          "active": true,
+          "weight": 1
+        }
+      ]
+    }
+  ],
+  "2001:6::/64": [
+    {
+      "prefix": "2001:6::/64",
+      "protocol": "connected",
+      "vrfId": 0,
+      "vrfName": "default",
+      "selected": true,
+      "destSelected": true,
+      "distance": 0,
+      "metric": 0,
+      "installed": true,
+      "table": 254,
+      "internalStatus": 16,
+      "internalFlags": 8,
+      "internalNextHopNum": 1,
+      "internalNextHopActiveNum": 1,
+      "nexthops": [
+        {
+          "flags": 3,
+          "fib": true,
+          "directlyConnected": true,
+          "interfaceName": "eth0",
+          "active": true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf
new file mode 100644 (file)
index 0000000..7d19d98
--- /dev/null
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce6
+!
+interface eth0
+ ipv6 address 2001:6::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:6::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..bfc9db9
--- /dev/null
@@ -0,0 +1,79 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 1 
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::2 remote-as 2
+ neighbor 2001::2 timers 3 10
+ neighbor 2001::2 timers connect 1
+ neighbor 2001::2 update-source 2001::1
+ neighbor 2001::2 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+  neighbor 2001::2 activate
+ exit-address-family
+ !
+ address-family ipv6 vpn
+  neighbor 2001::2 activate
+ exit-address-family
+ !
+ segment-routing srv6
+  locator loc1
+ !
+!
+router bgp 1 vrf vrf10
+ bgp router-id 192.0.2.1 
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv6 unicast
+  sid vpn export auto
+  rd vpn export 1:10
+  rt vpn both 99:99
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!
+ address-family ipv4 unicast
+  network 192.168.1.0/24
+  sid vpn export auto
+  rd vpn export 11:10
+  rt vpn both 77:77
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!
+router bgp 1 vrf vrf20
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+  sid vpn export auto
+  rd vpn export 1:20
+  rt vpn both 88:88
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json
new file mode 100644 (file)
index 0000000..6fc43e1
--- /dev/null
@@ -0,0 +1,169 @@
+{
+  "vrfName": "default",
+  "tableVersion": 2,
+  "routerId": "192.0.2.1",
+  "defaultLocPrf": 100,
+  "localAS": 1,
+  "routes": {
+    "routeDistinguishers": {
+      "1:10": {
+        "2001:1::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:1::",
+            "prefixLen": 64,
+            "network": "2001:1::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf10",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ],
+        "2001:3::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:3::",
+            "prefixLen": 64,
+            "network": "2001:3::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf10",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "1:20": {
+        "2001:5::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:5::",
+            "prefixLen": 64,
+            "network": "2001:5::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf20",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "2:10": {
+        "2001:2::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:2::",
+            "prefixLen": 64,
+            "network": "2001:2::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::2",
+            "path": "2",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::2",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "2:20": {
+        "2001:4::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:4::",
+            "prefixLen": 64,
+            "network": "2001:4::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::2",
+            "path": "2",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::2",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ],
+        "2001:6::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:6::",
+            "prefixLen": 64,
+            "network": "2001:6::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::2",
+            "path": "2",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::2",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      }
+    }
+  }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json
new file mode 100644 (file)
index 0000000..9783c7e
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json
new file mode 100644 (file)
index 0000000..80c1acf
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "192.168.2.0\/24":[
+    {
+      "prefix":"192.168.2.0\/24",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "labels":[
+            16
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:1::"
+          }
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json
new file mode 100644 (file)
index 0000000..9783c7e
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json
new file mode 100644 (file)
index 0000000..07ca64b
--- /dev/null
@@ -0,0 +1,54 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "192.168.2.0\/24":[
+    {
+      "prefix":"192.168.2.0\/24",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"2001::2",
+          "afi":"ipv6",
+          "labels":[
+            128
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:8::"
+          }
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json
new file mode 100644 (file)
index 0000000..6ac8dac
--- /dev/null
@@ -0,0 +1,77 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json
new file mode 100644 (file)
index 0000000..fac3d1d
--- /dev/null
@@ -0,0 +1,107 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "labels":[
+            32
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:2::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       } 
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json
new file mode 100644 (file)
index 0000000..69ce312
--- /dev/null
@@ -0,0 +1,77 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json
new file mode 100644 (file)
index 0000000..04e2305
--- /dev/null
@@ -0,0 +1,106 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "labels":[
+            128
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:8::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json
new file mode 100644 (file)
index 0000000..3cac156
--- /dev/null
@@ -0,0 +1,54 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "192.168.2.0\/24":[
+    {
+      "prefix":"192.168.2.0\/24",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"2001::2",
+          "afi":"ipv6",
+          "labels":[
+            16
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:1::"
+          }
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json
new file mode 100644 (file)
index 0000000..163e9d6
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "192.168.2.0\/24":[
+    {
+      "prefix":"192.168.2.0\/24",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"2001::2",
+          "afi":"ipv6",
+          "labels":[
+            128
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:8::"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json
new file mode 100644 (file)
index 0000000..1313f20
--- /dev/null
@@ -0,0 +1,106 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "labels":[
+            16
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:1::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json
new file mode 100644 (file)
index 0000000..51f249b
--- /dev/null
@@ -0,0 +1,106 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "labels":[
+            128
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:8::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+         "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json
new file mode 100644 (file)
index 0000000..6ac8dac
--- /dev/null
@@ -0,0 +1,77 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
+
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json
new file mode 100644 (file)
index 0000000..1c3dad0
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "192.168.1.0\/24":[
+    {
+      "prefix":"192.168.1.0\/24",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true
+       }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json
new file mode 100644 (file)
index 0000000..9579bb1
--- /dev/null
@@ -0,0 +1,112 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            32
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:2::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json
new file mode 100644 (file)
index 0000000..25f146f
--- /dev/null
@@ -0,0 +1,106 @@
+{
+  "2001:4::\/64":[
+    {
+      "prefix":"2001:4::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            48
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:3::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:5::\/64":[
+    {
+      "prefix":"2001:5::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "2001:6::\/64":[
+    {
+      "prefix":"2001:6::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            48
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:2:2:3::"
+          }
+        }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf
new file mode 100644 (file)
index 0000000..cf31a5c
--- /dev/null
@@ -0,0 +1,43 @@
+log file zebra.log
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::1/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:1::1/64
+ ip address 192.168.1.1/24
+!
+interface eth2 vrf vrf10
+ ipv6 address 2001:3::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:5::1/64
+!
+segment-routing
+ srv6
+  locators
+   locator loc1
+    prefix 2001:db8:1:1::/64 block-len 40 node-len 24 func-bits 16
+  !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:2:1::/64 2001::2
+ipv6 route 2001:db8:2:2::/64 2001::2
+ipv6 route 2001:db8:2:3::/64 2001::2
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf
new file mode 100644 (file)
index 0000000..892a9f7
--- /dev/null
@@ -0,0 +1,80 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp updates
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 2
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::1 remote-as 1
+ neighbor 2001::1 update-source 2001::2
+ neighbor 2001::1 timers 3 10
+ neighbor 2001::1 timers connect 1
+ neighbor 2001::1 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+  neighbor 2001::1 activate
+ exit-address-family
+ !
+ address-family ipv6 vpn
+  neighbor 2001::1 activate
+ exit-address-family
+ !
+ segment-routing srv6
+  locator loc1
+ !
+!
+router bgp 2 vrf vrf10
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv6 unicast
+  sid vpn export auto
+  rd vpn export 2:10
+  rt vpn both 99:99
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!
+ address-family ipv4 unicast
+  network 192.168.2.0/24
+  sid vpn export auto
+  rd vpn export 22:10
+  rt vpn both 77:77
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!
+router bgp 2 vrf vrf20
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+  sid vpn export auto
+  rd vpn export 2:20
+  rt vpn both 88:88
+  import vpn
+  export vpn
+  redistribute connected
+ exit-address-family
+!!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json
new file mode 100644 (file)
index 0000000..538e895
--- /dev/null
@@ -0,0 +1,169 @@
+{
+  "vrfName": "default",
+  "tableVersion": 2,
+  "routerId": "192.0.2.2",
+  "defaultLocPrf": 100,
+  "localAS": 2,
+  "routes": {
+    "routeDistinguishers": {
+      "1:10": {
+        "2001:1::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:1::",
+            "prefixLen": 64,
+            "network": "2001:1::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::1",
+            "path": "1",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::1",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ],
+        "2001:3::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:3::",
+            "prefixLen": 64,
+            "network": "2001:3::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::1",
+            "path": "1",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::1",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "1:20": {
+        "2001:5::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:5::",
+            "prefixLen": 64,
+            "network": "2001:5::/64",
+            "metric": 0,
+            "weight": 0,
+            "peerId": "2001::1",
+            "path": "1",
+            "origin": "incomplete",
+            "nexthops": [
+              {
+                "ip": "2001::1",
+                "hostname": "r1",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "2:10": {
+        "2001:2::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:2::",
+            "prefixLen": 64,
+            "network": "2001:2::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf10",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      },
+      "2:20": {
+        "2001:4::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:4::",
+            "prefixLen": 64,
+            "network": "2001:4::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf20",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ],
+        "2001:6::/64": [
+          {
+            "valid": true,
+            "bestpath": true,
+            "selectionReason": "First path received",
+            "pathFrom": "external",
+            "prefix": "2001:6::",
+            "prefixLen": 64,
+            "network": "2001:6::/64",
+            "metric": 0,
+            "weight": 32768,
+            "peerId": "(unspec)",
+            "path": "",
+            "origin": "incomplete",
+            "announceNexthopSelf": true,
+            "nhVrfName": "vrf20",
+            "nexthops": [
+              {
+                "ip": "::",
+                "hostname": "r2",
+                "afi": "ipv6",
+                "used": true
+              }
+            ]
+          }
+        ]
+      }
+    }
+  }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json
new file mode 100644 (file)
index 0000000..446bb8e
--- /dev/null
@@ -0,0 +1,106 @@
+{
+  "2001:1::\/64":[
+    {
+      "prefix":"2001:1::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            32
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:1:1:2::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:2::\/64":[
+    {
+      "prefix":"2001:2::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "2001:3::\/64":[
+    {
+      "prefix":"2001:3::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf10",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            32
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:1:1:2::"
+          }
+        }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf10",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":10,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json
new file mode 100644 (file)
index 0000000..8bc2fc2
--- /dev/null
@@ -0,0 +1,112 @@
+{
+  "2001:4::\/64":[
+    {
+      "prefix":"2001:4::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "2001:5::\/64":[
+    {
+      "prefix":"2001:5::\/64",
+      "protocol":"bgp",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "afi":"ipv6",
+          "vrf":"default",
+          "active":true,
+          "labels":[
+            48
+          ],
+          "weight":1,
+          "seg6local":{
+            "action":"unspec"
+          },
+          "seg6":{
+            "segs":"2001:db8:1:1:3::"
+          }
+        }
+      ]
+    }
+  ],
+  "2001:6::\/64":[
+    {
+      "prefix":"2001:6::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "selected":true,
+      "destSelected":true,
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ],
+  "fe80::\/64":[
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    },
+    {
+      "prefix":"fe80::\/64",
+      "protocol":"connected",
+      "vrfName":"vrf20",
+      "distance":0,
+      "metric":0,
+      "installed":true,
+      "table":20,
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "directlyConnected":true,
+          "active":true
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf
new file mode 100644 (file)
index 0000000..9771ee1
--- /dev/null
@@ -0,0 +1,43 @@
+log file zebra.log
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::2/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:2::1/64
+ ip address 192.168.2.1/24
+!
+interface eth2 vrf vrf20
+ ipv6 address 2001:4::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:6::1/64
+!
+segment-routing
+ srv6
+  locators
+   locator loc1
+    prefix 2001:db8:2:2::/64 block-len 40 node-len 24 func-bits 16
+  !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:1:1::/64 2001::1
+ipv6 route 2001:db8:1:2::/64 2001::1
+ipv6 route 2001:db8:1:3::/64 2001::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py
new file mode 100755 (executable)
index 0000000..cddcf6a
--- /dev/null
@@ -0,0 +1,453 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright 2023 6WIND S.A.
+# Authored by Dmytro Shytyi <dmytro.shytyi@6wind.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import required_linux_kernel_version
+
+
+def build_topo(tgen):
+    """
+     CE1     CE3      CE5
+    (eth0)  (eth0)   (eth0)
+      :2      :2      :2
+       |       |       |
+   192.168.1.0 |       |
+      /24      |       |
+      2001:   2001:   2001:
+     1::/64  3::/64  5::/64
+       |       |       |
+      :1      :1      :1
+   +-(eth1)--(eth2)---(eth3)-+
+   |     \   /          |    |
+   |    (vrf10)     (vrf20)  |
+   |             R1          |
+   +----------(eth0)---------+
+                :1
+                |
+            2001::/64
+                |
+                :2
+              (eth0)
+    +----------(eth0)--------------+
+    |            R2                |
+    |   (vrf10)       (vrf20)      |
+    |     /           /     \      |
+    +-(eth1)-----(eth2)-----(eth3)-+
+        :1         :1          :1
+         |          |           |
+      +------+   +------+   +------+
+     /  2001: \ /  2001: \ /  2001: \
+    /  2::/64  \ 4::/64  / \ 6::/64 /
+   /192.168.2.0|        /   \      /
+   \     /24  / \       |   |      |
+      +------+   +------+   +------+
+         |           |          |
+        :2          :2         :2
+      (eth0)      (eth0)      (eth0)
+        CE2         CE4         CE6
+    """
+
+    tgen.add_router("r1")
+    tgen.add_router("r2")
+    tgen.add_router("ce1")
+    tgen.add_router("ce2")
+    tgen.add_router("ce3")
+    tgen.add_router("ce4")
+    tgen.add_router("ce5")
+    tgen.add_router("ce6")
+
+    tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+    tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1")
+    tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1")
+    tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2")
+    tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2")
+    tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3")
+    tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3")
+
+
+def setup_module(mod):
+    result = required_linux_kernel_version("5.11")
+    if result is not True:
+        pytest.skip("Kernel requirements are not met")
+
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+    router_list = tgen.routers()
+    for i, (rname, router) in enumerate(tgen.routers().items(), 1):
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+        )
+
+    tgen.gears["r1"].run("modprobe vrf")
+    tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+    tgen.gears["r1"].run("ip link set vrf10 up")
+    tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+    tgen.gears["r1"].run("ip link set vrf20 up")
+    tgen.gears["r1"].run("ip link set eth1 master vrf10")
+    tgen.gears["r1"].run("ip link set eth2 master vrf10")
+    tgen.gears["r1"].run("ip link set eth3 master vrf20")
+    tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+    tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+
+    tgen.gears["r2"].run("modprobe vrf")
+    tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+    tgen.gears["r2"].run("ip link set vrf10 up")
+    tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+    tgen.gears["r2"].run("ip link set vrf20 up")
+    tgen.gears["r2"].run("ip link set eth1 master vrf10")
+    tgen.gears["r2"].run("ip link set eth2 master vrf20")
+    tgen.gears["r2"].run("ip link set eth3 master vrf20")
+    tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0")
+    tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0")
+    tgen.start_router()
+
+    # FOR DEVELOPER:
+    # If you want to stop some specific line and start interactive shell,
+    # please use tgen.mininet_cli() to start it.
+    # Example:
+    # tgen=get_topogen()
+    # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def open_json_file(filename):
+    try:
+        with open(filename, "r") as f:
+            return json.load(f)
+    except IOError:
+        assert False, "Could not read file {}".format(filename)
+
+
+def check_ping(name, dest_addr, expect_connected):
+    def _check(name, dest_addr, match):
+        tgen = get_topogen()
+        output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr))
+        logger.info(output)
+        if match not in output:
+            return True
+
+    match = ", {} packet loss".format("0%" if expect_connected else "100%")
+    logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+    tgen = get_topogen()
+    func = functools.partial(_check, name, dest_addr, match)
+    success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+    assert result is None, "Failed"
+
+
+def check_rib(name, cmd, expected_file):
+    def _check(name, cmd, expected_file):
+        logger.info("polling")
+        tgen = get_topogen()
+        router = tgen.gears[name]
+        output = json.loads(router.vtysh_cmd(cmd))
+        expected = open_json_file("{}/{}".format(CWD, expected_file))
+        return topotest.json_cmp(output, expected)
+
+    logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file))
+    tgen = get_topogen()
+    func = functools.partial(_check, name, cmd, expected_file)
+    success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+    assert result is None, "Failed"
+
+
+def test_rib():
+    check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json")
+    check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json")
+    check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json")
+    check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json")
+    check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json")
+    check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json")
+    check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json")
+    check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json")
+    check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json")
+    check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json")
+    check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json")
+    check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json")
+
+
+def test_ping():
+    check_ping("ce1", "2001:2::2", True)
+    check_ping("ce1", "2001:3::2", True)
+    check_ping("ce1", "2001:4::2", False)
+    check_ping("ce1", "2001:5::2", False)
+    check_ping("ce1", "2001:6::2", False)
+    check_ping("ce4", "2001:1::2", False)
+    check_ping("ce4", "2001:2::2", False)
+    check_ping("ce4", "2001:3::2", False)
+    check_ping("ce4", "2001:5::2", True)
+    check_ping("ce4", "2001:6::2", True)
+
+
+def test_sid_per_afv6_auto():
+    check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+    check_ping("ce1", "2001:2::2", True)
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv6 unicast
+           no sid vpn export auto
+        """
+    )
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv6 unicast
+           sid vpn export auto
+        """
+    )
+    check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json")
+    check_ping("ce1", "2001:2::2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv6 unicast
+           no sid vpn export auto
+        """
+    )
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_afv6_manual():
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv6 unicast
+           sid vpn export 8
+        """
+    )
+
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv6 unicast
+           no sid vpn export 8
+        """
+    )
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_afv4_auto():
+    check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+    check_ping("ce1", "192.168.2.2", True)
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv4 unicast
+           no sid vpn export auto
+        """
+    )
+
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", False)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv4 unicast
+           sid vpn export auto
+        """
+    )
+
+    check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json")
+    check_ping("ce1", "192.168.2.2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv4 unicast
+           no sid vpn export auto
+        """
+    )
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", False)
+
+
+def test_sid_per_afv4_manual():
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", False)
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv4 unicast
+           sid vpn export 8
+        """
+    )
+
+    check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json")
+    check_ping("ce1", "192.168.2.2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          address-family ipv4 unicast
+           no sid vpn export 8
+        """
+    )
+    check_ping("ce1", "192.168.2.2", False)
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json"
+    )
+
+
+def test_sid_per_vrf_auto():
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          sid vpn per-vrf export auto
+        """
+    )
+
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", True)
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+           no sid vpn per-vrf export auto
+        """
+    )
+
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", False)
+
+
+def test_sid_per_vrf_manual():
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", False)
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+          sid vpn per-vrf export 8
+        """
+    )
+
+    check_rib(
+        "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json"
+    )
+    check_ping("ce1", "2001:2::2", True)
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", True)
+
+    get_topogen().gears["r2"].vtysh_cmd(
+        """
+        configure terminal
+         router bgp 2 vrf vrf10
+           no sid vpn per-vrf export 8
+        """
+    )
+
+    check_rib(
+        "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json"
+    )
+    check_ping("ce1", "192.168.2.2", False)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index a9319a6aeddbd454f50093d2f58b3f25d8a9fbcc..cbc5ce1f099d1671385289ae12be0a6e572f01dd 100644 (file)
@@ -7,9 +7,9 @@ log stdout notifications
 log monitor notifications
 log commands
 !
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
 !
 interface eth0
  ipv6 address 2001::1/64
index 9e5fa0ac073f6cd344754e296793c1b065e2d7cc..449ca74d5e9b5af09a5f31fecffa44710deb245b 100644 (file)
@@ -7,9 +7,9 @@ log stdout notifications
 log monitor notifications
 log commands
 !
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
 !
 interface eth0
  ipv6 address 2001::2/64
index 2c560dfc066b044d2335538ddc1bab896ad1b76f..f913b9f0029250ce55dc1f43b16dbd3b79660119 100644 (file)
@@ -7,9 +7,9 @@ log stdout notifications
 log monitor notifications
 log commands
 !
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
 !
 interface eth0
  ipv6 address 2001::1/64
index b9277a9a8cdf2282050fe043c6f61b41e28770b1..201d0cce23f33c8e20483f8c1fe48eaa29afdb3a 100644 (file)
@@ -7,9 +7,9 @@ log stdout notifications
 log monitor notifications
 log commands
 !
-debug zebra packet
-debug zebra dplane
-debug zebra kernel
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
 !
 interface eth0
  ipv6 address 2001::2/64
index caebb0e9221d81ec919b613a3e1ce94c70c13e6a..fb6980a13988448ccccc601e12ec8cd6e2f22e62 100644 (file)
@@ -2,12 +2,12 @@ access-list access seq 10 permit 192.168.1.1/32
 !
 ip route 192.168.1.1/32 10.0.0.10
 !
-debug bgp bestpath
-debug bgp nht
-debug bgp updates
-debug bgp update-groups
-debug bgp zebra
-debug zebra rib detail
+!debug bgp bestpath
+!debug bgp nht
+!debug bgp updates
+!debug bgp update-groups
+!debug bgp zebra
+!debug zebra rib detail
 !
 router bgp 2
   address-family ipv4 uni
index 010e86aad704a10ef0f86da60b1eaa857282d249..129b812036f4394dfe5165fa26761a6a587c5e03 100644 (file)
@@ -1,6 +1,6 @@
-debug bgp updates
-debug bgp bestpath 40.0.0.0/8
-debug bgp zebra
+!debug bgp updates
+!debug bgp bestpath 40.0.0.0/8
+!debug bgp zebra
 !
 router bgp 2
   no bgp ebgp-requires-policy
@@ -8,4 +8,4 @@ router bgp 2
   neighbor 10.0.0.1 remote-as 1
   neighbor 10.0.0.10 remote-as 3
   address-family ipv4 uni
-     network 60.0.0.0/24
\ No newline at end of file
+     network 60.0.0.0/24
index 47b2452b81bdce2d1fe38202ec0a42ced7b0ad47..f89f3378fbf7aca0420d60d125ac5faa0012cbdd 100644 (file)
@@ -847,8 +847,6 @@ def test_bgp_unique_rid_chaos4_p2():
         for intf in topo["routers"][rtr]["links"].keys():
             topo1["routers"][rtr]["links"][intf].pop("ipv4")
             topo1["routers"][rtr]["links"][intf].pop("ipv6")
-            if intf is "lo":
-                topo1["routers"][rtr]["links"][intf].pop("ipv4")
 
     build_config_from_json(tgen, topo1, save_bkup=False)
 
index 8d8f64158ff46c985c96f81a02991049be8e2c49..9f2ee19357e67eeb238a38d214ff2cdd96d6947d 100644 (file)
@@ -1,5 +1,5 @@
 !
-debug bgp neighbor
+!debug bgp neighbor
 !
 router bgp 65534 vrf public
  bgp router-id 10.0.0.1
index 8de6f9bf7082a0ef9bef54d856bf22aab1288811..5c1b97262c95eb8ba9cb4e8de553856d898b9abc 100644 (file)
@@ -133,7 +133,7 @@ def test_static_timing():
             delta = (datetime.datetime.now() - tstamp).total_seconds()
             tot_delta += delta
 
-            router.logger.info(
+            router.logger.debug(
                 "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
                     load_command, output, delta
                 )
@@ -152,7 +152,7 @@ def test_static_timing():
 
     # Number of static routes
     router = tgen.gears["r1"]
-    output = router.run("vtysh -h | grep address-sanitizer")
+    output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False)
     if output == "":
         logger.info("No Address Sanitizer, generating 10000 routes")
         prefix_count = 10000
index df5d06602335b6984d7ff5ceb7640ce02f0b61f0..74e308dbc62fee71ed86eee0a577e25505a73cfb 100755 (executable)
@@ -1,27 +1,44 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
 """
 Topotest conftest.py file.
 """
 # pylint: disable=consider-using-f-string
 
 import glob
+import logging
 import os
-import pdb
 import re
+import resource
 import subprocess
 import sys
 import time
-import resource
 
-import pytest
 import lib.fixtures
-from lib import topolog
-from lib.micronet import Commander, proc_error
-from lib.micronet_cli import cli
-from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous
+import pytest
+from lib.micronet_compat import Mininet
 from lib.topogen import diagnose_env, get_topogen
-from lib.topolog import logger
-from lib.topotest import g_extra_config as topotest_extra_config
+from lib.topolog import get_test_logdir, logger
 from lib.topotest import json_cmp_result
+from munet import cli
+from munet.base import Commander, proc_error
+from munet.cleanup import cleanup_current, cleanup_previous
+from munet.config import ConfigOptionsProxy
+from munet.testing.util import pause_test
+
+from lib import topolog, topotest
+
+try:
+    # Used by munet native tests
+    from munet.testing.fixtures import event_loop, unet  # pylint: disable=all # noqa
+
+    @pytest.fixture(scope="module")
+    def rundir_module(pytestconfig):
+        d = os.path.join(pytestconfig.option.rundir, get_test_logdir())
+        logging.debug("rundir_module: test module rundir %s", d)
+        return d
+
+except (AttributeError, ImportError):
+    pass
 
 
 def pytest_addoption(parser):
@@ -59,12 +76,28 @@ def pytest_addoption(parser):
         help="Comma-separated list of routers to spawn gdb on, or 'all'",
     )
 
+    parser.addoption(
+        "--logd",
+        action="append",
+        metavar="DAEMON[,ROUTER[,...]",
+        help=(
+            "Tail-F the DAEMON log file on all or a subset of ROUTERs."
+            " Option can be given multiple times."
+        ),
+    )
+
     parser.addoption(
         "--pause",
         action="store_true",
         help="Pause after each test",
     )
 
+    parser.addoption(
+        "--pause-at-end",
+        action="store_true",
+        help="Pause before taking munet down",
+    )
+
     parser.addoption(
         "--pause-on-error",
         action="store_true",
@@ -78,6 +111,30 @@ def pytest_addoption(parser):
         help="Do not pause after (disables default when --shell or -vtysh given)",
     )
 
+    parser.addoption(
+        "--pcap",
+        default="",
+        metavar="NET[,NET...]",
+        help="Comma-separated list of networks to capture packets on, or 'all'",
+    )
+
+    parser.addoption(
+        "--perf",
+        action="append",
+        metavar="DAEMON[,ROUTER[,...]",
+        help=(
+            "Collect performance data from given DAEMON on all or a subset of ROUTERs."
+            " Option can be given multiple times."
+        ),
+    )
+
+    parser.addoption(
+        "--perf-options",
+        metavar="OPTS",
+        default="-g",
+        help="Options to pass to `perf record`.",
+    )
+
     rundir_help = "directory for running in and log files"
     parser.addini("rundir", rundir_help, default="/tmp/topotests")
     parser.addoption("--rundir", metavar="DIR", help=rundir_help)
@@ -133,7 +190,7 @@ def pytest_addoption(parser):
 
 
 def check_for_memleaks():
-    assert topotest_extra_config["valgrind_memleaks"]
+    assert topotest.g_pytest_config.option.valgrind_memleaks
 
     leaks = []
     tgen = get_topogen()  # pylint: disable=redefined-outer-name
@@ -177,16 +234,15 @@ def check_for_memleaks():
 
 @pytest.fixture(autouse=True, scope="module")
 def module_check_memtest(request):
-    del request  # disable unused warning
     yield
-    if topotest_extra_config["valgrind_memleaks"]:
+    if request.config.option.valgrind_memleaks:
         if get_topogen() is not None:
             check_for_memleaks()
 
 
 def pytest_runtest_logstart(nodeid, location):
     # location is (filename, lineno, testname)
-    topolog.logstart(nodeid, location, topotest_extra_config["rundir"])
+    topolog.logstart(nodeid, location, topotest.g_pytest_config.option.rundir)
 
 
 def pytest_runtest_logfinish(nodeid, location):
@@ -197,10 +253,9 @@ def pytest_runtest_logfinish(nodeid, location):
 @pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_call(item: pytest.Item) -> None:
     "Hook the function that is called to execute the test."
-    del item  # disable unused warning
 
     # For topology only run the CLI then exit
-    if topotest_extra_config["topology_only"]:
+    if item.config.option.topology_only:
         get_topogen().cli()
         pytest.exit("exiting after --topology-only")
 
@@ -208,7 +263,7 @@ def pytest_runtest_call(item: pytest.Item) -> None:
     yield
 
     # Check for leaks if requested
-    if topotest_extra_config["valgrind_memleaks"]:
+    if item.config.option.valgrind_memleaks:
         check_for_memleaks()
 
 
@@ -231,6 +286,7 @@ def pytest_configure(config):
     """
     Assert that the environment is correctly configured, and get extra config.
     """
+    topotest.g_pytest_config = ConfigOptionsProxy(config)
 
     if config.getoption("--collect-only"):
         return
@@ -252,11 +308,13 @@ def pytest_configure(config):
     # Set some defaults for the pytest.ini [pytest] section
     # ---------------------------------------------------
 
-    rundir = config.getoption("--rundir")
+    rundir = config.option.rundir
     if not rundir:
         rundir = config.getini("rundir")
     if not rundir:
         rundir = "/tmp/topotests"
+    config.option.rundir = rundir
+
     if not config.getoption("--junitxml"):
         config.option.xmlpath = os.path.join(rundir, "topotests.xml")
     xmlpath = config.option.xmlpath
@@ -269,8 +327,6 @@ def pytest_configure(config):
         mv_path = commander.get_exec_path("mv")
         commander.cmd_status([mv_path, xmlpath, xmlpath + suffix])
 
-    topotest_extra_config["rundir"] = rundir
-
     # Set the log_file (exec) to inside the rundir if not specified
     if not config.getoption("--log-file") and not config.getini("log_file"):
         config.option.log_file = os.path.join(rundir, "exec.log")
@@ -300,69 +356,19 @@ def pytest_configure(config):
         elif b and not is_xdist and not have_windows:
             pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature))
 
-    # ---------------------------------------
-    # Record our options in global dictionary
-    # ---------------------------------------
-
-    topotest_extra_config["rundir"] = rundir
-
-    asan_abort = config.getoption("--asan-abort")
-    topotest_extra_config["asan_abort"] = asan_abort
-
-    gdb_routers = config.getoption("--gdb-routers")
-    gdb_routers = gdb_routers.split(",") if gdb_routers else []
-    topotest_extra_config["gdb_routers"] = gdb_routers
-
-    gdb_daemons = config.getoption("--gdb-daemons")
-    gdb_daemons = gdb_daemons.split(",") if gdb_daemons else []
-    topotest_extra_config["gdb_daemons"] = gdb_daemons
-    assert_feature_windows(gdb_routers or gdb_daemons, "GDB")
-
-    gdb_breakpoints = config.getoption("--gdb-breakpoints")
-    gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else []
-    topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints
-
-    cli_on_error = config.getoption("--cli-on-error")
-    topotest_extra_config["cli_on_error"] = cli_on_error
-    assert_feature_windows(cli_on_error, "--cli-on-error")
-
-    shell = config.getoption("--shell")
-    topotest_extra_config["shell"] = shell.split(",") if shell else []
-    assert_feature_windows(shell, "--shell")
-
-    strace = config.getoption("--strace-daemons")
-    topotest_extra_config["strace_daemons"] = strace.split(",") if strace else []
-
-    shell_on_error = config.getoption("--shell-on-error")
-    topotest_extra_config["shell_on_error"] = shell_on_error
-    assert_feature_windows(shell_on_error, "--shell-on-error")
-
-    topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra")
-    topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks")
-
-    vtysh = config.getoption("--vtysh")
-    topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else []
-    assert_feature_windows(vtysh, "--vtysh")
-
-    vtysh_on_error = config.getoption("--vtysh-on-error")
-    topotest_extra_config["vtysh_on_error"] = vtysh_on_error
-    assert_feature_windows(vtysh_on_error, "--vtysh-on-error")
-
-    pause_on_error = vtysh or shell or config.getoption("--pause-on-error")
-    if config.getoption("--no-pause-on-error"):
-        pause_on_error = False
-
-    topotest_extra_config["pause_on_error"] = pause_on_error
-    assert_feature_windows(pause_on_error, "--pause-on-error")
-
-    pause = config.getoption("--pause")
-    topotest_extra_config["pause"] = pause
-    assert_feature_windows(pause, "--pause")
-
-    topology_only = config.getoption("--topology-only")
-    if topology_only and is_xdist:
+    #
+    # Check for window capability if given options that require window
+    #
+    assert_feature_windows(config.option.gdb_routers, "GDB")
+    assert_feature_windows(config.option.gdb_daemons, "GDB")
+    assert_feature_windows(config.option.cli_on_error, "--cli-on-error")
+    assert_feature_windows(config.option.shell, "--shell")
+    assert_feature_windows(config.option.shell_on_error, "--shell-on-error")
+    assert_feature_windows(config.option.vtysh, "--vtysh")
+    assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error")
+
+    if config.option.topology_only and is_xdist:
         pytest.exit("Cannot use --topology-only with distributed test mode")
-    topotest_extra_config["topology_only"] = topology_only
 
     # Check environment now that we have config
     if not diagnose_env(rundir):
@@ -428,10 +434,7 @@ def pytest_runtest_makereport(item, call):
             # We want to pause, if requested, on any error not just test cases
             # (e.g., call.when == "setup")
             if not pause:
-                pause = (
-                    topotest_extra_config["pause_on_error"]
-                    or topotest_extra_config["pause"]
-                )
+                pause = item.config.option.pause_on_error or item.config.option.pause
 
             # (topogen) Set topology error to avoid advancing in the test.
             tgen = get_topogen()  # pylint: disable=redefined-outer-name
@@ -443,9 +446,9 @@ def pytest_runtest_makereport(item, call):
     isatty = sys.stdout.isatty()
     error_cmd = None
 
-    if error and topotest_extra_config["vtysh_on_error"]:
+    if error and item.config.option.vtysh_on_error:
         error_cmd = commander.get_exec_path(["vtysh"])
-    elif error and topotest_extra_config["shell_on_error"]:
+    elif error and item.config.option.shell_on_error:
         error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"]))
 
     if error_cmd:
@@ -497,31 +500,16 @@ def pytest_runtest_makereport(item, call):
             if p.wait():
                 logger.warning("xterm proc failed: %s:", proc_error(p, o, e))
 
-    if error and topotest_extra_config["cli_on_error"]:
+    if error and item.config.option.cli_on_error:
         # Really would like something better than using this global here.
         # Not all tests use topogen though so get_topogen() won't work.
         if Mininet.g_mnet_inst:
-            cli(Mininet.g_mnet_inst, title=title, background=False)
+            cli.cli(Mininet.g_mnet_inst, title=title, background=False)
         else:
             logger.error("Could not launch CLI b/c no mininet exists yet")
 
-    while pause and isatty:
-        try:
-            user = raw_input(
-                'PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: '
-            )
-        except NameError:
-            user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ')
-        user = user.strip()
-
-        if user == "cli":
-            cli(Mininet.g_mnet_inst)
-        elif user == "pdb":
-            pdb.set_trace()  # pylint: disable=forgotten-debug-statement
-        elif user:
-            print('Unrecognized input: "%s"' % user)
-        else:
-            break
+    if pause and isatty:
+        pause_test()
 
 
 #
index da240e87a35b7297752afc96e1391c33e54f346f..359b655f012235c79c3bccc67affb7259343d107 100644 (file)
@@ -53,7 +53,7 @@
     ],
     "edges":[
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -96,7 +96,7 @@
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
diff --git a/tests/topotests/example_munet/munet.yaml b/tests/topotests/example_munet/munet.yaml
new file mode 100644 (file)
index 0000000..34e1470
--- /dev/null
@@ -0,0 +1,17 @@
+version: 1
+topology:
+  ipv6-enable: true
+  networks-autonumber: true
+  networks:
+    - name: net1
+    - name: net2
+  nodes:
+    - name: r1
+      kind: frr
+      connections: ["net1"]
+    - name: r2
+      kind: frr
+      connections: ["net1", "net2"]
+    - name: r3
+      kind: frr
+      connections: ["net2"]
diff --git a/tests/topotests/example_munet/r1/daemons b/tests/topotests/example_munet/r1/daemons
new file mode 100644 (file)
index 0000000..a454c95
--- /dev/null
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r1/frr.conf b/tests/topotests/example_munet/r1/frr.conf
new file mode 100644 (file)
index 0000000..468bda5
--- /dev/null
@@ -0,0 +1,7 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.1/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r1/vtysh.conf b/tests/topotests/example_munet/r1/vtysh.conf
new file mode 100644 (file)
index 0000000..f863f56
--- /dev/null
@@ -0,0 +1 @@
+service integrated-vtysh-config
\ No newline at end of file
diff --git a/tests/topotests/example_munet/r2/daemons b/tests/topotests/example_munet/r2/daemons
new file mode 100644 (file)
index 0000000..a454c95
--- /dev/null
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r2/frr.conf b/tests/topotests/example_munet/r2/frr.conf
new file mode 100644 (file)
index 0000000..77d9892
--- /dev/null
@@ -0,0 +1,10 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.1.2/24
+
+interface eth1
+ ip address 10.0.2.2/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r2/vtysh.conf b/tests/topotests/example_munet/r2/vtysh.conf
new file mode 100644 (file)
index 0000000..f863f56
--- /dev/null
@@ -0,0 +1 @@
+service integrated-vtysh-config
\ No newline at end of file
diff --git a/tests/topotests/example_munet/r3/daemons b/tests/topotests/example_munet/r3/daemons
new file mode 100644 (file)
index 0000000..a454c95
--- /dev/null
@@ -0,0 +1,6 @@
+zebra=1
+staticd=1
+vtysh_enable=1
+watchfrr_enable=1
+zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log"
+staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log"
diff --git a/tests/topotests/example_munet/r3/frr.conf b/tests/topotests/example_munet/r3/frr.conf
new file mode 100644 (file)
index 0000000..e0839e6
--- /dev/null
@@ -0,0 +1,7 @@
+log file /var/log/frr/frr.log
+service integrated-vtysh-config
+
+interface eth0
+ ip address 10.0.2.3/24
+
+ip route 10.0.0.0/8 blackhole
diff --git a/tests/topotests/example_munet/r3/vtysh.conf b/tests/topotests/example_munet/r3/vtysh.conf
new file mode 100644 (file)
index 0000000..f863f56
--- /dev/null
@@ -0,0 +1 @@
+service integrated-vtysh-config
\ No newline at end of file
diff --git a/tests/topotests/example_munet/test_munet.py b/tests/topotests/example_munet/test_munet.py
new file mode 100644 (file)
index 0000000..0d9599f
--- /dev/null
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 23 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+async def test_native_test(unet):
+    o = unet.hosts["r1"].cmd_nostatus("ip addr")
+    print(o)
index 5eef879e3f29ba1744e71a7a86e9d3b85395d536..ada8c0f5fbdaafd3eaa2d558b45d85efc6c13c3f 100644 (file)
@@ -121,14 +121,14 @@ def _check_interface_metrics(router, expected_metrics):
     tgen = get_topogen()
     router = tgen.gears[router]
     logger.info(f"check_interface_metrics {router}")
-    isis_interface_output = router.vtysh_cmd(
-        "show isis interface detail json"
-    )
+    isis_interface_output = router.vtysh_cmd("show isis interface detail json")
 
     intf_json = json.loads(isis_interface_output)
     for i in range(len(expected_metrics)):
-        metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0]["metric"]
-        if (metric != expected_metrics[i]):
+        metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0][
+            "metric"
+        ]
+        if metric != expected_metrics[i]:
             intf_name = intf_json["areas"][0]["circuits"][i]["interface"]["name"]
             return "{} with expected metric {} on {} got {}".format(
                 router.name, expected_metrics[i], intf_name, metric
@@ -139,9 +139,7 @@ def _check_interface_metrics(router, expected_metrics):
 def check_interface_metrics(router, expected_metrics):
     "Verfiy metrics on router's isis interfaces"
 
-    assertmsg = _check_interface_metrics(
-        router, expected_metrics
-    )
+    assertmsg = _check_interface_metrics(router, expected_metrics)
     assert assertmsg is True, assertmsg
 
 
@@ -151,9 +149,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics):
     tgen = get_topogen()
     router = tgen.gears[router]
     logger.info(f"check_lsp_metrics {router}")
-    isis_lsp_output = router.vtysh_cmd(
-        "show isis database detail {}".format(lsp)
-    )
+    isis_lsp_output = router.vtysh_cmd("show isis database detail {}".format(lsp))
 
     metrics_list = [int(i) for i in re.findall(r"Metric: (\d+)", isis_lsp_output)]
     if len(metrics_list) == 0:
@@ -170,9 +166,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics):
 def check_lsp_metrics(router, lsp, expected_metrics):
     "Verfiy metrics on router's lsp"
 
-    assertmsg = _check_lsp_metrics(
-        router, lsp, expected_metrics
-    )
+    assertmsg = _check_lsp_metrics(router, lsp, expected_metrics)
     assert assertmsg is True, assertmsg
 
 
@@ -183,14 +177,12 @@ def _check_ip_route(router, destination, expected_interface):
     tgen = get_topogen()
     router = tgen.gears[router]
     logger.info(f"check_ip_route {router}")
-    route_output = router.vtysh_cmd(
-        "show ip route {} json".format(destination)
-    )
+    route_output = router.vtysh_cmd("show ip route {} json".format(destination))
     route_json = json.loads(route_output)
 
     interface = route_json[destination][0]["nexthops"][0]["interfaceName"]
 
-    if (interface != expected_interface):
+    if interface != expected_interface:
         return "{} with expected route to {} got {} expected {}".format(
             router.name, destination, interface, expected_interface
         )
@@ -201,9 +193,7 @@ def _check_ip_route(router, destination, expected_interface):
 def check_ip_route(router, destination, expected_interface):
     "Verfiy IS-IS route"
 
-    assertmsg = _check_ip_route(
-        router, destination, expected_interface
-    )
+    assertmsg = _check_ip_route(router, destination, expected_interface)
     assert assertmsg is True, assertmsg
 
 
@@ -216,9 +206,7 @@ def test_isis_daemon_up():
 
     for router in ["r1", "r2", "r3", "r4"]:
         r = tgen.gears[router]
-        daemons = r.vtysh_cmd(
-            "show daemons"
-        )
+        daemons = r.vtysh_cmd("show daemons")
         assert "isisd" in daemons
 
     # Verify initial metric values.
@@ -420,9 +408,9 @@ def test_isis_advertise_high_metrics_route():
     Topology:
     
          r2
-        /  \
+       //  \\
       r1   r4
-        \  /
+       \\  //
          r3
     
     Devices are configured with preferred route between r1 and r4:
index fc700608b5342f8a5078c5c876a0019b5bb1e90b..b3d10b07ecdb46e1c1c0ba48ffa3a59e4e3cf145 100644 (file)
@@ -1,10 +1,10 @@
 hostname r5
 log file ldpd.log
 !
-debug mpls ldp zebra
-debug mpls ldp event
-debug mpls ldp errors
-debug mpls ldp sync
+!debug mpls ldp zebra
+!debug mpls ldp event
+!debug mpls ldp errors
+!debug mpls ldp sync
 !
 mpls ldp
  router-id 3.3.3.3
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf
new file mode 100644 (file)
index 0000000..5503baa
--- /dev/null
@@ -0,0 +1,96 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any red
+ !
+ flex-algo 202
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any blue
+ !
+ flex-algo 203
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any green
+ !
+ flex-algo 204
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any blue green
+ !
+ flex-algo 205
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any red green
+ !
+ flex-algo 206
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any red blue
+ !
+ flex-algo 207
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-all yellow orange
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 1
+ segment-routing prefix 1.1.1.1/32 algorithm 201 index 101
+ segment-routing prefix 1.1.1.1/32 algorithm 202 index 201
+ segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+ segment-routing prefix 1.1.1.1/32 algorithm 204 index 401
+ segment-routing prefix 1.1.1.1/32 algorithm 205 index 501
+ segment-routing prefix 1.1.1.1/32 algorithm 206 index 601
+ segment-routing prefix 1.1.1.1/32 algorithm 207 index 701
+ segment-routing prefix 2001:db8:1000::1/128 index 1001
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 201 index 1101
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 202 index 1201
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 204 index 1401
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 205 index 1501
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 206 index 1601
+ segment-routing prefix 2001:db8:1000::1/128 algorithm 207 index 1701
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..750abc1
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
\ No newline at end of file
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..13a9616
--- /dev/null
@@ -0,0 +1,115 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..d311e92
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..ce31766
--- /dev/null
@@ -0,0 +1,112 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..d311e92
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..e56e5af
--- /dev/null
@@ -0,0 +1,108 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..d311e92
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..9421990
--- /dev/null
@@ -0,0 +1,126 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
+
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..2b16c53
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20402,
+        "outLabelStack":[
+          20402
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20603,
+        "outLabelStack":[
+          20603
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20702,
+        "outLabelStack":[
+          20702
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.3"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21102,
+        "outLabelStack":[
+          21102
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21303,
+        "outLabelStack":[
+          21303
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21402,
+        "outLabelStack":[
+          21402
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21603,
+        "outLabelStack":[
+          21603
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21702,
+        "outLabelStack":[
+          21702
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf
new file mode 100644 (file)
index 0000000..5140eda
--- /dev/null
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt1
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-rt2
+ ip address 10.12.0.1/24
+ link-params
+  affinity red
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.13.0.1/24
+ link-params
+  affinity green yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf
new file mode 100644 (file)
index 0000000..8655e74
--- /dev/null
@@ -0,0 +1,96 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any red
+ !
+ flex-algo 202
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any blue
+ !
+ flex-algo 203
+  dataplane sr-mpls
+  advertise-definition
+  affinity exclude-any green
+ !
+ flex-algo 204
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any blue green
+ !
+ flex-algo 205
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any red green
+ !
+ flex-algo 206
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any red blue
+ !
+ flex-algo 207
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-all yellow orange
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 2
+ segment-routing prefix 2.2.2.2/32 algorithm 201 index 102
+ segment-routing prefix 2.2.2.2/32 algorithm 202 index 202
+ segment-routing prefix 2.2.2.2/32 algorithm 203 index 302
+ segment-routing prefix 2.2.2.2/32 algorithm 204 index 402
+ segment-routing prefix 2.2.2.2/32 algorithm 205 index 502
+ segment-routing prefix 2.2.2.2/32 algorithm 206 index 602
+ segment-routing prefix 2.2.2.2/32 algorithm 207 index 702
+ segment-routing prefix 2001:db8:1000::2/128 index 1002
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 201 index 1102
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 202 index 1202
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 203 index 1302
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 204 index 1402
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 205 index 1502
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 206 index 1602
+ segment-routing prefix 2001:db8:1000::2/128 algorithm 207 index 1702
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..0ed0eb3
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20311":{
+    "inLabel":20311,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21311":{
+    "inLabel":21311,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..b10cb70
--- /dev/null
@@ -0,0 +1,114 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..8b40738
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..358c431
--- /dev/null
@@ -0,0 +1,111 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..8b40738
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..04d07e6
--- /dev/null
@@ -0,0 +1,107 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..8b40738
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..099045a
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..defa3ef
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: yes
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..3db0ebf
--- /dev/null
@@ -0,0 +1,482 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20203":{
+    "inLabel":20203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20203,
+        "outLabelStack":[
+          20203
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20401,
+        "outLabelStack":[
+          20401
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20403":{
+    "inLabel":20403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20503":{
+    "inLabel":20503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20503,
+        "outLabelStack":[
+          20503
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20603":{
+    "inLabel":20603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20701,
+        "outLabelStack":[
+          20701
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20703":{
+    "inLabel":20703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21003":{
+    "inLabel":21003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21101,
+        "outLabelStack":[
+          21101
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21103":{
+    "inLabel":21103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21203":{
+    "inLabel":21203,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21203,
+        "outLabelStack":[
+          21203
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21303":{
+    "inLabel":21303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21401,
+        "outLabelStack":[
+          21401
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21403":{
+    "inLabel":21403,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21503":{
+    "inLabel":21503,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21503,
+        "outLabelStack":[
+          21503
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21603":{
+    "inLabel":21603,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21701,
+        "outLabelStack":[
+          21701
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  },
+  "21703":{
+    "inLabel":21703,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf
new file mode 100644 (file)
index 0000000..388348f
--- /dev/null
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt2
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-rt1
+ ip address 10.12.0.2/24
+ link-params
+  affinity red
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.23.0.2/24
+ link-params
+  affinity blue yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf
new file mode 100644 (file)
index 0000000..d77af81
--- /dev/null
@@ -0,0 +1,76 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+!debug northbound
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 201
+  dataplane sr-mpls
+ flex-algo 202
+  dataplane sr-mpls
+ flex-algo 203
+  dataplane sr-mpls
+ flex-algo 204
+  dataplane sr-mpls
+ flex-algo 205
+  dataplane sr-mpls
+ flex-algo 206
+  dataplane sr-mpls
+ flex-algo 207
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 3
+ segment-routing prefix 3.3.3.3/32 algorithm 201 index 103
+ segment-routing prefix 3.3.3.3/32 algorithm 202 index 203
+ segment-routing prefix 3.3.3.3/32 algorithm 203 index 303
+ segment-routing prefix 3.3.3.3/32 algorithm 204 index 403
+ segment-routing prefix 3.3.3.3/32 algorithm 205 index 503
+ segment-routing prefix 3.3.3.3/32 algorithm 206 index 603
+ segment-routing prefix 3.3.3.3/32 algorithm 207 index 703
+ segment-routing prefix 2001:db8:1000::3/128 index 1003
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 201 index 1103
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 202 index 1203
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 203 index 1303
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 204 index 1403
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 205 index 1503
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 206 index 1603
+ segment-routing prefix 2001:db8:1000::3/128 algorithm 207 index 1703
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..7954734
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
\ No newline at end of file
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..57755b0
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20311":{
+    "inLabel":20311,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20311,
+        "outLabelStack":[
+          20311
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21311":{
+    "inLabel":21311,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21311,
+        "outLabelStack":[
+          21311
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..2ccc4f1
--- /dev/null
@@ -0,0 +1,114 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: Not found
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..1b57f57
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..903c0f2
--- /dev/null
@@ -0,0 +1,111 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: None
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..1b57f57
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..f36d965
--- /dev/null
@@ -0,0 +1,107 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..1b57f57
--- /dev/null
@@ -0,0 +1,450 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..9ab0c74
--- /dev/null
@@ -0,0 +1,514 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21301":{
+    "inLabel":21301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21301,
+        "outLabelStack":[
+          21301
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref
new file mode 100644 (file)
index 0000000..85182db
--- /dev/null
@@ -0,0 +1,125 @@
+Area 1: Algorithm 201
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000001
+            Bit positions: 0
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 202
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000002
+            Bit positions: 1
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 203
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: 0x00000004
+            Bit positions: 2
+  Include-all admin-group: not-set
+  Include-any admin-group: not-set
+
+Area 1: Algorithm 204
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000006
+            Bit positions: 1, 2
+
+Area 1: Algorithm 205
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000005
+            Bit positions: 0, 2
+
+Area 1: Algorithm 206
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: not-set
+  Include-any admin-group: 0x00000003
+            Bit positions: 0, 1
+
+Area 1: Algorithm 207
+
+ Enabled Data-Planes: SR-MPLS
+
+ Elected and running Flexible-Algorithm Definition:
+  Source: 0000.0000.0002
+  Priority: 128
+  Equal to local: no
+  Local state: enabled
+  Calculation type: spf
+  Metric type: igp
+  Prefix-metric: disabled
+  Exclude SRLG: disabled
+  Exclude-any admin-group: not-set
+  Include-all admin-group: 0x00000018
+            Bit positions: 3, 4
+  Include-any admin-group: not-set
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref
new file mode 100644 (file)
index 0000000..8ae983a
--- /dev/null
@@ -0,0 +1,482 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20201":{
+    "inLabel":20201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20202":{
+    "inLabel":20202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20202,
+        "outLabelStack":[
+          20202
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20401":{
+    "inLabel":20401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20402":{
+    "inLabel":20402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20501":{
+    "inLabel":20501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20502":{
+    "inLabel":20502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20502,
+        "outLabelStack":[
+          20502
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20601":{
+    "inLabel":20601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20601,
+        "outLabelStack":[
+          20601
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20602":{
+    "inLabel":20602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20701":{
+    "inLabel":20701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.13.0.1"
+      }
+    ]
+  },
+  "20702":{
+    "inLabel":20702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "21001":{
+    "inLabel":21001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21002":{
+    "inLabel":21002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21101":{
+    "inLabel":21101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21102":{
+    "inLabel":21102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21201":{
+    "inLabel":21201,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21202":{
+    "inLabel":21202,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21202,
+        "outLabelStack":[
+          21202
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21302":{
+    "inLabel":21302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21401":{
+    "inLabel":21401,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21402":{
+    "inLabel":21402,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21501":{
+    "inLabel":21501,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21502":{
+    "inLabel":21502,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21502,
+        "outLabelStack":[
+          21502
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21601":{
+    "inLabel":21601,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":21601,
+        "outLabelStack":[
+          21601
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21602":{
+    "inLabel":21602,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  },
+  "21701":{
+    "inLabel":21701,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt1"
+      }
+    ]
+  },
+  "21702":{
+    "inLabel":21702,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "interface":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf
new file mode 100644 (file)
index 0000000..fb45ee1
--- /dev/null
@@ -0,0 +1,39 @@
+log file zebra.log
+!
+hostname rt3
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map red bit-position 0
+affinity-map blue bit-position 1
+affinity-map green bit-position 2
+affinity-map yellow bit-position 3
+affinity-map orange bit-position 4
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-rt1
+ ip address 10.13.0.3/24
+ link-params
+  affinity green yellow orange
+ exit-link-params
+!
+interface eth-rt2
+ ip address 10.23.0.3/24
+ link-params
+  affinity blue yellow orange
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py
new file mode 100755 (executable)
index 0000000..85600be
--- /dev/null
@@ -0,0 +1,583 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com>
+# Copyright 2023 6WIND S.A.
+
+"""
+test_isis_sr_flex_algo_topo1.py:
+
+[+] Flex-Algos 201 exclude red
+[+] Flex-Algos 202 exclude blue
+[+] Flex-Algos 203 exclude green
+[+] Flex-Algos 204 include-any blue green
+[+] Flex-Algos 205 include-any red green
+[+] Flex-Algos 206 include-any red blue
+[+] Flex-Algos 207 include-all yellow orange
+
+     +--------+  10.12.0.0/24  +--------+
+     |        |       red      |        |
+     |   RT1  |----------------|   RT2  |
+     |        |                |        |
+     +--------+                +--------+
+  10.13.0.0/24 \\             / 10.23.0.0/24
+          green \\           / blue
+         yellow  \\         / yellow
+          orange  +--------+ orange
+                  |        |
+                  |   RT3  |
+                  |        |
+                  +--------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import tempfile
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.isisd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+    "Build function"
+
+    def connect_routers(tgen, left_idx, right_idx):
+        left = "rt{}".format(left_idx)
+        right = "rt{}".format(right_idx)
+        switch = tgen.add_switch("s-{}-{}".format(left, right))
+        switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right))
+        switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left))
+        l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx)
+        tgen.gears[left].run("ip link set eth-{} down".format(right))
+        tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr))
+        tgen.gears[left].run("ip link set eth-{} up".format(right))
+        r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx)
+        tgen.gears[right].run("ip link set eth-{} down".format(left))
+        tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr))
+        tgen.gears[right].run("ip link set eth-{} up".format(left))
+
+    tgen.add_router("rt1")
+    tgen.add_router("rt2")
+    tgen.add_router("rt3")
+    connect_routers(tgen, 1, 2)
+    connect_routers(tgen, 2, 3)
+    connect_routers(tgen, 3, 1)
+
+    #
+    # Populate multi-dimensional dictionary containing all expected outputs
+    #
+    number_of_steps = 11
+    filenames = [
+        "show_mpls_table.ref",
+        "show_isis_flex_algo.ref",
+    ]
+    for rname in ["rt1", "rt2", "rt3"]:
+        outputs[rname] = {}
+        for step in range(1, number_of_steps + 1):
+            outputs[rname][step] = {}
+            for filename in filenames:
+                # Get snapshots relative to the expected network convergence
+                filename_pullpath = "{}/{}/step{}/{}".format(CWD, rname, step, filename)
+                outputs[rname][step][filename] = open(filename_pullpath).read()
+
+
+def setup_module(mod):
+    "Sets up the pytest environment"
+    tgen = Topogen(build_topo, mod.__name__)
+    frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+    if not os.path.isfile(os.path.join(frrdir, "pathd")):
+        pytest.skip("pathd daemon wasn't built")
+    tgen.start_topology()
+    router_list = tgen.routers()
+
+    # For all registered routers, load the zebra configuration file
+    for rname, router in router_list.items():
+        router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)))
+        router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)))
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def setup_testcase(msg):
+    logger.info(msg)
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    return tgen
+
+
+def router_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
+
+
+def router_compare_output(rname, command, reference):
+    "Compare router output"
+
+    logger.info('Comparing router "%s" "%s" output', rname, command)
+
+    tgen = get_topogen()
+
+    # Run test function until we get an result. Wait at most 60 seconds.
+    test_func = partial(topotest.router_output_cmp, tgen.gears[rname], command, reference)
+    result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+    assertmsg = '{} command "{}" output mismatches the expected result:\n{}'.format(rname, command, diff)
+    assert result, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergenece
+#
+# All flex-algo are defined and its fib entries are installed
+#
+def test_step1_mpls_lfib():
+    logger.info("Test (step 1)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][1]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][1]["show_mpls_table.ref"])
+
+
+#
+# Step 2
+#
+# Action(s):
+# - Disable flex-algo-203 definition advertisement on rt1
+#
+# Expected change(s):
+# - Nothing
+#
+# Description:
+#   No change occurs because it refers to the FAD set in rt2.
+#
+def test_step2_mpls_lfib():
+    logger.info("Test (step 2)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           no advertise-definition
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][2]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][2]["show_mpls_table.ref"])
+
+
+#
+# Step 3
+#
+# Action(s):
+# - Disable flex-algo-203 definition advertisement on rt2
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+#   When all FADs are disappeared, all their prefix sid routes are withdrawn.
+#
+def test_step3_mpls_lfib():
+    logger.info("Test (step 3)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt2"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           no advertise-definition
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][3]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][3]["show_mpls_table.ref"])
+
+
+#
+# Step 4
+#
+# Action(s):
+# - Enable flex-algo-203 definition advertisement on rt2
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203
+#
+# Description:
+#   Since the FAD is restored, the reachability to the Prefix-SID is restored.
+#
+def test_step4_mpls_lfib():
+    logger.info("Test (step 4)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt2"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           advertise-definition
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][4]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][4]["show_mpls_table.ref"])
+
+
+#
+# Step 5
+#
+# Action(s):
+# - Enable flex-algo-203 definition advertisement on rt1
+#
+# Expected change(s):
+# - Nothing
+#
+# Description:
+#   This does not affect the FIB, since there is already a FAD for rt2.
+#   However, the FAD owner will be changed from rt2 to rt1.
+#
+def test_step5_mpls_lfib():
+    logger.info("Test (step 5)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           advertise-definition
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][5]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][5]["show_mpls_table.ref"])
+
+
+#
+# Step 6
+#
+# Action(s):
+# - Disable flex-algo-203 SR-MPLS dataplane on rt1
+# - Disable flex-algo-203 SR-MPLS dataplane on rt2
+# - Disable flex-algo-203 SR-MPLS dataplane on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+#   Clear the Flex-Algo 203 whole settings on each routers. All routes related
+#   to it will be withdrawn.
+#
+def test_step6_mpls_lfib():
+    logger.info("Test (step 6)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    for rname in ["rt1", "rt2", "rt3"]:
+        tgen.gears[rname].vtysh_cmd(
+            """
+            configure terminal
+             router isis 1
+              flex-algo 203
+               no dataplane sr-mpls
+            """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][6]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][6]["show_mpls_table.ref"])
+
+
+#
+# Step 7
+#
+# Action(s):
+# - Disable flex-algo-203 all configuration on rt1
+# - Disable flex-algo-203 all configuration on rt2
+# - Disable flex-algo-203 all configuration on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203
+#
+# Description:
+#   Clear the Flex-Algo 203 whole settings on each routers. All routes related
+#   to it will be withdrawn.
+#
+def test_step7_mpls_lfib():
+    logger.info("Test (step 7)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    for rname in ["rt1", "rt2", "rt3"]:
+        tgen.gears[rname].vtysh_cmd(
+            """
+            configure terminal
+             router isis 1
+              no flex-algo 203
+            """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][7]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][7]["show_mpls_table.ref"])
+
+#
+# Step 8
+#
+# Action(s):
+# - Enable flex-algo-203 all configuration on rt1
+# - Enable flex-algo-203 all configuration on rt2
+# - Enable flex-algo-203 all configuration on rt3
+#
+# Expected change(s):
+# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203
+#
+# Description:
+#   All configurations were backed.
+#
+def test_step8_mpls_lfib():
+    logger.info("Test (step 8)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           advertise-definition
+           affinity exclude-any green
+           dataplane sr-mpls
+        """)
+
+    tgen.gears["rt2"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+           advertise-definition
+           affinity exclude-any green
+           dataplane sr-mpls
+        """)
+
+    tgen.gears["rt3"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          flex-algo 203
+          dataplane sr-mpls
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][8]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][8]["show_mpls_table.ref"])
+
+
+#
+# Step 9
+#
+# Action(s):
+# - Disable algorithm prefix-sid of algo-203 on rt1
+#
+# Expected change(s):
+# - rt1 should uninstall all Prefix-SIDs of flex-algo-203
+# - rt2 should uninstall Prefix-SIDs of rt1's flex-algo-203
+# - rt3 should uninstall Prefix-SIDs of rt1's flex-algo-203
+#
+def test_step9_mpls_lfib():
+    logger.info("Test (step 9)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          no segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+          no segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][9]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][9]["show_mpls_table.ref"])
+
+
+#
+# Step 10
+#
+# Action(s):
+# - Enable algorithm prefix-sid of algo-203 on rt1
+#
+# Expected change(s):
+# - rt1 should install all Prefix-SIDs of flex-algo-203
+# - rt2 should install Prefix-SIDs of rt1's flex-algo-203
+# - rt3 should install Prefix-SIDs of rt1's flex-algo-203
+#
+def test_step10_mpls_lfib():
+    logger.info("Test (step 10)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          segment-routing prefix 1.1.1.1/32 algorithm 203 index 301
+          segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][10]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][10]["show_mpls_table.ref"])
+
+
+#
+# Step 11
+#
+# Action(s):
+# - Update algorithm prefix-sid of algo-203 on rt1 from 301 to 311
+#
+# Expected change(s):
+# - rt2 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311
+# - rt3 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311
+#
+def test_step11_mpls_lfib():
+    logger.info("Test (step 11)")
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    tgen.gears["rt1"].vtysh_cmd(
+        """
+        configure terminal
+         router isis 1
+          segment-routing prefix 1.1.1.1/32 algorithm 203 index 311
+          segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1311
+        """)
+
+    # For Developers
+    # tgen.mininet_cli()
+    for rname in ["rt1", "rt2", "rt3"]:
+        router_compare_output(
+            rname, "show isis flex-algo",
+            outputs[rname][11]["show_isis_flex_algo.ref"])
+        router_compare_json_output(
+            rname, "show mpls table json",
+            outputs[rname][11]["show_mpls_table.ref"])
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/README.md b/tests/topotests/isis_sr_flex_algo_topo2/README.md
new file mode 100644 (file)
index 0000000..20282c4
--- /dev/null
@@ -0,0 +1,8 @@
+
+## test
+
+```
+fdk-enter rt9.pid iperf3 -s
+fdk-enter rt0.pid iperf3 -B 111.111.111.111 -c 222.222.222.222 -P20 -t 100000
+fdk-enter rt0.pid watch -n0.1 ip -s link show
+```
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh
new file mode 100755 (executable)
index 0000000..527f05b
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+if [ $# -ne 1 ]; then
+       echo "invalid command syntax" 1>&2
+       echo "Usage: $0 <0|128|129|130>" 1>&2
+       exit 1
+fi
+
+case "$1" in
+  0   ) echo ;;
+  128 ) echo ;;
+  129 ) echo ;;
+  130 ) echo ;;
+  *   )   echo "error" ; exit ;;
+esac
+
+R0=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt0.pid)
+R9=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt9.pid)
+
+set -x
+
+cat <<EOF | nsenter -a -t $R0 vtysh
+conf te
+segment-routing
+ traffic-eng
+  policy color 1 endpoint 9.9.9.9
+   name sid-algorithm
+   binding-sid 111
+   candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1
+  exit
+ exit
+exit
+EOF
+
+cat <<EOF | nsenter -a -t $R9 vtysh
+conf te
+segment-routing
+ traffic-eng
+  policy color 1 endpoint 10.10.10.10
+   name sid-algorithm
+   binding-sid 222
+   candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1
+  exit
+ exit
+exit
+EOF
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf
new file mode 100644 (file)
index 0000000..3915bec
--- /dev/null
@@ -0,0 +1,17 @@
+!
+router bgp 1
+ bgp router-id 10.10.10.10
+ no bgp network import-check
+ neighbor 9.9.9.9 remote-as 1
+ neighbor 9.9.9.9 update-source 10.10.10.10
+ !
+ address-family ipv4 unicast
+  network 10.255.0.0/24
+  neighbor 9.9.9.9 next-hop-self
+  neighbor 9.9.9.9 route-map sr-te in
+ exit-address-family
+!
+route-map sr-te permit 10
+ set sr-te color 1
+exit
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf
new file mode 100644 (file)
index 0000000..cbf2550
--- /dev/null
@@ -0,0 +1,56 @@
+password 1
+hostname rt0
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1000.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+  advertise-definition
+ !
+ flex-algo 129
+  dataplane sr-mpls
+  advertise-definition
+ !
+ flex-algo 130
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any blue
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 10.10.10.10/32 index 0
+ segment-routing prefix 10.10.10.10/32 algorithm 128 index 100
+ segment-routing prefix 10.10.10.10/32 algorithm 129 index 200
+ segment-routing prefix 10.10.10.10/32 algorithm 130 index 300
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf
new file mode 100644 (file)
index 0000000..c51b4e0
--- /dev/null
@@ -0,0 +1,20 @@
+log file pathd.log
+!
+hostname rt0
+!
+segment-routing
+ traffic-eng
+  segment-list sid-algorithm-0
+   index 10 mpls label 20009
+  exit
+  segment-list sid-algorithm-128
+   index 10 mpls label 20109
+  exit
+  segment-list sid-algorithm-129
+   index 10 mpls label 20209
+  exit
+  segment-list sid-algorithm-130
+   index 10 mpls label 20309
+  exit
+ exit
+exit
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json
new file mode 100644 (file)
index 0000000..51a1c25
--- /dev/null
@@ -0,0 +1,438 @@
+{
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20103,
+        "outLabelStack":[
+          20103
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20104":{
+    "inLabel":20104,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20104,
+        "outLabelStack":[
+          20104
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20109":{
+    "inLabel":20109,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20109,
+        "outLabelStack":[
+          20109
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20205":{
+    "inLabel":20205,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20206":{
+    "inLabel":20206,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20206,
+        "outLabelStack":[
+          20206
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20207":{
+    "inLabel":20207,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20207,
+        "outLabelStack":[
+          20207
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20208":{
+    "inLabel":20208,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20208,
+        "outLabelStack":[
+          20208
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20209":{
+    "inLabel":20209,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20209,
+        "outLabelStack":[
+          20209
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.1"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf
new file mode 100644 (file)
index 0000000..89837d4
--- /dev/null
@@ -0,0 +1,34 @@
+log file zebra.log
+!
+hostname rt0
+!
+!log stdout notifications
+!log monitor notifications
+!log commands
+!
+debug zebra packet
+debug zebra dplane
+debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface eth-rt1
+ ip address 10.1.0.10/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt5
+ ip address 10.5.0.10/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf
new file mode 100644 (file)
index 0000000..b6bba0c
--- /dev/null
@@ -0,0 +1,60 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt0
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1001.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 1
+ segment-routing prefix 1.1.1.1/32 algorithm 128 index 101
+ segment-routing prefix 1.1.1.1/32 algorithm 129 index 201
+ segment-routing prefix 1.1.1.1/32 algorithm 130 index 301
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json
new file mode 100644 (file)
index 0000000..5006625
--- /dev/null
@@ -0,0 +1,428 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.5"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20100":{
+    "inLabel":20100,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20103,
+        "outLabelStack":[
+          20103
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20103,
+        "outLabelStack":[
+          20103
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20104":{
+    "inLabel":20104,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      }
+    ]
+  },
+  "20109":{
+    "inLabel":20109,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20109,
+        "outLabelStack":[
+          20109
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20109,
+        "outLabelStack":[
+          20109
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.1.0.10"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf
new file mode 100644 (file)
index 0000000..25a9629
--- /dev/null
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt1
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-rt0
+ ip address 10.1.0.1/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt2
+ ip address 10.12.0.1/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt4
+ ip address 10.14.0.1/24
+!
+interface eth-rt5
+ ip address 10.15.0.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf
new file mode 100644 (file)
index 0000000..f051a68
--- /dev/null
@@ -0,0 +1,54 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1002.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 2
+ segment-routing prefix 2.2.2.2/32 algorithm 128 index 102
+ segment-routing prefix 2.2.2.2/32 algorithm 129 index 202
+ segment-routing prefix 2.2.2.2/32 algorithm 130 index 302
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json
new file mode 100644 (file)
index 0000000..679b41d
--- /dev/null
@@ -0,0 +1,408 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.6"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20100":{
+    "inLabel":20100,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20100,
+        "outLabelStack":[
+          20100
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20104":{
+    "inLabel":20104,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20104,
+        "outLabelStack":[
+          20104
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20104,
+        "outLabelStack":[
+          20104
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20109":{
+    "inLabel":20109,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20109,
+        "outLabelStack":[
+          20109
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.12.0.1"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf
new file mode 100644 (file)
index 0000000..d739a73
--- /dev/null
@@ -0,0 +1,37 @@
+log file zebra.log
+!
+hostname rt2
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.12.0.2/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt3
+ ip address 10.23.0.2/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt6
+ ip address 10.26.0.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf
new file mode 100644 (file)
index 0000000..644e656
--- /dev/null
@@ -0,0 +1,60 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+!debug isis events
+!debug isis route-events
+!debug isis spf-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt9
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1003.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 3
+ segment-routing prefix 3.3.3.3/32 algorithm 128 index 103
+ segment-routing prefix 3.3.3.3/32 algorithm 129 index 203
+ segment-routing prefix 3.3.3.3/32 algorithm 130 index 303
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json
new file mode 100644 (file)
index 0000000..f930faa
--- /dev/null
@@ -0,0 +1,428 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.7"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      }
+    ]
+  },
+  "20100":{
+    "inLabel":20100,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20100,
+        "outLabelStack":[
+          20100
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20100,
+        "outLabelStack":[
+          20100
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20104":{
+    "inLabel":20104,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.4"
+      }
+    ]
+  },
+  "20109":{
+    "inLabel":20109,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.23.0.2"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.9"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf
new file mode 100644 (file)
index 0000000..5c3bed0
--- /dev/null
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt3
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.23.0.3/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt4
+ ip address 10.34.0.3/24
+!
+interface eth-rt7
+ ip address 10.37.0.3/24
+!
+interface eth-rt9
+ ip address 10.39.0.3/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf
new file mode 100644 (file)
index 0000000..1ab200f
--- /dev/null
@@ -0,0 +1,52 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1004.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 4
+ segment-routing prefix 4.4.4.4/32 algorithm 128 index 104
+ segment-routing prefix 4.4.4.4/32 algorithm 129 index 204
+ segment-routing prefix 4.4.4.4/32 algorithm 130 index 304
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json
new file mode 100644 (file)
index 0000000..141e40d
--- /dev/null
@@ -0,0 +1,286 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.8"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      }
+    ]
+  },
+  "20100":{
+    "inLabel":20100,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20100,
+        "outLabelStack":[
+          20100
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.14.0.1"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      }
+    ]
+  },
+  "20109":{
+    "inLabel":20109,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20109,
+        "outLabelStack":[
+          20109
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.34.0.3"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf
new file mode 100644 (file)
index 0000000..9c00013
--- /dev/null
@@ -0,0 +1,31 @@
+log file zebra.log
+!
+hostname rt4
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt1
+ ip address 10.14.0.4/24
+!
+interface eth-rt3
+ ip address 10.34.0.4/24
+!
+interface eth-rt8
+ ip address 10.48.0.4/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf
new file mode 100644 (file)
index 0000000..54cc377
--- /dev/null
@@ -0,0 +1,60 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt0
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt1
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1005.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 5
+ segment-routing prefix 5.5.5.5/32 algorithm 128 index 105
+ segment-routing prefix 5.5.5.5/32 algorithm 129 index 205
+ segment-routing prefix 5.5.5.5/32 algorithm 130 index 305
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json
new file mode 100644 (file)
index 0000000..82ebfc0
--- /dev/null
@@ -0,0 +1,428 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.1"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.1"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.1"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.15.0.1"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20007,
+        "outLabelStack":[
+          20007
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20200":{
+    "inLabel":20200,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20206":{
+    "inLabel":20206,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20207":{
+    "inLabel":20207,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20207,
+        "outLabelStack":[
+          20207
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20207,
+        "outLabelStack":[
+          20207
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20208":{
+    "inLabel":20208,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      }
+    ]
+  },
+  "20209":{
+    "inLabel":20209,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20209,
+        "outLabelStack":[
+          20209
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20209,
+        "outLabelStack":[
+          20209
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.5.0.10"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20307,
+        "outLabelStack":[
+          20307
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.6"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf
new file mode 100644 (file)
index 0000000..61c599d
--- /dev/null
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt5
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt0
+ ip address 10.5.0.5/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt1
+ ip address 10.15.0.5/24
+!
+interface eth-rt6
+ ip address 10.56.0.5/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt8
+ ip address 10.58.0.5/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf
new file mode 100644 (file)
index 0000000..fc8660c
--- /dev/null
@@ -0,0 +1,54 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1006.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 6
+ segment-routing prefix 6.6.6.6/32 algorithm 128 index 106
+ segment-routing prefix 6.6.6.6/32 algorithm 129 index 206
+ segment-routing prefix 6.6.6.6/32 algorithm 130 index 306
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json
new file mode 100644 (file)
index 0000000..2cc7277
--- /dev/null
@@ -0,0 +1,408 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.2"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.2"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.2"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.26.0.2"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20200":{
+    "inLabel":20200,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20200,
+        "outLabelStack":[
+          20200
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20205":{
+    "inLabel":20205,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20207":{
+    "inLabel":20207,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20208":{
+    "inLabel":20208,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20208,
+        "outLabelStack":[
+          20208
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20208,
+        "outLabelStack":[
+          20208
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20209":{
+    "inLabel":20209,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20209,
+        "outLabelStack":[
+          20209
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.56.0.5"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20309,
+        "outLabelStack":[
+          20309
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.7"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf
new file mode 100644 (file)
index 0000000..b63401e
--- /dev/null
@@ -0,0 +1,37 @@
+log file zebra.log
+!
+hostname rt6
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt2
+ ip address 10.26.0.6/24
+!
+interface eth-rt5
+ ip address 10.56.0.6/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt7
+ ip address 10.67.0.6/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf
new file mode 100644 (file)
index 0000000..10dc981
--- /dev/null
@@ -0,0 +1,60 @@
+password 1
+hostname rt7
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt6
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt8
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt9
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1007.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+  dataplane sr-mpls
+ flex-algo 130
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 7.7.7.7/32 index 7
+ segment-routing prefix 7.7.7.7/32 algorithm 128 index 107
+ segment-routing prefix 7.7.7.7/32 algorithm 129 index 207
+ segment-routing prefix 7.7.7.7/32 algorithm 130 index 307
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json
new file mode 100644 (file)
index 0000000..aeaa604
--- /dev/null
@@ -0,0 +1,428 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.3"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.3"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.3"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.37.0.3"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      }
+    ]
+  },
+  "20200":{
+    "inLabel":20200,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20200,
+        "outLabelStack":[
+          20200
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20200,
+        "outLabelStack":[
+          20200
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20205":{
+    "inLabel":20205,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20205,
+        "outLabelStack":[
+          20205
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20205,
+        "outLabelStack":[
+          20205
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20206":{
+    "inLabel":20206,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20208":{
+    "inLabel":20208,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.8"
+      }
+    ]
+  },
+  "20209":{
+    "inLabel":20209,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20303,
+        "outLabelStack":[
+          20303
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.67.0.6"
+      }
+    ]
+  },
+  "20309":{
+    "inLabel":20309,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.9"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf
new file mode 100644 (file)
index 0000000..b5a28c7
--- /dev/null
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname rt7
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 7.7.7.7/32
+!
+interface eth-rt3
+ ip address 10.37.0.7/24
+!
+interface eth-rt6
+ ip address 10.67.0.7/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt8
+ ip address 10.78.0.7/24
+!
+interface eth-rt9
+ ip address 10.79.0.7/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf
new file mode 100644 (file)
index 0000000..4ca45a8
--- /dev/null
@@ -0,0 +1,50 @@
+password 1
+hostname rt8
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt5
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1008.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 129
+  dataplane sr-mpls
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 8.8.8.8/32 index 8
+ segment-routing prefix 8.8.8.8/32 algorithm 128 index 108
+ segment-routing prefix 8.8.8.8/32 algorithm 129 index 208
+ segment-routing prefix 8.8.8.8/32 algorithm 130 index 308
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json
new file mode 100644 (file)
index 0000000..27470b7
--- /dev/null
@@ -0,0 +1,286 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.4"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.4"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20003,
+        "outLabelStack":[
+          20003
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.4"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.48.0.4"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      }
+    ]
+  },
+  "20009":{
+    "inLabel":20009,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20009,
+        "outLabelStack":[
+          20009
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      }
+    ]
+  },
+  "20200":{
+    "inLabel":20200,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20200,
+        "outLabelStack":[
+          20200
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20205":{
+    "inLabel":20205,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20206":{
+    "inLabel":20206,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20206,
+        "outLabelStack":[
+          20206
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20206,
+        "outLabelStack":[
+          20206
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.58.0.5"
+      }
+    ]
+  },
+  "20207":{
+    "inLabel":20207,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      }
+    ]
+  },
+  "20209":{
+    "inLabel":20209,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20209,
+        "outLabelStack":[
+          20209
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.78.0.7"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf
new file mode 100644 (file)
index 0000000..dd63f8c
--- /dev/null
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname rt8
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+interface lo
+ ip address 8.8.8.8/32
+!
+interface eth-rt4
+ ip address 10.48.0.8/24
+!
+interface eth-rt5
+ ip address 10.58.0.8/24
+!
+interface eth-rt7
+ ip address 10.78.0.8/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf
new file mode 100644 (file)
index 0000000..386d811
--- /dev/null
@@ -0,0 +1,17 @@
+!
+router bgp 1
+ bgp router-id 9.9.9.9
+ no bgp network import-check
+ neighbor 10.10.10.10 remote-as 1
+ neighbor 10.10.10.10 update-source 9.9.9.9
+ !
+ address-family ipv4 unicast
+  network 10.255.9.0/24
+  neighbor 10.10.10.10 next-hop-self
+  neighbor 10.10.10.10 route-map sr-te in
+ exit-address-family
+!
+route-map sr-te permit 10
+ set sr-te color 1
+exit
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf
new file mode 100644 (file)
index 0000000..89eab27
--- /dev/null
@@ -0,0 +1,56 @@
+password 1
+hostname rt9
+log file isisd.log
+!
+!debug isis events
+!debug isis spf-events
+!debug isis route-events
+!debug isis sr-events
+!debug isis lsp-gen
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface eth-rt7
+ ip router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.1009.00
+ is-type level-1
+ topology ipv6-unicast
+ mpls-te on
+ !
+ flex-algo 128
+  dataplane sr-mpls
+  advertise-definition
+ !
+ flex-algo 129
+  dataplane sr-mpls
+  advertise-definition
+ !
+ flex-algo 130
+  dataplane sr-mpls
+  advertise-definition
+  affinity include-any blue
+ !
+ segment-routing on
+ segment-routing global-block 20000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 9.9.9.9/32 index 9
+ segment-routing prefix 9.9.9.9/32 algorithm 128 index 109
+ segment-routing prefix 9.9.9.9/32 algorithm 129 index 209
+ segment-routing prefix 9.9.9.9/32 algorithm 130 index 309
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf
new file mode 100644 (file)
index 0000000..3f9a8d9
--- /dev/null
@@ -0,0 +1,20 @@
+log file pathd.log
+!
+hostname rt9
+!
+segment-routing
+ traffic-eng
+  segment-list sid-algorithm-0
+   index 10 mpls label 20000
+  exit
+  segment-list sid-algorithm-128
+   index 10 mpls label 20100
+  exit
+  segment-list sid-algorithm-129
+   index 10 mpls label 20200
+  exit
+  segment-list sid-algorithm-130
+   index 10 mpls label 20300
+  exit
+ exit
+exit
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json
new file mode 100644 (file)
index 0000000..e98680c
--- /dev/null
@@ -0,0 +1,438 @@
+{
+  "20000":{
+    "inLabel":20000,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20000,
+        "outLabelStack":[
+          20000
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20001":{
+    "inLabel":20001,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20001,
+        "outLabelStack":[
+          20001
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20002":{
+    "inLabel":20002,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20002,
+        "outLabelStack":[
+          20002
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20003":{
+    "inLabel":20003,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20004":{
+    "inLabel":20004,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20004,
+        "outLabelStack":[
+          20004
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20005":{
+    "inLabel":20005,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20005,
+        "outLabelStack":[
+          20005
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20006":{
+    "inLabel":20006,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20006,
+        "outLabelStack":[
+          20006
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20007":{
+    "inLabel":20007,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20008":{
+    "inLabel":20008,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20008,
+        "outLabelStack":[
+          20008
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20100":{
+    "inLabel":20100,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20100,
+        "outLabelStack":[
+          20100
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20101":{
+    "inLabel":20101,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20101,
+        "outLabelStack":[
+          20101
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20102":{
+    "inLabel":20102,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20102,
+        "outLabelStack":[
+          20102
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20103":{
+    "inLabel":20103,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20104":{
+    "inLabel":20104,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20104,
+        "outLabelStack":[
+          20104
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20200":{
+    "inLabel":20200,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20200,
+        "outLabelStack":[
+          20200
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20205":{
+    "inLabel":20205,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20205,
+        "outLabelStack":[
+          20205
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20206":{
+    "inLabel":20206,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20206,
+        "outLabelStack":[
+          20206
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20207":{
+    "inLabel":20207,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20208":{
+    "inLabel":20208,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20208,
+        "outLabelStack":[
+          20208
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20300":{
+    "inLabel":20300,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      },
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20300,
+        "outLabelStack":[
+          20300
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20301":{
+    "inLabel":20301,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20301,
+        "outLabelStack":[
+          20301
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20302":{
+    "inLabel":20302,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20302,
+        "outLabelStack":[
+          20302
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20303":{
+    "inLabel":20303,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.39.0.3"
+      }
+    ]
+  },
+  "20305":{
+    "inLabel":20305,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20305,
+        "outLabelStack":[
+          20305
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20306":{
+    "inLabel":20306,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":20306,
+        "outLabelStack":[
+          20306
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  },
+  "20307":{
+    "inLabel":20307,
+    "installed":true,
+    "nexthops":[
+      {
+        "type":"SR (IS-IS)",
+        "outLabel":3,
+        "outLabelStack":[
+          3
+        ],
+        "distance":150,
+        "installed":true,
+        "nexthop":"10.79.0.7"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf
new file mode 100644 (file)
index 0000000..378a196
--- /dev/null
@@ -0,0 +1,34 @@
+log file zebra.log
+!
+hostname rt9
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug zebra packet
+!debug zebra dplane
+!debug zebra kernel
+!
+affinity-map blue bit-position 0
+!
+interface lo
+ ip address 9.9.9.9/32
+!
+interface eth-rt3
+ ip address 10.39.0.9/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+interface eth-rt7
+ ip address 10.79.0.9/24
+ link-params
+  affinity blue
+ exit-link-params
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py
new file mode 100755 (executable)
index 0000000..6a5f81d
--- /dev/null
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com>
+# Copyright 2023 6WIND S.A.
+
+"""
+test_isis_sr_flex_algo_topo2.py:
+
+[+] Flex-Algos 128
+[+] Flex-Algos 129
+[+] Flex-Algos 130 include-any blue
+
+            +--------+                  +--------+
+            |        |                  |        |
+            |  RT1   |------------------|  RT2   |
+            |        |                  |        |
+            +--------+                  +--------+
+           /     |    \\                     |    \\
+          /      |     \\                    |     \\
++--------+       |      \\                   |      \\
+|        |       |       +--------+          |       +--------+
+|  RT0   |       |       |        |          |       |        |
+|        |       |       |  RT4   |------------------|  RT3   |
++--------+       |       |        |          |       |        |
+          \\     |       +--------+          |       +--------+
+           \\    |           |               |            |    \\
+            +--------+       |          +--------+        |     \\
+            |        |       |          |        |        |      +--------+
+            |  RT5   |-------|----------|  RT6   |        |      |        |
+            |        |       |          |        |        |      |  RT9   |
+            +--------+       |          +--------+        |      |        |
+                      \\     |                    \\      |      +--------+
+                       \\    |                     \\     |     /
+                        \\   |                      \\    |    /
+                         +--------+                  +--------+
+                         |        |                  |        |
+                         |  RT8   |------------------|  RT7   |
+                         |        |                  |        |
+                         +--------+                  +--------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import time
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.isisd]
+
+
+def build_topo(tgen):
+    "Build function"
+
+    routers = []
+    for i in range(0, 10):
+        rt = tgen.add_router("rt{}".format(i))
+        rt.run("sysctl -w net.ipv4.fib_multipath_hash_policy=1")
+
+    def connect_routers(tgen, left_idx, right_idx):
+        left = "rt{}".format(left_idx)
+        right = "rt{}".format(right_idx)
+        switch = tgen.add_switch("s-{}-{}".format(left, right))
+        switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right))
+        switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left))
+        l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx)
+        tgen.gears[left].run("ip link set eth-{} down".format(right))
+        tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr))
+        tgen.gears[left].run("ip link set eth-{} up".format(right))
+        tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right))
+        r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx)
+        tgen.gears[right].run("ip link set eth-{} down".format(left))
+        tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr))
+        tgen.gears[right].run("ip link set eth-{} up".format(left))
+        tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left))
+
+    connect_routers(tgen, 0, 1)
+    connect_routers(tgen, 0, 5)
+    connect_routers(tgen, 1, 2)
+    connect_routers(tgen, 1, 4)
+    connect_routers(tgen, 1, 5)
+    connect_routers(tgen, 2, 3)
+    connect_routers(tgen, 2, 6)
+    connect_routers(tgen, 3, 4)
+    connect_routers(tgen, 3, 7)
+    connect_routers(tgen, 3, 9)
+    connect_routers(tgen, 4, 8)
+    connect_routers(tgen, 5, 6)
+    connect_routers(tgen, 5, 8)
+    connect_routers(tgen, 6, 7)
+    connect_routers(tgen, 7, 8)
+    connect_routers(tgen, 7, 9)
+
+
+def setup_module(mod):
+    "Sets up the pytest environment"
+    tgen = Topogen(build_topo, mod.__name__)
+    frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+    if not os.path.isfile(os.path.join(frrdir, "pathd")):
+        pytest.skip("pathd daemon wasn't built")
+    tgen.start_topology()
+    router_list = tgen.routers()
+
+    # For all registered routers, load the zebra configuration file
+    for rname, router in router_list.items():
+        router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)))
+        router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)))
+        if rname in ["rt0", "rt9"]:
+            router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)))
+            router.load_config( TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname)))
+            router.run("ip link add dum0 type dummy")
+            router.run("ip link set dum0 up")
+            if rname == "rt0":
+                router.run("ip addr add 10.255.0.1/24 dev dum0")
+            elif rname == "rt9":
+                router.run("ip addr add 10.255.9.1/24 dev dum0")
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def setup_testcase(msg):
+    logger.info(msg)
+    tgen = get_topogen()
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    return tgen
+
+def open_json_file(filename):
+    try:
+        with open(filename, "r") as f:
+            return json.load(f)
+    except IOError:
+        assert False, "Could not read file {}".format(filename)
+
+
+def check_rib(name, cmd, expected_file):
+    def _check(name, cmd, expected_file):
+        logger.info("polling")
+        tgen = get_topogen()
+        router = tgen.gears[name]
+        output = json.loads(router.vtysh_cmd(cmd))
+        expected = open_json_file("{}/{}".format(CWD, expected_file))
+        return topotest.json_cmp(output, expected)
+
+    logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file))
+    tgen = get_topogen()
+    func = partial(_check, name, cmd, expected_file)
+    success, result = topotest.run_and_expect(func, None, count=120, wait=0.5)
+    assert result is None, "Failed"
+
+
+def test_rib():
+    check_rib("rt0", "show mpls table json", "rt0/step1/route.json")
+    check_rib("rt1", "show mpls table json", "rt1/step1/route.json")
+    check_rib("rt2", "show mpls table json", "rt2/step1/route.json")
+    check_rib("rt3", "show mpls table json", "rt3/step1/route.json")
+    check_rib("rt4", "show mpls table json", "rt4/step1/route.json")
+    check_rib("rt5", "show mpls table json", "rt5/step1/route.json")
+    check_rib("rt6", "show mpls table json", "rt6/step1/route.json")
+    check_rib("rt7", "show mpls table json", "rt7/step1/route.json")
+    check_rib("rt8", "show mpls table json", "rt8/step1/route.json")
+    check_rib("rt9", "show mpls table json", "rt9/step1/route.json")
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index d7711b7e59116b11cf95f9f0a51b6053ecf2ec2b..b70626e85db60a7e99f1fb7c208a48fd1594c5c3 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -91,7 +91,7 @@
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 7d017b3430051ccf7e18df48b5f8fc0461c584b0..f029e5aab018796be9c8c2eb625de55d002fbb31 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 7b2074b69c772d3eb199a89f7ba39d94a024687d..aa2bafb15a76698b34aa9a5067e23691662da67c 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
@@ -91,7 +91,7 @@
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 528138477a53389cf23a378ff3c1035f30657b10..de6d108bb363905ccc273170aa6f2d985e3fa9e2 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -94,7 +94,7 @@
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 528138477a53389cf23a378ff3c1035f30657b10..de6d108bb363905ccc273170aa6f2d985e3fa9e2 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -94,7 +94,7 @@
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 72e441d186125c217567843ade30948c802dca1a..7daee99297a3e6d2cac7cafb4a7aad72f66c8f19 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -94,7 +94,7 @@
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index a5f50c3ebab9753a8c963356ba73e66caad73d1a..289eb1ebc2e96fe862e52190de9d575cbf48567f 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
@@ -94,7 +94,7 @@
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 447febce488297d57303bbf7cdd38c51871931a9..18eb42fd326f130085df228827e4e78463672aad 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
index 510e034eba4fbcaaf2b6b4f70b057d3a80090692..ede36cf93dd9bcb44d6e7e85ccf69ab64eace6d5 100644 (file)
@@ -50,7 +50,7 @@
     ],
     "edges":[
       {
-        "edge-id":1,
+        "edge-id":"2001:db8::1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":2,
+        "edge-id":"2001:db8::2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":65537,
+        "edge-id":"2001:db8:1::1:1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":65538,
+        "edge-id":"2001:db8:1::1:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196610,
+        "edge-id":"2001:db8:3::3:2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":196611,
+        "edge-id":"2001:db8:3::3:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":196612,
+        "edge-id":"2001:db8:5::3:4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
         ]
       },
       {
-        "edge-id":262147,
+        "edge-id":"2001:db8:5::4:3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0001",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167772931,
+        "edge-id":"10.0.3.3",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0003",
         }
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0002",
         }
       },
       {
-        "edge-id":167773188,
+        "edge-id":"10.0.4.4",
         "status":"Sync",
         "origin":"ISIS_L2",
         "advertised-router":"0000.0000.0004",
diff --git a/tests/topotests/kinds.yaml b/tests/topotests/kinds.yaml
new file mode 100644 (file)
index 0000000..127790e
--- /dev/null
@@ -0,0 +1,30 @@
+version: 1
+kinds:
+  - name: frr
+    cmd: |
+      chown frr:frr -R /var/run/frr
+      chown frr:frr -R /var/log/frr
+      /usr/lib/frr/frrinit.sh start
+      tail -F /var/log/frr/frr.log
+    cleanup-cmd: |
+      /usr/lib/frr/frrinit.sh stop
+    volumes:
+      - "./%NAME%:/etc/frr"
+      - "%RUNDIR%/var.log.frr:/var/log/frr"
+      - "%RUNDIR%/var.run.frr:/var/run/frr"
+    cap-add:
+      - SYS_ADMIN
+      - AUDIT_WRITE
+    merge: ["volumes"]
+cli:
+  commands:
+    - name: ""
+      exec: "vtysh -c '{}'"
+      format: "[ROUTER ...] COMMAND"
+      help: "execute vtysh COMMAND on the router[s]"
+      kinds: ["frr"]
+    - name: "vtysh"
+      exec: "/usr/bin/vtysh"
+      format: "vtysh ROUTER [ROUTER ...]"
+      new-window: true
+      kinds: ["frr"]
index 63281e9be33d0b27f5a216ca993bae6b515ffd45..c1c231de3d3c37d151c2413d351063cb1e3a375f 100644 (file)
@@ -2,9 +2,9 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.1.2",
+        "ifaceAddress":"10.0.1.2",
         "ifaceName":"r1-eth0:10.0.1.1"
       }
     ]
index f361d605ce30478c42ff0e6524ad1da8d1bcb3c8..ee69af5e23771c7e720eaaad93de9c3cbd9eecc4 100644 (file)
@@ -2,25 +2,25 @@
   "neighbors":{
     "1.1.1.1":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.1.1",
+        "ifaceAddress":"10.0.1.1",
         "ifaceName":"r2-eth0:10.0.1.2"
       }
     ],
     "3.3.3.3":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.2.3",
+        "ifaceAddress":"10.0.2.3",
         "ifaceName":"r2-eth1:10.0.2.2"
       }
     ],
     "4.4.4.4":[
       {
-        "priority":3,
+        "nbrPriority":3,
         "converged":"Full",
-        "address":"10.0.2.4",
+        "ifaceAddress":"10.0.2.4",
         "ifaceName":"r2-eth1:10.0.2.2"
       }
     ]
index 38794357ff5b24472bc0705cf655e03bbea14fb2..3f76542e9421c8e48e369562321650dec044e574 100644 (file)
@@ -2,17 +2,17 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.2",
+        "ifaceAddress":"10.0.2.2",
         "ifaceName":"r3-eth0:10.0.2.3"
       }
     ],
     "4.4.4.4":[
       {
-        "priority":3,
+        "nbrPriority":3,
         "converged":"Full",
-        "address":"10.0.2.4",
+        "ifaceAddress":"10.0.2.4",
         "ifaceName":"r3-eth0:10.0.2.3"
       }
     ]
index fccca693b92b03ee64e7b37f8e85d153f274fc14..5395cd25c990c22c5de8bf08665baa2b4293c18f 100644 (file)
@@ -3,17 +3,17 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.2",
+        "ifaceAddress":"10.0.2.2",
         "ifaceName":"r4-eth0:10.0.2.4"
       }
     ],
     "3.3.3.3":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.2.3",
+        "ifaceAddress":"10.0.2.3",
         "ifaceName":"r4-eth0:10.0.2.4"
       }
     ]
index 63281e9be33d0b27f5a216ca993bae6b515ffd45..c1c231de3d3c37d151c2413d351063cb1e3a375f 100644 (file)
@@ -2,9 +2,9 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.1.2",
+        "ifaceAddress":"10.0.1.2",
         "ifaceName":"r1-eth0:10.0.1.1"
       }
     ]
index f361d605ce30478c42ff0e6524ad1da8d1bcb3c8..ee69af5e23771c7e720eaaad93de9c3cbd9eecc4 100644 (file)
@@ -2,25 +2,25 @@
   "neighbors":{
     "1.1.1.1":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.1.1",
+        "ifaceAddress":"10.0.1.1",
         "ifaceName":"r2-eth0:10.0.1.2"
       }
     ],
     "3.3.3.3":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.2.3",
+        "ifaceAddress":"10.0.2.3",
         "ifaceName":"r2-eth1:10.0.2.2"
       }
     ],
     "4.4.4.4":[
       {
-        "priority":3,
+        "nbrPriority":3,
         "converged":"Full",
-        "address":"10.0.2.4",
+        "ifaceAddress":"10.0.2.4",
         "ifaceName":"r2-eth1:10.0.2.2"
       }
     ]
index 38794357ff5b24472bc0705cf655e03bbea14fb2..3f76542e9421c8e48e369562321650dec044e574 100644 (file)
@@ -2,17 +2,17 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.2",
+        "ifaceAddress":"10.0.2.2",
         "ifaceName":"r3-eth0:10.0.2.3"
       }
     ],
     "4.4.4.4":[
       {
-        "priority":3,
+        "nbrPriority":3,
         "converged":"Full",
-        "address":"10.0.2.4",
+        "ifaceAddress":"10.0.2.4",
         "ifaceName":"r3-eth0:10.0.2.3"
       }
     ]
index fccca693b92b03ee64e7b37f8e85d153f274fc14..5395cd25c990c22c5de8bf08665baa2b4293c18f 100644 (file)
@@ -3,17 +3,17 @@
   "neighbors":{
     "2.2.2.2":[
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.2",
+        "ifaceAddress":"10.0.2.2",
         "ifaceName":"r4-eth0:10.0.2.4"
       }
     ],
     "3.3.3.3":[
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.2.3",
+        "ifaceAddress":"10.0.2.3",
         "ifaceName":"r4-eth0:10.0.2.4"
       }
     ]
index 7efde22f3f3d6e636efb67e28af125b13f3baf41..e25523d18d7278e571573b06ae7e595843e94386 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "2.2.2.2": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.1.2",
+        "ifaceAddress": "10.0.1.2",
         "ifaceName": "r1-eth1:10.0.1.1",
-        "requestCounter": 0
+        "linkStateRequestListCounter": 0
       }
     ],
     "3.3.3.3": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.3",
+        "ifaceAddress": "10.0.2.3",
         "ifaceName": "r1-eth2:10.0.2.1",
-        "requestCounter": 0
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index 5bea193e01a276f69d84dfc0e6c0d96ea6acd09d..fa2ea86d67ea320abd23510732e3646b0f16edfb 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "1.1.1.1": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.1.1",
+        "ifaceAddress":"10.0.1.1",
         "ifaceName":"r2-eth1:10.0.1.2",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "3.3.3.3": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.3.3",
+        "ifaceAddress":"10.0.3.3",
         "ifaceName":"r2-eth2:10.0.3.2",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ]
   }
index 9966297d8a7135a2402e15e62b549f9c32630f6a..bf77e088d5df306bb67046fd0553f6dc84b707fc 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "1.1.1.1": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.1",
+        "ifaceAddress":"10.0.2.1",
         "ifaceName":"r3-eth1:10.0.2.3",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "2.2.2.2": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.3.2",
+        "ifaceAddress":"10.0.3.2",
         "ifaceName":"r3-eth2:10.0.3.3",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ]
   }
index d9192f1104a49965a40dc9a8126b72c5892ee5de..f47c2dfad771bc98ab2eaf76d22e47991c73a574 100644 (file)
@@ -2,12 +2,12 @@
   "neighbors": {
     "2.2.2.2": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.1.2",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.1.2",
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index ea78592bd511fc0b69b5b42ac60386bcb0441ce3..901282f876e61148c579e44e8b8b1e1dbaccc349 100644 (file)
@@ -2,40 +2,40 @@
   "neighbors": {
     "1.1.1.1": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.1.1",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.1.1",
+        "linkStateRequestListCounter": 0
       }
     ],
     "3.3.3.3": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.3",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.3",
+        "linkStateRequestListCounter": 0
       },
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.3.3",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.3.3",
+        "linkStateRequestListCounter": 0
       }
     ],
     "4.4.4.4": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.4",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.4",
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index d3c50247eae7d92cdae1b3281eb05d7510306537..164040ae3e029e135aa6d58128898145cba47d80 100644 (file)
@@ -2,30 +2,30 @@
   "neighbors": {
     "2.2.2.2": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.2",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.2",
+        "linkStateRequestListCounter": 0
       },
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.3.2",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.3.2",
+        "linkStateRequestListCounter": 0
       }
     ],
     "4.4.4.4": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.4",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.4",
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index 20751a288447640fc7708f4eeec857b416ef9a21..98c759a6ff3a7ed5565ac9b9dee678d5d17ad431 100644 (file)
@@ -2,22 +2,22 @@
   "neighbors": {
     "2.2.2.2": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.2",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.2",
+        "linkStateRequestListCounter": 0
       }
     ],
     "3.3.3.3": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 1,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 1,
         "converged": "Full",
-        "address": "10.0.2.3",
-        "requestCounter": 0
+        "ifaceAddress": "10.0.2.3",
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index 90c8195416f16d85ec6ac260b364566322a3b4c6..9acb4f7b8c2f98882b12f8356ae971e1b69d0520 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "2.2.2.2": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 2,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 2,
         "converged": "Full",
-        "address": "10.0.1.2",
+        "ifaceAddress": "10.0.1.2",
         "ifaceName": "r1-eth1:10.0.1.1",
-        "requestCounter": 0
+        "linkStateRequestListCounter": 0
       }
     ],
     "3.3.3.3": [
       {
-        "dbSummaryCounter": 0,
-        "retransmitCounter": 0,
-        "priority": 2,
+        "databaseSummaryListCounter": 0,
+        "linkStateRetransmissionListCounter": 0,
+        "nbrPriority": 2,
         "converged": "Full",
-        "address": "10.0.2.3",
+        "ifaceAddress": "10.0.2.3",
         "ifaceName": "r1-eth2:10.0.2.1",
-        "requestCounter": 0
+        "linkStateRequestListCounter": 0
       }
     ]
   }
index 29dde53c6d7a3f99339df1e34eae9fee29124bce..66341999020bb20912fc071b614845d811a55f5a 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "1.1.1.1": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.1.1",
+        "ifaceAddress":"10.0.1.1",
         "ifaceName":"r2-eth1:10.0.1.2",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "3.3.3.3": [
       {
-        "priority":2,
+        "nbrPriority":2,
         "converged":"Full",
-        "address":"10.0.3.3",
+        "ifaceAddress":"10.0.3.3",
         "ifaceName":"r2-eth2:10.0.3.2",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ]
   }
index 9966297d8a7135a2402e15e62b549f9c32630f6a..bf77e088d5df306bb67046fd0553f6dc84b707fc 100644 (file)
@@ -2,24 +2,24 @@
   "neighbors": {
     "1.1.1.1": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.2.1",
+        "ifaceAddress":"10.0.2.1",
         "ifaceName":"r3-eth1:10.0.2.3",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "2.2.2.2": [
       {
-        "priority":1,
+        "nbrPriority":1,
         "converged":"Full",
-        "address":"10.0.3.2",
+        "ifaceAddress":"10.0.3.2",
         "ifaceName":"r3-eth2:10.0.3.3",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ]
   }
index e5a1e758379c1aa312c9fc00eb0678e1f95e38f4..d19d8db75cbddf81b2356352389c9d9d4d65e15c 100644 (file)
@@ -492,7 +492,7 @@ def save_initial_config_on_routers(tgen):
     # Get all running configs in parallel
     procs = {}
     for rname in router_list:
-        logger.info("Fetching running config for router %s", rname)
+        logger.debug("Fetching running config for router %s", rname)
         procs[rname] = router_list[rname].popen(
             ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
             stdin=None,
@@ -548,7 +548,7 @@ def reset_config_on_routers(tgen, routerName=None):
     #
     procs = {}
     for rname in router_list:
-        logger.info("Fetching running config for router %s", rname)
+        logger.debug("Fetching running config for router %s", rname)
         procs[rname] = router_list[rname].popen(
             ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
             stdin=None,
@@ -570,7 +570,7 @@ def reset_config_on_routers(tgen, routerName=None):
     #
     procs = {}
     for rname in router_list:
-        logger.info(
+        logger.debug(
             "Generating delta for router %s to new configuration (gen %d)", rname, gen
         )
         procs[rname] = tgen.net.popen(
@@ -599,7 +599,7 @@ def reset_config_on_routers(tgen, routerName=None):
     #
     procs = {}
     for rname in router_list:
-        logger.info("Applying delta config on router %s", rname)
+        logger.debug("Applying delta config on router %s", rname)
 
         procs[rname] = router_list[rname].popen(
             ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)],
@@ -611,7 +611,7 @@ def reset_config_on_routers(tgen, routerName=None):
         output, _ = p.communicate()
         vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen))
         if not p.returncode:
-            router_list[rname].logger.info(
+            router_list[rname].logger.debug(
                 '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
                     vtysh_command, output
                 )
@@ -640,7 +640,7 @@ def reset_config_on_routers(tgen, routerName=None):
     if show_router_config:
         procs = {}
         for rname in router_list:
-            logger.info("Fetching running config for router %s", rname)
+            logger.debug("Fetching running config for router %s", rname)
             procs[rname] = router_list[rname].popen(
                 ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
                 stdin=None,
@@ -657,7 +657,7 @@ def reset_config_on_routers(tgen, routerName=None):
                     output,
                 )
             else:
-                logger.info(
+                logger.debug(
                     "Configuration on router %s after reset:\n%s", rname, output
                 )
 
@@ -742,7 +742,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
             frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname)
             with open(frr_cfg_file, "r+") as cfg:
                 data = cfg.read()
-                logger.info(
+                logger.debug(
                     "Applying following configuration on router %s (gen: %d):\n%s",
                     rname,
                     gen,
@@ -775,7 +775,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
         frr_cfg_file = frr_cfg_file_fmt.format(rname)
         vtysh_command = "vtysh -f " + frr_cfg_file
         if not p.returncode:
-            router_list[rname].logger.info(
+            router_list[rname].logger.debug(
                 '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
                     vtysh_command, output
                 )
@@ -821,7 +821,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False):
                     output,
                 )
             else:
-                logger.info("New configuration for router %s:\n%s", rname, output)
+                logger.debug("New configuration for router %s:\n%s", rname, output)
 
     logger.debug("Exiting API: load_config_to_routers")
     return not errors
@@ -957,10 +957,10 @@ def generate_support_bundle():
         bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None)
 
     for rname, rnode in router_list.items():
-        logger.info("Waiting on support bundle for %s", rname)
+        logger.debug("Waiting on support bundle for %s", rname)
         output, error = bundle_procs[rname].communicate()
         if output:
-            logger.info(
+            logger.debug(
                 "Output from collecting support bundle for %s:\n%s", rname, output
             )
         if error:
@@ -1234,15 +1234,15 @@ def add_interfaces_to_vlan(tgen, input_dict):
                         cmd = "ip link add link {} name {} type vlan id {}".format(
                             interface, vlan_intf, vlan
                         )
-                        logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+                        logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
                         result = rnode.run(cmd)
-                        logger.info("result %s", result)
+                        logger.debug("result %s", result)
 
                         # Bringing interface up
                         cmd = "ip link set {} up".format(vlan_intf)
-                        logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+                        logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
                         result = rnode.run(cmd)
-                        logger.info("result %s", result)
+                        logger.debug("result %s", result)
 
                         # Assigning IP address
                         ifaddr = ipaddress.ip_interface(
@@ -1254,9 +1254,9 @@ def add_interfaces_to_vlan(tgen, input_dict):
                         cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format(
                             ifaddr.version, vlan_intf, ifaddr
                         )
-                        logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+                        logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
                         result = rnode.run(cmd)
-                        logger.info("result %s", result)
+                        logger.debug("result %s", result)
 
 
 def tcpdump_capture_start(
@@ -1567,12 +1567,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
                             vrf["name"], vrf["id"]
                         )
 
-                        logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd)
+                        logger.debug(
+                            "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd
+                        )
                         rnode.run(cmd)
 
                         # Kernel cmd - Bring down VRF
                         cmd = "ip link set dev {} down".format(name)
-                        logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd)
+                        logger.debug(
+                            "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd
+                        )
                         rnode.run(cmd)
 
                     else:
@@ -1581,14 +1585,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
                             cmd = "ip link add {} type vrf table {}".format(
                                 name, table_id
                             )
-                            logger.info(
+                            logger.debug(
                                 "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd
                             )
                             rnode.run(cmd)
 
                             # Kernel cmd - Bring up VRF
                             cmd = "ip link set dev {} up".format(name)
-                            logger.info(
+                            logger.debug(
                                 "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd
                             )
                             rnode.run(cmd)
@@ -1616,7 +1620,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
                                         interface_name, _vrf
                                     )
 
-                                    logger.info(
+                                    logger.debug(
                                         "[DUT: %s]: Running" " kernel cmd [%s]",
                                         c_router,
                                         cmd,
@@ -1683,7 +1687,7 @@ def create_interface_in_kernel(
     cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format(
         ifaddr.version, name, ifaddr
     )
-    logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+    logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
     rnode.run(cmd)
 
     if vrf:
@@ -1715,7 +1719,7 @@ def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False
         action = "down"
         cmd = "{} {} {}".format(cmd, intf_name, action)
 
-    logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+    logger.debug("[DUT: %s]: Running command: %s", dut, cmd)
     rnode.run(cmd)
 
 
@@ -1968,7 +1972,7 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
             )
 
             if initial_wait > 0:
-                logger.info("Waiting for [%s]s as initial delay", initial_wait)
+                logger.debug("Waiting for [%s]s as initial delay", initial_wait)
                 sleep(initial_wait)
 
             invert_logic = not _expected
@@ -2027,13 +2031,13 @@ def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
                         return saved_failure
 
                 if saved_failure:
-                    logger.info(
+                    logger.debug(
                         "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short",
                         retry_sleep,
                         seconds_left,
                     )
                 else:
-                    logger.info(
+                    logger.debug(
                         "Sleeping %ds until next retry with %.1f retry time left",
                         retry_sleep,
                         seconds_left,
@@ -3357,7 +3361,19 @@ def socat_send_mld_join(
 
         # Run socat command to send IGMP join
         logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd))
-        output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd))
+        output = rnode.run("set +m; {} echo $!".format(socat_cmd))
+
+        # Check if socat join process is running
+        if output:
+            pid = output.split()[0]
+            rnode.run("touch /var/run/frr/socat_join.pid")
+            rnode.run("echo %s >> /var/run/frr/socat_join.pid" % pid)
+        else:
+            errormsg = "Socat join is not sent for {}. Error {}".format(
+                mld_group, output
+            )
+            logger.error(output)
+            return errormsg
 
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return True
@@ -3415,7 +3431,7 @@ def socat_send_pim6_traffic(
         if multicast_hops:
             socat_cmd += "multicast-hops=255'"
 
-        socat_cmd += " &>{}/socat.logs &".format(tgen.logdir)
+        socat_cmd += " >{}/socat.logs &".format(tgen.logdir)
 
         # Run socat command to send pim6 traffic
         logger.info(
@@ -3435,7 +3451,20 @@ def socat_send_pim6_traffic(
             )
 
         rnode.run("chmod 755 {}".format(traffic_shell_script))
-        output = rnode.run("{} &> /dev/null".format(traffic_shell_script))
+        output = rnode.run("{} &>/dev/null & echo $!".format(traffic_shell_script))
+
+        # Check if socat traffic process is running
+        if output:
+            pid = output.split()[0]
+            rnode.run("touch /var/run/frr/socat_traffic.pid")
+            rnode.run("echo %s >> /var/run/frr/socat_traffic.pid" % pid)
+
+        else:
+            errormsg = "Socat traffic is not sent for {}. Error {}".format(
+                mld_group, output
+            )
+            logger.error(output)
+            return errormsg
 
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return True
@@ -3465,18 +3494,30 @@ def kill_socat(tgen, dut=None, action=None):
         if dut is not None and router != dut:
             continue
 
+        traffic_shell_script = "{}/{}/traffic.sh".format(tgen.logdir, router)
+        pid_socat_join = rnode.run("cat /var/run/frr/socat_join.pid")
+        pid_socat_traffic = rnode.run("cat /var/run/frr/socat_traffic.pid")
         if action == "remove_mld_join":
-            cmd = "ps -ef | grep socat | grep UDP6-RECV | grep {}".format(router)
+            pids = pid_socat_join
         elif action == "remove_mld_traffic":
-            cmd = "ps -ef | grep socat | grep UDP6-SEND | grep {}".format(router)
+            pids = pid_socat_traffic
         else:
-            cmd = "ps -ef | grep socat".format(router)
-
-        awk_cmd = "awk -F' ' '{print $2}' | xargs kill -9 &>/dev/null &"
-        cmd = "{} | {}".format(cmd, awk_cmd)
+            pids = "\n".join([pid_socat_join, pid_socat_traffic])
 
-        logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd))
-        rnode.run(cmd)
+        if os.path.exists(traffic_shell_script):
+            cmd = (
+                "ps -ef | grep %s | awk -F' ' '{print $2}' | xargs kill -9"
+                % traffic_shell_script
+            )
+            logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd))
+            rnode.run(cmd)
+
+        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))
 
index 6457bbefdd54a3f3fdf86838906ff6dc7d3f19d3..5dd12d581edd3b23aa7bade11d01911b64f5bc53 100755 (executable)
@@ -21,7 +21,8 @@ try:
     import grpc
     import grpc_tools
 
-    from micronet import commander
+    sys.path.append(os.path.dirname(CWD))
+    from munet.base import commander
 
     commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
     commander.cmd_raises(
index 13810091682ef4e61c7d6dbc9a2cd5e143262c76..f4aa8278f17bf0ab6473daa13c450dcb138dff5a 100644 (file)
 #
 # July 9 2021, Christian Hopps <chopps@labn.net>
 #
-# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2021-2023, LabN Consulting, L.L.C.
 #
-import datetime
-import logging
-import os
-import re
-import shlex
-import subprocess
-import sys
-import tempfile
-import time as time_mod
-import traceback
-
-root_hostname = subprocess.check_output("hostname")
-
-# This allows us to cleanup any leftovers later on
-os.environ["MICRONET_PID"] = str(os.getpid())
-
-
-class Timeout(object):
-    def __init__(self, delta):
-        self.started_on = datetime.datetime.now()
-        self.expires_on = self.started_on + datetime.timedelta(seconds=delta)
-
-    def elapsed(self):
-        elapsed = datetime.datetime.now() - self.started_on
-        return elapsed.total_seconds()
-
-    def is_expired(self):
-        return datetime.datetime.now() > self.expires_on
-
-
-def is_string(value):
-    """Return True if value is a string."""
-    try:
-        return isinstance(value, basestring)  # type: ignore
-    except NameError:
-        return isinstance(value, str)
-
-
-def shell_quote(command):
-    """Return command wrapped in single quotes."""
-    if sys.version_info[0] >= 3:
-        return shlex.quote(command)
-    return "'{}'".format(command.replace("'", "'\"'\"'"))  # type: ignore
-
-
-def cmd_error(rc, o, e):
-    s = "rc {}".format(rc)
-    o = "\n\tstdout: " + o.strip() if o and o.strip() else ""
-    e = "\n\tstderr: " + e.strip() if e and e.strip() else ""
-    return s + o + e
-
-
-def proc_error(p, o, e):
-    args = p.args if is_string(p.args) else " ".join(p.args)
-    s = "rc {} pid {}\n\targs: {}".format(p.returncode, p.pid, args)
-    o = "\n\tstdout: " + o.strip() if o and o.strip() else ""
-    e = "\n\tstderr: " + e.strip() if e and e.strip() else ""
-    return s + o + e
-
-
-def comm_error(p):
-    rc = p.poll()
-    assert rc is not None
-    if not hasattr(p, "saved_output"):
-        p.saved_output = p.communicate()
-    return proc_error(p, *p.saved_output)
-
-
-class Commander(object):  # pylint: disable=R0205
-    """
-    Commander.
-
-    An object that can execute commands.
-    """
-
-    tmux_wait_gen = 0
-
-    def __init__(self, name, logger=None):
-        """Create a Commander."""
-        self.name = name
-        self.last = None
-        self.exec_paths = {}
-        self.pre_cmd = []
-        self.pre_cmd_str = ""
-
-        if not logger:
-            self.logger = logging.getLogger(__name__ + ".commander." + name)
-        else:
-            self.logger = logger
-
-        self.cwd = self.cmd_raises("pwd").strip()
-
-    def set_logger(self, logfile):
-        self.logger = logging.getLogger(__name__ + ".commander." + self.name)
-        if is_string(logfile):
-            handler = logging.FileHandler(logfile, mode="w")
-        else:
-            handler = logging.StreamHandler(logfile)
-
-        fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format(
-            self.__class__.__name__, self.name
-        )
-        handler.setFormatter(logging.Formatter(fmt=fmtstr))
-        self.logger.addHandler(handler)
-
-    def set_pre_cmd(self, pre_cmd=None):
-        if not pre_cmd:
-            self.pre_cmd = []
-            self.pre_cmd_str = ""
-        else:
-            self.pre_cmd = pre_cmd
-            self.pre_cmd_str = " ".join(self.pre_cmd) + " "
-
-    def __str__(self):
-        return "Commander({})".format(self.name)
-
-    def get_exec_path(self, binary):
-        """Return the full path to the binary executable.
-
-        `binary` :: binary name or list of binary names
-        """
-        if is_string(binary):
-            bins = [binary]
-        else:
-            bins = binary
-        for b in bins:
-            if b in self.exec_paths:
-                return self.exec_paths[b]
-
-            rc, output, _ = self.cmd_status("which " + b, warn=False)
-            if not rc:
-                return os.path.abspath(output.strip())
-            return None
-
-    def get_tmp_dir(self, uniq):
-        return os.path.join(tempfile.mkdtemp(), uniq)
-
-    def test(self, flags, arg):
-        """Run test binary, with flags and arg"""
-        test_path = self.get_exec_path(["test"])
-        rc, output, _ = self.cmd_status([test_path, flags, arg], warn=False)
-        return not rc
-
-    def path_exists(self, path):
-        """Check if path exists."""
-        return self.test("-e", path)
-
-    def _get_cmd_str(self, cmd):
-        if is_string(cmd):
-            return self.pre_cmd_str + cmd
-        cmd = self.pre_cmd + cmd
-        return " ".join(cmd)
-
-    def _get_sub_args(self, cmd, defaults, **kwargs):
-        if is_string(cmd):
-            defaults["shell"] = True
-            pre_cmd = self.pre_cmd_str
-        else:
-            defaults["shell"] = False
-            pre_cmd = self.pre_cmd
-            cmd = [str(x) for x in cmd]
-        defaults.update(kwargs)
-        return pre_cmd, cmd, defaults
-
-    def _popen(self, method, cmd, skip_pre_cmd=False, **kwargs):
-        if sys.version_info[0] >= 3:
-            defaults = {
-                "encoding": "utf-8",
-                "stdout": subprocess.PIPE,
-                "stderr": subprocess.PIPE,
-            }
-        else:
-            defaults = {
-                "stdout": subprocess.PIPE,
-                "stderr": subprocess.PIPE,
-            }
-        pre_cmd, cmd, defaults = self._get_sub_args(cmd, defaults, **kwargs)
-
-        self.logger.debug('%s: %s("%s", kwargs: %s)', self, method, cmd, defaults)
-
-        actual_cmd = cmd if skip_pre_cmd else pre_cmd + cmd
-        p = subprocess.Popen(actual_cmd, **defaults)
-        if not hasattr(p, "args"):
-            p.args = actual_cmd
-        return p, actual_cmd
-
-    def set_cwd(self, cwd):
-        self.logger.warning("%s: 'cd' (%s) does not work outside namespaces", self, cwd)
-        self.cwd = cwd
-
-    def popen(self, cmd, **kwargs):
-        """
-        Creates a pipe with the given `command`.
-
-        Args:
-            command: `str` or `list` of command to open a pipe with.
-            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
-                then will be invoked with shell=True, otherwise `command` is a list and
-                will be invoked with shell=False.
-
-        Returns:
-            a subprocess.Popen object.
-        """
-        p, _ = self._popen("popen", cmd, **kwargs)
-        return p
-
-    def cmd_status(self, cmd, raises=False, warn=True, stdin=None, **kwargs):
-        """Execute a command."""
-
-        # We are not a shell like mininet, so we need to intercept this
-        chdir = False
-        if not is_string(cmd):
-            cmds = cmd
-        else:
-            # XXX we can drop this when the code stops assuming it works
-            m = re.match(r"cd(\s*|\s+(\S+))$", cmd)
-            if m and m.group(2):
-                self.logger.warning(
-                    "Bad call to 'cd' (chdir) emulating, use self.set_cwd():\n%s",
-                    "".join(traceback.format_stack(limit=12)),
-                )
-                assert is_string(cmd)
-                chdir = True
-                cmd += " && pwd"
-
-            # If we are going to run under bash then we don't need shell=True!
-            cmds = ["/bin/bash", "-c", cmd]
-
-        pinput = None
-
-        if is_string(stdin) or isinstance(stdin, bytes):
-            pinput = stdin
-            stdin = subprocess.PIPE
-
-        p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs)
-        stdout, stderr = p.communicate(input=pinput)
-        rc = p.wait()
-
-        # For debugging purposes.
-        self.last = (rc, actual_cmd, cmd, stdout, stderr)
-
-        if rc:
-            if warn:
-                self.logger.warning(
-                    "%s: proc failed: %s:", self, proc_error(p, stdout, stderr)
-                )
-            if raises:
-                # error = Exception("stderr: {}".format(stderr))
-                # This annoyingly doesn't' show stderr when printed normally
-                error = subprocess.CalledProcessError(rc, actual_cmd)
-                error.stdout, error.stderr = stdout, stderr
-                raise error
-        elif chdir:
-            self.set_cwd(stdout.strip())
-
-        return rc, stdout, stderr
-
-    def cmd_legacy(self, cmd, **kwargs):
-        """Execute a command with stdout and stderr joined, *IGNORES ERROR*."""
-
-        defaults = {"stderr": subprocess.STDOUT}
-        defaults.update(kwargs)
-        _, stdout, _ = self.cmd_status(cmd, raises=False, **defaults)
-        return stdout
-
-    def cmd_raises(self, cmd, **kwargs):
-        """Execute a command. Raise an exception on errors"""
-
-        rc, stdout, _ = self.cmd_status(cmd, raises=True, **kwargs)
-        assert rc == 0
-        return stdout
-
-    # Run a command in a new window (gnome-terminal, screen, tmux, xterm)
-    def run_in_window(
-        self,
-        cmd,
-        wait_for=False,
-        background=False,
-        name=None,
-        title=None,
-        forcex=False,
-        new_window=False,
-        tmux_target=None,
-    ):
-        """
-        Run a command in a new window (TMUX, Screen or XTerm).
-
-        Args:
-            wait_for: True to wait for exit from command or `str` as channel neme to signal on exit, otherwise False
-            background: Do not change focus to new window.
-            title: Title for new pane (tmux) or window (xterm).
-            name: Name of the new window (tmux)
-            forcex: Force use of X11.
-            new_window: Open new window (instead of pane) in TMUX
-            tmux_target: Target for tmux pane.
-
-        Returns:
-            the pane/window identifier from TMUX (depends on `new_window`)
-        """
-
-        channel = None
-        if is_string(wait_for):
-            channel = wait_for
-        elif wait_for is True:
-            channel = "{}-wait-{}".format(os.getpid(), Commander.tmux_wait_gen)
-            Commander.tmux_wait_gen += 1
-
-        sudo_path = self.get_exec_path(["sudo"])
-        nscmd = sudo_path + " " + self.pre_cmd_str + cmd
-        if "TMUX" in os.environ and not forcex:
-            cmd = [self.get_exec_path("tmux")]
-            if new_window:
-                cmd.append("new-window")
-                cmd.append("-P")
-                if name:
-                    cmd.append("-n")
-                    cmd.append(name)
-                if tmux_target:
-                    cmd.append("-t")
-                    cmd.append(tmux_target)
-            else:
-                cmd.append("split-window")
-                cmd.append("-P")
-                cmd.append("-h")
-                if not tmux_target:
-                    tmux_target = os.getenv("TMUX_PANE", "")
-            if background:
-                cmd.append("-d")
-            if tmux_target:
-                cmd.append("-t")
-                cmd.append(tmux_target)
-            if title:
-                nscmd = "printf '\033]2;{}\033\\'; {}".format(title, nscmd)
-            if channel:
-                nscmd = 'trap "tmux wait -S {}; exit 0" EXIT; {}'.format(channel, nscmd)
-            cmd.append(nscmd)
-        elif "STY" in os.environ and not forcex:
-            # wait for not supported in screen for now
-            channel = None
-            cmd = [self.get_exec_path("screen")]
-            if title:
-                cmd.append("-t")
-                cmd.append(title)
-            if not os.path.exists(
-                "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"])
-            ):
-                cmd = ["sudo", "-u", os.environ["SUDO_USER"]] + cmd
-            cmd.extend(nscmd.split(" "))
-        elif "DISPLAY" in os.environ:
-            # We need it broken up for xterm
-            user_cmd = cmd
-            cmd = [self.get_exec_path("xterm")]
-            if "SUDO_USER" in os.environ:
-                cmd = [self.get_exec_path("sudo"), "-u", os.environ["SUDO_USER"]] + cmd
-            if title:
-                cmd.append("-T")
-                cmd.append(title)
-            cmd.append("-e")
-            cmd.append(sudo_path)
-            cmd.extend(self.pre_cmd)
-            cmd.extend(["bash", "-c", user_cmd])
-            # if channel:
-            #    return self.cmd_raises(cmd, skip_pre_cmd=True)
-            # else:
-            p = self.popen(
-                cmd,
-                skip_pre_cmd=True,
-                stdin=None,
-                shell=False,
-            )
-            time_mod.sleep(2)
-            if p.poll() is not None:
-                self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p))
-            return p
-        else:
-            self.logger.error(
-                "DISPLAY, STY, and TMUX not in environment, can't open window"
-            )
-            raise Exception("Window requestd but TMUX, Screen and X11 not available")
-
-        pane_info = self.cmd_raises(cmd, skip_pre_cmd=True).strip()
-
-        # Re-adjust the layout
-        if "TMUX" in os.environ:
-            self.cmd_status(
-                "tmux select-layout -t {} tiled".format(
-                    pane_info if not tmux_target else tmux_target
-                ),
-                skip_pre_cmd=True,
-            )
-
-        # Wait here if we weren't handed the channel to wait for
-        if channel and wait_for is True:
-            cmd = [self.get_exec_path("tmux"), "wait", channel]
-            self.cmd_status(cmd, skip_pre_cmd=True)
-
-        return pane_info
-
-    def delete(self):
-        pass
-
-
-class LinuxNamespace(Commander):
-    """
-    A linux Namespace.
-
-    An object that creates and executes commands in a linux namespace
-    """
-
-    def __init__(
-        self,
-        name,
-        net=True,
-        mount=True,
-        uts=True,
-        cgroup=False,
-        ipc=False,
-        pid=False,
-        time=False,
-        user=False,
-        set_hostname=True,
-        private_mounts=None,
-        logger=None,
-    ):
-        """
-        Create a new linux namespace.
-
-        Args:
-            name: Internal name for the namespace.
-            net: Create network namespace.
-            mount: Create network namespace.
-            uts: Create UTS (hostname) namespace.
-            cgroup: Create cgroup namespace.
-            ipc: Create IPC namespace.
-            pid: Create PID namespace, also mounts new /proc.
-            time: Create time namespace.
-            user: Create user namespace, also keeps capabilities.
-                set_hostname: Set the hostname to `name`, uts must also be True.
-                private_mounts: List of strings of the form
-                "[/external/path:]/internal/path. If no external path is specified a
-                tmpfs is mounted on the internal path. Any paths specified are first
-                passed to `mkdir -p`.
-            logger: Passed to superclass.
-        """
-        super(LinuxNamespace, self).__init__(name, logger)
-
-        self.logger.debug("%s: Creating", self)
-
-        self.intfs = []
-
-        nslist = []
-        cmd = ["/usr/bin/unshare"]
-        flags = ""
-        self.a_flags = []
-        self.ifnetns = {}
-
-        if cgroup:
-            nslist.append("cgroup")
-            flags += "C"
-        if ipc:
-            nslist.append("ipc")
-            flags += "i"
-        if mount:
-            nslist.append("mnt")
-            flags += "m"
-        if net:
-            nslist.append("net")
-            flags += "n"
-        if pid:
-            nslist.append("pid")
-            flags += "f"
-            flags += "p"
-            cmd.append("--mount-proc")
-        if time:
-            # XXX this filename is probably wrong
-            nslist.append("time")
-            flags += "T"
-        if user:
-            nslist.append("user")
-            flags += "U"
-            cmd.append("--keep-caps")
-        if uts:
-            nslist.append("uts")
-            flags += "u"
-
-        if flags:
-            aflags = flags.replace("f", "")
-            if aflags:
-                self.a_flags = ["-" + x for x in aflags]
-            cmd.extend(["-" + x for x in flags])
-
-        if pid:
-            cmd.append(commander.get_exec_path("tini"))
-            cmd.append("-vvv")
-        cmd.append("/bin/cat")
-
-        # Using cat and a stdin PIPE is nice as it will exit when we do. However, we
-        # also detach it from the pgid so that signals do not propagate to it. This is
-        # b/c it would exit early (e.g., ^C) then, at least the main micronet proc which
-        # has no other processes like frr daemons running, will take the main network
-        # namespace with it, which will remove the bridges and the veth pair (because
-        # the bridge side veth is deleted).
-        self.logger.debug("%s: creating namespace process: %s", self, cmd)
-        p = subprocess.Popen(
-            cmd,
-            stdin=subprocess.PIPE,
-            stdout=open("/dev/null", "w"),
-            stderr=open("/dev/null", "w"),
-            text=True,
-            start_new_session=True,  # detach from pgid so signals don't propagate
-            shell=False,
-        )
-        self.p = p
-        self.pid = p.pid
-
-        self.logger.debug("%s: namespace pid: %d", self, self.pid)
-
-        # -----------------------------------------------
-        # Now let's wait until unshare completes it's job
-        # -----------------------------------------------
-        timeout = Timeout(30)
-        while p.poll() is None and not timeout.is_expired():
-            for fname in tuple(nslist):
-                ours = os.readlink("/proc/self/ns/{}".format(fname))
-                theirs = os.readlink("/proc/{}/ns/{}".format(self.pid, fname))
-                # See if their namespace is different
-                if ours != theirs:
-                    nslist.remove(fname)
-            if not nslist:
-                break
-            elapsed = int(timeout.elapsed())
-            if elapsed <= 3:
-                time_mod.sleep(0.1)
-            elif elapsed > 10:
-                self.logger.warning("%s: unshare taking more than %ss", self, elapsed)
-                time_mod.sleep(3)
-            else:
-                self.logger.info("%s: unshare taking more than %ss", self, elapsed)
-                time_mod.sleep(1)
-        assert p.poll() is None, "unshare unexpectedly exited!"
-        assert not nslist, "unshare never unshared!"
-
-        # Set pre-command based on our namespace proc
-        self.base_pre_cmd = ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid)]
-        if not pid:
-            self.base_pre_cmd.append("-F")
-        self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd])
-
-        # Remount sysfs and cgroup to pickup any changes
-        self.cmd_raises("mount -t sysfs sysfs /sys")
-        self.cmd_raises(
-            "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup"
-        )
-
-        # Set the hostname to the namespace name
-        if uts and set_hostname:
-            # Debugging get the root hostname
-            self.cmd_raises("hostname " + self.name)
-            nroot = subprocess.check_output("hostname")
-            if root_hostname != nroot:
-                result = self.p.poll()
-                assert root_hostname == nroot, "STATE of namespace process {}".format(
-                    result
-                )
-
-        if private_mounts:
-            if is_string(private_mounts):
-                private_mounts = [private_mounts]
-            for m in private_mounts:
-                s = m.split(":", 1)
-                if len(s) == 1:
-                    self.tmpfs_mount(s[0])
-                else:
-                    self.bind_mount(s[0], s[1])
-
-        o = self.cmd_legacy("ls -l /proc/{}/ns".format(self.pid))
-        self.logger.debug("namespaces:\n %s", o)
-
-        # Doing this here messes up all_protocols ipv6 check
-        self.cmd_raises("ip link set lo up")
-
-    def __str__(self):
-        return "LinuxNamespace({})".format(self.name)
-
-    def tmpfs_mount(self, inner):
-        self.cmd_raises("mkdir -p " + inner)
-        self.cmd_raises("mount -n -t tmpfs tmpfs " + inner)
-
-    def bind_mount(self, outer, inner):
-        self.cmd_raises("mkdir -p " + inner)
-        self.cmd_raises("mount --rbind {} {} ".format(outer, inner))
-
-    def add_vlan(self, vlanname, linkiface, vlanid):
-        self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid)
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        self.cmd_raises(
-            [
-                ip_path,
-                "link",
-                "add",
-                "link",
-                linkiface,
-                "name",
-                vlanname,
-                "type",
-                "vlan",
-                "id",
-                vlanid,
-            ]
-        )
-        self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"])
-
-    def add_loop(self, loopname):
-        self.logger.debug("Adding Linux iface: %s", loopname)
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"])
-        self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"])
-
-    def add_l3vrf(self, vrfname, tableid):
-        self.logger.debug("Adding Linux VRF: %s", vrfname)
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        self.cmd_raises(
-            [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid]
-        )
-        self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"])
-
-    def del_iface(self, iface):
-        self.logger.debug("Removing Linux Iface: %s", iface)
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        self.cmd_raises([ip_path, "link", "del", iface])
-
-    def attach_iface_to_l3vrf(self, ifacename, vrfname):
-        self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname)
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        if vrfname:
-            self.cmd_raises(
-                [ip_path, "link", "set", "dev", ifacename, "master", vrfname]
-            )
-        else:
-            self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"])
-
-    def add_netns(self, ns):
-        self.logger.debug("Adding network namespace %s", ns)
-
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        if os.path.exists("/run/netns/{}".format(ns)):
-            self.logger.warning("%s: Removing existing nsspace %s", self, ns)
-            try:
-                self.delete_netns(ns)
-            except Exception as ex:
-                self.logger.warning(
-                    "%s: Couldn't remove existing nsspace %s: %s",
-                    self,
-                    ns,
-                    str(ex),
-                    exc_info=True,
-                )
-        self.cmd_raises([ip_path, "netns", "add", ns])
-
-    def delete_netns(self, ns):
-        self.logger.debug("Deleting network namespace %s", ns)
-
-        ip_path = self.get_exec_path("ip")
-        assert ip_path, "XXX missing ip command!"
-        self.cmd_raises([ip_path, "netns", "delete", ns])
-
-    def set_intf_netns(self, intf, ns, up=False):
-        # In case a user hard-codes 1 thinking it "resets"
-        ns = str(ns)
-        if ns == "1":
-            ns = str(self.pid)
-
-        self.logger.debug("Moving interface %s to namespace %s", intf, ns)
-
-        cmd = "ip link set {} netns " + ns
-        if up:
-            cmd += " up"
-        self.intf_ip_cmd(intf, cmd)
-        if ns == str(self.pid):
-            # If we are returning then remove from dict
-            if intf in self.ifnetns:
-                del self.ifnetns[intf]
-        else:
-            self.ifnetns[intf] = ns
-
-    def reset_intf_netns(self, intf):
-        self.logger.debug("Moving interface %s to default namespace", intf)
-        self.set_intf_netns(intf, str(self.pid))
-
-    def intf_ip_cmd(self, intf, cmd):
-        """Run an ip command for considering an interfaces possible namespace.
-
-        `cmd` - format is run using the interface name on the command
-        """
-        if intf in self.ifnetns:
-            assert cmd.startswith("ip ")
-            cmd = "ip -n " + self.ifnetns[intf] + cmd[2:]
-        self.cmd_raises(cmd.format(intf))
-
-    def set_cwd(self, cwd):
-        # Set pre-command based on our namespace proc
-        self.logger.debug("%s: new CWD %s", self, cwd)
-        self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + cwd])
-
-    def register_interface(self, ifname):
-        if ifname not in self.intfs:
-            self.intfs.append(ifname)
-
-    def delete(self):
-        if self.p and self.p.poll() is None:
-            if sys.version_info[0] >= 3:
-                try:
-                    self.p.terminate()
-                    self.p.communicate(timeout=10)
-                except subprocess.TimeoutExpired:
-                    self.p.kill()
-                    self.p.communicate(timeout=2)
-            else:
-                self.p.kill()
-                self.p.communicate()
-        self.set_pre_cmd(["/bin/false"])
-
-
-class SharedNamespace(Commander):
-    """
-    Share another namespace.
-
-    An object that executes commands in an existing pid's linux namespace
-    """
-
-    def __init__(self, name, pid, aflags=("-a",), logger=None):
-        """
-        Share a linux namespace.
-
-        Args:
-            name: Internal name for the namespace.
-            pid: PID of the process to share with.
-        """
-        super(SharedNamespace, self).__init__(name, logger)
-
-        self.logger.debug("%s: Creating", self)
-
-        self.pid = pid
-        self.intfs = []
-        self.a_flags = aflags
-
-        # Set pre-command based on our namespace proc
-        self.set_pre_cmd(
-            ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + self.cwd]
-        )
-
-    def __str__(self):
-        return "SharedNamespace({})".format(self.name)
-
-    def set_cwd(self, cwd):
-        # Set pre-command based on our namespace proc
-        self.logger.debug("%s: new CWD %s", self, cwd)
-        self.set_pre_cmd(
-            ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + cwd]
-        )
-
-    def register_interface(self, ifname):
-        if ifname not in self.intfs:
-            self.intfs.append(ifname)
-
-
-class Bridge(SharedNamespace):
-    """
-    A linux bridge.
-    """
-
-    next_brid_ord = 0
-
-    @classmethod
-    def _get_next_brid(cls):
-        brid_ord = cls.next_brid_ord
-        cls.next_brid_ord += 1
-        return brid_ord
-
-    def __init__(self, name=None, unet=None, logger=None):
-        """Create a linux Bridge."""
-
-        self.unet = unet
-        self.brid_ord = self._get_next_brid()
-        if name:
-            self.brid = name
-        else:
-            self.brid = "br{}".format(self.brid_ord)
-            name = self.brid
-
-        super(Bridge, self).__init__(name, unet.pid, aflags=unet.a_flags, logger=logger)
-
-        self.logger.debug("Bridge: Creating")
-
-        assert len(self.brid) <= 16  # Make sure fits in IFNAMSIZE
-        self.cmd_raises("ip link delete {} || true".format(self.brid))
-        self.cmd_raises("ip link add {} type bridge".format(self.brid))
-        self.cmd_raises("ip link set {} up".format(self.brid))
-
-        self.logger.debug("%s: Created, Running", self)
-
-    def __str__(self):
-        return "Bridge({})".format(self.brid)
-
-    def delete(self):
-        """Stop the bridge (i.e., delete the linux resources)."""
-
-        rc, o, e = self.cmd_status("ip link show {}".format(self.brid), warn=False)
-        if not rc:
-            rc, o, e = self.cmd_status(
-                "ip link delete {}".format(self.brid), warn=False
-            )
-        if rc:
-            self.logger.error(
-                "%s: error deleting bridge %s: %s",
-                self,
-                self.brid,
-                cmd_error(rc, o, e),
-            )
-        else:
-            self.logger.debug("%s: Deleted.", self)
-
-
-class Micronet(LinuxNamespace):  # pylint: disable=R0205
-    """
-    Micronet.
-    """
-
-    def __init__(self):
-        """Create a Micronet."""
-
-        self.hosts = {}
-        self.switches = {}
-        self.links = {}
-        self.macs = {}
-        self.rmacs = {}
-
-        super(Micronet, self).__init__("micronet", mount=True, net=True, uts=True)
-
-        self.logger.debug("%s: Creating", self)
-
-    def __str__(self):
-        return "Micronet()"
-
-    def __getitem__(self, key):
-        if key in self.switches:
-            return self.switches[key]
-        return self.hosts[key]
-
-    def add_host(self, name, cls=LinuxNamespace, **kwargs):
-        """Add a host to micronet."""
-
-        self.logger.debug("%s: add_host %s", self, name)
-
-        self.hosts[name] = cls(name, **kwargs)
-        # Create a new mounted FS for tracking nested network namespaces creatd by the
-        # user with `ip netns add`
-        self.hosts[name].tmpfs_mount("/run/netns")
-
-    def add_link(self, name1, name2, if1, if2):
-        """Add a link between switch and host to micronet."""
-        isp2p = False
-        if name1 in self.switches:
-            assert name2 in self.hosts
-        elif name2 in self.switches:
-            assert name1 in self.hosts
-            name1, name2 = name2, name1
-            if1, if2 = if2, if1
-        else:
-            # p2p link
-            assert name1 in self.hosts
-            assert name2 in self.hosts
-            isp2p = True
-
-        lname = "{}:{}-{}:{}".format(name1, if1, name2, if2)
-        self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "")
-        self.links[lname] = (name1, if1, name2, if2)
-
-        # And create the veth now.
-        if isp2p:
-            lhost, rhost = self.hosts[name1], self.hosts[name2]
-            lifname = "i1{:x}".format(lhost.pid)
-            rifname = "i2{:x}".format(rhost.pid)
-            self.cmd_raises(
-                "ip link add {} type veth peer name {}".format(lifname, rifname)
-            )
-
-            self.cmd_raises("ip link set {} netns {}".format(lifname, lhost.pid))
-            lhost.cmd_raises("ip link set {} name {}".format(lifname, if1))
-            lhost.cmd_raises("ip link set {} up".format(if1))
-            lhost.register_interface(if1)
-
-            self.cmd_raises("ip link set {} netns {}".format(rifname, rhost.pid))
-            rhost.cmd_raises("ip link set {} name {}".format(rifname, if2))
-            rhost.cmd_raises("ip link set {} up".format(if2))
-            rhost.register_interface(if2)
-        else:
-            switch = self.switches[name1]
-            host = self.hosts[name2]
-
-            assert len(if1) <= 16 and len(if2) <= 16  # Make sure fits in IFNAMSIZE
-
-            self.logger.debug("%s: Creating veth pair for link %s", self, lname)
-            self.cmd_raises(
-                "ip link add {} type veth peer name {} netns {}".format(
-                    if1, if2, host.pid
-                )
-            )
-            self.cmd_raises("ip link set {} netns {}".format(if1, switch.pid))
-            switch.register_interface(if1)
-            host.register_interface(if2)
-            self.cmd_raises("ip link set {} master {}".format(if1, switch.brid))
-            self.cmd_raises("ip link set {} up".format(if1))
-            host.cmd_raises("ip link set {} up".format(if2))
-
-        # Cache the MAC values, and reverse mapping
-        self.get_mac(name1, if1)
-        self.get_mac(name2, if2)
-
-    def add_switch(self, name):
-        """Add a switch to micronet."""
-
-        self.logger.debug("%s: add_switch %s", self, name)
-        self.switches[name] = Bridge(name, self)
-
-    def get_mac(self, name, ifname):
-        if name in self.hosts:
-            dev = self.hosts[name]
-        else:
-            dev = self.switches[name]
-
-        if (name, ifname) not in self.macs:
-            _, output, _ = dev.cmd_status("ip -o link show " + ifname)
-            m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output)
-            mac = m.group(2)
-            self.macs[(name, ifname)] = mac
-            self.rmacs[mac] = (name, ifname)
-
-        return self.macs[(name, ifname)]
-
-    def delete(self):
-        """Delete the micronet topology."""
-
-        self.logger.debug("%s: Deleting.", self)
-
-        for lname, (_, _, rname, rif) in self.links.items():
-            host = self.hosts[rname]
-
-            self.logger.debug("%s: Deleting veth pair for link %s", self, lname)
-
-            rc, o, e = host.cmd_status("ip link delete {}".format(rif), warn=False)
-            if rc:
-                self.logger.error(
-                    "Error deleting veth pair %s: %s", lname, cmd_error(rc, o, e)
-                )
-
-        self.links = {}
-
-        for host in self.hosts.values():
-            try:
-                host.delete()
-            except Exception as error:
-                self.logger.error(
-                    "%s: error while deleting host %s: %s", self, host, error
-                )
-
-        self.hosts = {}
-
-        for switch in self.switches.values():
-            try:
-                switch.delete()
-            except Exception as error:
-                self.logger.error(
-                    "%s: error while deleting switch %s: %s", self, switch, error
-                )
-        self.switches = {}
-
-        self.logger.debug("%s: Deleted.", self)
-
-        super(Micronet, self).delete()
-
-
-# ---------------------------
-# Root level utility function
-# ---------------------------
-
-
-def get_exec_path(binary):
-    base = Commander("base")
-    return base.get_exec_path(binary)
-
-
-commander = Commander("micronet")
+# flake8: noqa
+
+from munet.base import BaseMunet as Micronet
+from munet.base import (
+    Bridge,
+    Commander,
+    LinuxNamespace,
+    SharedNamespace,
+    Timeout,
+    cmd_error,
+    comm_error,
+    commander,
+    get_exec_path,
+    proc_error,
+    root_hostname,
+    shell_quote,
+)
diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py
deleted file mode 100644 (file)
index e54b75f..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-# -*- coding: utf-8 eval: (blacken-mode 1) -*-
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# July 24 2021, Christian Hopps <chopps@labn.net>
-#
-# Copyright (c) 2021, LabN Consulting, L.L.C.
-#
-import argparse
-import logging
-import os
-import pty
-import re
-import readline
-import select
-import socket
-import subprocess
-import sys
-import tempfile
-import termios
-import tty
-
-
-ENDMARKER = b"\x00END\x00"
-
-
-def lineiter(sock):
-    s = ""
-    while True:
-        sb = sock.recv(256)
-        if not sb:
-            return
-
-        s += sb.decode("utf-8")
-        i = s.find("\n")
-        if i != -1:
-            yield s[:i]
-            s = s[i + 1 :]
-
-
-def spawn(unet, host, cmd):
-    if sys.stdin.isatty():
-        old_tty = termios.tcgetattr(sys.stdin)
-        tty.setraw(sys.stdin.fileno())
-    try:
-        master_fd, slave_fd = pty.openpty()
-
-        # use os.setsid() make it run in a new process group, or bash job
-        # control will not be enabled
-        p = unet.hosts[host].popen(
-            cmd,
-            preexec_fn=os.setsid,
-            stdin=slave_fd,
-            stdout=slave_fd,
-            stderr=slave_fd,
-            universal_newlines=True,
-        )
-
-        while p.poll() is None:
-            r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25)
-            if sys.stdin in r:
-                d = os.read(sys.stdin.fileno(), 10240)
-                os.write(master_fd, d)
-            elif master_fd in r:
-                o = os.read(master_fd, 10240)
-                if o:
-                    os.write(sys.stdout.fileno(), o)
-    finally:
-        # restore tty settings back
-        if sys.stdin.isatty():
-            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
-
-
-def doline(unet, line, writef):
-    def host_cmd_split(unet, cmd):
-        csplit = cmd.split()
-        for i, e in enumerate(csplit):
-            if e not in unet.hosts:
-                break
-        hosts = csplit[:i]
-        if not hosts:
-            hosts = sorted(unet.hosts.keys())
-        cmd = " ".join(csplit[i:])
-        return hosts, cmd
-
-    line = line.strip()
-    m = re.match(r"^(\S+)(?:\s+(.*))?$", line)
-    if not m:
-        return True
-
-    cmd = m.group(1)
-    oargs = m.group(2) if m.group(2) else ""
-    if cmd == "q" or cmd == "quit":
-        return False
-    if cmd == "hosts":
-        writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys())))
-    elif cmd in ["term", "vtysh", "xterm"]:
-        args = oargs.split()
-        if not args or (len(args) == 1 and args[0] == "*"):
-            args = sorted(unet.hosts.keys())
-        hosts = [unet.hosts[x] for x in args if x in unet.hosts]
-        for host in hosts:
-            if cmd == "t" or cmd == "term":
-                host.run_in_window("bash", title="sh-%s" % host)
-            elif cmd == "v" or cmd == "vtysh":
-                host.run_in_window("vtysh", title="vt-%s" % host)
-            elif cmd == "x" or cmd == "xterm":
-                host.run_in_window("bash", title="sh-%s" % host, forcex=True)
-    elif cmd == "sh":
-        hosts, cmd = host_cmd_split(unet, oargs)
-        for host in hosts:
-            if sys.stdin.isatty():
-                spawn(unet, host, cmd)
-            else:
-                if len(hosts) > 1:
-                    writef("------ Host: %s ------\n" % host)
-                output = unet.hosts[host].cmd_legacy(cmd)
-                writef(output)
-                if len(hosts) > 1:
-                    writef("------- End: %s ------\n" % host)
-        writef("\n")
-    elif cmd == "h" or cmd == "help":
-        writef(
-            """
-Commands:
-  help                       :: this help
-  sh [hosts] <shell-command> :: execute <shell-command> on <host>
-  term [hosts]               :: open shell terminals for hosts
-  vtysh [hosts]              :: open vtysh terminals for hosts
-  [hosts] <vtysh-command>    :: execute vtysh-command on hosts\n\n"""
-        )
-    else:
-        hosts, cmd = host_cmd_split(unet, line)
-        for host in hosts:
-            if len(hosts) > 1:
-                writef("------ Host: %s ------\n" % host)
-            output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd))
-            writef(output)
-            if len(hosts) > 1:
-                writef("------- End: %s ------\n" % host)
-        writef("\n")
-    return True
-
-
-def cli_server_setup(unet):
-    sockdir = tempfile.mkdtemp("-sockdir", "pyt")
-    sockpath = os.path.join(sockdir, "cli-server.sock")
-    try:
-        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        sock.settimeout(10)
-        sock.bind(sockpath)
-        sock.listen(1)
-        return sock, sockdir, sockpath
-    except Exception:
-        unet.cmd_status("rm -rf " + sockdir)
-        raise
-
-
-def cli_server(unet, server_sock):
-    sock, addr = server_sock.accept()
-
-    # Go into full non-blocking mode now
-    sock.settimeout(None)
-
-    for line in lineiter(sock):
-        line = line.strip()
-
-        def writef(x):
-            xb = x.encode("utf-8")
-            sock.send(xb)
-
-        if not doline(unet, line, writef):
-            return
-        sock.send(ENDMARKER)
-
-
-def cli_client(sockpath, prompt="unet> "):
-    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-    sock.settimeout(10)
-    sock.connect(sockpath)
-
-    # Go into full non-blocking mode now
-    sock.settimeout(None)
-
-    print("\n--- Micronet CLI Starting ---\n\n")
-    while True:
-        if sys.version_info[0] == 2:
-            line = raw_input(prompt)  # pylint: disable=E0602
-        else:
-            line = input(prompt)
-        if line is None:
-            return
-
-        # Need to put \n back
-        line += "\n"
-
-        # Send the CLI command
-        sock.send(line.encode("utf-8"))
-
-        def bendswith(b, sentinel):
-            slen = len(sentinel)
-            return len(b) >= slen and b[-slen:] == sentinel
-
-        # Collect the output
-        rb = b""
-        while not bendswith(rb, ENDMARKER):
-            lb = sock.recv(4096)
-            if not lb:
-                return
-            rb += lb
-
-        # Remove the marker
-        rb = rb[: -len(ENDMARKER)]
-
-        # Write the output
-        sys.stdout.write(rb.decode("utf-8"))
-
-
-def local_cli(unet, outf, prompt="unet> "):
-    print("\n--- Micronet CLI Starting ---\n\n")
-    while True:
-        if sys.version_info[0] == 2:
-            line = raw_input(prompt)  # pylint: disable=E0602
-        else:
-            line = input(prompt)
-        if line is None:
-            return
-        if not doline(unet, line, outf.write):
-            return
-
-
-def cli(
-    unet,
-    histfile=None,
-    sockpath=None,
-    force_window=False,
-    title=None,
-    prompt=None,
-    background=True,
-):
-    logger = logging.getLogger("cli-client")
-
-    if prompt is None:
-        prompt = "unet> "
-
-    if force_window or not sys.stdin.isatty():
-        # Run CLI in another window b/c we have no tty.
-        sock, sockdir, sockpath = cli_server_setup(unet)
-
-        python_path = unet.get_exec_path(["python3", "python"])
-        us = os.path.realpath(__file__)
-        cmd = "{} {}".format(python_path, us)
-        if histfile:
-            cmd += " --histfile=" + histfile
-        if title:
-            cmd += " --prompt={}".format(title)
-        cmd += " " + sockpath
-
-        try:
-            unet.run_in_window(cmd, new_window=True, title=title, background=background)
-            return cli_server(unet, sock)
-        finally:
-            unet.cmd_status("rm -rf " + sockdir)
-
-    if not unet:
-        logger.debug("client-cli using sockpath %s", sockpath)
-
-    try:
-        if histfile is None:
-            histfile = os.path.expanduser("~/.micronet-history.txt")
-            if not os.path.exists(histfile):
-                if unet:
-                    unet.cmd("touch " + histfile)
-                else:
-                    subprocess.run("touch " + histfile)
-        if histfile:
-            readline.read_history_file(histfile)
-    except Exception:
-        pass
-
-    try:
-        if sockpath:
-            cli_client(sockpath, prompt=prompt)
-        else:
-            local_cli(unet, sys.stdout, prompt=prompt)
-    except EOFError:
-        pass
-    except Exception as ex:
-        logger.critical("cli: got exception: %s", ex, exc_info=True)
-        raise
-    finally:
-        readline.write_history_file(histfile)
-
-
-if __name__ == "__main__":
-    logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log")
-    logger = logging.getLogger("cli-client")
-    logger.info("Start logging cli-client")
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--histfile", help="file to user for history")
-    parser.add_argument("--prompt-text", help="prompt string to use")
-    parser.add_argument("socket", help="path to pair of sockets to communicate over")
-    args = parser.parse_args()
-
-    prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> "
-    cli(None, args.histfile, args.socket, prompt=prompt)
index 5a69c56d8dde7e259c18a7157e765d39b32d5991..c5c2adc545bdfa03c7058849099c24534f030571 100644 (file)
 #
 # July 11 2021, Christian Hopps <chopps@labn.net>
 #
-# Copyright (c) 2021, LabN Consulting, L.L.C
+# Copyright (c) 2021-2023, LabN Consulting, L.L.C
 #
-
-import glob
-import logging
+import ipaddress
 import os
-import signal
-import time
-
-from lib.micronet import LinuxNamespace, Micronet
-from lib.micronet_cli import cli
-
-
-def get_pids_with_env(has_var, has_val=None):
-    result = {}
-    for pidenv in glob.iglob("/proc/*/environ"):
-        pid = pidenv.split("/")[2]
-        try:
-            with open(pidenv, "rb") as rfb:
-                envlist = [
-                    x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0")
-                ]
-                envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist]
-                envdict = dict(envlist)
-                if has_var not in envdict:
-                    continue
-                if has_val is None:
-                    result[pid] = envdict
-                elif envdict[has_var] == str(has_val):
-                    result[pid] = envdict
-        except Exception:
-            # E.g., process exited and files are gone
-            pass
-    return result
-
-
-def _kill_piddict(pids_by_upid, sig):
-    for upid, pids in pids_by_upid:
-        logging.info(
-            "Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid
-        )
-        for pid in pids:
-            try:
-                os.kill(int(pid), sig)
-            except Exception:
-                pass
-
-
-def _get_our_pids():
-    ourpid = str(os.getpid())
-    piddict = get_pids_with_env("MICRONET_PID", ourpid)
-    pids = [x for x in piddict if x != ourpid]
-    if pids:
-        return {ourpid: pids}
-    return {}
-
-
-def _get_other_pids():
-    piddict = get_pids_with_env("MICRONET_PID")
-    unet_pids = {d["MICRONET_PID"] for d in piddict.values()}
-    pids_by_upid = {p: set() for p in unet_pids}
-    for pid, envdict in piddict.items():
-        pids_by_upid[envdict["MICRONET_PID"]].add(pid)
-    # Filter out any child pid sets whos micronet pid is still running
-    return {x: y for x, y in pids_by_upid.items() if x not in y}
-
-
-def _get_pids_by_upid(ours):
-    if ours:
-        return _get_our_pids()
-    return _get_other_pids()
-
-
-def _cleanup_pids(ours):
-    pids_by_upid = _get_pids_by_upid(ours).items()
-    if not pids_by_upid:
-        return
-
-    _kill_piddict(pids_by_upid, signal.SIGTERM)
-
-    # Give them 5 second to exit cleanly
-    logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
-    for _ in range(0, 5):
-        pids_by_upid = _get_pids_by_upid(ours).items()
-        if not pids_by_upid:
-            return
-        time.sleep(1)
-
-    pids_by_upid = _get_pids_by_upid(ours).items()
-    _kill_piddict(pids_by_upid, signal.SIGKILL)
-
-
-def cleanup_current():
-    """Attempt to cleanup preview runs.
-
-    Currently this only scans for old processes.
-    """
-    logging.info("reaping current micronet processes")
-    _cleanup_pids(True)
-
 
-def cleanup_previous():
-    """Attempt to cleanup preview runs.
-
-    Currently this only scans for old processes.
-    """
-    logging.info("reaping past micronet processes")
-    _cleanup_pids(False)
+from munet import cli
+from munet.base import BaseMunet, LinuxNamespace
 
 
 class Node(LinuxNamespace):
     """Node (mininet compat)."""
 
-    def __init__(self, name, **kwargs):
-        """
-        Create a Node.
-        """
-        self.params = kwargs
+    def __init__(self, name, rundir=None, **kwargs):
+        nkwargs = {}
 
+        if "unet" in kwargs:
+            nkwargs["unet"] = kwargs["unet"]
         if "private_mounts" in kwargs:
-            private_mounts = kwargs["private_mounts"]
-        else:
-            private_mounts = kwargs.get("privateDirs", [])
+            nkwargs["private_mounts"] = kwargs["private_mounts"]
+        if "logger" in kwargs:
+            nkwargs["logger"] = kwargs["logger"]
 
-        logger = kwargs.get("logger")
+        # This is expected by newer munet CLI code
+        self.config_dirname = ""
+        self.config = {"kind": "frr"}
+        self.mgmt_ip = None
+        self.mgmt_ip6 = None
 
-        super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts)
+        super().__init__(name, **nkwargs)
+
+        self.rundir = self.unet.rundir.joinpath(self.name)
 
     def cmd(self, cmd, **kwargs):
         """Execute a command, joins stdout, stderr, ignores exit status."""
 
         return super(Node, self).cmd_legacy(cmd, **kwargs)
 
-    def config(self, lo="up", **params):
+    def config_host(self, lo="up", **params):
         """Called by Micronet when topology is built (but not started)."""
         # mininet brings up loopback here.
         del params
@@ -148,25 +52,79 @@ class Node(LinuxNamespace):
     def terminate(self):
         return
 
+    def add_vlan(self, vlanname, linkiface, vlanid):
+        self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid)
+        ip_path = self.get_exec_path("ip")
+        assert ip_path, "XXX missing ip command!"
+        self.cmd_raises(
+            [
+                ip_path,
+                "link",
+                "add",
+                "link",
+                linkiface,
+                "name",
+                vlanname,
+                "type",
+                "vlan",
+                "id",
+                vlanid,
+            ]
+        )
+        self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"])
+
+    def add_loop(self, loopname):
+        self.logger.debug("Adding Linux iface: %s", loopname)
+        ip_path = self.get_exec_path("ip")
+        assert ip_path, "XXX missing ip command!"
+        self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"])
+        self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"])
+
+    def add_l3vrf(self, vrfname, tableid):
+        self.logger.debug("Adding Linux VRF: %s", vrfname)
+        ip_path = self.get_exec_path("ip")
+        assert ip_path, "XXX missing ip command!"
+        self.cmd_raises(
+            [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid]
+        )
+        self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"])
+
+    def del_iface(self, iface):
+        self.logger.debug("Removing Linux Iface: %s", iface)
+        ip_path = self.get_exec_path("ip")
+        assert ip_path, "XXX missing ip command!"
+        self.cmd_raises([ip_path, "link", "del", iface])
+
+    def attach_iface_to_l3vrf(self, ifacename, vrfname):
+        self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname)
+        ip_path = self.get_exec_path("ip")
+        assert ip_path, "XXX missing ip command!"
+        if vrfname:
+            self.cmd_raises(
+                [ip_path, "link", "set", "dev", ifacename, "master", vrfname]
+            )
+        else:
+            self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"])
+
+    set_cwd = LinuxNamespace.set_ns_cwd
+
 
 class Topo(object):  # pylint: disable=R0205
     def __init__(self, *args, **kwargs):
         raise Exception("Remove Me")
 
 
-class Mininet(Micronet):
+class Mininet(BaseMunet):
     """
     Mininet using Micronet.
     """
 
     g_mnet_inst = None
 
-    def __init__(self, controller=None):
+    def __init__(self, rundir=None, pytestconfig=None):
         """
         Create a Micronet.
         """
-        assert not controller
-
         if Mininet.g_mnet_inst is not None:
             Mininet.g_mnet_inst.stop()
         Mininet.g_mnet_inst = self
@@ -181,7 +139,145 @@ class Mininet(Micronet):
         # to set permissions to root:frr 770 to make this unneeded in that case
         # os.umask(0)
 
-        super(Mininet, self).__init__()
+        super(Mininet, self).__init__(
+            pid=False, rundir=rundir, pytestconfig=pytestconfig
+        )
+
+        # From munet/munet/native.py
+        with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+            f.write(f"{self.pid}\n")
+
+        with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+            f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+        hosts_file = os.path.join(self.rundir, "hosts.txt")
+        with open(hosts_file, "w", encoding="ascii") as hf:
+            hf.write(
+                f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+            )
+        self.bind_mount(hosts_file, "/etc/hosts")
+
+        # Common CLI commands for any topology
+        cdict = {
+            "commands": [
+                #
+                # Window commands.
+                #
+                {
+                    "name": "pcap",
+                    "format": "pcap NETWORK",
+                    "help": (
+                        "capture packets from NETWORK into file capture-NETWORK.pcap"
+                        " the command is run within a new window which also shows"
+                        " packet summaries. NETWORK can also be an interface specified"
+                        " as HOST:INTF. To capture inside the host namespace."
+                    ),
+                    "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap",
+                    "top-level": True,
+                    "new-window": {"background": True},
+                },
+                {
+                    "name": "term",
+                    "format": "term HOST [HOST ...]",
+                    "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all",
+                    "exec": "bash",
+                    "new-window": True,
+                },
+                {
+                    "name": "vtysh",
+                    "exec": "/usr/bin/vtysh",
+                    "format": "vtysh ROUTER [ROUTER ...]",
+                    "new-window": True,
+                    "kinds": ["frr"],
+                },
+                {
+                    "name": "xterm",
+                    "format": "xterm HOST [HOST ...]",
+                    "help": "open XTerm[s] on HOST[S], * for all",
+                    "exec": "bash",
+                    "new-window": {
+                        "forcex": True,
+                    },
+                },
+                {
+                    "name": "logd",
+                    "exec": "tail -F %RUNDIR%/{}.log",
+                    "format": "logd HOST [HOST ...] DAEMON",
+                    "help": (
+                        "tail -f on the logfile of the given "
+                        "DAEMON for the given HOST[S]"
+                    ),
+                    "new-window": True,
+                },
+                {
+                    "name": "stdlog",
+                    "exec": (
+                        "[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log "
+                        "|| tail -F /var/log/frr.log"
+                    ),
+                    "format": "stdlog HOST [HOST ...]",
+                    "help": "tail -f on the `frr.log` for the given HOST[S]",
+                    "new-window": True,
+                },
+                {
+                    "name": "stdout",
+                    "exec": "tail -F %RUNDIR%/{0}.err",
+                    "format": "stdout HOST [HOST ...] DAEMON",
+                    "help": (
+                        "tail -f on the stdout of the given DAEMON for the given HOST[S]"
+                    ),
+                    "new-window": True,
+                },
+                {
+                    "name": "stderr",
+                    "exec": "tail -F %RUNDIR%/{0}.out",
+                    "format": "stderr HOST [HOST ...] DAEMON",
+                    "help": (
+                        "tail -f on the stderr of the given DAEMON for the given HOST[S]"
+                    ),
+                    "new-window": True,
+                },
+                #
+                # Non-window commands.
+                #
+                {
+                    "name": "",
+                    "exec": "vtysh -c '{}'",
+                    "format": "[ROUTER ...] COMMAND",
+                    "help": "execute vtysh COMMAND on the router[s]",
+                    "kinds": ["frr"],
+                },
+                {
+                    "name": "sh",
+                    "format": "[HOST ...] sh <SHELL-COMMAND>",
+                    "help": "execute <SHELL-COMMAND> on hosts",
+                    "exec": "{}",
+                },
+                {
+                    "name": "shi",
+                    "format": "[HOST ...] shi <INTERACTIVE-COMMAND>",
+                    "help": "execute <INTERACTIVE-COMMAND> on HOST[s]",
+                    "exec": "{}",
+                    "interactive": True,
+                },
+            ]
+        }
+
+        cli.add_cli_config(self, cdict)
+
+        shellopt = self.cfgopt.get_option_list("--shell")
+        if "all" in shellopt or "." in shellopt:
+            self.run_in_window("bash")
+
+        # This is expected by newer munet CLI code
+        self.config_dirname = ""
+        self.config = {}
 
         self.logger.debug("%s: Creating", self)
 
@@ -219,12 +315,15 @@ class Mininet(Micronet):
 
                 host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf))
 
+                # can be used by munet cli
+                host.mgmt_ip = ipaddress.ip_address(ip)
+
             if "defaultRoute" in params:
                 host.cmd_raises(
                     "ip route add default {}".format(params["defaultRoute"])
                 )
 
-            host.config()
+            host.config_host()
 
             self.configured_hosts.add(name)
 
@@ -236,6 +335,24 @@ class Mininet(Micronet):
 
     def start(self):
         """Start the micronet topology."""
+        pcapopt = self.cfgopt.get_option_list("--pcap")
+        if "all" in pcapopt:
+            pcapopt = self.switches.keys()
+        for pcap in pcapopt:
+            if ":" in pcap:
+                host, intf = pcap.split(":")
+                pcap = f"{host}-{intf}"
+                host = self.hosts[host]
+            else:
+                host = self
+                intf = pcap
+            pcapfile = f"{self.rundir}/capture-{pcap}.pcap"
+            host.run_in_window(
+                f"tshark -s 9200 -i {intf} -P -w {pcapfile}",
+                background=True,
+                title=f"cap:{pcap}",
+            )
+
         self.logger.debug("%s: Starting (no-op).", self)
 
     def stop(self):
@@ -250,4 +367,4 @@ class Mininet(Micronet):
             Mininet.g_mnet_inst = None
 
     def cli(self):
-        cli(self)
+        cli.cli(self)
index 23b1f2e533cf7ba394d6fe90c9485f557607fc35..dad87440bc65b6a192e31d93f0043547cd5791d9 100644 (file)
@@ -577,15 +577,15 @@ def verify_ospf_neighbor(
             "ospf": {
                 "neighbors": {
                     "r1": {
-                        "state": "Full",
+                        "nbrState": "Full",
                         "role": "DR"
                     },
                     "r2": {
-                        "state": "Full",
+                        "nbrState": "Full",
                         "role": "DROther"
                     },
                     "r3": {
-                        "state": "Full",
+                        "nbrState": "Full",
                         "role": "DROther"
                     }
                 }
@@ -642,13 +642,13 @@ def verify_ospf_neighbor(
                 neighbor_ip = neighbor_ip.lower()
                 nbr_rid = data_rid
                 try:
-                    nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
-                    intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1]
+                    nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0]
+                    intf_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[1]
                 except KeyError:
                     errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid)
                     return errormsg
 
-                nbr_state = nbr_data.setdefault("state", None)
+                nbr_state = nbr_data.setdefault("nbrState", None)
                 nbr_role = nbr_data.setdefault("role", None)
 
                 if nbr_state:
@@ -724,8 +724,9 @@ def verify_ospf_neighbor(
                 nh_state = None
                 neighbor_ip = neighbor_ip.lower()
                 nbr_rid = data_rid
+
                 try:
-                    nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
+                    nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0]
                 except KeyError:
                     errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
                         router, nbr_rid, ospf_nbr
@@ -2477,7 +2478,7 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None):
     input_dict = {
                     "helperSupport":"Disabled",
                     "strictLsaCheck":"Enabled",
-                    "restartSupoort":"Planned and Unplanned Restarts",
+                    "restartSupport":"Planned and Unplanned Restarts",
                     "supportedGracePeriod":1800
                 }
     result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
index 6878d93f37244bcb33cbd3ed6c1e633132365ec3..925890b324912aa7bbcd5001aef93a5d9f20ef22 100644 (file)
@@ -5120,6 +5120,75 @@ def verify_pim6_config(tgen, input_dict, expected=True):
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return True
 
+
+@retry(retry_timeout=62)
+def verify_local_mld_groups(tgen, dut, interface, group_addresses):
+    """
+    Verify local MLD groups are received from an intended interface
+    by running "show ipv6 mld join json" command
+    Parameters
+    ----------
+    * `tgen`: topogen object
+    * `dut`: device under test
+    * `interface`: interface, from which IGMP groups are configured
+    * `group_addresses`: MLD group address
+    Usage
+    -----
+    dut = "r1"
+    interface = "r1-r0-eth0"
+    group_address = "ffaa::1"
+    result = verify_local_mld_groups(tgen, dut, interface, group_address)
+    Returns
+    -------
+    errormsg(str) or True
+    """
+
+    logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+    if dut not in tgen.routers():
+        return False
+
+    rnode = tgen.routers()[dut]
+    logger.info("[DUT: %s]: Verifying local MLD groups received:", dut)
+    show_ipv6_local_mld_json = run_frr_cmd(
+        rnode, "show ipv6 mld join json", isjson=True
+    )
+
+    if type(group_addresses) is not list:
+        group_addresses = [group_addresses]
+
+    if interface not in show_ipv6_local_mld_json["default"]:
+
+        errormsg = (
+            "[DUT %s]: Verifying local MLD group received"
+            " from interface %s [FAILED]!! " % (dut, interface)
+        )
+        return errormsg
+
+    for grp_addr in group_addresses:
+        found = False
+        if grp_addr in show_ipv6_local_mld_json["default"][interface]:
+            found = True
+            break
+        if not found:
+            errormsg = (
+                "[DUT %s]: Verifying local MLD group received"
+                " from interface %s [FAILED]!! "
+                " Expected: %s " % (dut, interface, grp_addr)
+            )
+            return errormsg
+
+        logger.info(
+            "[DUT %s]: Verifying local MLD group %s received "
+            "from interface %s [PASSED]!! ",
+            dut,
+            grp_addr,
+            interface,
+        )
+
+    logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+    return True
+
     # def cleanup(self):
     #     super(McastTesterHelper, self).cleanup()
 
index 41da660b7d027afd1fae978147784cfd072efd47..2d6138990ef87e3dcd37c58690d28a7ebdacd645 100644 (file)
@@ -25,6 +25,7 @@ Basic usage instructions:
 * After running stop Mininet with: tgen.stop_topology()
 """
 
+import configparser
 import grp
 import inspect
 import json
@@ -33,20 +34,16 @@ import os
 import platform
 import pwd
 import re
+import shlex
 import subprocess
 import sys
 from collections import OrderedDict
 
-if sys.version_info[0] > 2:
-    import configparser
-else:
-    import ConfigParser as configparser
-
 import lib.topolog as topolog
 from lib.micronet import Commander
 from lib.micronet_compat import Mininet
 from lib.topolog import logger
-from lib.topotest import g_extra_config
+from munet.testing.util import pause_test
 
 from lib import topotest
 
@@ -192,7 +189,7 @@ class Topogen(object):
         self._load_config()
 
         # Create new log directory
-        self.logdir = topotest.get_logs_path(g_extra_config["rundir"])
+        self.logdir = topotest.get_logs_path(topotest.g_pytest_config.option.rundir)
         subprocess.check_call(
             "mkdir -p {0} && chmod 1777 {0}".format(self.logdir), shell=True
         )
@@ -212,7 +209,10 @@ class Topogen(object):
         # Mininet(Micronet) to build the actual topology.
         assert not inspect.isclass(topodef)
 
-        self.net = Mininet(controller=None)
+        self.net = Mininet(rundir=self.logdir, pytestconfig=topotest.g_pytest_config)
+
+        # Adjust the parent namespace
+        topotest.fix_netns_limits(self.net)
 
         # New direct way: Either a dictionary defines the topology or a build function
         # is supplied, or a json filename all of which build the topology by calling
@@ -236,7 +236,6 @@ class Topogen(object):
                 self.add_topology_from_dict(topodef)
 
     def add_topology_from_dict(self, topodef):
-
         keylist = (
             topodef.keys()
             if isinstance(topodef, OrderedDict)
@@ -451,7 +450,18 @@ class Topogen(object):
         first is a simple kill with no sleep, the second will sleep if not
         killed and try with a different signal.
         """
+        pause = bool(self.net.cfgopt.get_option("--pause-at-end"))
+        pause = pause or bool(self.net.cfgopt.get_option("--pause"))
+        if pause:
+            try:
+                pause_test("Before MUNET delete")
+            except KeyboardInterrupt:
+                print("^C...continuing")
+            except Exception as error:
+                self.logger.error("\n...continuing after error: %s", error)
+
         logger.info("stopping topology: {}".format(self.modname))
+
         errors = ""
         for gear in self.gears.values():
             errors += gear.stop()
@@ -504,7 +514,7 @@ class Topogen(object):
 
     def set_error(self, message, code=None):
         "Sets an error message and signal other tests to skip."
-        logger.info(message)
+        logger.info("setting error msg: %s", message)
 
         # If no code is defined use a sequential number
         if code is None:
@@ -749,8 +759,8 @@ class TopoRouter(TopoGear):
         """
         super(TopoRouter, self).__init__(tgen, name, **params)
         self.routertype = params.get("routertype", "frr")
-        if "privateDirs" not in params:
-            params["privateDirs"] = self.PRIVATE_DIRS
+        if "private_mounts" not in params:
+            params["private_mounts"] = self.PRIVATE_DIRS
 
         # Propagate the router log directory
         logfile = self._setup_tmpdir()
@@ -799,7 +809,7 @@ class TopoRouter(TopoGear):
                     grep_cmd = "grep 'ip {}' {}".format(daemonstr, source)
                 else:
                     grep_cmd = "grep 'router {}' {}".format(daemonstr, source)
-                result = self.run(grep_cmd).strip()
+                result = self.run(grep_cmd, warn=False).strip()
                 if result:
                     self.load_config(daemon)
         else:
@@ -822,7 +832,7 @@ class TopoRouter(TopoGear):
         all routers.
         """
         daemonstr = self.RD.get(daemon)
-        self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source))
+        self.logger.debug('loading "{}" configuration: {}'.format(daemonstr, source))
         self.net.loadConf(daemonstr, source, param)
 
     def check_router_running(self):
@@ -858,7 +868,7 @@ class TopoRouter(TopoGear):
                             "conf t",
                             "log file {}.log debug".format(daemon),
                             "log commands",
-                            "log timestamp precision 3",
+                            "log timestamp precision 6",
                         ]
                     ),
                     daemon=daemon,
@@ -908,7 +918,7 @@ class TopoRouter(TopoGear):
                             "conf t",
                             "log file {}.log debug".format(daemon),
                             "log commands",
-                            "log timestamp precision 3",
+                            "log timestamp precision 6",
                         ]
                     ),
                     daemon=daemon,
@@ -943,18 +953,20 @@ class TopoRouter(TopoGear):
         if daemon is not None:
             dparam += "-d {}".format(daemon)
 
-        vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command)
+        vtysh_command = "vtysh {} -c {} 2>/dev/null".format(
+            dparam, shlex.quote(command)
+        )
 
-        self.logger.info('vtysh command => "{}"'.format(command))
+        self.logger.debug("vtysh command => {}".format(shlex.quote(command)))
         output = self.run(vtysh_command)
 
         dbgout = output.strip()
         if dbgout:
             if "\n" in dbgout:
                 dbgout = dbgout.replace("\n", "\n\t")
-                self.logger.info("vtysh result:\n\t{}".format(dbgout))
+                self.logger.debug("vtysh result:\n\t{}".format(dbgout))
             else:
-                self.logger.info('vtysh result: "{}"'.format(dbgout))
+                self.logger.debug('vtysh result: "{}"'.format(dbgout))
 
         if isjson is False:
             return output
@@ -994,7 +1006,7 @@ class TopoRouter(TopoGear):
 
         dbgcmds = commands if is_string(commands) else "\n".join(commands)
         dbgcmds = "\t" + dbgcmds.replace("\n", "\n\t")
-        self.logger.info("vtysh command => FILE:\n{}".format(dbgcmds))
+        self.logger.debug("vtysh command => FILE:\n{}".format(dbgcmds))
 
         res = self.run(vtysh_command)
         os.unlink(fname)
@@ -1003,9 +1015,9 @@ class TopoRouter(TopoGear):
         if dbgres:
             if "\n" in dbgres:
                 dbgres = dbgres.replace("\n", "\n\t")
-                self.logger.info("vtysh result:\n\t{}".format(dbgres))
+                self.logger.debug("vtysh result:\n\t{}".format(dbgres))
             else:
-                self.logger.info('vtysh result: "{}"'.format(dbgres))
+                self.logger.debug('vtysh result: "{}"'.format(dbgres))
         return res
 
     def report_memory_leaks(self, testname):
@@ -1097,7 +1109,7 @@ class TopoHost(TopoGear):
         * `ip`: the IP address (string) for the host interface
         * `defaultRoute`: the default route that will be installed
           (e.g. 'via 10.0.0.1')
-        * `privateDirs`: directories that will be mounted on a different domain
+        * `private_mounts`: directories that will be mounted on a different domain
           (e.g. '/etc/important_dir').
         """
         super(TopoHost, self).__init__(tgen, name, **params)
@@ -1117,10 +1129,10 @@ class TopoHost(TopoGear):
 
     def __str__(self):
         gear = super(TopoHost, self).__str__()
-        gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format(
+        gear += ' TopoHost<ip="{}",defaultRoute="{}",private_mounts="{}">'.format(
             self.params["ip"],
             self.params["defaultRoute"],
-            str(self.params["privateDirs"]),
+            str(self.params["private_mounts"]),
         )
         return gear
 
@@ -1143,10 +1155,10 @@ class TopoExaBGP(TopoHost):
           (e.g. 'via 10.0.0.1')
 
         Note: the different between a host and a ExaBGP peer is that this class
-        has a privateDirs already defined and contains functions to handle ExaBGP
-        things.
+        has a private_mounts already defined and contains functions to handle
+        ExaBGP things.
         """
-        params["privateDirs"] = self.PRIVATE_DIRS
+        params["private_mounts"] = self.PRIVATE_DIRS
         super(TopoExaBGP, self).__init__(tgen, name, **params)
 
     def __str__(self):
@@ -1191,6 +1203,7 @@ class TopoExaBGP(TopoHost):
 # Diagnostic function
 #
 
+
 # Disable linter branch warning. It is expected to have these here.
 # pylint: disable=R0912
 def diagnose_env_linux(rundir):
index d35b908e120ed9851dcefdbdd5c674b0f8465a2c..967f09ecbd900e6e1fb2e4975c9fe573b4d5d4b2 100644 (file)
@@ -9,13 +9,13 @@
 # Network Device Education Foundation, Inc. ("NetDEF")
 #
 
+import configparser
 import difflib
 import errno
 import functools
 import glob
 import json
 import os
-import pdb
 import platform
 import re
 import resource
@@ -24,22 +24,17 @@ import subprocess
 import sys
 import tempfile
 import time
+from collections.abc import Mapping
 from copy import deepcopy
 
 import lib.topolog as topolog
+from lib.micronet_compat import Node
 from lib.topolog import logger
-
-if sys.version_info[0] > 2:
-    import configparser
-    from collections.abc import Mapping
-else:
-    import ConfigParser as configparser
-    from collections import Mapping
+from munet.base import Timeout
 
 from lib import micronet
-from lib.micronet_compat import Node
 
-g_extra_config = {}
+g_pytest_config = None
 
 
 def get_logs_path(rundir):
@@ -352,7 +347,7 @@ def run_and_expect(func, what, count=20, wait=3):
             count, wait
         )
 
-    logger.info(
+    logger.debug(
         "'{}' polling started (interval {} secs, maximum {} tries)".format(
             func_name, wait, count
         )
@@ -366,7 +361,7 @@ def run_and_expect(func, what, count=20, wait=3):
             continue
 
         end_time = time.time()
-        logger.info(
+        logger.debug(
             "'{}' succeeded after {:.2f} seconds".format(
                 func_name, end_time - start_time
             )
@@ -409,7 +404,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None):
             count, wait
         )
 
-    logger.info(
+    logger.debug(
         "'{}' polling started (interval {} secs, maximum wait {} secs)".format(
             func_name, wait, int(wait * count)
         )
@@ -432,7 +427,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None):
             continue
 
         end_time = time.time()
-        logger.info(
+        logger.debug(
             "'{}' succeeded after {:.2f} seconds".format(
                 func_name, end_time - start_time
             )
@@ -474,32 +469,6 @@ def int2dpid(dpid):
         )
 
 
-def pid_exists(pid):
-    "Check whether pid exists in the current process table."
-
-    if pid <= 0:
-        return False
-    try:
-        os.waitpid(pid, os.WNOHANG)
-    except:
-        pass
-    try:
-        os.kill(pid, 0)
-    except OSError as err:
-        if err.errno == errno.ESRCH:
-            # ESRCH == No such process
-            return False
-        elif err.errno == errno.EPERM:
-            # EPERM clearly means there's a process to deny access to
-            return True
-        else:
-            # According to "man 2 kill" possible error values are
-            # (EINVAL, EPERM, ESRCH)
-            raise
-    else:
-        return True
-
-
 def get_textdiff(text1, text2, title1="", title2="", **opts):
     "Returns empty string if same or formatted diff"
 
@@ -1086,7 +1055,7 @@ 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.*"
+        filepattern = logdir + "/" + router + ".asan." + component + ".*"
         logger.debug(
             "Log check for %s on %s, pattern %s\n" % (component, router, filepattern)
         )
@@ -1130,7 +1099,7 @@ def _sysctl_atleast(commander, variable, min_value):
             valstr = " ".join([str(x) for x in min_value])
         else:
             valstr = str(min_value)
-        logger.info("Increasing sysctl %s from %s to %s", variable, cur_val, valstr)
+        logger.debug("Increasing sysctl %s from %s to %s", variable, cur_val, valstr)
         commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr))
 
 
@@ -1161,7 +1130,7 @@ def _sysctl_assure(commander, variable, value):
             valstr = " ".join([str(x) for x in value])
         else:
             valstr = str(value)
-        logger.info("Changing sysctl %s from %s to %s", variable, cur_val, valstr)
+        logger.debug("Changing sysctl %s from %s to %s", variable, cur_val, valstr)
         commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr))
 
 
@@ -1204,7 +1173,7 @@ def rlimit_atleast(rname, min_value, raises=False):
         soft, hard = cval
         if soft < min_value:
             nval = (min_value, hard if min_value < hard else min_value)
-            logger.info("Increasing rlimit %s from %s to %s", rname, cval, nval)
+            logger.debug("Increasing rlimit %s from %s to %s", rname, cval, nval)
             resource.setrlimit(rname, nval)
     except subprocess.CalledProcessError as error:
         logger.warning(
@@ -1215,10 +1184,9 @@ def rlimit_atleast(rname, min_value, raises=False):
 
 
 def fix_netns_limits(ns):
-
     # Maximum read and write socket buffer sizes
-    sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20])
-    sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20])
+    sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2**20])
+    sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2**20])
 
     sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0)
     sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0)
@@ -1277,8 +1245,8 @@ def fix_host_limits():
     sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024)
 
     # Maximum read and write socket buffer sizes
-    sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20)
-    sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20)
+    sysctl_atleast(None, "net.core.rmem_max", 16 * 2**20)
+    sysctl_atleast(None, "net.core.wmem_max", 16 * 2**20)
 
     # Garbage Collection Settings for ARP and Neighbors
     sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024)
@@ -1303,7 +1271,8 @@ def fix_host_limits():
 def setup_node_tmpdir(logdir, name):
     # Cleanup old log, valgrind, and core files.
     subprocess.check_call(
-        "rm -rf {0}/{1}.valgrind.* {1}.*.asan {0}/{1}/".format(logdir, name), shell=True
+        "rm -rf {0}/{1}.valgrind.* {0}/{1}.asan.* {0}/{1}/".format(logdir, name),
+        shell=True,
     )
 
     # Setup the per node directory.
@@ -1318,8 +1287,7 @@ def setup_node_tmpdir(logdir, name):
 class Router(Node):
     "A Node with IPv4/IPv6 forwarding enabled"
 
-    def __init__(self, name, **params):
-
+    def __init__(self, name, *posargs, **params):
         # Backward compatibility:
         #   Load configuration defaults like topogen.
         self.config_defaults = configparser.ConfigParser(
@@ -1335,11 +1303,13 @@ class Router(Node):
             os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini")
         )
 
+        self.perf_daemons = {}
+
         # If this topology is using old API and doesn't have logdir
         # specified, then attempt to generate an unique logdir.
         self.logdir = params.get("logdir")
         if self.logdir is None:
-            self.logdir = get_logs_path(g_extra_config["rundir"])
+            self.logdir = get_logs_path(g_pytest_config.getoption("--rundir"))
 
         if not params.get("logger"):
             # If logger is present topogen has already set this up
@@ -1347,7 +1317,7 @@ class Router(Node):
             l = topolog.get_logger(name, log_level="debug", target=logfile)
             params["logger"] = l
 
-        super(Router, self).__init__(name, **params)
+        super(Router, self).__init__(name, *posargs, **params)
 
         self.daemondir = None
         self.hasmpls = False
@@ -1407,8 +1377,8 @@ class Router(Node):
 
     # pylint: disable=W0221
     # Some params are only meaningful for the parent class.
-    def config(self, **params):
-        super(Router, self).config(**params)
+    def config_host(self, **params):
+        super(Router, self).config_host(**params)
 
         # User did not specify the daemons directory, try to autodetect it.
         self.daemondir = params.get("daemondir")
@@ -1478,11 +1448,11 @@ class Router(Node):
 
         logger.info("%s: stopping %s", self.name, ", ".join([x[0] for x in running]))
         for name, pid in running:
-            logger.info("{}: sending SIGTERM to {}".format(self.name, name))
+            logger.debug("{}: sending SIGTERM to {}".format(self.name, name))
             try:
                 os.kill(pid, signal.SIGTERM)
             except OSError as err:
-                logger.info(
+                logger.debug(
                     "%s: could not kill %s (%s): %s", self.name, name, pid, str(err)
                 )
 
@@ -1526,10 +1496,13 @@ class Router(Node):
     def removeIPs(self):
         for interface in self.intfNames():
             try:
-                self.intf_ip_cmd(interface, "ip address flush " + interface)
+                self.intf_ip_cmd(interface, "ip -4 address flush " + interface)
+                self.intf_ip_cmd(
+                    interface, "ip -6 address flush " + interface + " scope global"
+                )
             except Exception as ex:
                 logger.error("%s can't remove IPs %s", self, str(ex))
-                # pdb.set_trace()
+                # breakpoint()
                 # assert False, "can't remove IPs %s" % str(ex)
 
     def checkCapability(self, daemon, param):
@@ -1560,7 +1533,7 @@ class Router(Node):
                 router_relative = os.path.join(script_dir, self.name, tail)
                 if self.path_exists(router_relative):
                     source = router_relative
-                    self.logger.info(
+                    self.logger.debug(
                         "using router relative configuration: {}".format(source)
                     )
 
@@ -1595,10 +1568,7 @@ class Router(Node):
 
             if (daemon == "zebra") and (self.daemons["mgmtd"] == 0):
                 # Add mgmtd with zebra - if it exists
-                try:
-                    mgmtd_path = os.path.join(self.daemondir, "mgmtd")
-                except:
-                    pdb.set_trace()
+                mgmtd_path = os.path.join(self.daemondir, "mgmtd")
                 if os.path.isfile(mgmtd_path):
                     self.daemons["mgmtd"] = 1
                     self.daemons_options["mgmtd"] = ""
@@ -1606,18 +1576,14 @@ class Router(Node):
 
             if (daemon == "zebra") and (self.daemons["staticd"] == 0):
                 # Add staticd with zebra - if it exists
-                try:
-                    staticd_path = os.path.join(self.daemondir, "staticd")
-                except:
-                    pdb.set_trace()
-
+                staticd_path = os.path.join(self.daemondir, "staticd")
                 if os.path.isfile(staticd_path):
                     self.daemons["staticd"] = 1
                     self.daemons_options["staticd"] = ""
                     # Auto-Started staticd has no config, so it will read from zebra config
 
         else:
-            logger.info("No daemon {} known".format(daemon))
+            logger.warning("No daemon {} known".format(daemon))
         # print "Daemons after:", self.daemons
 
     def runInWindow(self, cmd, title=None):
@@ -1685,8 +1651,7 @@ class Router(Node):
         # used
         self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels")
 
-        shell_routers = g_extra_config["shell"]
-        if "all" in shell_routers or self.name in shell_routers:
+        if g_pytest_config.name_in_option_list(self.name, "--shell"):
             self.run_in_window(os.getenv("SHELL", "bash"), title="sh-%s" % self.name)
 
         if self.daemons["eigrpd"] == 1:
@@ -1703,8 +1668,7 @@ class Router(Node):
 
         status = self.startRouterDaemons(tgen=tgen)
 
-        vtysh_routers = g_extra_config["vtysh"]
-        if "all" in vtysh_routers or self.name in vtysh_routers:
+        if g_pytest_config.name_in_option_list(self.name, "--vtysh"):
             self.run_in_window("vtysh", title="vt-%s" % self.name)
 
         if self.unified_config:
@@ -1724,13 +1688,13 @@ class Router(Node):
     def startRouterDaemons(self, daemons=None, tgen=None):
         "Starts FRR daemons for this router."
 
-        asan_abort = g_extra_config["asan_abort"]
-        gdb_breakpoints = g_extra_config["gdb_breakpoints"]
-        gdb_daemons = g_extra_config["gdb_daemons"]
-        gdb_routers = g_extra_config["gdb_routers"]
-        valgrind_extra = g_extra_config["valgrind_extra"]
-        valgrind_memleaks = g_extra_config["valgrind_memleaks"]
-        strace_daemons = g_extra_config["strace_daemons"]
+        asan_abort = bool(g_pytest_config.option.asan_abort)
+        gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints")
+        gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons")
+        gdb_routers = g_pytest_config.get_option_list("--gdb-routers")
+        valgrind_extra = bool(g_pytest_config.option.valgrind_extra)
+        valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks)
+        strace_daemons = g_pytest_config.get_option_list("--strace-daemons")
 
         # Get global bundle data
         if not self.path_exists("/etc/frr/support_bundle_commands.conf"):
@@ -1754,11 +1718,31 @@ class Router(Node):
         self.reportCores = True
 
         # XXX: glue code forward ported from removed function.
-        if self.version == None:
+        if self.version is None:
             self.version = self.cmd(
                 os.path.join(self.daemondir, "bgpd") + " -v"
             ).split()[2]
             logger.info("{}: running version: {}".format(self.name, self.version))
+
+        perfds = {}
+        perf_options = g_pytest_config.get_option("--perf-options", "-g")
+        for perf in g_pytest_config.get_option("--perf", []):
+            if "," in perf:
+                daemon, routers = perf.split(",", 1)
+                perfds[daemon] = routers.split(",")
+            else:
+                daemon = perf
+                perfds[daemon] = ["all"]
+
+        logd_options = {}
+        for logd in g_pytest_config.get_option("--logd", []):
+            if "," in logd:
+                daemon, routers = logd.split(",", 1)
+                logd_options[daemon] = routers.split(",")
+            else:
+                daemon = logd
+                logd_options[daemon] = ["all"]
+
         # If `daemons` was specified then some upper API called us with
         # specific daemons, otherwise just use our own configuration.
         daemons_list = []
@@ -1770,22 +1754,36 @@ class Router(Node):
                 if self.daemons[daemon] == 1:
                     daemons_list.append(daemon)
 
+        tail_log_files = []
+        check_daemon_files = []
+
         def start_daemon(daemon, extra_opts=None):
             daemon_opts = self.daemons_options.get(daemon, "")
+
+            # get pid and vty filenames and remove the files
+            m = re.match(r"(.* |^)-n (\d+)( ?.*|$)", daemon_opts)
+            dfname = daemon if not m else "{}-{}".format(daemon, m.group(2))
+            runbase = "/var/run/{}/{}".format(self.routertype, dfname)
+            # If this is a new system bring-up remove the pid/vty files, otherwise
+            # do not since apparently presence of the pidfile impacts BGP GR
+            self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase))
+
             rediropt = " > {0}.out 2> {0}.err".format(daemon)
             if daemon == "snmpd":
                 binary = "/usr/sbin/snmpd"
                 cmdenv = ""
                 cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format(
                     daemon_opts
-                ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype)
+                ) + "{}.pid -x /etc/frr/agentx".format(runbase)
+                # check_daemon_files.append(runbase + ".pid")
             else:
                 binary = os.path.join(self.daemondir, daemon)
+                check_daemon_files.extend([runbase + ".pid", runbase + ".vty"])
 
                 cmdenv = "ASAN_OPTIONS="
                 if asan_abort:
-                    cmdenv = "abort_on_error=1:"
-                cmdenv += "log_path={0}/{1}.{2}.asan ".format(
+                    cmdenv += "abort_on_error=1:"
+                cmdenv += "log_path={0}/{1}.asan.{2} ".format(
                     self.logdir, self.name, daemon
                 )
 
@@ -1808,9 +1806,15 @@ class Router(Node):
                         daemon, self.logdir, self.name
                     )
 
-                cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format(
-                    daemon_opts, daemon
-                )
+                cmdopt = "{} --command-log-always ".format(daemon_opts)
+                cmdopt += "--log file:{}.log --log-level debug".format(daemon)
+
+                if daemon in logd_options:
+                    logdopt = logd_options[daemon]
+                    if "all" in logdopt or self.name in logdopt:
+                        tail_log_files.append(
+                            "{}/{}/{}.log".format(self.logdir, self.name, daemon)
+                        )
             if extra_opts:
                 cmdopt += " " + extra_opts
 
@@ -1837,6 +1841,23 @@ class Router(Node):
                 logger.info(
                     "%s: %s %s launched in gdb window", self, self.routertype, daemon
                 )
+            elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]):
+                cmdopt += rediropt
+                cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt])
+                p = self.popen(cmd)
+                self.perf_daemons[daemon] = p
+                if p.poll() and p.returncode:
+                    self.logger.error(
+                        '%s: Failed to launch "%s" (%s) with perf using: %s',
+                        self,
+                        daemon,
+                        p.returncode,
+                        cmd,
+                    )
+                else:
+                    logger.debug(
+                        "%s: %s %s started with perf", self, self.routertype, daemon
+                    )
             else:
                 if daemon != "snmpd":
                     cmdopt += " -d "
@@ -1859,7 +1880,7 @@ class Router(Node):
                         else "",
                     )
                 else:
-                    logger.info("%s: %s %s started", self, self.routertype, daemon)
+                    logger.debug("%s: %s %s started", self, self.routertype, daemon)
 
         # Start mgmtd first
         if "mgmtd" in daemons_list:
@@ -1888,15 +1909,6 @@ class Router(Node):
             while "snmpd" in daemons_list:
                 daemons_list.remove("snmpd")
 
-        if daemons is None:
-            # Fix Link-Local Addresses on initial startup
-            # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
-            _, output, _ = self.cmd_status(
-                "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; echo $i: $mac; [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done",
-                stderr=subprocess.STDOUT,
-            )
-            logger.debug("Set MACs:\n%s", output)
-
         # Now start all the other daemons
         for daemon in daemons_list:
             if self.daemons[daemon] == 0:
@@ -1904,16 +1916,50 @@ class Router(Node):
             start_daemon(daemon)
 
         # Check if daemons are running.
-        rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
-        if re.search(r"No such file or directory", rundaemons):
-            return "Daemons are not running"
+        wait_time = 30 if (gdb_routers or gdb_daemons) else 10
+        timeout = Timeout(wait_time)
+        for remaining in timeout:
+            if not check_daemon_files:
+                break
+            check = check_daemon_files[0]
+            if self.path_exists(check):
+                check_daemon_files.pop(0)
+                continue
+            self.logger.debug("Waiting {}s for {} to appear".format(remaining, check))
+            time.sleep(0.5)
+
+        if check_daemon_files:
+            assert False, "Timeout({}) waiting for {} to appear on {}".format(
+                wait_time, check_daemon_files[0], self.name
+            )
 
         # Update the permissions on the log files
         self.cmd("chown frr:frr -R {}/{}".format(self.logdir, self.name))
         self.cmd("chmod ug+rwX,o+r -R {}/{}".format(self.logdir, self.name))
 
+        if "frr" in logd_options:
+            logdopt = logd_options["frr"]
+            if "all" in logdopt or self.name in logdopt:
+                tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name))
+
+        for tailf in tail_log_files:
+            self.run_in_window("tail -f " + tailf, title=tailf, background=True)
+
         return ""
 
+    def pid_exists(self, pid):
+        if pid <= 0:
+            return False
+        try:
+            # If we are not using PID namespaces then we will be a parent of the pid,
+            # otherwise the init process of the PID namespace will have reaped the proc.
+            os.waitpid(pid, os.WNOHANG)
+        except Exception:
+            pass
+
+        rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False)
+        return rc == 0 or "No such process" not in e
+
     def killRouterDaemons(
         self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1"
     ):
@@ -1933,15 +1979,15 @@ class Router(Node):
                     if re.search(r"%s" % daemon, d):
                         daemonpidfile = d.rstrip()
                         daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip()
-                        if daemonpid.isdigit() and pid_exists(int(daemonpid)):
-                            logger.info(
+                        if daemonpid.isdigit() and self.pid_exists(int(daemonpid)):
+                            logger.debug(
                                 "{}: killing {}".format(
                                     self.name,
                                     os.path.basename(daemonpidfile.rsplit(".", 1)[0]),
                                 )
                             )
-                            os.kill(int(daemonpid), signal.SIGKILL)
-                            if pid_exists(int(daemonpid)):
+                            self.cmd_status("kill -KILL {}".format(daemonpid))
+                            if self.pid_exists(int(daemonpid)):
                                 numRunning += 1
                         while wait and numRunning > 0:
                             sleep(
@@ -1955,7 +2001,7 @@ class Router(Node):
                             for d in dmns[:-1]:
                                 if re.search(r"%s" % daemon, d):
                                     daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
-                                    if daemonpid.isdigit() and pid_exists(
+                                    if daemonpid.isdigit() and self.pid_exists(
                                         int(daemonpid)
                                     ):
                                         logger.info(
@@ -1966,8 +2012,10 @@ class Router(Node):
                                                 ),
                                             )
                                         )
-                                        os.kill(int(daemonpid), signal.SIGKILL)
-                                    if daemonpid.isdigit() and not pid_exists(
+                                        self.cmd_status(
+                                            "kill -KILL {}".format(daemonpid)
+                                        )
+                                    if daemonpid.isdigit() and not self.pid_exists(
                                         int(daemonpid)
                                     ):
                                         numRunning -= 1
@@ -2198,7 +2246,7 @@ class Router(Node):
                 log = self.getStdErr(daemon)
                 if "memstats" in log:
                     # Found memory leak
-                    logger.info(
+                    logger.warning(
                         "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log)
                     )
                     if not leakfound:
index 06c18d7dfae56dcb37f85f75d74048cae39f81d9..921b4e622cb811a06bdd1016efcc9d191ec5e801 100644 (file)
@@ -217,7 +217,9 @@ def test_mgmt_commit_check(request):
             ]
         }
     }
-    result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+    result = verify_rib(
+        tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+    )
     assert (
         result is not True
     ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
@@ -319,7 +321,9 @@ def test_mgmt_commit_abort(request):
             ]
         }
     }
-    result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+    result = verify_rib(
+        tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+    )
     assert (
         result is not True
     ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
@@ -372,7 +376,7 @@ def test_mgmt_delete_config(request):
     assert (
         result is True
     ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
-    
+
     step("Mgmt delete config")
     raw_config = {
         "r1": {
@@ -387,7 +391,9 @@ def test_mgmt_delete_config(request):
     assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
 
     step("Verify that the route is deleted from RIB")
-    result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+    result = verify_rib(
+        tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+    )
     assert (
         result is not True
     ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(tc_name)
@@ -461,7 +467,9 @@ def test_mgmt_chaos_stop_start_frr(request):
     dut = "r1"
     protocol = "static"
     input_dict_4 = {"r1": {"static_routes": [{"network": "192.1.11.200/32"}]}}
-    result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol)
+    result = verify_rib(
+        tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False
+    )
     assert (
         result is not True
     ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
diff --git a/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json
new file mode 100644 (file)
index 0000000..d7053bf
--- /dev/null
@@ -0,0 +1,249 @@
+{
+    "address_types": ["ipv6"],
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {
+        "ipv6": "fd00::",
+        "v6mask": 64
+    },
+    "lo_prefix": {
+        "ipv6": "2001:db8:f::",
+        "v6mask": 128
+    },
+    "routers": {
+        "r1": {
+            "links": {
+                "r4": {"ipv6": "auto", "pim6": "enable"},
+                "r2": {"ipv6": "auto", "pim6": "enable"},
+                "r3": {"ipv6": "auto", "pim6": "enable"},
+                "i1": {"ipv6": "auto", "pim6": "enable"},
+                "i2": {"ipv6": "auto", "pim6": "enable"}
+            },
+
+            "bgp": {
+                "local_as": "100",
+                "router_id": "192.168.1.1",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r4": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                },
+                                "r2": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                },
+                                "r3": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "redistribute": [
+                                {"redist_type": "static"},
+                                {"redist_type": "connected"}
+                            ],
+                            "neighbor": {
+                                "r4": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                },
+                                "r2": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                },
+                                "r3": {
+                                    "dest_link": {
+                                        "r1": {}
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "r2": {
+            "links": {
+                "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"},
+                "r1": {"ipv6": "auto", "pim6": "enable"},
+                "r4": {"ipv6": "auto", "pim6": "enable"}
+            },
+            "bgp": {
+                "local_as": "100",
+                "router_id": "192.168.1.2",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2": {}
+                                    }
+                                },
+                                "r4": {
+                                    "dest_link": {
+                                        "r2": {}
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "redistribute": [
+                                {"redist_type": "static"},
+                                {"redist_type": "connected"}
+                            ],
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2": {}
+                                    }
+                                },
+                                "r4": {
+                                    "dest_link": {
+                                        "r2": {}
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "r3": {
+            "links": {
+                "r1": {"ipv6": "auto", "pim6": "enable"},
+                "r4": {"ipv6": "auto", "pim6": "enable"}
+            },
+            "bgp": {
+                "local_as": "100",
+                "router_id": "192.168.1.3",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r3": {}
+                                    }
+                                },
+                                "r4": {
+                                    "dest_link": {
+                                        "r3": {}
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "redistribute": [
+                                {"redist_type": "static"},
+                                {"redist_type": "connected"}
+                            ],
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r3": {}
+                                    }
+                                },
+                                "r4": {
+                                    "dest_link": {
+                                        "r3": {}
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "r4": {
+            "links": {
+                "r2": {"ipv6": "auto", "pim6": "enable"},
+                "r3": {"ipv6": "auto", "pim6": "enable"},
+                "i4": {"ipv6": "auto", "pim6": "enable"},
+                "r1": {"ipv6": "auto", "pim6": "enable"}
+            },
+            "bgp": {
+                "local_as": "100",
+                "router_id": "192.168.1.4",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                },
+                                "r2": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                },
+                                "r3": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "redistribute": [
+                                {"redist_type": "static"},
+                                {"redist_type": "connected"}
+                            ],
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                },
+                                "r2": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                },
+                                "r3": {
+                                    "dest_link": {
+                                        "r4": {}
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        "i1": {
+            "links": {
+                "r1": {"ipv6": "auto"}
+            }
+        },
+        "i2": {
+            "links": {
+                "r1": {"ipv6": "auto"}
+            }
+        },
+        "i4": {
+            "links": {
+                "r4": {"ipv6": "auto"}
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py
new file mode 100644 (file)
index 0000000..2c4fb4e
--- /dev/null
@@ -0,0 +1,915 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2023 by VMware, Inc. ("VMware")
+#
+
+"""
+Following tests are covered to test_multicast_pim_mld_local_tier_1:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. Verify static MLD group populated when static "ip mld join <grp>" in configured
+2. Verify mroute and upstream populated with correct OIL/IIF with static imld join
+3. Verify local MLD join not allowed for non multicast group
+4. Verify static MLD group removed from DUT while removing "ip mld join" CLI
+5. Verify static MLD groups after removing and adding MLD config
+"""
+
+import os
+import sys
+import time
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from re import search as re_search
+from re import findall as findall
+
+from lib.common_config import (
+    start_topology,
+    write_test_header,
+    write_test_footer,
+    step,
+    kill_router_daemons,
+    start_router_daemons,
+    reset_config_on_routers,
+    do_countdown,
+    apply_raw_config,
+    socat_send_pim6_traffic,
+)
+
+from lib.pim import (
+    create_pim_config,
+    verify_mroutes,
+    verify_upstream_iif,
+    verify_mld_groups,
+    clear_pim6_mroute,
+    McastTesterHelper,
+    verify_pim_neighbors,
+    create_mld_config,
+    verify_mld_groups,
+    verify_local_mld_groups,
+    verify_pim_rp_info,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+r1_r2_links = []
+r1_r3_links = []
+r2_r1_links = []
+r2_r4_links = []
+r3_r1_links = []
+r3_r4_links = []
+r4_r2_links = []
+r4_r3_links = []
+
+pytestmark = [pytest.mark.pim6d, pytest.mark.staticd]
+
+TOPOLOGY = """
+               +-------------------+
+               |                   |
+        i1--- R1-------R2----------R4---i2
+               |                   |
+               +-------R3----------+
+
+
+    Description:
+    i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send MLD
+                                     join and traffic
+    R1 - DUT (LHR)
+    R2 - RP
+    R3 - Transit
+    R4 - (FHR)
+
+"""
+# Global variables
+
+GROUP_RANGE = "ffaa::/16"
+RP_RANGE = "ff00::/8"
+GROUP_RANGE_1 = [
+    "ffaa::1/128",
+    "ffaa::2/128",
+    "ffaa::3/128",
+    "ffaa::4/128",
+    "ffaa::5/128",
+]
+MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"]
+MLD_JOIN_RANGE_2 = [
+    "ff02::1:ff00:0",
+    "ff02::d",
+    "fe80::250:56ff:feb7:d8d5",
+    "2001::4",
+    "2002::5",
+]
+
+
+def setup_module(mod):
+    """
+    Sets up the pytest environment
+
+    * `mod`: module name
+    """
+
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+    logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    json_file = "{}/multicast_mld_local_join.json".format(CWD)
+    tgen = Topogen(json_file, mod.__name__)
+    global topo
+    topo = tgen.json_topo
+    # ... and here it calls Mininet initialization functions.
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start daemons and then start routers
+    start_topology(tgen)
+
+    # Don"t run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Verify PIM neighbors
+    result = verify_pim_neighbors(tgen, topo)
+    assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result)
+
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """Teardown the pytest environment"""
+
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+    logger.info(
+        "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+    )
+    logger.info("=" * 40)
+
+
+#####################################################
+#
+#   Testcases
+#
+#####################################################
+
+
+def test_mld_local_joins_p0(request):
+    """
+    Verify static MLD group populated when static
+    "ipv6 mld join <grp>" in configured
+    """
+
+    tgen = get_topogen()
+    tc_name = request.node.name
+    write_test_header(tc_name)
+
+    # Don"t run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    reset_config_on_routers(tgen)
+
+    step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+    step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+    intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+    intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+                    intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+                }
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("Configure static RP for (ffaa::1-5) as R2")
+
+    input_dict = {
+        "r2": {
+            "pim6": {
+                "rp": [
+                    {
+                        "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+                            "/"
+                        )[0],
+                        "group_addr_range": GROUP_RANGE,
+                    }
+                ]
+            }
+        }
+    }
+    result = create_pim_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interfaces = [intf_r1_i1, intf_r1_i2]
+    for interface in interfaces:
+        result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+        assert result is True, "Testcase {} :Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step("verify mld groups using show ipv6  mld groups")
+    interfaces = [intf_r1_i1, intf_r1_i2]
+    for interface in interfaces:
+        result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+        assert result is True, "Testcase {} :Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    write_test_footer(tc_name)
+
+
+def test_mroute_with_mld_local_joins_p0(request):
+    """
+    Verify mroute and upstream populated with correct OIL/IIF with
+    static mld join
+    """
+    tgen = get_topogen()
+    tc_name = request.node.name
+    write_test_header(tc_name)
+
+    # Don"t run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    reset_config_on_routers(tgen)
+
+    step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+    step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+    step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+    intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+    intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+                    intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}},
+                }
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("Configure static RP for (ffaa::1-5) as R2")
+
+    input_dict = {
+        "r2": {
+            "pim6": {
+                "rp": [
+                    {
+                        "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+                            "/"
+                        )[0],
+                        "group_addr_range": GROUP_RANGE,
+                    }
+                ]
+            }
+        }
+    }
+    result = create_pim_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interfaces = [intf_r1_i1, intf_r1_i2]
+    for interface in interfaces:
+        result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+        assert result is True, "Testcase {} :Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step("verify mld groups using show ipv6  mld groups")
+    interfaces = [intf_r1_i1, intf_r1_i2]
+    for interface in interfaces:
+        result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+        assert result is True, "Testcase {} :Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step("verify RP-info populated in DUT")
+    dut = "r1"
+    rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+    SOURCE = "Static"
+    oif = topo["routers"]["r1"]["links"]["r2"]["interface"]
+    result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+    intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+    intf = topo["routers"]["i4"]["links"]["r4"]["interface"]
+    result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step(
+        "'show ipv6  mroute' showing correct RPF and OIF interface for (*,G)"
+        " and (S,G) entries on all the nodes"
+    )
+    source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+    intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+
+    input_dict_starg = [
+        {
+            "dut": "r1",
+            "src_address": "*",
+            "iif": intf_r1_r2,
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        },
+        {
+            "dut": "r1",
+            "src_address": "*",
+            "iif": intf_r1_r2,
+            "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+        },
+    ]
+
+    input_dict_sg = [
+        {
+            "dut": "r1",
+            "src_address": source_i6,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        },
+        {
+            "dut": "r1",
+            "src_address": source_i6,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+        },
+    ]
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+    step("Verify mroutes not created with local interface ip ")
+    input_dict_local_sg = [
+        {
+            "dut": "r1",
+            "src_address": intf_r1_i1,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        },
+        {
+            "dut": "r1",
+            "src_address": intf_r1_i2,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+        },
+    ]
+
+    for data in input_dict_local_sg:
+        result = verify_mroutes(
+            tgen,
+            data["dut"],
+            data["src_address"],
+            MLD_JOIN_RANGE_1,
+            data["iif"],
+            data["oil"],
+            expected=False,
+        )
+        assert result is not True, (
+            "Testcase {} : Failed Error: {}"
+            "sg created with local interface ip".format(tc_name, result)
+        )
+
+        result = verify_upstream_iif(
+            tgen,
+            data["dut"],
+            data["iif"],
+            data["src_address"],
+            MLD_JOIN_RANGE_1,
+            expected=False,
+        )
+        assert result is not True, (
+            "Testcase {} : Failed Error: {}"
+            "upstream created with local interface ip".format(tc_name, result)
+        )
+
+    write_test_footer(tc_name)
+
+
+def test_remove_add_mld_local_joins_p1(request):
+    """
+    Verify static MLD group removed from DUT while
+    removing "ip mld join" CLI
+    """
+
+    tgen = get_topogen()
+    tc_name = request.node.name
+    write_test_header(tc_name)
+
+    # Don"t run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    reset_config_on_routers(tgen)
+
+    step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+    step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+    step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+    intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+                }
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("Configure static RP for (ffaa::1-5) as R2")
+
+    input_dict = {
+        "r2": {
+            "pim6": {
+                "rp": [
+                    {
+                        "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+                            "/"
+                        )[0],
+                        "group_addr_range": GROUP_RANGE,
+                    }
+                ]
+            }
+        }
+    }
+    result = create_pim_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("verify mld groups using show ipv6  mld groups")
+
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("verify RP-info populated in DUT")
+    dut = "r1"
+    rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+    SOURCE = "Static"
+    oif = topo["routers"]["r1"]["links"]["r2"]["interface"]
+    result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+    intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+    intf = topo["routers"]["i4"]["links"]["r4"]["interface"]
+    result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step(
+        "'show ipv6  mroute' showing correct RPF and OIF interface for (*,G)"
+        " and (S,G) entries on all the nodes"
+    )
+    source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+    intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+    input_dict_starg = [
+        {
+            "dut": "r1",
+            "src_address": "*",
+            "iif": intf_r1_r2,
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        }
+    ]
+
+    input_dict_sg = [
+        {
+            "dut": "r1",
+            "src_address": source_i6,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        }
+    ]
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+    step("Remove MLD join from DUT")
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {
+                        "mld": {
+                            "join": MLD_JOIN_RANGE_1,
+                            "delete_attr": True,
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join removed using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(
+        tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False
+    )
+    assert (
+        result is not True
+    ), "Testcase {} :Failed \n Error: {}" "MLD join still present".format(
+        tc_name, result
+    )
+
+    step("verify mld groups removed using show ipv6  mld groups")
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False)
+    assert (
+        result is not True
+    ), "Testcase {} :Failed \n Error: {}" "MLD groups still present".format(
+        tc_name, result
+    )
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+                expected=False,
+            )
+            assert (
+                result is not True
+            ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen,
+                data["dut"],
+                data["iif"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                expected=False,
+            )
+            assert (
+                result is not True
+            ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+                tc_name, result
+            )
+
+    step("Add MLD join on DUT again")
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {
+                        "mld": {
+                            "join": MLD_JOIN_RANGE_1,
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("verify mld groups using show ipv6  mld groups")
+
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+    write_test_footer(tc_name)
+
+
+def test_remove_add_mld_config_with_local_joins_p1(request):
+    """
+    Verify static MLD groups after removing
+    and adding MLD config
+    """
+
+    tgen = get_topogen()
+    tc_name = request.node.name
+    write_test_header(tc_name)
+
+    # Don"t run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    reset_config_on_routers(tgen)
+
+    step("Enable the PIM on all the interfaces of R1, R2, R3, R4")
+    step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected")
+    step("Enable the MLD on R11 interfac of R1 and configure local mld groups")
+
+    intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+                }
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("Configure static RP for (ffaa::1-5) as R2")
+
+    input_dict = {
+        "r2": {
+            "pim6": {
+                "rp": [
+                    {
+                        "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+                            "/"
+                        )[0],
+                        "group_addr_range": GROUP_RANGE,
+                    }
+                ]
+            }
+        }
+    }
+    result = create_pim_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("verify mld groups using show ipv6  mld groups")
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)")
+    intf_ip = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+    intf = topo["routers"]["i4"]["links"]["r4"]["interface"]
+    result = socat_send_pim6_traffic(tgen, "i4", "UDP6-SEND", MLD_JOIN_RANGE_1, intf)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step(
+        "'show ipv6  mroute' showing correct RPF and OIF interface for (*,G)"
+        " and (S,G) entries on all the nodes"
+    )
+    source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0]
+
+    intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"]
+    input_dict_starg = [
+        {
+            "dut": "r1",
+            "src_address": "*",
+            "iif": intf_r1_r2,
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        }
+    ]
+
+    input_dict_sg = [
+        {
+            "dut": "r1",
+            "src_address": source_i6,
+            "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+            "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+        }
+    ]
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+    step("Remove mld and mld version 2 from DUT interface")
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {intf_r1_i1: {"mld": {"version": "1", "delete": True}}}
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(
+        tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False
+    )
+    assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    step("verify mld groups using show ipv6  mld groups")
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False)
+    assert (
+        result is not True
+    ), "Testcase {} :Failed \n Error: {}" "MLD grsp still present".format(
+        tc_name, result
+    )
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+                expected=False,
+            )
+            assert (
+                result is not True
+            ), "Testcase {} : Failed Error: {}" "mroutes still present".format(
+                tc_name, result
+            )
+
+    step("Add mld and mld version 2 from DUT interface")
+    input_dict = {
+        "r1": {
+            "mld": {
+                "interfaces": {
+                    intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}
+                }
+            }
+        }
+    }
+
+    result = create_mld_config(tgen, topo, input_dict)
+    assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+    step("verify static mld join using show ipv6  mld join")
+    dut = "r1"
+    interface = intf_r1_i1
+    result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("verify mld groups using show ipv6 mld groups")
+    interface = intf_r1_i1
+    result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify mroutes and iff upstream for local mld groups")
+    for input_dict in [input_dict_starg, input_dict_sg]:
+        for data in input_dict:
+            result = verify_mroutes(
+                tgen,
+                data["dut"],
+                data["src_address"],
+                MLD_JOIN_RANGE_1,
+                data["iif"],
+                data["oil"],
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+            result = verify_upstream_iif(
+                tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1
+            )
+            assert result is True, "Testcase {} : Failed Error: {}".format(
+                tc_name, result
+            )
+
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index a76ff2dd9cf195ae98a0fc41f347cefa870288ed..87b04b41beb24e34b88c86367e72460a18762178 100644 (file)
@@ -62,6 +62,7 @@ from lib.common_config import (
     socat_send_mld_join,
     socat_send_pim6_traffic,
     get_frr_ipv6_linklocal,
+    kill_socat,
 )
 from lib.bgp import create_router_bgp
 from lib.pim import (
@@ -158,10 +159,6 @@ def setup_module(mod):
     # Creating configuration from JSON
     build_config_from_json(tgen, tgen.json_topo)
 
-    # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
-    global app_helper
-    app_helper = McastTesterHelper(tgen)
-
     logger.info("Running setup_module() done")
 
 
@@ -172,7 +169,8 @@ def teardown_module():
 
     tgen = get_topogen()
 
-    app_helper.cleanup()
+    # Clean up socat
+    kill_socat(tgen)
 
     # Stop toplogy and Remove tmp files
     tgen.stop_topology()
index ceef68fece03b9b0b451fce14374c835ab2bf709..788a839918b04da4c1c12324c3acd1a354ebd035 100644 (file)
@@ -53,6 +53,7 @@ from lib.common_config import (
     socat_send_mld_join,
     socat_send_pim6_traffic,
     get_frr_ipv6_linklocal,
+    kill_socat,
 )
 from lib.bgp import create_router_bgp
 from lib.pim import (
@@ -149,10 +150,6 @@ def setup_module(mod):
     # Creating configuration from JSON
     build_config_from_json(tgen, tgen.json_topo)
 
-    # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
-    global app_helper
-    app_helper = McastTesterHelper(tgen)
-
     logger.info("Running setup_module() done")
 
 
@@ -163,7 +160,8 @@ def teardown_module():
 
     tgen = get_topogen()
 
-    app_helper.cleanup()
+    # Clean up socat
+    kill_socat(tgen)
 
     # Stop toplogy and Remove tmp files
     tgen.stop_topology()
index 95b4004e14a837e437dc9c194c5d6690c7b55a99..977cd477c87e38fb92c87d5a8a607c25df0e15a8 100755 (executable)
@@ -172,6 +172,9 @@ def teardown_module():
     logger.info("Running teardown_module to delete topology")
     tgen = get_topogen()
 
+    # Clean up socat
+    kill_socat(tgen)
+
     # Stop toplogy and Remove tmp files
     tgen.stop_topology()
 
index 2fedb6e517e58eac224c3fdd3723b4aa644f0e45..a61164baa2c0a24aa22b49fe3dedf53c790c8388 100755 (executable)
@@ -176,6 +176,9 @@ def teardown_module():
     logger.info("Running teardown_module to delete topology")
     tgen = get_topogen()
 
+    # Clean up socat
+    kill_socat(tgen)
+
     # Stop toplogy and Remove tmp files
     tgen.stop_topology()
 
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
new file mode 100644 (file)
index 0000000..715aa1d
--- /dev/null
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
new file mode 100644 (file)
index 0000000..3bbcce1
--- /dev/null
@@ -0,0 +1,51 @@
+{
+  "totalGroups":5,
+  "watermarkLimit":0,
+  "l1-i1-eth1":{
+    "name":"l1-i1-eth1",
+    "state":"up",
+    "address":"10.0.8.2",
+    "index":"*",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "groups":[
+      {
+        "group":"225.1.1.1",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      },
+      {
+        "group":"225.1.1.2",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      },
+      {
+        "group":"225.1.1.3",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      },
+      {
+        "group":"225.1.1.4",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      },
+      {
+        "group":"225.1.1.5",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      }
+    ]
+  }
+}
+
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
new file mode 100644 (file)
index 0000000..876befa
--- /dev/null
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
new file mode 100644 (file)
index 0000000..a3fb496
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "totalGroups":5,
+  "watermarkLimit":0,
+  "l1-i1-eth1":{
+    "name":"l1-i1-eth1",
+    "state":"up",
+    "address":"10.0.8.2",
+    "index":"*",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "groups":[
+      {
+        "group":"225.1.1.5",
+        "timer":"*",
+        "sourcesCount":1,
+        "version":2,
+        "uptime":"*"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
new file mode 100644 (file)
index 0000000..11ac5a0
--- /dev/null
@@ -0,0 +1 @@
+{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json
new file mode 100644 (file)
index 0000000..10ae1af
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  "l1-i1-eth1":{
+    "name":"l1-i1-eth1",
+    "225.1.1.1":{
+      "group":"225.1.1.1",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    },
+    "225.1.1.2":{
+      "group":"225.1.1.2",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    },
+    "225.1.1.3":{
+      "group":"225.1.1.3",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    },
+    "225.1.1.4":{
+      "group":"225.1.1.4",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    },
+    "225.1.1.5":{
+      "group":"225.1.1.5",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    }
+  }
+}
+
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json
new file mode 100644 (file)
index 0000000..7a19975
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "l1-i1-eth1":{
+    "name":"l1-i1-eth1",
+    "225.1.1.4":{
+      "group":"225.1.1.4",
+      "sources":[
+        {
+          "source":"*",
+          "timer":"*",
+          "forwarded":true,
+          "uptime":"*"
+        }
+      ]
+    }
+  }
+}
index 2ffd3a3ac0ef914188eb37449d827ef1b1c358e9..2c1241c0ccc9db342428671dc4c557c3c3a3edbc 100755 (executable)
@@ -42,6 +42,8 @@ import time
 import datetime
 import pytest
 from time import sleep
+import json
+import functools
 
 pytestmark = pytest.mark.pimd
 
@@ -54,8 +56,8 @@ sys.path.append(os.path.join(CWD, "../lib/"))
 
 # pylint: disable=C0413
 # Import topogen and topotest helpers
-from lib.topogen import Topogen, get_topogen
-
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
 from lib.common_config import (
     start_topology,
     write_test_header,
@@ -1510,6 +1512,108 @@ def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request):
         )
         assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
 
+    # IGMP JSON verification
+    step("Verify IGMP group and source JSON for single interface and group")
+    router = tgen.gears["l1"]
+
+    reffile = os.path.join(CWD, "igmp_group_all_detail.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp vrf default groups detail json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP group detailed output on l1 for all interfaces and all groups is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_single_if_group_all_brief.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp vrf default groups l1-i1-eth1 json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP group output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_single_if_group_all_detail.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp vrf default groups l1-i1-eth1 detail json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP group detailed output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_single_if_single_group_brief.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP group output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_single_if_single_group_detail.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 detail json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP group detailed output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_source_single_if_group_all.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp sources l1-i1-eth1 json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
+    reffile = os.path.join(CWD, "igmp_source_single_if_single_group.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp,
+        router,
+        "show ip igmp sources l1-i1-eth1 225.1.1.4 json",
+        expected,
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 and group 225.1.1.4 is not as expected. Expected: {}".format(
+        expected
+    )
+    assert res is None, assertmsg
+
     step(
         "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
         " receiver interface of FRR1"
diff --git a/tests/topotests/munet/__init__.py b/tests/topotests/munet/__init__.py
new file mode 100644 (file)
index 0000000..e1f18e5
--- /dev/null
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module to import various objects to root namespace."""
+from .base import BaseMunet
+from .base import Bridge
+from .base import Commander
+from .base import LinuxNamespace
+from .base import SharedNamespace
+from .base import cmd_error
+from .base import comm_error
+from .base import get_exec_path
+from .base import proc_error
+from .native import L3Bridge
+from .native import L3NamespaceNode
+from .native import Munet
+from .native import to_thread
+
+
+__all__ = [
+    "BaseMunet",
+    "Bridge",
+    "Commander",
+    "L3Bridge",
+    "L3NamespaceNode",
+    "LinuxNamespace",
+    "Munet",
+    "SharedNamespace",
+    "cmd_error",
+    "comm_error",
+    "get_exec_path",
+    "proc_error",
+    "to_thread",
+]
diff --git a/tests/topotests/munet/__main__.py b/tests/topotests/munet/__main__.py
new file mode 100644 (file)
index 0000000..4419ab9
--- /dev/null
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 2 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""The main function for standalone operation."""
+import argparse
+import asyncio
+import logging
+import logging.config
+import os
+import subprocess
+import sys
+
+from . import cli
+from . import parser
+from .base import get_event_loop
+from .cleanup import cleanup_previous
+from .compat import PytestConfig
+
+
+logger = None
+
+
+async def forever():
+    while True:
+        await asyncio.sleep(3600)
+
+
+async def run_and_wait(args, unet):
+    tasks = []
+
+    if not args.topology_only:
+        # add the cmd.wait()s returned from unet.run()
+        tasks += await unet.run()
+
+    if sys.stdin.isatty() and not args.no_cli:
+        # Run an interactive CLI
+        task = cli.async_cli(unet)
+    else:
+        if args.no_wait:
+            logger.info("Waiting for all node cmd to complete")
+            task = asyncio.gather(*tasks, return_exceptions=True)
+        else:
+            logger.info("Waiting on signal to exit")
+            task = asyncio.create_task(forever())
+            task = asyncio.gather(task, *tasks, return_exceptions=True)
+
+    try:
+        await task
+    finally:
+        # Basically we are canceling tasks from unet.run() which are just async calls to
+        # node.cmd_p.wait() so we've stopped waiting for them to complete, but not
+        # actually canceld/killed the cmd_p process.
+        for task in tasks:
+            task.cancel()
+
+
+async def async_main(args, config):
+    status = 3
+
+    # Setup the namespaces and network addressing.
+
+    unet = await parser.async_build_topology(
+        config, rundir=args.rundir, args=args, pytestconfig=PytestConfig(args)
+    )
+    logger.info("Topology up: rundir: %s", unet.rundir)
+
+    try:
+        status = await run_and_wait(args, unet)
+    except KeyboardInterrupt:
+        logger.info("Exiting, received KeyboardInterrupt in async_main")
+    except asyncio.CancelledError as ex:
+        logger.info("task canceled error: %s cleaning up", ex)
+    except Exception as error:
+        logger.info("Exiting, unexpected exception %s", error, exc_info=True)
+    else:
+        logger.info("Exiting normally")
+
+    logger.debug("main: async deleting")
+    try:
+        await unet.async_delete()
+    except KeyboardInterrupt:
+        status = 2
+        logger.warning("Received KeyboardInterrupt while cleaning up.")
+    except Exception as error:
+        status = 2
+        logger.info("Deleting, unexpected exception %s", error, exc_info=True)
+    return status
+
+
+def main(*args):
+    ap = argparse.ArgumentParser(args)
+    cap = ap.add_argument_group(title="Config", description="config related options")
+
+    cap.add_argument("-c", "--config", help="config file (yaml, toml, json, ...)")
+    cap.add_argument(
+        "-d", "--rundir", help="runtime directory for tempfiles, logs, etc"
+    )
+    cap.add_argument(
+        "--kinds-config",
+        help="kinds config file, overrides default search (yaml, toml, json, ...)",
+    )
+    cap.add_argument(
+        "--project-root", help="directory to stop searching for kinds config at"
+    )
+    rap = ap.add_argument_group(title="Runtime", description="runtime related options")
+    rap.add_argument(
+        "-C",
+        "--cleanup",
+        action="store_true",
+        help="Remove the entire rundir (not just node subdirs) prior to running.",
+    )
+    rap.add_argument(
+        "--gdb", metavar="NODE-LIST", help="comma-sep list of hosts to run gdb on"
+    )
+    rap.add_argument(
+        "--gdb-breakpoints",
+        metavar="BREAKPOINT-LIST",
+        help="comma-sep list of breakpoints to set",
+    )
+    rap.add_argument(
+        "--host",
+        action="store_true",
+        help="no isolation for top namespace, bridges exposed to default namespace",
+    )
+    rap.add_argument(
+        "--pcap",
+        metavar="TARGET-LIST",
+        help="comma-sep list of capture targets (NETWORK or NODE:IFNAME)",
+    )
+    rap.add_argument(
+        "--shell", metavar="NODE-LIST", help="comma-sep list of nodes to open shells on"
+    )
+    rap.add_argument(
+        "--stderr",
+        metavar="NODE-LIST",
+        help="comma-sep list of nodes to open windows viewing stderr",
+    )
+    rap.add_argument(
+        "--stdout",
+        metavar="NODE-LIST",
+        help="comma-sep list of nodes to open windows viewing stdout",
+    )
+    rap.add_argument(
+        "--topology-only",
+        action="store_true",
+        help="Do not run any node commands",
+    )
+    rap.add_argument("--unshare-inline", action="store_true", help=argparse.SUPPRESS)
+    rap.add_argument(
+        "--validate-only",
+        action="store_true",
+        help="Validate the config against the schema definition",
+    )
+    rap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+    rap.add_argument(
+        "-V", "--version", action="store_true", help="print the verison number and exit"
+    )
+    eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options")
+    eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)")
+    eap.add_argument(
+        "--no-kill",
+        action="store_true",
+        help="Do not kill previous running processes",
+    )
+    eap.add_argument(
+        "--no-cli", action="store_true", help="Do not run the interactive CLI"
+    )
+    eap.add_argument("--no-wait", action="store_true", help="Exit after commands")
+
+    args = ap.parse_args()
+
+    if args.version:
+        from importlib import metadata  # pylint: disable=C0415
+
+        print(metadata.version("munet"))
+        sys.exit(0)
+
+    rundir = args.rundir if args.rundir else "/tmp/munet"
+    args.rundir = rundir
+
+    if args.cleanup:
+        if os.path.exists(rundir):
+            if not os.path.exists(f"{rundir}/config.json"):
+                logging.critical(
+                    'unsafe: won\'t clean up rundir "%s" as '
+                    "previous config.json not present",
+                    rundir,
+                )
+                sys.exit(1)
+            else:
+                subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True)
+
+    subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+    os.environ["MUNET_RUNDIR"] = rundir
+
+    parser.setup_logging(args)
+
+    global logger  # pylint: disable=W0603
+    logger = logging.getLogger("munet")
+
+    config = parser.get_config(args.config)
+    logger.info("Loaded config from %s", config["config_pathname"])
+    if not config["topology"]["nodes"]:
+        logger.critical("No nodes defined in config file")
+        return 1
+
+    if not args.no_kill:
+        cleanup_previous()
+
+    loop = None
+    status = 4
+    try:
+        parser.validate_config(config, logger, args)
+        if args.validate_only:
+            return 0
+        # Executes the cmd for each node.
+        loop = get_event_loop()
+        status = loop.run_until_complete(async_main(args, config))
+    except KeyboardInterrupt:
+        logger.info("Exiting, received KeyboardInterrupt in main")
+    except Exception as error:
+        logger.info("Exiting, unexpected exception %s", error, exc_info=True)
+    finally:
+        if loop:
+            loop.close()
+
+    return status
+
+
+if __name__ == "__main__":
+    exit_status = main()
+    sys.exit(exit_status)
diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py
new file mode 100644 (file)
index 0000000..eb4b088
--- /dev/null
@@ -0,0 +1,3068 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements core functionality for library or standalone use."""
+import asyncio
+import datetime
+import errno
+import ipaddress
+import logging
+import os
+import platform
+import re
+import readline
+import shlex
+import signal
+import subprocess
+import sys
+import tempfile
+import time as time_mod
+
+from collections import defaultdict
+from pathlib import Path
+from typing import Union
+
+from . import config as munet_config
+from . import linux
+
+
+try:
+    import pexpect
+
+    from pexpect.fdpexpect import fdspawn
+    from pexpect.popen_spawn import PopenSpawn
+
+    have_pexpect = True
+except ImportError:
+    have_pexpect = False
+
+PEXPECT_PROMPT = "PEXPECT_PROMPT>"
+PEXPECT_CONTINUATION_PROMPT = "PEXPECT_PROMPT+"
+
+root_hostname = subprocess.check_output("hostname")
+our_pid = os.getpid()
+
+
+class MunetError(Exception):
+    """A generic munet error."""
+
+
+class CalledProcessError(subprocess.CalledProcessError):
+    """Improved logging subclass of subprocess.CalledProcessError."""
+
+    def __str__(self):
+        o = self.output.strip() if self.output else ""
+        e = self.stderr.strip() if self.stderr else ""
+        s = f"returncode: {self.returncode} command: {self.cmd}"
+        o = "\n\tstdout: " + o if o else ""
+        e = "\n\tstderr: " + e if e else ""
+        return s + o + e
+
+    def __repr__(self):
+        o = self.output.strip() if self.output else ""
+        e = self.stderr.strip() if self.stderr else ""
+        return f"munet.base.CalledProcessError({self.returncode}, {self.cmd}, {o}, {e})"
+
+
+class Timeout:
+    """An object to passively monitor for timeouts."""
+
+    def __init__(self, delta):
+        self.delta = datetime.timedelta(seconds=delta)
+        self.started_on = datetime.datetime.now()
+        self.expires_on = self.started_on + self.delta
+
+    def elapsed(self):
+        elapsed = datetime.datetime.now() - self.started_on
+        return elapsed.total_seconds()
+
+    def is_expired(self):
+        return datetime.datetime.now() > self.expires_on
+
+    def remaining(self):
+        remaining = self.expires_on - datetime.datetime.now()
+        return remaining.total_seconds()
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        remaining = self.remaining()
+        if remaining <= 0:
+            raise StopIteration()
+        return remaining
+
+
+def fsafe_name(name):
+    return "".join(x if x.isalnum() else "_" for x in name)
+
+
+def indent(s):
+    return "\t" + s.replace("\n", "\n\t")
+
+
+def shell_quote(command):
+    """Return command wrapped in single quotes."""
+    if sys.version_info[0] >= 3:
+        return shlex.quote(command)
+    return "'" + command.replace("'", "'\"'\"'") + "'"
+
+
+def cmd_error(rc, o, e):
+    s = f"rc {rc}"
+    o = "\n\tstdout: " + o.strip() if o and o.strip() else ""
+    e = "\n\tstderr: " + e.strip() if e and e.strip() else ""
+    return s + o + e
+
+
+def proc_str(p):
+    if hasattr(p, "args"):
+        args = p.args if isinstance(p.args, str) else " ".join(p.args)
+    else:
+        args = ""
+    return f"proc pid: {p.pid} args: {args}"
+
+
+def proc_error(p, o, e):
+    if hasattr(p, "args"):
+        args = p.args if isinstance(p.args, str) else " ".join(p.args)
+    else:
+        args = ""
+
+    s = f"rc {p.returncode} pid {p.pid}"
+    a = "\n\targs: " + args if args else ""
+    o = "\n\tstdout: " + (o.strip() if o and o.strip() else "*empty*")
+    e = "\n\tstderr: " + (e.strip() if e and e.strip() else "*empty*")
+    return s + a + o + e
+
+
+def comm_error(p):
+    rc = p.poll()
+    assert rc is not None
+    if not hasattr(p, "saved_output"):
+        p.saved_output = p.communicate()
+    return proc_error(p, *p.saved_output)
+
+
+async def acomm_error(p):
+    rc = p.returncode
+    assert rc is not None
+    if not hasattr(p, "saved_output"):
+        p.saved_output = await p.communicate()
+    return proc_error(p, *p.saved_output)
+
+
+def get_kernel_version():
+    kvs = (
+        subprocess.check_output("uname -r", shell=True, text=True).strip().split("-", 1)
+    )
+    kv = kvs[0].split(".")
+    kv = [int(x) for x in kv]
+    return kv
+
+
+def convert_number(value) -> int:
+    """Convert a number value with a possible suffix to an integer.
+
+    >>> convert_number("100k") == 100 * 1024
+    True
+    >>> convert_number("100M") == 100 * 1000 * 1000
+    True
+    >>> convert_number("100Gi") == 100 * 1024 * 1024 * 1024
+    True
+    >>> convert_number("55") == 55
+    True
+    """
+    if value is None:
+        raise ValueError("Invalid value None for convert_number")
+    rate = str(value)
+    base = 1000
+    if rate[-1] == "i":
+        base = 1024
+        rate = rate[:-1]
+    suffix = "KMGTPEZY"
+    index = suffix.find(rate[-1])
+    if index == -1:
+        base = 1024
+        index = suffix.lower().find(rate[-1])
+    if index != -1:
+        rate = rate[:-1]
+    return int(rate) * base ** (index + 1)
+
+
+def is_file_like(fo):
+    return isinstance(fo, int) or hasattr(fo, "fileno")
+
+
+def get_tc_bits_value(user_value):
+    value = convert_number(user_value) / 1000
+    return f"{value:03f}kbit"
+
+
+def get_tc_bytes_value(user_value):
+    # Raw numbers are bytes in tc
+    return convert_number(user_value)
+
+
+def get_tmp_dir(uniq):
+    return os.path.join(tempfile.mkdtemp(), uniq)
+
+
+async def _async_get_exec_path(binary, cmdf, cache):
+    if isinstance(binary, str):
+        bins = [binary]
+    else:
+        bins = binary
+    for b in bins:
+        if b in cache:
+            return cache[b]
+
+        rc, output, _ = await cmdf("which " + b, warn=False)
+        if not rc:
+            cache[b] = os.path.abspath(output.strip())
+            return cache[b]
+    return None
+
+
+def _get_exec_path(binary, cmdf, cache):
+    if isinstance(binary, str):
+        bins = [binary]
+    else:
+        bins = binary
+    for b in bins:
+        if b in cache:
+            return cache[b]
+
+        rc, output, _ = cmdf("which " + b, warn=False)
+        if not rc:
+            cache[b] = os.path.abspath(output.strip())
+            return cache[b]
+    return None
+
+
+def get_event_loop():
+    """Configure and return our non-thread using event loop.
+
+    This function configures a new child watcher to not use threads.
+    Threads cannot be used when we inline unshare a PID namespace.
+    """
+    policy = asyncio.get_event_loop_policy()
+    loop = policy.get_event_loop()
+    owatcher = policy.get_child_watcher()
+    logging.debug(
+        "event_loop_fixture: global policy %s, current loop %s, current watcher %s",
+        policy,
+        loop,
+        owatcher,
+    )
+
+    policy.set_child_watcher(None)
+    owatcher.close()
+
+    try:
+        watcher = asyncio.PidfdChildWatcher()  # pylint: disable=no-member
+    except Exception:
+        watcher = asyncio.SafeChildWatcher()
+    loop = policy.get_event_loop()
+
+    logging.debug(
+        "event_loop_fixture: attaching new watcher %s to loop and setting in policy",
+        watcher,
+    )
+    watcher.attach_loop(loop)
+    policy.set_child_watcher(watcher)
+    policy.set_event_loop(loop)
+    assert asyncio.get_event_loop_policy().get_child_watcher() is watcher
+
+    return loop
+
+
+class Commander:  # pylint: disable=R0904
+    """An object that can execute commands."""
+
+    tmux_wait_gen = 0
+
+    def __init__(self, name, logger=None, unet=None, **kwargs):
+        """Create a Commander.
+
+        Args:
+            name: name of the commander object
+            logger: logger to use for logging commands a defualt is supplied if this
+                is None
+            unet: unet that owns this object, only used by Commander in run_in_window,
+                otherwise can be None.
+        """
+        # del kwargs  # deal with lint warning
+        # logging.warning("Commander: name %s kwargs %s", name, kwargs)
+
+        self.name = name
+        self.unet = unet
+        self.deleting = False
+        self.last = None
+        self.exec_paths = {}
+
+        if not logger:
+            logname = f"munet.{self.__class__.__name__.lower()}.{name}"
+            self.logger = logging.getLogger(logname)
+            self.logger.setLevel(logging.DEBUG)
+        else:
+            self.logger = logger
+
+        super().__init__(**kwargs)
+
+    @property
+    def is_vm(self):
+        return False
+
+    @property
+    def is_container(self):
+        return False
+
+    def set_logger(self, logfile):
+        self.logger = logging.getLogger(__name__ + ".commander." + self.name)
+        self.logger.setLevel(logging.DEBUG)
+        if isinstance(logfile, str):
+            handler = logging.FileHandler(logfile, mode="w")
+        else:
+            handler = logging.StreamHandler(logfile)
+
+        fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format(
+            self.__class__.__name__, self.name
+        )
+        handler.setFormatter(logging.Formatter(fmt=fmtstr))
+        self.logger.addHandler(handler)
+
+    def _get_pre_cmd(self, use_str, use_pty, **kwargs):
+        """Get the pre-user-command values.
+
+        The values returned here should be what is required to cause the user's command
+        to execute in the correct context (e.g., namespace, container, sshremote).
+        """
+        del kwargs
+        del use_pty
+        return "" if use_str else []
+
+    def __str__(self):
+        return f"{self.__class__.__name__}({self.name})"
+
+    async def async_get_exec_path(self, binary):
+        """Return the full path to the binary executable.
+
+        `binary` :: binary name or list of binary names
+        """
+        return await _async_get_exec_path(
+            binary, self.async_cmd_status_nsonly, self.exec_paths
+        )
+
+    def get_exec_path(self, binary):
+        """Return the full path to the binary executable.
+
+        `binary` :: binary name or list of binary names
+        """
+        return _get_exec_path(binary, self.cmd_status_nsonly, self.exec_paths)
+
+    def get_exec_path_host(self, binary):
+        """Return the full path to the binary executable.
+
+        If the object is actually a derived class (e.g., a container) this method will
+        return the exec path for the native namespace rather than the container. The
+        path is the one which the other xxx_host methods will use.
+
+        `binary` :: binary name or list of binary names
+        """
+        return get_exec_path_host(binary)
+
+    def test(self, flags, arg):
+        """Run test binary, with flags and arg."""
+        test_path = self.get_exec_path(["test"])
+        rc, _, _ = self.cmd_status([test_path, flags, arg], warn=False)
+        return not rc
+
+    def test_nsonly(self, flags, arg):
+        """Run test binary, with flags and arg."""
+        test_path = self.get_exec_path(["test"])
+        rc, _, _ = self.cmd_status_nsonly([test_path, flags, arg], warn=False)
+        return not rc
+
+    def path_exists(self, path):
+        """Check if path exists."""
+        return self.test("-e", path)
+
+    async def cleanup_pid(self, pid, kill_pid=None):
+        """Signal a pid to exit with escalating forcefulness."""
+        if kill_pid is None:
+            kill_pid = pid
+
+        for sn in (signal.SIGHUP, signal.SIGKILL):
+            self.logger.debug(
+                "%s: %s %s (wait %s)", self, signal.Signals(sn).name, kill_pid, pid
+            )
+
+            os.kill(kill_pid, sn)
+
+            # No need to wait after this.
+            if sn == signal.SIGKILL:
+                return
+
+            # try each signal, waiting 15 seconds for exit before advancing
+            wait_sec = 30
+            self.logger.debug("%s: waiting %ss for pid to exit", self, wait_sec)
+            for _ in Timeout(wait_sec):
+                try:
+                    status = os.waitpid(pid, os.WNOHANG)
+                    if status == (0, 0):
+                        await asyncio.sleep(0.1)
+                    else:
+                        self.logger.debug("pid %s exited status %s", pid, status)
+                        return
+                except OSError as error:
+                    if error.errno == errno.ECHILD:
+                        self.logger.debug("%s: pid %s was reaped", self, pid)
+                    else:
+                        self.logger.warning(
+                            "%s: error waiting on pid %s: %s", self, pid, error
+                        )
+                    return
+            self.logger.debug("%s: timeout waiting on pid %s to exit", self, pid)
+
+    def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwargs):
+        """Returns pre-command, cmd, and default keyword args."""
+        assert not isinstance(cmd_list, str)
+
+        defaults["shell"] = False
+        pre_cmd_list = self._get_pre_cmd(False, use_pty, ns_only=ns_only, **kwargs)
+        cmd_list = [str(x) for x in cmd_list]
+
+        # os_env = {k: v for k, v in os.environ.items() if k.startswith("MUNET")}
+        # env = {**os_env, **(kwargs["env"] if "env" in kwargs else {})}
+        env = {**(kwargs["env"] if "env" in kwargs else os.environ)}
+        if "MUNET_NODENAME" not in env:
+            env["MUNET_NODENAME"] = self.name
+        kwargs["env"] = env
+
+        defaults.update(kwargs)
+
+        return pre_cmd_list, cmd_list, defaults
+
+    def _common_prologue(self, async_exec, method, cmd, skip_pre_cmd=False, **kwargs):
+        cmd_list = self._get_cmd_as_list(cmd)
+        if method == "_spawn":
+            defaults = {
+                "encoding": "utf-8",
+                "codec_errors": "ignore",
+            }
+        else:
+            defaults = {
+                "stdout": subprocess.PIPE,
+                "stderr": subprocess.PIPE,
+            }
+            if not async_exec:
+                defaults["encoding"] = "utf-8"
+
+        pre_cmd_list, cmd_list, defaults = self._get_sub_args(
+            cmd_list, defaults, **kwargs
+        )
+
+        use_pty = kwargs.get("use_pty", False)
+        if method == "_spawn":
+            # spawn doesn't take "shell" keyword arg
+            if "shell" in defaults:
+                del defaults["shell"]
+            # this is required to avoid receiving a STOPPED signal on expect!
+            if not use_pty:
+                defaults["preexec_fn"] = os.setsid
+            defaults["env"]["PS1"] = "$ "
+
+        self.logger.debug(
+            '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)',
+            self,
+            "XXX" if method == "_spawn" else "",
+            method,
+            cmd_list,
+            pre_cmd_list if not skip_pre_cmd else "",
+            use_pty,
+            defaults,
+        )
+
+        actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list
+        return actual_cmd_list, defaults
+
+    async def _async_popen(self, method, cmd, **kwargs):
+        """Create a new asynchronous subprocess."""
+        acmd, kwargs = self._common_prologue(True, method, cmd, **kwargs)
+        p = await asyncio.create_subprocess_exec(*acmd, **kwargs)
+        return p, acmd
+
+    def _popen(self, method, cmd, **kwargs):
+        """Create a subprocess."""
+        acmd, kwargs = self._common_prologue(False, method, cmd, **kwargs)
+        p = subprocess.Popen(acmd, **kwargs)
+        return p, acmd
+
+    def _fdspawn(self, fo, **kwargs):
+        defaults = {}
+        defaults.update(kwargs)
+
+        if "echo" in defaults:
+            del defaults["echo"]
+
+        if "encoding" not in defaults:
+            defaults["encoding"] = "utf-8"
+            if "codec_errors" not in defaults:
+                defaults["codec_errors"] = "ignore"
+        encoding = defaults["encoding"]
+
+        self.logger.debug("%s: _fdspawn(%s, kwargs: %s)", self, fo, defaults)
+
+        p = fdspawn(fo, **defaults)
+
+        # We don't have TTY like conversions of LF to CRLF
+        p.crlf = os.linesep.encode(encoding)
+
+        # we own the socket now detach the file descriptor to keep it from closing
+        if hasattr(fo, "detach"):
+            fo.detach()
+
+        return p
+
+    def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs):
+        logging.debug(
+            '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s',
+            self,
+            cmd,
+            skip_pre_cmd,
+            use_pty,
+            echo,
+            kwargs,
+        )
+        actual_cmd, defaults = self._common_prologue(
+            False, "_spawn", cmd, skip_pre_cmd=skip_pre_cmd, use_pty=use_pty, **kwargs
+        )
+
+        self.logger.debug(
+            '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)',
+            self,
+            "PopenSpawn" if not use_pty else "pexpect.spawn",
+            actual_cmd,
+            use_pty,
+            echo,
+            defaults,
+        )
+
+        # We don't specify a timeout it defaults to 30s is that OK?
+        if not use_pty:
+            p = PopenSpawn(actual_cmd, **defaults)
+        else:
+            p = pexpect.spawn(actual_cmd[0], actual_cmd[1:], echo=echo, **defaults)
+        return p, actual_cmd
+
+    def spawn(
+        self,
+        cmd,
+        spawned_re,
+        expects=(),
+        sends=(),
+        use_pty=False,
+        logfile=None,
+        logfile_read=None,
+        logfile_send=None,
+        trace=None,
+        **kwargs,
+    ):
+        """Create a spawned send/expect process.
+
+        Args:
+            cmd: list of args to exec/popen with, or an already open socket
+            spawned_re: what to look for to know when done, `spawn` returns when seen
+            expects: a list of regex other than `spawned_re` to look for. Commonly,
+                "ogin:" or "[Pp]assword:"r.
+            sends: what to send when an element of `expects` matches. So e.g., the
+                username or password if thats what corresponding expect matched. Can
+                be the empty string to send nothing.
+            use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+            trace: if true then log send/expects
+            **kwargs - kwargs passed on the _spawn.
+
+        Returns:
+            A pexpect process.
+
+        Raises:
+            pexpect.TIMEOUT, pexpect.EOF as documented in `pexpect`
+            CalledProcessError if EOF is seen and `cmd` exited then
+                raises a CalledProcessError to indicate the failure.
+        """
+        if is_file_like(cmd):
+            assert not use_pty
+            ac = "*socket*"
+            p = self._fdspawn(cmd, **kwargs)
+        else:
+            p, ac = self._spawn(cmd, use_pty=use_pty, **kwargs)
+
+        if logfile:
+            p.logfile = logfile
+        if logfile_read:
+            p.logfile_read = logfile_read
+        if logfile_send:
+            p.logfile_send = logfile_send
+
+        # for spawned shells (i.e., a direct command an not a console)
+        # this is wrong and will cause 2 prompts
+        if not use_pty:
+            # This isn't very nice looking
+            p.echo = False
+            if not is_file_like(cmd):
+                p.isalive = lambda: p.proc.poll() is None
+            if not hasattr(p, "close"):
+                p.close = p.wait
+
+        # Do a quick check to see if we got the prompt right away, otherwise we may be
+        # at a console so we send a \n to re-issue the prompt
+        index = p.expect([spawned_re, pexpect.TIMEOUT, pexpect.EOF], timeout=0.1)
+        if index == 0:
+            assert p.match is not None
+            self.logger.debug(
+                "%s: got spawned_re quick: '%s' matching '%s'",
+                self,
+                p.match.group(0),
+                spawned_re,
+            )
+            return p
+
+        # Now send a CRLF to cause the prompt (or whatever else) to re-issue
+        p.send("\n")
+        try:
+            patterns = [spawned_re, *expects]
+
+            self.logger.debug("%s: expecting: %s", self, patterns)
+
+            while index := p.expect(patterns):
+                if trace:
+                    assert p.match is not None
+                    self.logger.debug(
+                        "%s: got expect: '%s' matching %d '%s', sending '%s'",
+                        self,
+                        p.match.group(0),
+                        index,
+                        patterns[index],
+                        sends[index - 1],
+                    )
+                if sends[index - 1]:
+                    p.send(sends[index - 1])
+
+                self.logger.debug("%s: expecting again: %s", self, patterns)
+            self.logger.debug(
+                "%s: got spawned_re: '%s' matching '%s'",
+                self,
+                p.match.group(0),
+                spawned_re,
+            )
+            return p
+        except pexpect.TIMEOUT:
+            self.logger.error(
+                "%s: TIMEOUT looking for spawned_re '%s' expect buffer so far:\n%s",
+                self,
+                spawned_re,
+                indent(p.buffer),
+            )
+            raise
+        except pexpect.EOF as eoferr:
+            if p.isalive():
+                raise
+            rc = p.status
+            before = indent(p.before)
+            error = CalledProcessError(rc, ac, output=before)
+            self.logger.error(
+                "%s: EOF looking for spawned_re '%s' before EOF:\n%s",
+                self,
+                spawned_re,
+                before,
+            )
+            p.close()
+            raise error from eoferr
+
+    async def shell_spawn(
+        self,
+        cmd,
+        prompt,
+        expects=(),
+        sends=(),
+        use_pty=False,
+        will_echo=False,
+        is_bourne=True,
+        init_newline=False,
+        **kwargs,
+    ):
+        """Create a shell REPL (read-eval-print-loop).
+
+        Args:
+            cmd: shell and list of args to popen with, or an already open socket
+            prompt: the REPL prompt to look for, the function returns when seen
+            expects: a list of regex other than `spawned_re` to look for. Commonly,
+                "ogin:" or "[Pp]assword:"r.
+            sends: what to send when an element of `expects` matches. So e.g., the
+                username or password if thats what corresponding expect matched. Can
+                be the empty string to send nothing.
+            is_bourne: if False then do not modify shell prompt for internal
+                parser friently format, and do not expect continuation prompts.
+            init_newline: send an initial newline for non-bourne shell spawns, otherwise
+                expect the prompt simply from running the command
+            use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+            will_echo: bash is buggy in that it echo's to non-tty unlike any other
+                sh/ksh, set this value to true if running back
+            **kwargs - kwargs passed on the _spawn.
+        """
+        combined_prompt = r"({}|{})".format(re.escape(PEXPECT_PROMPT), prompt)
+
+        assert not is_file_like(cmd) or not use_pty
+        p = self.spawn(
+            cmd,
+            combined_prompt,
+            expects=expects,
+            sends=sends,
+            use_pty=use_pty,
+            echo=False,
+            **kwargs,
+        )
+        assert not p.echo
+
+        if not is_bourne:
+            if init_newline:
+                p.send("\n")
+            return ShellWrapper(p, prompt, will_echo=will_echo)
+
+        ps1 = PEXPECT_PROMPT
+        ps2 = PEXPECT_CONTINUATION_PROMPT
+
+        # Avoid problems when =/usr/bin/env= prints the values
+        ps1p = ps1[:5] + "${UNSET_V}" + ps1[5:]
+        ps2p = ps2[:5] + "${UNSET_V}" + ps2[5:]
+
+        ps1 = re.escape(ps1)
+        ps2 = re.escape(ps2)
+
+        extra = "PAGER=cat; export PAGER; TERM=dumb; unset HISTFILE; set +o emacs +o vi"
+        pchg = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''\n".format(ps1p, ps2p)
+        p.send(pchg)
+        return ShellWrapper(p, ps1, ps2, extra_init_cmd=extra, will_echo=will_echo)
+
+    def popen(self, cmd, **kwargs):
+        """Creates a pipe with the given `command`.
+
+        Args:
+            cmd: `str` or `list` of command to open a pipe with.
+            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+                then will be invoked with `bash -c`, otherwise `command` is a list and
+                will be invoked without a shell.
+
+        Returns:
+            a subprocess.Popen object.
+        """
+        return self._popen("popen", cmd, **kwargs)[0]
+
+    def popen_nsonly(self, cmd, **kwargs):
+        """Creates a pipe with the given `command`.
+
+        Args:
+            cmd: `str` or `list` of command to open a pipe with.
+            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+                then will be invoked with `bash -c`, otherwise `command` is a list and
+                will be invoked without a shell.
+
+        Returns:
+            a subprocess.Popen object.
+        """
+        return self._popen("popen_nsonly", cmd, ns_only=True, **kwargs)[0]
+
+    async def async_popen(self, cmd, **kwargs):
+        """Creates a pipe with the given `command`.
+
+        Args:
+            cmd: `str` or `list` of command to open a pipe with.
+            **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+                `command` is a string then will be invoked with `bash -c`, otherwise
+                `command` is a list and will be invoked without a shell.
+
+        Returns:
+            a asyncio.subprocess.Process object.
+        """
+        p, _ = await self._async_popen("async_popen", cmd, **kwargs)
+        return p
+
+    async def async_popen_nsonly(self, cmd, **kwargs):
+        """Creates a pipe with the given `command`.
+
+        Args:
+            cmd: `str` or `list` of command to open a pipe with.
+            **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+                `command` is a string then will be invoked with `bash -c`, otherwise
+                `command` is a list and will be invoked without a shell.
+
+        Returns:
+            a asyncio.subprocess.Process object.
+        """
+        p, _ = await self._async_popen(
+            "async_popen_nsonly", cmd, ns_only=True, **kwargs
+        )
+        return p
+
+    async def async_cleanup_proc(self, p, pid=None):
+        """Terminate a process started with a popen call.
+
+        Args:
+            p: return value from :py:`async_popen`, :py:`popen`, et al.
+            pid: pid to signal instead of p.pid, typically a child of
+                cmd_p == nsenter.
+
+        Returns:
+            None on success, the ``p`` if multiple timeouts occur even
+            after a SIGKILL sent.
+        """
+        if not p:
+            return None
+
+        if p.returncode is not None:
+            if isinstance(p, subprocess.Popen):
+                o, e = p.communicate()
+            else:
+                o, e = await p.communicate()
+            self.logger.debug(
+                "%s: cmd_p already exited status: %s", self, proc_error(p, o, e)
+            )
+            return None
+
+        if pid is None:
+            pid = p.pid
+
+        self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid)
+        try:
+            # This will SIGHUP and wait a while then SIGKILL and return immediately
+            await self.cleanup_pid(p.pid, pid)
+
+            # Wait another 2 seconds after the possible SIGKILL above for the
+            # parent nsenter to cleanup and exit
+            wait_secs = 2
+            if isinstance(p, subprocess.Popen):
+                o, e = p.communicate(timeout=wait_secs)
+            else:
+                o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs)
+            self.logger.debug(
+                "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e)
+            )
+        except (asyncio.TimeoutError, subprocess.TimeoutExpired):
+            self.logger.warning("%s: SIGKILL timeout", self)
+            return p
+        except Exception as error:
+            self.logger.warning(
+                "%s: kill unexpected exception: %s", self, error, exc_info=True
+            )
+            return p
+        return None
+
+    @staticmethod
+    def _cmd_status_input(stdin):
+        pinput = None
+        if isinstance(stdin, (bytes, str)):
+            pinput = stdin
+            stdin = subprocess.PIPE
+        return pinput, stdin
+
+    def _cmd_status_finish(self, p, c, ac, o, e, raises, warn):
+        rc = p.returncode
+        self.last = (rc, ac, c, o, e)
+        if rc:
+            if warn:
+                self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e))
+            if raises:
+                # error = Exception("stderr: {}".format(stderr))
+                # This annoyingly doesnt' show stderr when printed normally
+                raise CalledProcessError(rc, ac, o, e)
+        return rc, o, e
+
+    def _cmd_status(self, cmds, raises=False, warn=True, stdin=None, **kwargs):
+        """Execute a command."""
+        pinput, stdin = Commander._cmd_status_input(stdin)
+        p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs)
+        o, e = p.communicate(pinput)
+        return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn)
+
+    async def _async_cmd_status(
+        self, cmds, raises=False, warn=True, stdin=None, text=None, **kwargs
+    ):
+        """Execute a command."""
+        pinput, stdin = Commander._cmd_status_input(stdin)
+        p, actual_cmd = await self._async_popen(
+            "async_cmd_status", cmds, stdin=stdin, **kwargs
+        )
+
+        if text is False:
+            encoding = None
+        else:
+            encoding = kwargs.get("encoding", "utf-8")
+
+        if encoding is not None and isinstance(pinput, str):
+            pinput = pinput.encode(encoding)
+        o, e = await p.communicate(pinput)
+        if encoding is not None:
+            o = o.decode(encoding) if o is not None else o
+            e = e.decode(encoding) if e is not None else e
+        return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn)
+
+    def _get_cmd_as_list(self, cmd):
+        """Given a list or string return a list form for execution.
+
+        If `cmd` is a string then the returned list uses bash and looks
+        like this: ["/bin/bash", "-c", cmd]. Some node types override
+        this function if they utilize a different shell as to return
+        a different list of values.
+
+        Args:
+            cmd: list or string representing the command to execute.
+
+        Returns:
+            list of commands to execute.
+        """
+        if not isinstance(cmd, str):
+            cmds = cmd
+        else:
+            # Make sure the code doesn't think `cd` will work.
+            assert not re.match(r"cd(\s*|\s+(\S+))$", cmd)
+            cmds = ["/bin/bash", "-c", cmd]
+        return cmds
+
+    def cmd_nostatus(self, cmd, **kwargs):
+        """Run given command returning output[s].
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+                then will be invoked with `bash -c`, otherwise `command` is a list and
+                will be invoked without a shell.
+
+        Returns:
+            if "stderr" is in kwargs and not equal to subprocess.STDOUT, then
+            both stdout and stderr are returned, otherwise stderr is combined
+            with stdout and only stdout is returned.
+        """
+        #
+        # This method serves as the basis for all derived sync cmd variations, so to
+        # override sync cmd behavior simply override this function and *not* the other
+        # variations, unless you are changing only that variation's behavior
+        #
+
+        # XXX change this back to _cmd_status instead of cmd_status when we
+        # consolidate and cleanup the container overrides of *cmd_* functions
+
+        cmds = cmd
+        if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT:
+            _, o, e = self.cmd_status(cmds, **kwargs)
+            return o, e
+        if "stderr" in kwargs:
+            del kwargs["stderr"]
+        _, o, _ = self.cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs)
+        return o
+
+    def cmd_status(self, cmd, **kwargs):
+        """Run given command returning status and outputs.
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+                then will be invoked with `bash -c`, otherwise `command` is a list and
+                will be invoked without a shell.
+
+        Returns:
+            (status, output, error) are returned
+            status: the returncode of the command.
+            output: stdout as a string from the command.
+            error: stderr as a string from the command.
+        """
+        #
+        # This method serves as the basis for all derived sync cmd variations, so to
+        # override sync cmd behavior simply override this function and *not* the other
+        # variations, unless you are changing only that variation's behavior
+        #
+        return self._cmd_status(cmd, **kwargs)
+
+    def cmd_raises(self, cmd, **kwargs):
+        """Execute a command. Raise an exception on errors.
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+                then will be invoked with `bash -c`, otherwise `command` is a list and
+                will be invoked without a shell.
+
+        Returns:
+            output: stdout as a string from the command.
+
+        Raises:
+            CalledProcessError: on non-zero exit status
+        """
+        _, stdout, _ = self._cmd_status(cmd, raises=True, **kwargs)
+        return stdout
+
+    def cmd_nostatus_nsonly(self, cmd, **kwargs):
+        # Make sure the command runs on the host and not in any container.
+        return self.cmd_nostatus(cmd, ns_only=True, **kwargs)
+
+    def cmd_status_nsonly(self, cmd, **kwargs):
+        # Make sure the command runs on the host and not in any container.
+        return self._cmd_status(cmd, ns_only=True, **kwargs)
+
+    def cmd_raises_nsonly(self, cmd, **kwargs):
+        # Make sure the command runs on the host and not in any container.
+        _, stdout, _ = self._cmd_status(cmd, raises=True, ns_only=True, **kwargs)
+        return stdout
+
+    async def async_cmd_status(self, cmd, **kwargs):
+        """Run given command returning status and outputs.
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+                `cmd` is a string then will be invoked with `bash -c`, otherwise
+                `cmd` is a list and will be invoked without a shell.
+
+        Returns:
+            (status, output, error) are returned
+            status: the returncode of the command.
+            output: stdout as a string from the command.
+            error: stderr as a string from the command.
+        """
+        #
+        # This method serves as the basis for all derived async cmd variations, so to
+        # override async cmd behavior simply override this function and *not* the other
+        # variations, unless you are changing only that variation's behavior
+        #
+        return await self._async_cmd_status(cmd, **kwargs)
+
+    async def async_cmd_nostatus(self, cmd, **kwargs):
+        """Run given command returning output[s].
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+                `cmd` is a string then will be invoked with `bash -c`, otherwise
+                `cmd` is a list and will be invoked without a shell.
+
+        Returns:
+            if "stderr" is in kwargs and not equal to subprocess.STDOUT, then
+            both stdout and stderr are returned, otherwise stderr is combined
+            with stdout and only stdout is returned.
+
+        """
+        # XXX change this back to _async_cmd_status instead of cmd_status when we
+        # consolidate and cleanup the container overrides of *cmd_* functions
+
+        cmds = cmd
+        if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT:
+            _, o, e = await self._async_cmd_status(cmds, **kwargs)
+            return o, e
+        if "stderr" in kwargs:
+            del kwargs["stderr"]
+        _, o, _ = await self._async_cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs)
+        return o
+
+    async def async_cmd_raises(self, cmd, **kwargs):
+        """Execute a command. Raise an exception on errors.
+
+        Args:
+            cmd: `str` or `list` of the command to execute.  If a string is given
+                it is run using a shell, otherwise the list is executed directly
+                as the binary and arguments.
+            **kwargs: kwargs is eventually passed on to create_subprocess_exec. If
+                `cmd` is a string then will be invoked with `bash -c`, otherwise
+                `cmd` is a list and will be invoked without a shell.
+
+        Returns:
+            output: stdout as a string from the command.
+
+        Raises:
+            CalledProcessError: on non-zero exit status
+        """
+        _, stdout, _ = await self._async_cmd_status(cmd, raises=True, **kwargs)
+        return stdout
+
+    async def async_cmd_status_nsonly(self, cmd, **kwargs):
+        # Make sure the command runs on the host and not in any container.
+        return await self._async_cmd_status(cmd, ns_only=True, **kwargs)
+
+    async def async_cmd_raises_nsonly(self, cmd, **kwargs):
+        # Make sure the command runs on the host and not in any container.
+        _, stdout, _ = await self._async_cmd_status(
+            cmd, raises=True, ns_only=True, **kwargs
+        )
+        return stdout
+
+    def cmd_legacy(self, cmd, **kwargs):
+        """Execute a command with stdout and stderr joined, *IGNORES ERROR*."""
+        defaults = {"stderr": subprocess.STDOUT}
+        defaults.update(kwargs)
+        _, stdout, _ = self._cmd_status(cmd, raises=False, **defaults)
+        return stdout
+
+    # Run a command in a new window (gnome-terminal, screen, tmux, xterm)
+    def run_in_window(
+        self,
+        cmd,
+        wait_for=False,
+        background=False,
+        name=None,
+        title=None,
+        forcex=False,
+        new_window=False,
+        tmux_target=None,
+        ns_only=False,
+    ):
+        """Run a command in a new window (TMUX, Screen or XTerm).
+
+        Args:
+            cmd: string to execute.
+            wait_for: True to wait for exit from command or `str` as channel neme to
+                signal on exit, otherwise False
+            background: Do not change focus to new window.
+            title: Title for new pane (tmux) or window (xterm).
+            name: Name of the new window (tmux)
+            forcex: Force use of X11.
+            new_window: Open new window (instead of pane) in TMUX
+            tmux_target: Target for tmux pane.
+
+        Returns:
+            the pane/window identifier from TMUX (depends on `new_window`)
+        """
+        channel = None
+        if isinstance(wait_for, str):
+            channel = wait_for
+        elif wait_for is True:
+            channel = "{}-wait-{}".format(our_pid, Commander.tmux_wait_gen)
+            Commander.tmux_wait_gen += 1
+
+        if forcex or ("TMUX" not in os.environ and "STY" not in os.environ):
+            root_level = False
+        else:
+            root_level = True
+
+        # SUDO: The important thing to note is that with all these methods we are
+        # executing on the users windowing system, so even though we are normally
+        # running as root, we will not be when the command is dispatched. Also
+        # in the case of SCREEN and X11 we need to sudo *back* to the user as well
+        # This is also done by SSHRemote by defualt so we should *not* sudo back
+        # if we are SSHRemote.
+
+        # XXX need to test ssh in screen
+        # XXX need to test ssh in Xterm
+        sudo_path = get_exec_path_host(["sudo"])
+        # This first test case seems same as last but using list instead of string?
+        if self.is_vm and self.use_ssh:  # pylint: disable=E1101
+            if isinstance(cmd, str):
+                cmd = shlex.split(cmd)
+            cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd
+
+            # get the ssh cmd
+            cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)]
+            unet = self.unet  # pylint: disable=E1101
+            uns_cmd = unet._get_pre_cmd(  # pylint: disable=W0212
+                False, True, ns_only=True, root_level=root_level
+            )
+            # get the nsenter for munet
+            nscmd = [
+                sudo_path,
+                *uns_cmd,
+                *cmd,
+            ]
+        else:
+            # This is the command to execute to be inside the namespace.
+            # We are getting into trouble with quoting.
+            # Why aren't we passing in MUNET_RUNDIR?
+            cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}"
+            # We need sudo b/c we are executing as the user inside the window system.
+            sudo_path = get_exec_path_host(["sudo"])
+            nscmd = (
+                sudo_path
+                + " "
+                + self._get_pre_cmd(True, True, ns_only=ns_only, root_level=root_level)
+                + " "
+                + cmd
+            )
+
+        if "TMUX" in os.environ and not forcex:
+            cmd = [get_exec_path_host("tmux")]
+            if new_window:
+                cmd.append("new-window")
+                cmd.append("-P")
+                if name:
+                    cmd.append("-n")
+                    cmd.append(name)
+                if tmux_target:
+                    cmd.append("-t")
+                    cmd.append(tmux_target)
+            else:
+                cmd.append("split-window")
+                cmd.append("-P")
+                cmd.append("-h")
+                if not tmux_target:
+                    tmux_target = os.getenv("TMUX_PANE", "")
+            if background:
+                cmd.append("-d")
+            if tmux_target:
+                cmd.append("-t")
+                cmd.append(tmux_target)
+
+            # nscmd is always added as single string argument
+            if not isinstance(nscmd, str):
+                nscmd = shlex.join(nscmd)
+            if title:
+                nscmd = f"printf '\033]2;{title}\033\\'; {nscmd}"
+            if channel:
+                nscmd = f'trap "tmux wait -S {channel}; exit 0" EXIT; {nscmd}'
+            cmd.append(nscmd)
+
+        elif "STY" in os.environ and not forcex:
+            # wait for not supported in screen for now
+            channel = None
+            cmd = [get_exec_path_host("screen")]
+            if not os.path.exists(
+                "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"])
+            ):
+                # XXX not appropriate for ssh
+                cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd
+
+            if not isinstance(nscmd, str):
+                nscmd = shlex.join(nscmd)
+            cmd.append(nscmd)
+        elif "DISPLAY" in os.environ:
+            cmd = [get_exec_path_host("xterm")]
+            if "SUDO_USER" in os.environ:
+                # Do this b/c making things work as root with xauth seems hard
+                cmd = [
+                    get_exec_path_host("sudo"),
+                    "-Eu",
+                    os.environ["SUDO_USER"],
+                ] + cmd
+            if title:
+                cmd.append("-T")
+                cmd.append(title)
+
+            cmd.append("-e")
+            if isinstance(nscmd, str):
+                cmd.extend(shlex.split(nscmd))
+            else:
+                cmd.extend(nscmd)
+
+            # if channel:
+            #    return self.cmd_raises(cmd, skip_pre_cmd=True)
+            # else:
+            p = commander.popen(
+                cmd,
+                # skip_pre_cmd=True,
+                stdin=None,
+                shell=False,
+            )
+            # We should reap the child and report the error then.
+            time_mod.sleep(2)
+            if p.poll() is not None:
+                self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p))
+            return p
+        else:
+            self.logger.error(
+                "DISPLAY, STY, and TMUX not in environment, can't open window"
+            )
+            raise Exception("Window requestd but TMUX, Screen and X11 not available")
+
+        # pane_info = self.cmd_raises(cmd, skip_pre_cmd=True, ns_only=True).strip()
+        # We are prepending the nsenter command, so use unet.rootcmd
+        pane_info = commander.cmd_raises(cmd).strip()
+
+        # Re-adjust the layout
+        if "TMUX" in os.environ:
+            cmd = [
+                get_exec_path_host("tmux"),
+                "select-layout",
+                "-t",
+                pane_info if not tmux_target else tmux_target,
+                "tiled",
+            ]
+            commander.cmd_status(cmd)
+
+        # Wait here if we weren't handed the channel to wait for
+        if channel and wait_for is True:
+            cmd = [get_exec_path_host("tmux"), "wait", channel]
+            # commander.cmd_status(cmd, skip_pre_cmd=True)
+            commander.cmd_status(cmd)
+
+        return pane_info
+
+    def delete(self):
+        """Calls self.async_delete within an exec loop."""
+        asyncio.run(self.async_delete())
+
+    async def _async_delete(self):
+        """Delete this objects resources.
+
+        This is the actual implementation of the resource cleanup, each class
+        should cleanup it's own resources, generally catching and reporting,
+        but not reraising any exceptions for it's own cleanup, then it should
+        invoke `super()._async_delete() without catching any exceptions raised
+        therein. See other examples in `base.py` or `native.py`
+        """
+        self.logger.info("%s: deleted", self)
+
+    async def async_delete(self):
+        """Delete the Commander (or derived object).
+
+        The actual implementation for any class should be in `_async_delete`
+        new derived classes should look at the documentation for that function.
+        """
+        try:
+            self.deleting = True
+            await self._async_delete()
+        except Exception as error:
+            self.logger.error("%s: error while deleting: %s", self, error)
+
+
+class InterfaceMixin:
+    """A mixin class to support interface functionality."""
+
+    def __init__(self, *args, **kwargs):
+        # del kwargs  # get rid of lint
+        # logging.warning("InterfaceMixin: args: %s kwargs: %s", args, kwargs)
+
+        self._intf_addrs = defaultdict(lambda: [None, None])
+        self.net_intfs = {}
+        self.next_intf_index = 0
+        self.basename = "eth"
+        # self.basename = name + "-eth"
+        super().__init__(*args, **kwargs)
+
+    @property
+    def intfs(self):
+        return sorted(self._intf_addrs.keys())
+
+    @property
+    def networks(self):
+        return sorted(self.net_intfs.keys())
+
+    def get_intf_addr(self, ifname, ipv6=False):
+        if ifname not in self._intf_addrs:
+            return None
+        return self._intf_addrs[ifname][bool(ipv6)]
+
+    def set_intf_addr(self, ifname, ifaddr):
+        ifaddr = ipaddress.ip_interface(ifaddr)
+        self._intf_addrs[ifname][ifaddr.version == 6] = ifaddr
+
+    def net_addr(self, netname, ipv6=False):
+        if netname not in self.net_intfs:
+            return None
+        return self.get_intf_addr(self.net_intfs[netname], ipv6=ipv6)
+
+    def set_intf_basename(self, basename):
+        self.basename = basename
+
+    def get_next_intf_name(self):
+        while True:
+            ifname = self.basename + str(self.next_intf_index)
+            self.next_intf_index += 1
+            if ifname not in self._intf_addrs:
+                break
+        return ifname
+
+    def get_ns_ifname(self, ifname):
+        """Return a namespace unique interface name.
+
+        This function is primarily overriden by L3QemuVM, IOW by any class
+        that doesn't create it's own network namespace and will share that
+        with the root (unet) namespace.
+
+        Args:
+            ifname: the interface name.
+
+        Returns:
+            A name unique to the namespace of this object. By defualt the assumption
+            is the ifname is namespace unique.
+        """
+        return ifname
+
+    def register_interface(self, ifname):
+        if ifname not in self._intf_addrs:
+            self._intf_addrs[ifname] = [None, None]
+
+    def register_network(self, netname, ifname):
+        if netname in self.net_intfs:
+            assert self.net_intfs[netname] == ifname
+        else:
+            self.net_intfs[netname] = ifname
+
+    def get_linux_tc_args(self, ifname, config):
+        """Get interface constraints (jitter, delay, rate) for linux TC.
+
+        The keys and their values are as follows:
+
+        delay (int): number of microseconds
+        jitter (int): number of microseconds
+        jitter-correlation (float): % correlation to previous (default 10%)
+        loss (float): % of loss
+        loss-correlation (float): % correlation to previous (default 0%)
+        rate  (int or str): bits per second, string allows for use of
+            {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000
+        """
+        del ifname  # unused
+
+        netem_args = ""
+
+        def get_number(c, v, d=None):
+            if v not in c or c[v] is None:
+                return d
+            return convert_number(c[v])
+
+        delay = get_number(config, "delay")
+        if delay is not None:
+            netem_args += f" delay {delay}usec"
+
+        jitter = get_number(config, "jitter")
+        if jitter is not None:
+            if not delay:
+                raise ValueError("jitter but no delay specified")
+            jitter_correlation = get_number(config, "jitter-correlation", 10)
+            netem_args += f" {jitter}usec {jitter_correlation}%"
+
+        loss = get_number(config, "loss")
+        if loss is not None:
+            loss_correlation = get_number(config, "loss-correlation", 0)
+            if loss_correlation:
+                netem_args += f" loss {loss}% {loss_correlation}%"
+            else:
+                netem_args += f" loss {loss}%"
+
+        if (o_rate := config.get("rate")) is None:
+            return netem_args, ""
+
+        #
+        # This comment is not correct, but is trying to talk through/learn the
+        # machinery.
+        #
+        # tokens arrive at `rate` into token buffer.
+        # limit - number of bytes that can be queued waiting for tokens
+        #   -or-
+        # latency - maximum amount of time a packet may sit in TBF queue
+        #
+        # So this just allows receiving faster than rate for latency amount of
+        # time, before dropping.
+        #
+        # latency = sizeofbucket(limit) / rate (peakrate?)
+        #
+        #   32kbit
+        # -------- = latency = 320ms
+        #  100kbps
+        #
+        #  -but then-
+        # burst ([token] buffer) the largest number of instantaneous
+        # tokens available (i.e, bucket size).
+
+        tbf_args = ""
+        DEFLIMIT = 1518 * 1
+        DEFBURST = 1518 * 2
+        try:
+            tc_rate = o_rate["rate"]
+            tc_rate = convert_number(tc_rate)
+            limit = convert_number(o_rate.get("limit", DEFLIMIT))
+            burst = convert_number(o_rate.get("burst", DEFBURST))
+        except (KeyError, TypeError):
+            tc_rate = convert_number(o_rate)
+            limit = convert_number(DEFLIMIT)
+            burst = convert_number(DEFBURST)
+        tbf_args += f" rate {tc_rate/1000}kbit"
+        if delay:
+            # give an extra 1/10 of buffer space to handle delay
+            tbf_args += f" limit {limit} burst {burst}"
+        else:
+            tbf_args += f" limit {limit} burst {burst}"
+
+        return netem_args, tbf_args
+
+    def set_intf_constraints(self, ifname, **constraints):
+        """Set interface outbound constraints.
+
+        Set outbound constraints (jitter, delay, rate) for an interface. All arguments
+        may also be passed as a string and will be converted to numerical format. All
+        arguments are also optional. If not specified then that existing constraint will
+        be cleared.
+
+        Args:
+            ifname: the name of the interface
+            delay (int): number of microseconds.
+            jitter (int): number of microseconds.
+            jitter-correlation (float): Percent correlation to previous (default 10%).
+            loss (float): Percent of loss.
+            loss-correlation (float): Percent correlation to previous (default 25%).
+            rate (int): bits per second, string allows for use of
+                {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000.
+        """
+        nsifname = self.get_ns_ifname(ifname)
+        netem_args, tbf_args = self.get_linux_tc_args(nsifname, constraints)
+        count = 1
+        selector = f"root handle {count}:"
+        if netem_args:
+            self.cmd_raises(
+                f"tc qdisc add dev {nsifname} {selector} netem {netem_args}"
+            )
+            count += 1
+            selector = f"parent {count-1}: handle {count}"
+        # Place rate limit after delay otherwise limit/burst too complex
+        if tbf_args:
+            self.cmd_raises(f"tc qdisc add dev {nsifname} {selector} tbf {tbf_args}")
+
+        self.cmd_raises(f"tc qdisc show dev {nsifname}")
+
+
+class LinuxNamespace(Commander, InterfaceMixin):
+    """A linux Namespace.
+
+    An object that creates and executes commands in a linux namespace
+    """
+
+    def __init__(
+        self,
+        name,
+        net=True,
+        mount=True,
+        uts=True,
+        cgroup=False,
+        ipc=False,
+        pid=False,
+        time=False,
+        user=False,
+        unshare_inline=False,
+        set_hostname=True,
+        private_mounts=None,
+        **kwargs,
+    ):
+        """Create a new linux namespace.
+
+        Args:
+            name: Internal name for the namespace.
+            net: Create network namespace.
+            mount: Create network namespace.
+            uts: Create UTS (hostname) namespace.
+            cgroup: Create cgroup namespace.
+            ipc: Create IPC namespace.
+            pid: Create PID namespace, also mounts new /proc.
+            time: Create time namespace.
+            user: Create user namespace, also keeps capabilities.
+            set_hostname: Set the hostname to `name`, uts must also be True.
+            private_mounts: List of strings of the form
+                "[/external/path:]/internal/path. If no external path is specified a
+                tmpfs is mounted on the internal path. Any paths specified are first
+                passed to `mkdir -p`.
+            unshare_inline: Unshare the process itself rather than using a proxy.
+            logger: Passed to superclass.
+        """
+        # logging.warning("LinuxNamespace: name %s kwargs %s", name, kwargs)
+
+        super().__init__(name, **kwargs)
+
+        unet = self.unet
+
+        self.logger.debug("%s: creating", self)
+
+        self.cwd = os.path.abspath(os.getcwd())
+
+        self.nsflags = []
+        self.ifnetns = {}
+        self.uflags = 0
+        self.p_ns_fds = None
+        self.p_ns_fnames = None
+        self.pid_ns = False
+        self.init_pid = None
+        self.unshare_inline = unshare_inline
+        self.nsenter_fork = True
+
+        #
+        # Collect the namespaces to unshare
+        #
+        if hasattr(self, "proc_path") and self.proc_path:  # pylint: disable=no-member
+            pp = Path(self.proc_path)  # pylint: disable=no-member
+        else:
+            pp = unet.proc_path if unet else Path("/proc")
+        pp = pp.joinpath("%P%", "ns")
+
+        flags = ""
+        uflags = 0
+        nslist = []
+        nsflags = []
+        if cgroup:
+            nselm = "cgroup"
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            flags += "C"
+            uflags |= linux.CLONE_NEWCGROUP
+        if ipc:
+            nselm = "ipc"
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            flags += "i"
+            uflags |= linux.CLONE_NEWIPC
+        if mount or pid:
+            # We need a new mount namespace for pid
+            nselm = "mnt"
+            nslist.append(nselm)
+            nsflags.append(f"--mount={pp / nselm}")
+            mount = True
+            flags += "m"
+            uflags |= linux.CLONE_NEWNS
+        if net:
+            nselm = "net"
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            # if pid:
+            #     os.system(f"touch /tmp/netns-{name}")
+            #     cmd.append(f"--net=/tmp/netns-{name}")
+            # else:
+            flags += "n"
+            uflags |= linux.CLONE_NEWNET
+        if pid:
+            self.pid_ns = True
+            # We look for this b/c the unshare pid will share with /sibn/init
+            nselm = "pid_for_children"
+            nslist.append(nselm)
+            nsflags.append(f"--pid={pp / nselm}")
+            flags += "p"
+            uflags |= linux.CLONE_NEWPID
+        if time:
+            nselm = "time"
+            # XXX time_for_children?
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            flags += "T"
+            uflags |= linux.CLONE_NEWTIME
+        if user:
+            nselm = "user"
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            flags += "U"
+            uflags |= linux.CLONE_NEWUSER
+        if uts:
+            nselm = "uts"
+            nslist.append(nselm)
+            nsflags.append(f"--{nselm}={pp / nselm}")
+            flags += "u"
+            uflags |= linux.CLONE_NEWUTS
+
+        assert flags, "LinuxNamespace with no namespaces requested"
+
+        # Should look path up using resources maybe...
+        mutini_path = get_our_script_path("mutini")
+        if not mutini_path:
+            mutini_path = get_our_script_path("mutini.py")
+        assert mutini_path
+        cmd = [mutini_path, f"--unshare-flags={flags}", "-v"]
+        fname = fsafe_name(self.name) + "-mutini.log"
+        fname = (unet or self).rundir.joinpath(fname)
+        stdout = open(fname, "w", encoding="utf-8")
+        stderr = subprocess.STDOUT
+
+        #
+        # Save the current namespace info to compare against later
+        #
+
+        if not unet:
+            nsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist}
+        else:
+            nsdict = {
+                x: os.readlink(f"{unet.proc_path}/{unet.pid}/ns/{x}") for x in nslist
+            }
+
+        #
+        # (A) Basically we need to save the pid of the unshare call for nsenter.
+        #
+        # For `unet is not None` (node case) the level this exists at is based on wether
+        # unet is using a forking nsenter or not. So if unet.nsenter_fork == True then
+        # we need the child pid of the p.pid (child of pid returned to us), otherwise
+        # unet.nsenter_fork == False and we just use p.pid as it will be unshare after
+        # nsenter exec's it.
+        #
+        # For the `unet is None` (unet case) the unshare is at the top level or
+        # non-existent so we always save the returned p.pid. If we are unshare_inline we
+        # won't have a __pre_cmd but we can save our child_pid to kill later, otherwise
+        # we set unet.pid to None b/c there's literally nothing to do.
+        #
+        # ---------------------------------------------------------------------------
+        # Breakdown for nested (non-unet) namespace creation, and what PID
+        # to use for __pre_cmd nsenter use.
+        # ---------------------------------------------------------------------------
+        #
+        # tl;dr
+        #   - for non-inline unshare: Use BBB with pid_for_children, unless none/none
+        #     #then (AAA) returned
+        #   - for inline unshare: use returned pid (AAA) with pid_for_children
+        #
+        # All commands use unet.popen to launch the unshare of mutini or cat.
+        # mutini for PID unshare, otherwise cat. AAA is the returned pid BBB is the
+        # child of the returned.
+        #
+        # Unshare Variant
+        # ---------------
+        #
+        # Here we are running mutini if we are creating new pid namespace workspace,
+        # cat otherwise.
+        #
+        # [PID+PID] pid tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    -   uuu  AAA  nsenter (forking, from unet) (in unet namespaces -pid)
+        # BBB    -   AAA  AAA  unshare --fork --kill-child (forking)
+        # CCC    1   BBB  CCC  mutini (non-forking since it is pid 1 in new namespace)
+        #
+        # Use BBB if we use pid_for_children, CCC for all
+        #
+        # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid
+        # tree looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        # AAA  uuu  AAA  nsenter (forking) (in unet namespaces -pid)
+        # BBB  AAA  AAA  unshare -> cat (from unshare non-forking)
+        #
+        # Use BBB for all
+        #
+        # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid
+        # tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    -   uuu  AAA  nsenter -> unshare --fork --kill-child
+        # BBB    1   AAA  AAA  mutini (non-forking since it is pid 1 in new namespace)
+        #
+        # Use AAA if we use pid_for_children, BBB for all
+        #
+        # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree
+        # looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        # AAA  uuu  AAA  nsenter -> unshare -> cat
+        #
+        # Use AAA for all, there's no BBB
+        #
+        # Inline-Unshare Variant
+        # ----------------------
+        #
+        # For unshare_inline and new PID namespace we have unshared all but our PID
+        # namespace, but our children end up in the new namespace so the fork popen
+        # does is good enough.
+        #
+        # [PID+PID] pid tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    -   uuu  AAA  unshare --fork --kill-child (forking)
+        # BBB    1   AAA  BBB  mutini
+        #
+        # Use AAA if we use pid_for_children, BBB for all
+        #
+        # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid
+        # tree looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        # AAA  uuu  AAA  unshare -> cat
+        #
+        # Use AAA for all
+        #
+        # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid
+        # tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    -   uuu  AAA  unshare --fork --kill-child
+        # BBB    1   AAA  BBB  mutini
+        #
+        # Use AAA if we use pid_for_children, BBB for all
+        #
+        # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree
+        # looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        # AAA  uuu  AAA  unshare -> cat
+        #
+        # Use AAA for all.
+        #
+        #
+        # ---------------------------------------------------------------------------
+        # Breakdown for unet namespace creation, and what PID to use for __pre_cmd
+        # ---------------------------------------------------------------------------
+        #
+        # tl;dr: save returned PID or nothing.
+        #   - for non-inline unshare: Use AAA with pid_for_children (returned pid)
+        #   - for inline unshare: no __precmd as the fork in popen is enough.
+        #
+        # Use commander to launch the unshare mutini/cat (for PID/none
+        # workspace PID) for non-inline case. AAA is the returned pid BBB is the child
+        # of the returned.
+        #
+        # Unshare Variant
+        # ---------------
+        #
+        # Here we are running mutini if we are creating new pid namespace workspace,
+        # cat otherwise.
+        #
+        # [PID] for unet pid creation pid tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    -   uuu  AAA  unshare --fork --kill-child (forking)
+        # BBB    1   AAA  BBB  mutini
+        #
+        # Use AAA if we use pid_for_children, BBB for all
+        #
+        # [none] for unet non-pid, pid tree looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        # AAA  uuu  AAA  unshare -> cat
+        #
+        # Use AAA for all
+        #
+        # Inline-Unshare Variant
+        # -----------------------
+        #
+        # For unshare_inline and new PID namespace we have unshared all but our PID
+        # namespace, but our children end up in the new namespace so the fork in popen
+        # does is good enough.
+        #
+        # [PID] for unet pid creation pid tree looks like this:
+        #
+        # PID  NSPID PPID PGID
+        # uuu    -   N/A  uuu  main unet process
+        # AAA    1   uuu  AAA  mutini
+        #
+        # Save p / p.pid, but don't configure any nsenter, uneeded.
+        #
+        # Use nothing as the fork when doing a popen is enough to be in all the right
+        # namepsaces.
+        #
+        # [none] for unet non-pid, pid tree looks like this:
+        #
+        # PID  PPID PGID
+        # uuu  N/A  uuu  main unet process
+        #
+        # Nothing, no __pre_cmd.
+        #
+        #
+
+        self.ppid = os.getppid()
+        self.unshare_inline = unshare_inline
+        if unshare_inline:
+            assert unet is None
+            self.uflags = uflags
+            #
+            # Open file descriptors for current namespaces for later resotration.
+            #
+            try:
+                kversion = [int(x) for x in platform.release().split("-")[0].split(".")]
+                kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8)
+            except ValueError:
+                kvok = False
+            if (
+                not kvok
+                or sys.version_info[0] < 3
+                or (sys.version_info[0] == 3 and sys.version_info[1] < 9)
+            ):
+                # get list of namespace file descriptors before we unshare
+                self.p_ns_fds = []
+                self.p_ns_fnames = []
+                tmpflags = uflags
+                for i in range(0, 64):
+                    v = 1 << i
+                    if (tmpflags & v) == 0:
+                        continue
+                    tmpflags &= ~v
+                    if v in linux.namespace_files:
+                        path = os.path.join("/proc/self", linux.namespace_files[v])
+                        if os.path.exists(path):
+                            self.p_ns_fds.append(os.open(path, 0))
+                            self.p_ns_fnames.append(f"{path} -> {os.readlink(path)}")
+                            self.logger.debug(
+                                "%s: saving old namespace fd %s (%s)",
+                                self,
+                                self.p_ns_fnames[-1],
+                                self.p_ns_fds[-1],
+                            )
+                    if not tmpflags:
+                        break
+            else:
+                self.p_ns_fds = None
+                self.p_ns_fnames = None
+                self.ppid_fd = linux.pidfd_open(self.ppid)
+
+            self.logger.debug(
+                "%s: unshare to new namespaces %s",
+                self,
+                linux.clone_flag_string(uflags),
+            )
+
+            linux.unshare(uflags)
+
+            if not pid:
+                p = None
+                self.pid = None
+                self.nsenter_fork = False
+            else:
+                # Need to fork to create the PID namespace, but we need to continue
+                # running from the parent so that things like pytest work. We'll execute
+                # a mutini process to manage the child init 1 duties.
+                #
+                # We (the parent pid) can no longer create threads, due to that being
+                # restricted by the kernel. See EINVAL in clone(2).
+                #
+                p = commander.popen(
+                    [mutini_path, "-v"],
+                    stdin=subprocess.PIPE,
+                    stdout=stdout,
+                    stderr=stderr,
+                    text=True,
+                    # new session/pgid so signals don't propagate
+                    start_new_session=True,
+                    shell=False,
+                )
+                self.pid = p.pid
+                self.nsenter_fork = False
+        else:
+            # Using cat and a stdin PIPE is nice as it will exit when we do. However,
+            # we also detach it from the pgid so that signals do not propagate to it.
+            # This is b/c it would exit early (e.g., ^C) then, at least the main munet
+            # proc which has no other processes like frr daemons running, will take the
+            # main network namespace with it, which will remove the bridges and the
+            # veth pair (because the bridge side veth is deleted).
+            self.logger.debug("%s: creating namespace process: %s", self, cmd)
+
+            # Use the parent unet process if we have one this will cause us to inherit
+            # the namespaces correctly even in the non-inline case.
+            parent = self.unet if self.unet else commander
+
+            p = parent.popen(
+                cmd,
+                stdin=subprocess.PIPE,
+                stdout=stdout,
+                stderr=stderr,
+                text=True,
+                start_new_session=not unet,
+                shell=False,
+            )
+
+            # The pid number returned is in the global pid namespace. For unshare_inline
+            # this can be unfortunate b/c our /proc has been remounted in our new pid
+            # namespace and won't contain global pid namespace pids. To solve for this
+            # we get all the pid values for the process below.
+
+            # See (A) above for when we need the child pid.
+            self.logger.debug("%s: namespace process: %s", self, proc_str(p))
+            self.pid = p.pid
+            if unet and unet.nsenter_fork:
+                assert not unet.unshare_inline
+                # Need child pid of p.pid
+                pgrep = unet.rootcmd.get_exec_path("pgrep")
+                # a sing fork was done
+                child_pid = unet.rootcmd.cmd_raises([pgrep, "-o", "-P", str(p.pid)])
+                self.pid = int(child_pid.strip())
+                self.logger.debug("%s: child of namespace process: %s", self, pid)
+
+        self.p = p
+
+        # Let's always have a valid value.
+        if self.pid is None:
+            self.pid = our_pid
+
+        #
+        # Let's find all our pids in the nested PID namespaces
+        #
+        if unet:
+            proc_path = unet.proc_path
+        else:
+            proc_path = self.proc_path if hasattr(self, "proc_path") else "/proc"
+        proc_path = f"{proc_path}/{self.pid}"
+
+        pid_status = open(f"{proc_path}/status", "r", encoding="ascii").read()
+        m = re.search(r"\nNSpid:((?:\t[0-9]+)+)\n", pid_status)
+        self.pids = [int(x) for x in m.group(1).strip().split("\t")]
+        assert self.pids[0] == self.pid
+
+        self.logger.debug("%s: namespace scoped pids: %s", self, self.pids)
+
+        # -----------------------------------------------
+        # Now let's wait until unshare completes it's job
+        # -----------------------------------------------
+        timeout = Timeout(30)
+        if self.pid is not None and self.pid != our_pid:
+            while (not p or not p.poll()) and not timeout.is_expired():
+                # check new namespace values against old (nsdict), unshare
+                # can actually take a bit to complete.
+                for fname in tuple(nslist):
+                    # self.pid will be the global pid b/c we didn't unshare_inline
+                    nspath = f"{proc_path}/ns/{fname}"
+                    try:
+                        nsf = os.readlink(nspath)
+                    except OSError as error:
+                        self.logger.debug(
+                            "unswitched: error (ok) checking %s: %s", nspath, error
+                        )
+                        continue
+                    if nsdict[fname] != nsf:
+                        self.logger.debug(
+                            "switched: original %s current %s", nsdict[fname], nsf
+                        )
+                        nslist.remove(fname)
+                    elif unshare_inline:
+                        logging.warning(
+                            "unshare_inline not unshared %s == %s", nsdict[fname], nsf
+                        )
+                    else:
+                        self.logger.debug(
+                            "unswitched: current %s elapsed: %s", nsf, timeout.elapsed()
+                        )
+                if not nslist:
+                    self.logger.debug(
+                        "all done waiting for unshare after %s", timeout.elapsed()
+                    )
+                    break
+
+                elapsed = int(timeout.elapsed())
+                if elapsed <= 3:
+                    time_mod.sleep(0.1)
+                else:
+                    self.logger.info(
+                        "%s: unshare taking more than %ss: %s", self, elapsed, nslist
+                    )
+                    time_mod.sleep(1)
+
+        if p is not None and p.poll():
+            self.logger.error("%s: namespace process failed: %s", self, comm_error(p))
+            assert p.poll() is None, "unshare failed"
+
+        #
+        # Setup the pre-command to enter the target namespace from the running munet
+        # process using self.pid
+        #
+
+        if pid:
+            nsenter_fork = True
+        elif unet and unet.nsenter_fork:
+            # if unet created a pid namespace we need to enter it since we aren't
+            # entering a child pid namespace we created for the node. Otherwise
+            # we have a /proc remounted under unet, but our process is running in
+            # the root pid namepsace
+            nselm = "pid_for_children"
+            nsflags.append(f"--pid={pp / nselm}")
+            nsenter_fork = True
+        else:
+            # We dont need a fork.
+            nsflags.append("-F")
+            nsenter_fork = False
+
+        # Save nsenter values if running from root namespace
+        # we need this for the unshare_inline case when run externally (e.g., from
+        # within tmux server).
+        root_nsflags = [x.replace("%P%", str(self.pid)) for x in nsflags]
+        self.__root_base_pre_cmd = ["/usr/bin/nsenter", *root_nsflags]
+        self.__root_pre_cmd = list(self.__root_base_pre_cmd)
+
+        if unshare_inline:
+            assert unet is None
+            # We have nothing to do here since our process is now in the correct
+            # namespaces and children will inherit from us, even the PID namespace will
+            # be corrent b/c commands are run by first forking.
+            self.nsenter_fork = False
+            self.nsflags = []
+            self.__base_pre_cmd = []
+        else:
+            # We will use nsenter
+            self.nsenter_fork = nsenter_fork
+            self.nsflags = nsflags
+            self.__base_pre_cmd = list(self.__root_base_pre_cmd)
+
+        self.__pre_cmd = list(self.__base_pre_cmd)
+
+        # Always mark new mount namespaces as recursive private
+        if mount:
+            # if self.p is None and not pid:
+            self.cmd_raises_nsonly("mount --make-rprivate /")
+
+        # We need to remount the procfs for the new PID namespace, since we aren't using
+        # unshare(1) which does that for us.
+        if pid and unshare_inline:
+            assert mount
+            self.cmd_raises_nsonly("mount -t proc proc /proc")
+
+        # We do not want cmd_status in child classes (e.g., container) for
+        # the remaining setup calls in this __init__ function.
+
+        if net:
+            # Remount /sys to pickup any changes in the network, but keep root
+            # /sys/fs/cgroup. This pattern could be made generic and supported for any
+            # overlapping mounts
+            if mount:
+                tmpmnt = f"/tmp/cgm-{self.pid}"
+                self.cmd_status_nsonly(
+                    f"mkdir {tmpmnt} && mount --rbind /sys/fs/cgroup {tmpmnt}"
+                )
+                rc = o = e = None
+                for i in range(0, 10):
+                    rc, o, e = self.cmd_status_nsonly(
+                        "mount -t sysfs sysfs /sys", warn=False
+                    )
+                    if not rc:
+                        break
+                    self.logger.debug(
+                        "got error mounting new sysfs will retry: %s",
+                        cmd_error(rc, o, e),
+                    )
+                    time_mod.sleep(1)
+                else:
+                    raise Exception(cmd_error(rc, o, e))
+
+                self.cmd_status_nsonly(
+                    f"mount --move {tmpmnt} /sys/fs/cgroup && rmdir {tmpmnt}"
+                )
+
+            # Original micronet code
+            # self.cmd_raises_nsonly("mount -t sysfs sysfs /sys")
+            # self.cmd_raises_nsonly(
+            #     "mount -o rw,nosuid,nodev,noexec,relatime "
+            #     "-t cgroup2 cgroup /sys/fs/cgroup"
+            # )
+
+        # Set the hostname to the namespace name
+        if uts and set_hostname:
+            self.cmd_status_nsonly("hostname " + self.name)
+            nroot = subprocess.check_output("hostname")
+            if unshare_inline or (unet and unet.unshare_inline):
+                assert (
+                    root_hostname != nroot
+                ), f'hostname unchanged from "{nroot}" wanted "{self.name}"'
+            else:
+                # Assert that we didn't just change the host hostname
+                assert (
+                    root_hostname == nroot
+                ), f'root hostname "{root_hostname}" changed to "{nroot}"!'
+
+        if private_mounts:
+            if isinstance(private_mounts, str):
+                private_mounts = [private_mounts]
+            for m in private_mounts:
+                s = m.split(":", 1)
+                if len(s) == 1:
+                    self.tmpfs_mount(s[0])
+                else:
+                    self.bind_mount(s[0], s[1])
+
+        # this will fail if running inside the namespace with PID
+        if pid:
+            o = self.cmd_nostatus_nsonly("ls -l /proc/1/ns")
+        else:
+            o = self.cmd_nostatus_nsonly("ls -l /proc/self/ns")
+
+        self.logger.debug("namespaces:\n %s", o)
+
+        # will cache the path, which is important in delete to avoid running a shell
+        # which can hang during cleanup
+        self.ip_path = get_exec_path_host("ip")
+        if net:
+            self.cmd_status_nsonly([self.ip_path, "link", "set", "lo", "up"])
+
+        self.logger.info("%s: created", self)
+
+    def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+        """Get the pre-user-command values.
+
+        The values returned here should be what is required to cause the user's command
+        to execute in the correct context (e.g., namespace, container, sshremote).
+        """
+        del kwargs
+        del ns_only
+        del use_pty
+        pre_cmd = self.__root_pre_cmd if root_level else self.__pre_cmd
+        return shlex.join(pre_cmd) if use_str else list(pre_cmd)
+
+    def tmpfs_mount(self, inner):
+        self.logger.debug("Mounting tmpfs on %s", inner)
+        self.cmd_raises("mkdir -p " + inner)
+        self.cmd_raises("mount -n -t tmpfs tmpfs " + inner)
+
+    def bind_mount(self, outer, inner):
+        self.logger.debug("Bind mounting %s on %s", outer, inner)
+        if commander.test("-f", outer):
+            self.cmd_raises(f"mkdir -p {os.path.dirname(inner)} && touch {inner}")
+        else:
+            if not commander.test("-e", outer):
+                commander.cmd_raises_nsonly(f"mkdir -p {outer}")
+            self.cmd_raises(f"mkdir -p {inner}")
+        self.cmd_raises("mount --rbind {} {} ".format(outer, inner))
+
+    def add_netns(self, ns):
+        self.logger.debug("Adding network namespace %s", ns)
+
+        if os.path.exists("/run/netns/{}".format(ns)):
+            self.logger.warning("%s: Removing existing nsspace %s", self, ns)
+            try:
+                self.delete_netns(ns)
+            except Exception as ex:
+                self.logger.warning(
+                    "%s: Couldn't remove existing nsspace %s: %s",
+                    self,
+                    ns,
+                    str(ex),
+                    exc_info=True,
+                )
+        self.cmd_raises_nsonly([self.ip_path, "netns", "add", ns])
+
+    def delete_netns(self, ns):
+        self.logger.debug("Deleting network namespace %s", ns)
+        self.cmd_raises_nsonly([self.ip_path, "netns", "delete", ns])
+
+    def set_intf_netns(self, intf, ns, up=False):
+        # In case a user hard-codes 1 thinking it "resets"
+        ns = str(ns)
+        if ns == "1":
+            ns = str(self.pid)
+
+        self.logger.debug("Moving interface %s to namespace %s", intf, ns)
+
+        cmd = [self.ip_path, "link", "set", intf, "netns", ns]
+        if up:
+            cmd.append("up")
+        self.intf_ip_cmd(intf, cmd)
+        if ns == str(self.pid):
+            # If we are returning then remove from dict
+            if intf in self.ifnetns:
+                del self.ifnetns[intf]
+        else:
+            self.ifnetns[intf] = ns
+
+    def reset_intf_netns(self, intf):
+        self.logger.debug("Moving interface %s to default namespace", intf)
+        self.set_intf_netns(intf, str(self.pid))
+
+    def intf_ip_cmd(self, intf, cmd):
+        """Run an ip command, considering an interface's possible namespace."""
+        if intf in self.ifnetns:
+            if isinstance(cmd, list):
+                assert cmd[0].endswith("ip")
+                cmd[1:1] = ["-n", self.ifnetns[intf]]
+            else:
+                assert cmd.startswith("ip ")
+                cmd = "ip -n " + self.ifnetns[intf] + cmd[2:]
+        self.cmd_raises_nsonly(cmd)
+
+    def intf_tc_cmd(self, intf, cmd):
+        """Run a tc command, considering an interface's possible namespace."""
+        if intf in self.ifnetns:
+            if isinstance(cmd, list):
+                assert cmd[0].endswith("tc")
+                cmd[1:1] = ["-n", self.ifnetns[intf]]
+            else:
+                assert cmd.startswith("tc ")
+                cmd = "tc -n " + self.ifnetns[intf] + cmd[2:]
+        self.cmd_raises_nsonly(cmd)
+
+    def set_ns_cwd(self, cwd: Union[str, Path]):
+        """Common code for changing pre_cmd and pre_nscmd."""
+        self.logger.debug("%s: new CWD %s", self, cwd)
+        self.__root_pre_cmd = self.__root_base_pre_cmd + ["--wd=" + str(cwd)]
+        if self.__pre_cmd:
+            self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)]
+        elif self.unshare_inline:
+            os.chdir(cwd)
+
+    async def _async_delete(self):
+        if type(self) == LinuxNamespace:  # pylint: disable=C0123
+            self.logger.info("%s: deleting", self)
+        else:
+            self.logger.debug("%s: LinuxNamespace sub-class deleting", self)
+
+        # Signal pid namespace proc to exit
+        if (
+            (self.p is None or self.p.pid != self.pid)
+            and self.pid
+            and self.pid != our_pid
+        ):
+            self.logger.debug(
+                "cleanup pid on separate pid %s from proc pid %s",
+                self.pid,
+                self.p.pid if self.p else None,
+            )
+            await self.cleanup_pid(self.pid)
+
+        if self.p is not None:
+            self.logger.debug("cleanup proc pid %s", self.p.pid)
+            await self.async_cleanup_proc(self.p)
+
+        # return to the previous namespace, need to do this in case anothe munet
+        # is being created, especially when it plans to inherit the parent's (host)
+        # namespace.
+        if self.uflags:
+            logging.info("restoring from inline unshare: cwd: %s", os.getcwd())
+            # This only works in linux>=5.8
+            if self.p_ns_fds is None:
+                self.logger.debug(
+                    "%s: restoring namespaces %s",
+                    self,
+                    linux.clone_flag_string(self.uflags),
+                )
+                # fd = linux.pidfd_open(self.ppid)
+                fd = self.ppid_fd
+                retry = 3
+                for i in range(0, retry):
+                    try:
+                        linux.setns(fd, self.uflags)
+                    except OSError as error:
+                        self.logger.warning(
+                            "%s: could not reset to old namespace fd %s: %s",
+                            self,
+                            fd,
+                            error,
+                        )
+                        if i == retry - 1:
+                            raise
+                        time_mod.sleep(1)
+                os.close(fd)
+            else:
+                while self.p_ns_fds:
+                    fd = self.p_ns_fds.pop()
+                    fname = self.p_ns_fnames.pop()
+                    self.logger.debug(
+                        "%s: restoring namespace from fd %s (%s)", self, fname, fd
+                    )
+                    retry = 3
+                    for i in range(0, retry):
+                        try:
+                            linux.setns(fd, 0)
+                            break
+                        except OSError as error:
+                            self.logger.warning(
+                                "%s: could not reset to old namespace fd %s (%s): %s",
+                                self,
+                                fname,
+                                fd,
+                                error,
+                            )
+                            if i == retry - 1:
+                                raise
+                        time_mod.sleep(1)
+                    os.close(fd)
+                self.p_ns_fds = None
+                self.p_ns_fnames = None
+            logging.info("restored from unshare: cwd: %s", os.getcwd())
+
+        self.__root_base_pre_cmd = ["/bin/false"]
+        self.__base_pre_cmd = ["/bin/false"]
+        self.__root_pre_cmd = ["/bin/false"]
+        self.__pre_cmd = ["/bin/false"]
+
+        await super()._async_delete()
+
+
+class SharedNamespace(Commander):
+    """Share another namespace.
+
+    An object that executes commands in an existing pid's linux namespace
+    """
+
+    def __init__(self, name, pid=None, nsflags=None, **kwargs):
+        """Share a linux namespace.
+
+        Args:
+            name: Internal name for the namespace.
+            pid: PID of the process to share with.
+            nsflags: nsenter flags to pass to inherit namespaces from
+        """
+        super().__init__(name, **kwargs)
+
+        self.logger.debug("%s: Creating", self)
+
+        self.cwd = os.path.abspath(os.getcwd())
+        self.pid = pid if pid is not None else our_pid
+
+        nsflags = (x.replace("%P%", str(self.pid)) for x in nsflags) if nsflags else []
+        self.__base_pre_cmd = ["/usr/bin/nsenter", *nsflags] if nsflags else []
+        self.__pre_cmd = self.__base_pre_cmd
+        self.ip_path = self.get_exec_path("ip")
+
+    def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+        """Get the pre-user-command values.
+
+        The values returned here should be what is required to cause the user's command
+        to execute in the correct context (e.g., namespace, container, sshremote).
+        """
+        del kwargs
+        del ns_only
+        del use_pty
+        assert not root_level
+        return shlex.join(self.__pre_cmd) if use_str else list(self.__pre_cmd)
+
+    def set_ns_cwd(self, cwd: Union[str, Path]):
+        """Common code for changing pre_cmd and pre_nscmd."""
+        self.logger.debug("%s: new CWD %s", self, cwd)
+        self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)]
+
+
+class Bridge(SharedNamespace, InterfaceMixin):
+    """A linux bridge."""
+
+    next_ord = 1
+
+    @classmethod
+    def _get_next_id(cls):
+        # Do not use `cls` here b/c that makes the variable class specific
+        n = Bridge.next_ord
+        Bridge.next_ord = n + 1
+        return n
+
+    def __init__(self, name=None, mtu=None, unet=None, **kwargs):
+        """Create a linux Bridge."""
+        self.id = self._get_next_id()
+        if not name:
+            name = "br{}".format(self.id)
+
+        unet_pid = our_pid if unet.pid is None else unet.pid
+
+        super().__init__(name, pid=unet_pid, nsflags=unet.nsflags, unet=unet, **kwargs)
+
+        self.set_intf_basename(self.name + "-e")
+
+        self.mtu = mtu
+
+        self.logger.debug("Bridge: Creating")
+
+        assert len(self.name) <= 16  # Make sure fits in IFNAMSIZE
+        self.cmd_raises(f"ip link delete {name} || true")
+        self.cmd_raises(f"ip link add {name} type bridge")
+        if self.mtu:
+            self.cmd_raises(f"ip link set {name} mtu {self.mtu}")
+        self.cmd_raises(f"ip link set {name} up")
+
+        self.logger.debug("%s: Created, Running", self)
+
+    def get_ifname(self, netname):
+        return self.net_intfs[netname] if netname in self.net_intfs else None
+
+    async def _async_delete(self):
+        """Stop the bridge (i.e., delete the linux resources)."""
+        if type(self) == Bridge:  # pylint: disable=C0123
+            self.logger.info("%s: deleting", self)
+        else:
+            self.logger.debug("%s: Bridge sub-class deleting", self)
+
+        rc, o, e = await self.async_cmd_status(
+            [self.ip_path, "link", "show", self.name],
+            stdin=subprocess.DEVNULL,
+            start_new_session=True,
+            warn=False,
+        )
+        if not rc:
+            rc, o, e = await self.async_cmd_status(
+                [self.ip_path, "link", "delete", self.name],
+                stdin=subprocess.DEVNULL,
+                start_new_session=True,
+                warn=False,
+            )
+        if rc:
+            self.logger.error(
+                "%s: error deleting bridge %s: %s",
+                self,
+                self.name,
+                cmd_error(rc, o, e),
+            )
+        await super()._async_delete()
+
+
+class BaseMunet(LinuxNamespace):
+    """Munet."""
+
+    def __init__(
+        self,
+        name="munet",
+        isolated=True,
+        pid=True,
+        rundir=None,
+        pytestconfig=None,
+        **kwargs,
+    ):
+        """Create a Munet."""
+        # logging.warning("BaseMunet: %s", name)
+
+        self.hosts = {}
+        self.switches = {}
+        self.links = {}
+        self.macs = {}
+        self.rmacs = {}
+        self.isolated = isolated
+
+        self.cli_server = None
+        self.cli_sockpath = None
+        self.cli_histfile = None
+        self.cli_in_window_cmds = {}
+        self.cli_run_cmds = {}
+
+        #
+        # We need a directory for various files
+        #
+        if not rundir:
+            rundir = "/tmp/munet"
+        self.rundir = Path(rundir)
+
+        #
+        # Always having a global /proc is required to keep things from exploding
+        # complexity with nested new pid namespaces..
+        #
+        if pid:
+            self.proc_path = Path(tempfile.mkdtemp(suffix="-proc", prefix="mu-"))
+            logging.debug("%s: mounting /proc on proc_path %s", name, self.proc_path)
+            linux.mount("proc", str(self.proc_path), "proc")
+        else:
+            self.proc_path = Path("/proc")
+
+        #
+        # Now create a root level commander that works regardless of whether we inline
+        # unshare or not. Save it in the global variable as well
+        #
+
+        if not self.isolated:
+            self.rootcmd = commander
+        elif not pid:
+            nsflags = (
+                f"--mount={self.proc_path / '1/ns/mnt'}",
+                f"--net={self.proc_path / '1/ns/net'}",
+                f"--uts={self.proc_path / '1/ns/uts'}",
+                # f"--ipc={self.proc_path / '1/ns/ipc'}",
+                # f"--time={self.proc_path / '1/ns/time'}",
+                # f"--cgroup={self.proc_path / '1/ns/cgroup'}",
+            )
+            self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags)
+        else:
+            # XXX user
+            nsflags = (
+                # XXX Backing up PID namespace just doesn't work.
+                # f"--pid={self.proc_path / '1/ns/pid_for_children'}",
+                f"--mount={self.proc_path / '1/ns/mnt'}",
+                f"--net={self.proc_path / '1/ns/net'}",
+                f"--uts={self.proc_path / '1/ns/uts'}",
+                # f"--ipc={self.proc_path / '1/ns/ipc'}",
+                # f"--time={self.proc_path / '1/ns/time'}",
+                # f"--cgroup={self.proc_path / '1/ns/cgroup'}",
+            )
+            self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags)
+        global roothost  # pylint: disable=global-statement
+
+        roothost = self.rootcmd
+
+        self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig)
+
+        super().__init__(
+            name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs
+        )
+
+        # This allows us to cleanup any leftover running munet's
+        if "MUNET_PID" in os.environ:
+            if os.environ["MUNET_PID"] != str(our_pid):
+                logging.error(
+                    "Found env MUNET_PID != our pid %s, instead its %s, changing",
+                    our_pid,
+                    os.environ["MUNET_PID"],
+                )
+        os.environ["MUNET_PID"] = str(our_pid)
+
+        # this is for testing purposes do not use
+        if not BaseMunet.g_unet:
+            BaseMunet.g_unet = self
+
+        self.logger.debug("%s: Creating", self)
+
+    def __getitem__(self, key):
+        if key in self.switches:
+            return self.switches[key]
+        return self.hosts[key]
+
+    def add_host(self, name, cls=LinuxNamespace, **kwargs):
+        """Add a host to munet."""
+        self.logger.debug("%s: add_host %s(%s)", self, cls.__name__, name)
+
+        self.hosts[name] = cls(name, unet=self, **kwargs)
+
+        # Create a new mounted FS for tracking nested network namespaces creatd by the
+        # user with `ip netns add`
+
+        # XXX why is this failing with podman???
+        # self.hosts[name].tmpfs_mount("/run/netns")
+
+        return self.hosts[name]
+
+    def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints):
+        """Add a link between switch and node or 2 nodes.
+
+        If constraints are given they are applied to each endpoint. See
+        `InterfaceMixin::set_intf_constraints()` for more info.
+        """
+        isp2p = False
+
+        try:
+            name1 = node1.name
+        except AttributeError:
+            if node1 in self.switches:
+                node1 = self.switches[node1]
+            else:
+                node1 = self.hosts[node1]
+            name1 = node1.name
+
+        try:
+            name2 = node2.name
+        except AttributeError:
+            if node2 in self.switches:
+                node2 = self.switches[node2]
+            else:
+                node2 = self.hosts[node2]
+            name2 = node2.name
+
+        if name1 in self.switches:
+            assert name2 in self.hosts
+        elif name2 in self.switches:
+            assert name1 in self.hosts
+            name1, name2 = name2, name1
+            if1, if2 = if2, if1
+        else:
+            # p2p link
+            assert name1 in self.hosts
+            assert name2 in self.hosts
+            isp2p = True
+
+        lname = "{}:{}-{}:{}".format(name1, if1, name2, if2)
+        self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "")
+        self.links[lname] = (name1, if1, name2, if2)
+
+        # And create the veth now.
+        if isp2p:
+            lhost, rhost = self.hosts[name1], self.hosts[name2]
+            lifname = "i1{:x}".format(lhost.pid)
+
+            # Done at root level
+            nsif1 = lhost.get_ns_ifname(if1)
+            nsif2 = rhost.get_ns_ifname(if2)
+
+            # Use pids[-1] to get the unet scoped pid for hosts
+            self.cmd_raises_nsonly(
+                f"ip link add {lifname} type veth peer name {nsif2}"
+                f" netns {rhost.pids[-1]}"
+            )
+            self.cmd_raises_nsonly(f"ip link set {lifname} netns {lhost.pids[-1]}")
+
+            lhost.cmd_raises_nsonly("ip link set {} name {}".format(lifname, nsif1))
+            if mtu:
+                lhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu))
+            lhost.cmd_raises_nsonly("ip link set {} up".format(nsif1))
+            lhost.register_interface(if1)
+
+            if mtu:
+                rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu))
+            rhost.cmd_raises_nsonly("ip link set {} up".format(nsif2))
+            rhost.register_interface(if2)
+        else:
+            switch = self.switches[name1]
+            rhost = self.hosts[name2]
+
+            nsif1 = switch.get_ns_ifname(if1)
+            nsif2 = rhost.get_ns_ifname(if2)
+
+            if mtu is None:
+                mtu = switch.mtu
+
+            if len(nsif1) > 16:
+                self.logger.error('"%s" len %s > 16', nsif1, len(nsif1))
+            elif len(nsif2) > 16:
+                self.logger.error('"%s" len %s > 16', nsif2, len(nsif2))
+            assert len(nsif1) <= 16 and len(nsif2) <= 16  # Make sure fits in IFNAMSIZE
+
+            self.logger.debug("%s: Creating veth pair for link %s", self, lname)
+
+            # Use pids[-1] to get the unet scoped pid for hosts
+            # switch is already in our namespace so nothing to convert.
+            self.cmd_raises_nsonly(
+                f"ip link add {nsif1} type veth peer name {nsif2}"
+                f" netns {rhost.pids[-1]}"
+            )
+
+            if mtu:
+                # if switch.mtu:
+                #     # the switch interface should match the switch config
+                #     switch.cmd_raises_nsonly(
+                #         "ip link set {} mtu {}".format(if1, switch.mtu)
+                #     )
+                switch.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu))
+                rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu))
+
+            switch.register_interface(if1)
+            rhost.register_interface(if2)
+            rhost.register_network(switch.name, if2)
+
+            switch.cmd_raises_nsonly(f"ip link set {nsif1} master {switch.name}")
+
+            switch.cmd_raises_nsonly(f"ip link set {nsif1} up")
+            rhost.cmd_raises_nsonly(f"ip link set {nsif2} up")
+
+        # Cache the MAC values, and reverse mapping
+        self.get_mac(name1, nsif1)
+        self.get_mac(name2, nsif2)
+
+        # Setup interface constraints if provided
+        if intf_constraints:
+            node1.set_intf_constraints(if1, **intf_constraints)
+            node2.set_intf_constraints(if2, **intf_constraints)
+
+    def add_switch(self, name, cls=Bridge, **kwargs):
+        """Add a switch to munet."""
+        self.logger.debug("%s: add_switch %s(%s)", self, cls.__name__, name)
+        self.switches[name] = cls(name, unet=self, **kwargs)
+        return self.switches[name]
+
+    def get_mac(self, name, ifname):
+        if name in self.hosts:
+            dev = self.hosts[name]
+        else:
+            dev = self.switches[name]
+
+        nsifname = self.get_ns_ifname(ifname)
+
+        if (name, ifname) not in self.macs:
+            _, output, _ = dev.cmd_status_nsonly("ip -o link show " + nsifname)
+            m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output)
+            mac = m.group(2)
+            self.macs[(name, ifname)] = mac
+            self.rmacs[mac] = (name, ifname)
+
+        return self.macs[(name, ifname)]
+
+    async def _delete_link(self, lname):
+        rname, rif = self.links[lname][2:4]
+        host = self.hosts[rname]
+        nsrif = host.get_ns_ifname(rif)
+
+        self.logger.debug("%s: Deleting veth pair for link %s", self, lname)
+        rc, o, e = await host.async_cmd_status_nsonly(
+            [self.ip_path, "link", "delete", nsrif],
+            stdin=subprocess.DEVNULL,
+            start_new_session=True,
+            warn=False,
+        )
+        if rc:
+            self.logger.error("Err del veth pair %s: %s", lname, cmd_error(rc, o, e))
+
+    async def _delete_links(self):
+        # for x in self.links:
+        #     await self._delete_link(x)
+        return await asyncio.gather(*[self._delete_link(x) for x in self.links])
+
+    async def _async_delete(self):
+        """Delete the munet topology."""
+        # logger = self.logger if False else logging
+        logger = self.logger
+        if type(self) == BaseMunet:  # pylint: disable=C0123
+            logger.info("%s: deleting.", self)
+        else:
+            logger.debug("%s: BaseMunet sub-class deleting.", self)
+
+        logger.debug("Deleting links")
+        try:
+            await self._delete_links()
+        except Exception as error:
+            logger.error("%s: error deleting links: %s", self, error, exc_info=True)
+
+        logger.debug("Deleting hosts and bridges")
+        try:
+            # Delete hosts and switches, wait for them all to complete
+            # even if there is an exception.
+            htask = [x.async_delete() for x in self.hosts.values()]
+            stask = [x.async_delete() for x in self.switches.values()]
+            await asyncio.gather(*htask, *stask, return_exceptions=True)
+        except Exception as error:
+            logger.error(
+                "%s: error deleting hosts and switches: %s", self, error, exc_info=True
+            )
+
+        self.links = {}
+        self.hosts = {}
+        self.switches = {}
+
+        try:
+            if self.cli_server:
+                self.cli_server.cancel()
+                self.cli_server = None
+            if self.cli_sockpath:
+                await self.async_cmd_status(
+                    "rm -rf " + os.path.dirname(self.cli_sockpath)
+                )
+                self.cli_sockpath = None
+        except Exception as error:
+            logger.error(
+                "%s: error cli server or sockpaths: %s", self, error, exc_info=True
+            )
+
+        try:
+            if self.cli_histfile:
+                readline.write_history_file(self.cli_histfile)
+                self.cli_histfile = None
+        except Exception as error:
+            logger.error(
+                "%s: error saving history file: %s", self, error, exc_info=True
+            )
+
+        # XXX for some reason setns during the delete is changing our dir to /.
+        cwd = os.getcwd()
+
+        try:
+            await super()._async_delete()
+        except Exception as error:
+            logger.error(
+                "%s: error deleting parent classes: %s", self, error, exc_info=True
+            )
+        os.chdir(cwd)
+
+        try:
+            if self.proc_path and str(self.proc_path) != "/proc":
+                logger.debug("%s: umount, remove proc_path %s", self, self.proc_path)
+                linux.umount(str(self.proc_path), 0)
+                os.rmdir(self.proc_path)
+        except Exception as error:
+            logger.warning(
+                "%s: error umount and removing proc_path %s: %s",
+                self,
+                self.proc_path,
+                error,
+                exc_info=True,
+            )
+            try:
+                linux.umount(str(self.proc_path), linux.MNT_DETACH)
+            except Exception as error2:
+                logger.error(
+                    "%s: error umount with detach proc_path %s: %s",
+                    self,
+                    self.proc_path,
+                    error2,
+                    exc_info=True,
+                )
+
+        if BaseMunet.g_unet == self:
+            BaseMunet.g_unet = None
+
+
+BaseMunet.g_unet = None
+
+if True:  # pylint: disable=using-constant-test
+
+    class ShellWrapper:
+        """A Read-Execute-Print-Loop (REPL) interface.
+
+        A newline or prompt changing command should be sent to the
+        spawned child prior to creation as the `prompt` will be `expect`ed
+        """
+
+        def __init__(
+            self,
+            spawn,
+            prompt,
+            continuation_prompt=None,
+            extra_init_cmd=None,
+            will_echo=False,
+            escape_ansi=False,
+        ):
+            self.echo = will_echo
+            self.escape = (
+                re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") if escape_ansi else None
+            )
+
+            logging.debug(
+                'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s',
+                prompt,
+                will_echo,
+                spawn.echo,
+            )
+
+            self.child = spawn
+            if self.child.echo:
+                logging.info("Setting child to echo")
+                self.child.setecho(False)
+                self.child.waitnoecho()
+                assert not self.child.echo
+
+            self.prompt = prompt
+            self.cont_prompt = continuation_prompt
+
+            # Use expect_exact if we can as it should be faster
+            self.expects = [prompt]
+            if re.escape(prompt) == prompt and hasattr(self.child, "expect_exact"):
+                self._expectf = self.child.expect_exact
+            else:
+                self._expectf = self.child.expect
+            if continuation_prompt:
+                self.expects.append(continuation_prompt)
+                if re.escape(continuation_prompt) != continuation_prompt:
+                    self._expectf = self.child.expect
+
+            if extra_init_cmd:
+                self.expect_prompt()
+                self.child.sendline(extra_init_cmd)
+            self.expect_prompt()
+
+        def expect_prompt(self, timeout=-1):
+            return self._expectf(self.expects, timeout=timeout)
+
+        def run_command(self, command, timeout=-1):
+            """Pexpect REPLWrapper compatible run_command.
+
+            This will split `command` into lines and feed each one to the shell.
+
+            Args:
+                command: string of commands separated by newlines, a trailing
+                    newline will cause and empty line to be sent.
+                timeout: pexpect timeout value.
+            """
+            lines = command.splitlines()
+            if command[-1] == "\n":
+                lines.append("")
+            output = ""
+            index = 0
+            for line in lines:
+                self.child.sendline(line)
+                index = self.expect_prompt(timeout=timeout)
+                output += self.child.before
+
+            if index:
+                if hasattr(self.child, "kill"):
+                    self.child.kill(signal.SIGINT)
+                else:
+                    self.child.send("\x03")
+                self.expect_prompt(timeout=30 if self.child.timeout is None else -1)
+                raise ValueError("Continuation prompt found at end of commands")
+
+            if self.escape:
+                output = self.escape.sub("", output)
+
+            return output
+
+        def cmd_nostatus(self, cmd, timeout=-1):
+            r"""Execute a shell command.
+
+            Returns:
+                (strip/cleaned \r) output
+            """
+            output = self.run_command(cmd, timeout)
+            output = output.replace("\r\n", "\n")
+            if self.echo:
+                # remove the command
+                idx = output.find(cmd)
+                if idx == -1:
+                    logging.warning(
+                        "Didn't find command ('%s') in expected output ('%s')",
+                        cmd,
+                        output,
+                    )
+                else:
+                    # Remove up to and including the command from the output stream
+                    output = output[idx + len(cmd) :]
+
+            return output.replace("\r", "").strip()
+
+        def cmd_status(self, cmd, timeout=-1):
+            r"""Execute a shell command.
+
+            Returns:
+                status and (strip/cleaned \r) output
+            """
+            # Run the command getting the output
+            output = self.cmd_nostatus(cmd, timeout)
+
+            # Now get the status
+            scmd = "echo $?"
+            rcstr = self.run_command(scmd)
+            rcstr = rcstr.replace("\r\n", "\n")
+            if self.echo:
+                # remove the command
+                idx = rcstr.find(scmd)
+                if idx == -1:
+                    if self.echo:
+                        logging.warning(
+                            "Didn't find status ('%s') in expected output ('%s')",
+                            scmd,
+                            rcstr,
+                        )
+                    try:
+                        rc = int(rcstr)
+                    except Exception:
+                        rc = 255
+                else:
+                    rcstr = rcstr[idx + len(scmd) :].strip()
+            try:
+                rc = int(rcstr)
+            except ValueError as error:
+                logging.error(
+                    "%s: error with expected status output: %s: %s",
+                    self,
+                    error,
+                    rcstr,
+                    exc_info=True,
+                )
+                rc = 255
+            return rc, output
+
+        def cmd_raises(self, cmd, timeout=-1):
+            r"""Execute a shell command.
+
+            Returns:
+                (strip/cleaned \r) ouptut
+
+            Raises:
+               CalledProcessError: on non-zero exit status
+            """
+            rc, output = self.cmd_status(cmd, timeout)
+            if rc:
+                raise CalledProcessError(rc, cmd, output)
+            return output
+
+
+# ---------------------------
+# Root level utility function
+# ---------------------------
+
+
+def get_exec_path(binary):
+    return commander.get_exec_path(binary)
+
+
+def get_exec_path_host(binary):
+    return commander.get_exec_path(binary)
+
+
+def get_our_script_path(script):
+    # would be nice to find this w/o using a path lookup
+    sdir = os.path.dirname(os.path.abspath(__file__))
+    spath = os.path.join(sdir, script)
+    if os.path.exists(spath):
+        return spath
+    return get_exec_path(script)
+
+
+commander = Commander("munet")
+roothost = None
diff --git a/tests/topotests/munet/cleanup.py b/tests/topotests/munet/cleanup.py
new file mode 100644 (file)
index 0000000..c641cda
--- /dev/null
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""Provides functionality to cleanup processes on posix systems."""
+import glob
+import logging
+import os
+import signal
+
+
+def get_pids_with_env(has_var, has_val=None):
+    result = {}
+    for pidenv in glob.iglob("/proc/*/environ"):
+        pid = pidenv.split("/")[2]
+        try:
+            with open(pidenv, "rb") as rfb:
+                envlist = [
+                    x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0")
+                ]
+                envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist]
+                envdict = dict(envlist)
+                if has_var not in envdict:
+                    continue
+                if has_val is None:
+                    result[pid] = envdict
+                elif envdict[has_var] == str(has_val):
+                    result[pid] = envdict
+        except Exception:
+            # E.g., process exited and files are gone
+            pass
+    return result
+
+
+def _kill_piddict(pids_by_upid, sig):
+    ourpid = str(os.getpid())
+    for upid, pids in pids_by_upid:
+        logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid)
+        for pid in pids:
+            try:
+                if pid != ourpid:
+                    cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read()
+                    cmdline = cmdline.replace("\x00", " ")
+                    logging.info("killing proc %s (%s)", pid, cmdline)
+                    os.kill(int(pid), sig)
+            except Exception:
+                pass
+
+
+def _get_our_pids():
+    ourpid = str(os.getpid())
+    piddict = get_pids_with_env("MUNET_PID", ourpid)
+    pids = [x for x in piddict if x != ourpid]
+    if pids:
+        return {ourpid: pids}
+    return {}
+
+
+def _get_other_pids():
+    piddict = get_pids_with_env("MUNET_PID")
+    unet_pids = {d["MUNET_PID"] for d in piddict.values()}
+    pids_by_upid = {p: set() for p in unet_pids}
+    for pid, envdict in piddict.items():
+        unet_pid = envdict["MUNET_PID"]
+        pids_by_upid[unet_pid].add(pid)
+    # Filter out any child pid sets whos munet pid is still running
+    return {x: y for x, y in pids_by_upid.items() if x not in y}
+
+
+def _get_pids_by_upid(ours):
+    if ours:
+        return _get_our_pids()
+    return _get_other_pids()
+
+
+def _cleanup_pids(ours):
+    pids_by_upid = _get_pids_by_upid(ours).items()
+    if not pids_by_upid:
+        return
+
+    t = "current" if ours else "previous"
+    logging.info("Reaping %s munet processes", t)
+
+    # _kill_piddict(pids_by_upid, signal.SIGTERM)
+
+    # # Give them 5 second to exit cleanly
+    # logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
+    # for _ in range(0, 5):
+    #     pids_by_upid = _get_pids_by_upid(ours).items()
+    #     if not pids_by_upid:
+    #         return
+    #     time.sleep(1)
+
+    pids_by_upid = _get_pids_by_upid(ours).items()
+    _kill_piddict(pids_by_upid, signal.SIGKILL)
+
+
+def cleanup_current():
+    """Attempt to cleanup preview runs.
+
+    Currently this only scans for old processes.
+    """
+    _cleanup_pids(True)
+
+
+def cleanup_previous():
+    """Attempt to cleanup preview runs.
+
+    Currently this only scans for old processes.
+    """
+    _cleanup_pids(False)
diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py
new file mode 100644 (file)
index 0000000..f58ea99
--- /dev/null
@@ -0,0 +1,964 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# July 24 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements a CLI."""
+import argparse
+import asyncio
+import functools
+import logging
+import multiprocessing
+import os
+import pty
+import re
+import readline
+import select
+import shlex
+import socket
+import subprocess
+import sys
+import tempfile
+import termios
+import tty
+
+
+try:
+    from . import linux
+    from .config import list_to_dict_with_key
+except ImportError:
+    # We cannot use relative imports and still run this module directly as a script, and
+    # there are some use cases where we want to run this file as a script.
+    sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+    import linux
+
+    from config import list_to_dict_with_key
+
+
+ENDMARKER = b"\x00END\x00"
+
+logger = logging.getLogger(__name__)
+
+
+def lineiter(sock):
+    s = ""
+    while True:
+        sb = sock.recv(256)
+        if not sb:
+            return
+
+        s += sb.decode("utf-8")
+        i = s.find("\n")
+        if i != -1:
+            yield s[:i]
+            s = s[i + 1 :]
+
+
+# Would be nice to convert to async, but really not needed as used
+def spawn(unet, host, cmd, iow, ns_only):
+    if sys.stdin.isatty():
+        old_tty = termios.tcgetattr(sys.stdin)
+        tty.setraw(sys.stdin.fileno())
+
+    try:
+        master_fd, slave_fd = pty.openpty()
+
+        ns = unet.hosts[host] if host and host != unet else unet
+        popenf = ns.popen_nsonly if ns_only else ns.popen
+
+        # use os.setsid() make it run in a new process group, or bash job
+        # control will not be enabled
+        p = popenf(
+            cmd,
+            # _common_prologue, later in call chain, only does this for use_pty == False
+            preexec_fn=os.setsid,
+            stdin=slave_fd,
+            stdout=slave_fd,
+            stderr=slave_fd,
+            universal_newlines=True,
+            use_pty=True,
+            # XXX this is actually implementing "run on host" for real
+            # skip_pre_cmd=ns_only,
+        )
+        iow.write("\r")
+        iow.flush()
+
+        while p.poll() is None:
+            r, _, _ = select.select([sys.stdin, master_fd], [], [], 0.25)
+            if sys.stdin in r:
+                d = os.read(sys.stdin.fileno(), 10240)
+                os.write(master_fd, d)
+            elif master_fd in r:
+                o = os.read(master_fd, 10240)
+                if o:
+                    iow.write(o.decode("utf-8"))
+                    iow.flush()
+    finally:
+        # restore tty settings back
+        if sys.stdin.isatty():
+            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
+
+
+def is_host_regex(restr):
+    return len(restr) > 2 and restr[0] == "/" and restr[-1] == "/"
+
+
+def get_host_regex(restr):
+    if len(restr) < 3 or restr[0] != "/" or restr[-1] != "/":
+        return None
+    return re.compile(restr[1:-1])
+
+
+def host_in(restr, names):
+    """Determine if matcher is a regex that matches one of names."""
+    if not (regexp := get_host_regex(restr)):
+        return restr in names
+    for name in names:
+        if regexp.fullmatch(name):
+            return True
+    return False
+
+
+def expand_host(restr, names):
+    """Expand name or regexp into list of hosts."""
+    hosts = []
+    regexp = get_host_regex(restr)
+    if not regexp:
+        assert restr in names
+        hosts.append(restr)
+    else:
+        for name in names:
+            if regexp.fullmatch(name):
+                hosts.append(name)
+    return sorted(hosts)
+
+
+def expand_hosts(restrs, names):
+    """Expand list of host names or regex into list of hosts."""
+    hosts = []
+    for restr in restrs:
+        hosts += expand_host(restr, names)
+    return sorted(hosts)
+
+
+def host_cmd_split(unet, line, toplevel):
+    all_hosts = set(unet.hosts)
+    csplit = line.split()
+    i = 0
+    banner = False
+    for i, e in enumerate(csplit):
+        if is_re := is_host_regex(e):
+            banner = True
+        if not host_in(e, all_hosts):
+            if not is_re:
+                break
+    else:
+        i += 1
+
+    if i == 0 and csplit and csplit[0] == "*":
+        hosts = sorted(all_hosts)
+        csplit = csplit[1:]
+        banner = True
+    elif i == 0 and csplit and csplit[0] == ".":
+        hosts = [unet]
+        csplit = csplit[1:]
+    else:
+        hosts = expand_hosts(csplit[:i], all_hosts)
+        csplit = csplit[i:]
+
+        if not hosts and not csplit[:i]:
+            if toplevel:
+                hosts = [unet]
+            else:
+                hosts = sorted(all_hosts)
+                banner = True
+
+    if not csplit:
+        return hosts, "", "", True
+
+    i = line.index(csplit[0])
+    i += len(csplit[0])
+    return hosts, csplit[0], line[i:].strip(), banner
+
+
+def win_cmd_host_split(unet, cmd, kinds, defall):
+    if kinds:
+        all_hosts = {
+            x for x in unet.hosts if unet.hosts[x].config.get("kind", "") in kinds
+        }
+    else:
+        all_hosts = set(unet.hosts)
+
+    csplit = cmd.split()
+    i = 0
+    for i, e in enumerate(csplit):
+        if not host_in(e, all_hosts):
+            if not is_host_regex(e):
+                break
+    else:
+        i += 1
+
+    if i == 0 and csplit and csplit[0] == "*":
+        hosts = sorted(all_hosts)
+        csplit = csplit[1:]
+    elif i == 0 and csplit and csplit[0] == ".":
+        hosts = [unet]
+        csplit = csplit[1:]
+    else:
+        hosts = expand_hosts(csplit[:i], all_hosts)
+
+        if not hosts and defall and not csplit[:i]:
+            hosts = sorted(all_hosts)
+
+    # Filter hosts based on cmd
+    cmd = " ".join(csplit[i:])
+    return hosts, cmd
+
+
+def proc_readline(fd, prompt, histfile):
+    """Read a line of input from user while running in a sub-process."""
+    # How do we change the command though, that's what's displayed in ps normally
+    linux.set_process_name("Munet CLI")
+    try:
+        # For some reason sys.stdin is fileno == 16 and useless
+        sys.stdin = os.fdopen(0)
+        histfile = init_history(None, histfile)
+        line = input(prompt)
+        readline.write_history_file(histfile)
+        if line is None:
+            os.write(fd, b"\n")
+        os.write(fd, bytes(f":{str(line)}\n", encoding="utf-8"))
+    except EOFError:
+        os.write(fd, b"\n")
+    except KeyboardInterrupt:
+        os.write(fd, b"I\n")
+    except Exception as error:
+        os.write(fd, bytes(f"E{str(error)}\n", encoding="utf-8"))
+
+
+async def async_input_reader(rfd):
+    """Read a line of input from the user input sub-process pipe."""
+    rpipe = os.fdopen(rfd, mode="r")
+    reader = asyncio.StreamReader()
+
+    def protocol_factory():
+        return asyncio.StreamReaderProtocol(reader)
+
+    loop = asyncio.get_event_loop()
+    transport, _ = await loop.connect_read_pipe(protocol_factory, rpipe)
+    o = await reader.readline()
+    transport.close()
+
+    o = o.decode("utf-8").strip()
+    if not o:
+        return None
+    if o[0] == "I":
+        raise KeyboardInterrupt()
+    if o[0] == "E":
+        raise Exception(o[1:])
+    assert o[0] == ":"
+    return o[1:]
+
+
+#
+# A lot of work to add async `input` handling without creating a thread. We cannot use
+# threads when unshare_inline is used with pid namespace per kernel clone(2)
+# restriction.
+#
+async def async_input(prompt, histfile):
+    """Asynchronously read a line from the user."""
+    rfd, wfd = os.pipe()
+    p = multiprocessing.Process(target=proc_readline, args=(wfd, prompt, histfile))
+    p.start()
+    logging.debug("started async_input input process: %s", p)
+    try:
+        return await async_input_reader(rfd)
+    finally:
+        logging.debug("joining async_input input process")
+        p.join()
+
+
+def make_help_str(unet):
+
+    w = sorted([x if x else "" for x in unet.cli_in_window_cmds])
+    ww = unet.cli_in_window_cmds
+    u = sorted([x if x else "" for x in unet.cli_run_cmds])
+    uu = unet.cli_run_cmds
+
+    s = (
+        """
+Basic Commands:
+  cli   :: open a secondary CLI window
+  help  :: this help
+  hosts :: list hosts
+  quit  :: quit the cli
+
+  HOST can be a host or one of the following:
+    - '*' for all hosts
+    - '.' for the parent munet
+    - a regex specified between '/' (e.g., '/rtr.*/')
+
+New Window Commands:\n"""
+        + "\n".join([f"  {ww[v][0]}\t:: {ww[v][1]}" for v in w])
+        + """\nInline Commands:\n"""
+        + "\n".join([f"  {uu[v][0]}\t:: {uu[v][1]}" for v in u])
+        + "\n"
+    )
+    return s
+
+
+def get_shcmd(unet, host, kinds, execfmt, ucmd):
+    if host is None:
+        h = None
+        kind = None
+    elif host is unet or host == "":
+        h = unet
+        kind = ""
+    else:
+        h = unet.hosts[host]
+        kind = h.config.get("kind", "")
+        if kinds and kind not in kinds:
+            return ""
+    if not isinstance(execfmt, str):
+        execfmt = execfmt.get(kind, {}).get("exec", "")
+    if not execfmt:
+        return ""
+
+    # Do substitutions for {} in string
+    numfmt = len(re.findall(r"{\d*}", execfmt))
+    if numfmt > 1:
+        ucmd = execfmt.format(*shlex.split(ucmd))
+    elif numfmt:
+        ucmd = execfmt.format(ucmd)
+    elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)):
+        if execfmt.endswith('"'):
+            fstring = "f'''" + execfmt + "'''"
+        else:
+            fstring = 'f"""' + execfmt + '"""'
+        ucmd = eval(  # pylint: disable=W0123
+            fstring,
+            globals(),
+            {"host": h, "unet": unet, "user_input": ucmd},
+        )
+    else:
+        # No variable or usercmd substitution at all.
+        ucmd = execfmt
+
+    # Do substitution for munet variables
+    ucmd = ucmd.replace("%CONFIGDIR%", str(unet.config_dirname))
+    if host is None or host is unet:
+        ucmd = ucmd.replace("%RUNDIR%", str(unet.rundir))
+        return ucmd.replace("%NAME%", ".")
+    ucmd = ucmd.replace("%RUNDIR%", str(os.path.join(unet.rundir, host)))
+    if h.mgmt_ip:
+        ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip))
+    elif h.mgmt_ip6:
+        ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip6))
+    if h.mgmt_ip6:
+        ucmd = ucmd.replace("%IP6ADDR%", str(h.mgmt_ip6))
+    return ucmd.replace("%NAME%", str(host))
+
+
+async def run_command(
+    unet,
+    outf,
+    line,
+    execfmt,
+    banner,
+    hosts,
+    toplevel,
+    kinds,
+    ns_only=False,
+    interactive=False,
+):
+    """Runs a command on a set of hosts.
+
+    Runs `execfmt`. Prior to executing the string the following transformations are
+    performed on it.
+
+    `execfmt` may also be a dictionary of dicitonaries keyed on kind with `exec` holding
+    the kind's execfmt string.
+
+    - if `{}` is present then `str.format` is called to replace `{}` with any extra
+       input values after the command and hosts are removed from the input.
+    - else if any `{digits}` are present then `str.format` is called to replace
+      `{digits}` with positional args obtained from the addittional user input
+      first passed to `shlex.split`.
+    - else f-string style interpolation is performed on the string with
+      the local variables `host` (the current node object or None),
+      `unet` (the Munet object), and `user_input` (the additional command input)
+      defined.
+
+    The output is sent to `outf`.  If `ns_only` is True then the `execfmt` is
+    run using `Commander.cmd_status_nsonly` otherwise it is run with
+    `Commander.cmd_status`.
+    """
+    if kinds:
+        logging.info("Filtering hosts to kinds: %s", kinds)
+        hosts = [x for x in hosts if unet.hosts[x].config.get("kind", "") in kinds]
+        logging.info("Filtered hosts: %s", hosts)
+
+    if not hosts:
+        if not toplevel:
+            return
+        hosts = [unet]
+
+    # if unknowns := [x for x in hosts if x not in unet.hosts]:
+    #     outf.write("%% Unknown host[s]: %s\n" % ", ".join(unknowns))
+    #     return
+
+    # if sys.stdin.isatty() and interactive:
+    if interactive:
+        for host in hosts:
+            shcmd = get_shcmd(unet, host, kinds, execfmt, line)
+            if not shcmd:
+                continue
+            if len(hosts) > 1 or banner:
+                outf.write(f"------ Host: {host} ------\n")
+            spawn(unet, host if not toplevel else unet, shcmd, outf, ns_only)
+            if len(hosts) > 1 or banner:
+                outf.write(f"------- End: {host} ------\n")
+        outf.write("\n")
+        return
+
+    aws = []
+    for host in hosts:
+        shcmd = get_shcmd(unet, host, kinds, execfmt, line)
+        if not shcmd:
+            continue
+        if toplevel:
+            ns = unet
+        else:
+            ns = unet.hosts[host] if host and host != unet else unet
+        if ns_only:
+            cmdf = ns.async_cmd_status_nsonly
+        else:
+            cmdf = ns.async_cmd_status
+        aws.append(cmdf(shcmd, warn=False, stderr=subprocess.STDOUT))
+
+    results = await asyncio.gather(*aws, return_exceptions=True)
+    for host, result in zip(hosts, results):
+        if isinstance(result, Exception):
+            o = str(result) + "\n"
+            rc = -1
+        else:
+            rc, o, _ = result
+        if len(hosts) > 1 or banner:
+            outf.write(f"------ Host: {host} ------\n")
+        if rc:
+            outf.write(f"*** non-zero exit status: {rc}\n")
+        outf.write(o)
+        if len(hosts) > 1 or banner:
+            outf.write(f"------- End: {host} ------\n")
+
+
+cli_builtins = ["cli", "help", "hosts", "quit"]
+
+
+class Completer:
+    """A completer class for the CLI."""
+
+    def __init__(self, unet):
+        self.unet = unet
+
+    def complete(self, text, state):
+        line = readline.get_line_buffer()
+        tokens = line.split()
+        # print(f"\nXXX: tokens: {tokens} text: '{text}' state: {state}'\n")
+
+        first_token = not tokens or (text and len(tokens) == 1)
+
+        # If we have already have a builtin command we are done
+        if tokens and tokens[0] in cli_builtins:
+            return [None]
+
+        cli_run_cmds = set(self.unet.cli_run_cmds.keys())
+        top_run_cmds = {x for x in cli_run_cmds if self.unet.cli_run_cmds[x][3]}
+        cli_run_cmds -= top_run_cmds
+        cli_win_cmds = set(self.unet.cli_in_window_cmds.keys())
+        hosts = set(self.unet.hosts.keys())
+        is_window_cmd = bool(tokens) and tokens[0] in cli_win_cmds
+        done_set = set()
+        if bool(tokens):
+            if text:
+                done_set = set(tokens[:-1])
+            else:
+                done_set = set(tokens)
+
+        # Determine the domain for completions
+        if not tokens or first_token:
+            all_cmds = (
+                set(cli_builtins) | hosts | cli_run_cmds | cli_win_cmds | top_run_cmds
+            )
+        elif is_window_cmd:
+            all_cmds = hosts
+        elif tokens and tokens[0] in top_run_cmds:
+            # nothing to complete if a top level command
+            pass
+        elif not bool(done_set & cli_run_cmds):
+            all_cmds = hosts | cli_run_cmds
+
+        if not text:
+            completes = all_cmds
+        else:
+            # print(f"\nXXX: all_cmds: {all_cmds} text: '{text}'\n")
+            completes = {x + " " for x in all_cmds if x.startswith(text)}
+
+        # print(f"\nXXX: completes: {completes} text: '{text}' state: {state}'\n")
+        # remove any completions already present
+        completes -= done_set
+        completes = sorted(completes) + [None]
+        return completes[state]
+
+
+async def doline(
+    unet, line, outf, background=False, notty=False
+):  # pylint: disable=R0911
+
+    line = line.strip()
+    m = re.fullmatch(r"^(\S+)(?:\s+(.*))?$", line)
+    if not m:
+        return True
+
+    cmd = m.group(1)
+    nline = m.group(2) if m.group(2) else ""
+
+    if cmd in ("q", "quit"):
+        return False
+
+    if cmd == "help":
+        outf.write(make_help_str(unet))
+        return True
+    if cmd in ("h", "hosts"):
+        outf.write(f"% Hosts:\t{' '.join(sorted(unet.hosts.keys()))}\n")
+        return True
+    if cmd == "cli":
+        await remote_cli(
+            unet,
+            "secondary> ",
+            "Secondary CLI",
+            background,
+        )
+        return True
+
+    #
+    # In window commands
+    #
+
+    if cmd in unet.cli_in_window_cmds:
+        execfmt, toplevel, kinds, kwargs = unet.cli_in_window_cmds[cmd][2:]
+
+        # if toplevel:
+        #     ucmd = " ".join(nline.split())
+        # else:
+        hosts, ucmd = win_cmd_host_split(unet, nline, kinds, False)
+        if not hosts:
+            if not toplevel:
+                return True
+            hosts = [unet]
+
+        if isinstance(execfmt, str):
+            found_brace = "{}" in execfmt
+        else:
+            found_brace = False
+            for d in execfmt.values():
+                if "{}" in d["exec"]:
+                    found_brace = True
+                    break
+        if not found_brace and ucmd and not toplevel:
+            # CLI command does not expect user command so treat as hosts of which some
+            # must be unknown
+            unknowns = [x for x in ucmd.split() if x not in unet.hosts]
+            outf.write(f"% Unknown host[s]: {' '.join(unknowns)}\n")
+            return True
+
+        try:
+            if not hosts and toplevel:
+                hosts = [unet]
+
+            for host in hosts:
+                shcmd = get_shcmd(unet, host, kinds, execfmt, ucmd)
+                if toplevel or host == unet:
+                    unet.run_in_window(shcmd, **kwargs)
+                else:
+                    unet.hosts[host].run_in_window(shcmd, **kwargs)
+        except Exception as error:
+            outf.write(f"% Error: {error}\n")
+        return True
+
+    #
+    # Inline commands
+    #
+
+    toplevel = unet.cli_run_cmds[cmd][3] if cmd in unet.cli_run_cmds else False
+    # if toplevel:
+    #     logging.debug("top-level: cmd: '%s' nline: '%s'", cmd, nline)
+    #     hosts = None
+    #     banner = False
+    # else:
+
+    hosts, cmd, nline, banner = host_cmd_split(unet, line, toplevel)
+    hoststr = "munet" if hosts == [unet] else f"{hosts}"
+    logging.debug("hosts: '%s' cmd: '%s' nline: '%s'", hoststr, cmd, nline)
+
+    if cmd in unet.cli_run_cmds:
+        pass
+    elif "" in unet.cli_run_cmds:
+        nline = f"{cmd} {nline}"
+        cmd = ""
+    else:
+        outf.write(f"% Unknown command: {cmd} {nline}\n")
+        return True
+
+    execfmt, toplevel, kinds, ns_only, interactive = unet.cli_run_cmds[cmd][2:]
+    if interactive and notty:
+        outf.write("% Error: interactive command must be run from primary CLI\n")
+        return True
+
+    await run_command(
+        unet,
+        outf,
+        nline,
+        execfmt,
+        banner,
+        hosts,
+        toplevel,
+        kinds,
+        ns_only,
+        interactive,
+    )
+
+    return True
+
+
+async def cli_client(sockpath, prompt="munet> "):
+    """Implement the user-facing CLI for a remote munet reached by a socket."""
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    sock.settimeout(10)
+    sock.connect(sockpath)
+
+    # Go into full non-blocking mode now
+    sock.settimeout(None)
+
+    print("\n--- Munet CLI Starting ---\n\n")
+    while True:
+        line = input(prompt)
+        if line is None:
+            return
+
+        # Need to put \n back
+        line += "\n"
+
+        # Send the CLI command
+        sock.send(line.encode("utf-8"))
+
+        def bendswith(b, sentinel):
+            slen = len(sentinel)
+            return len(b) >= slen and b[-slen:] == sentinel
+
+        # Collect the output
+        rb = b""
+        while not bendswith(rb, ENDMARKER):
+            lb = sock.recv(4096)
+            if not lb:
+                return
+            rb += lb
+
+        # Remove the marker
+        rb = rb[: -len(ENDMARKER)]
+
+        # Write the output
+        sys.stdout.write(rb.decode("utf-8"))
+
+
+async def local_cli(unet, outf, prompt, histfile, background):
+    """Implement the user-side CLI for local munet."""
+    assert unet is not None
+    completer = Completer(unet)
+    readline.parse_and_bind("tab: complete")
+    readline.set_completer(completer.complete)
+
+    print("\n--- Munet CLI Starting ---\n\n")
+    while True:
+        try:
+            line = await async_input(prompt, histfile)
+            if line is None:
+                return
+
+            if not await doline(unet, line, outf, background):
+                return
+        except KeyboardInterrupt:
+            outf.write("%% Caught KeyboardInterrupt\nUse ^D or 'quit' to exit")
+
+
+def init_history(unet, histfile):
+    try:
+        if histfile is None:
+            histfile = os.path.expanduser("~/.munet-history.txt")
+            if not os.path.exists(histfile):
+                if unet:
+                    unet.cmd("touch " + histfile)
+                else:
+                    subprocess.run("touch " + histfile, shell=True, check=True)
+        if histfile:
+            readline.read_history_file(histfile)
+        return histfile
+    except Exception as error:
+        logging.warning("init_history failed: %s", error)
+    return None
+
+
+async def cli_client_connected(unet, background, reader, writer):
+    """Handle CLI commands inside the munet process from a socket."""
+    # # Go into full non-blocking mode now
+    # client.settimeout(None)
+    logging.debug("cli client connected")
+    while True:
+        line = await reader.readline()
+        if not line:
+            logging.debug("client closed cli connection")
+            break
+        line = line.decode("utf-8").strip()
+
+        class EncodingFile:
+            """Wrap a writer to encode in utf-8."""
+
+            def __init__(self, writer):
+                self.writer = writer
+
+            def write(self, x):
+                self.writer.write(x.encode("utf-8"))
+
+            def flush(self):
+                self.writer.flush()
+
+        if not await doline(unet, line, EncodingFile(writer), background, notty=True):
+            logging.debug("server closing cli connection")
+            return
+
+        writer.write(ENDMARKER)
+        await writer.drain()
+
+
+async def remote_cli(unet, prompt, title, background):
+    """Open a CLI in a new window."""
+    try:
+        if not unet.cli_sockpath:
+            sockpath = os.path.join(tempfile.mkdtemp("-sockdir", "pty-"), "cli.sock")
+            ccfunc = functools.partial(cli_client_connected, unet, background)
+            s = await asyncio.start_unix_server(ccfunc, path=sockpath)
+            unet.cli_server = asyncio.create_task(s.serve_forever(), name="cli-task")
+            unet.cli_sockpath = sockpath
+            logging.info("server created on :\n%s\n", sockpath)
+
+        # Open a new window with a new CLI
+        python_path = await unet.async_get_exec_path(["python3", "python"])
+        us = os.path.realpath(__file__)
+        cmd = f"{python_path} {us}"
+        if unet.cli_histfile:
+            cmd += " --histfile=" + unet.cli_histfile
+        if prompt:
+            cmd += f" --prompt='{prompt}'"
+        cmd += " " + unet.cli_sockpath
+        unet.run_in_window(cmd, title=title, background=False)
+    except Exception as error:
+        logging.error("cli server: unexpected exception: %s", error)
+
+
+def add_cli_in_window_cmd(
+    unet, name, helpfmt, helptxt, execfmt, toplevel, kinds, **kwargs
+):
+    """Adds a CLI command to the CLI.
+
+    The command `cmd` is added to the commands executable by the user from the CLI.  See
+    `base.Commander.run_in_window` for the arguments that can be passed in `args` and
+    `kwargs` to this function.
+
+    Args:
+        unet: unet object
+        name: command string (no spaces)
+        helpfmt: format of command to display in help (left side)
+        helptxt: help string for command (right side)
+        execfmt: interpreter `cmd` to pass to `host.run_in_window()`, if {} present then
+          allow for user commands to be entered and inserted. May also be a dict of dict
+          keyed on kind with sub-key of "exec" providing the `execfmt` string for that
+          kind.
+        toplevel: run command in common top-level namespaec not inside hosts
+        kinds: limit CLI command to nodes which match list of kinds.
+        **kwargs: keyword args to pass to `host.run_in_window()`
+    """
+    unet.cli_in_window_cmds[name] = (helpfmt, helptxt, execfmt, toplevel, kinds, kwargs)
+
+
+def add_cli_run_cmd(
+    unet,
+    name,
+    helpfmt,
+    helptxt,
+    execfmt,
+    toplevel,
+    kinds,
+    ns_only=False,
+    interactive=False,
+):
+    """Adds a CLI command to the CLI.
+
+    The command `cmd` is added to the commands executable by the user from the CLI.
+    See `run_command` above in the `doline` function and for the arguments that can
+    be passed in to this function.
+
+    Args:
+        unet: unet object
+        name: command string (no spaces)
+        helpfmt: format of command to display in help (left side)
+        helptxt: help string for command (right side)
+        execfmt: format string to insert user cmds into for execution. May also be a
+          dict of dict keyed on kind with sub-key of "exec" providing the `execfmt`
+          string for that kind.
+        toplevel: run command in common top-level namespaec not inside hosts
+        kinds: limit CLI command to nodes which match list of kinds.
+        ns_only: Should execute the command on the host vs in the node namespace.
+        interactive: Should execute the command inside an allocated pty (interactive)
+    """
+    unet.cli_run_cmds[name] = (
+        helpfmt,
+        helptxt,
+        execfmt,
+        toplevel,
+        kinds,
+        ns_only,
+        interactive,
+    )
+
+
+def add_cli_config(unet, config):
+    """Adds CLI commands based on config.
+
+    All exec strings will have %CONFIGDIR%, %NAME% and %RUNDIR% replaced with the
+    corresponding config directory and the current nodes `name` and `rundir`.
+    Additionally, the exec string will have f-string style interpolation performed
+    with the local variables `host` (node object or None), `unet` (Munet object) and
+    `user_input` (if provided to the CLI command) defined.
+
+    The format of the config dictionary can be seen in the following example.
+    The first list entry represents the default command because it has no `name` key.
+
+      commands:
+        - help: "run the given FRR command using vtysh"
+          format: "[HOST ...] FRR-CLI-COMMAND"
+          exec: "vtysh -c {}"
+          ns-only: false        # the default
+          interactive: false    # the default
+        - name: "vtysh"
+          help: "Open a FRR CLI inside new terminal[s] on the given HOST[s]"
+          format: "vtysh HOST [HOST ...]"
+          exec: "vtysh"
+          new-window: true
+        - name: "capture"
+          help: "Capture packets on a given network"
+          format: "pcap NETWORK"
+          exec: "tshark -s 9200 -i {0} -w /tmp/capture-{0}.pcap"
+          new-window: true
+          top-level: true # run in top-level container namespace, above hosts
+
+    The `new_window` key can also be a dictionary which will be passed as keyward
+    arguments to the `Commander.run_in_window()` function.
+
+    Args:
+        unet: unet object
+        config: dictionary of cli config
+    """
+    for cli_cmd in config.get("commands", []):
+        name = cli_cmd.get("name", None)
+        helpfmt = cli_cmd.get("format", "")
+        helptxt = cli_cmd.get("help", "")
+        execfmt = list_to_dict_with_key(cli_cmd.get("exec-kind"), "kind")
+        if not execfmt:
+            execfmt = cli_cmd.get("exec", "bash -c '{}'")
+        toplevel = cli_cmd.get("top-level", False)
+        kinds = cli_cmd.get("kinds", [])
+        stdargs = (unet, name, helpfmt, helptxt, execfmt, toplevel, kinds)
+        new_window = cli_cmd.get("new-window", None)
+        if isinstance(new_window, dict):
+            add_cli_in_window_cmd(*stdargs, **new_window)
+        elif bool(new_window):
+            add_cli_in_window_cmd(*stdargs)
+        else:
+            # on-host is deprecated it really implemented "ns-only"
+            add_cli_run_cmd(
+                *stdargs,
+                cli_cmd.get("ns-only", cli_cmd.get("on-host")),
+                cli_cmd.get("interactive", False),
+            )
+
+
+def cli(
+    unet,
+    histfile=None,
+    sockpath=None,
+    force_window=False,
+    title=None,
+    prompt=None,
+    background=True,
+):
+    asyncio.run(
+        async_cli(unet, histfile, sockpath, force_window, title, prompt, background)
+    )
+
+
+async def async_cli(
+    unet,
+    histfile=None,
+    sockpath=None,
+    force_window=False,
+    title=None,
+    prompt=None,
+    background=True,
+):
+    if prompt is None:
+        prompt = "munet> "
+
+    if force_window or not sys.stdin.isatty():
+        await remote_cli(unet, prompt, title, background)
+
+    if not unet:
+        logger.debug("client-cli using sockpath %s", sockpath)
+
+    try:
+        if sockpath:
+            await cli_client(sockpath, prompt)
+        else:
+            await local_cli(unet, sys.stdout, prompt, histfile, background)
+    except KeyboardInterrupt:
+        print("\n...^C exiting CLI")
+    except EOFError:
+        pass
+    except Exception as ex:
+        logger.critical("cli: got exception: %s", ex, exc_info=True)
+        raise
+
+
+if __name__ == "__main__":
+    # logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log")
+    logging.basicConfig(level=logging.DEBUG)
+    logger = logging.getLogger("cli-client")
+    logger.info("Start logging cli-client")
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--histfile", help="file to user for history")
+    parser.add_argument("--prompt", help="prompt string to use")
+    parser.add_argument("socket", help="path to pair of sockets to communicate over")
+    cli_args = parser.parse_args()
+
+    cli_prompt = cli_args.prompt if cli_args.prompt else "munet> "
+    asyncio.run(
+        async_cli(
+            None,
+            cli_args.histfile,
+            cli_args.socket,
+            prompt=cli_prompt,
+            background=False,
+        )
+    )
diff --git a/tests/topotests/munet/compat.py b/tests/topotests/munet/compat.py
new file mode 100644 (file)
index 0000000..e82a7d5
--- /dev/null
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# November 16 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Provide compatible APIs."""
+
+
+class PytestConfig:
+    """Pytest config duck-type-compatible object using argprase args."""
+
+    class Namespace:
+        """A namespace defined by a dictionary of values."""
+
+        def __init__(self, args):
+            self.args = args
+
+        def __getattr__(self, attr):
+            return self.args[attr] if attr in self.args else None
+
+    def __init__(self, args):
+        self.args = vars(args)
+        self.option = PytestConfig.Namespace(self.args)
+
+    def getoption(self, name, default=None, skip=False):
+        assert not skip
+        if name.startswith("--"):
+            name = name[2:]
+        name = name.replace("-", "_")
+        if name in self.args:
+            return self.args[name] if self.args[name] is not None else default
+        return default
diff --git a/tests/topotests/munet/config.py b/tests/topotests/munet/config.py
new file mode 100644 (file)
index 0000000..2870ae6
--- /dev/null
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# June 25 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+"""A module that defines common configuration utility functions."""
+import logging
+
+from collections.abc import Iterable
+from copy import deepcopy
+from typing import overload
+
+
+def find_with_kv(lst, k, v):
+    if lst:
+        for e in lst:
+            if k in e and e[k] == v:
+                return e
+    return {}
+
+
+def find_all_with_kv(lst, k, v):
+    rv = []
+    if lst:
+        for e in lst:
+            if k in e and e[k] == v:
+                rv.append(e)
+    return rv
+
+
+def find_matching_net_config(name, cconf, oconf):
+    p = find_all_with_kv(oconf.get("connections", {}), "to", name)
+    if not p:
+        return {}
+
+    rname = cconf.get("remote-name", None)
+    if not rname:
+        return p[0]
+
+    return find_with_kv(p, "name", rname)
+
+
+def merge_using_key(a, b, k):
+    # First get a dict of indexes in `a` for the key value of `k` in objects of `a`
+    m = list(a)
+    mi = {o[k]: i for i, o in enumerate(m)}
+    for o in b:
+        bkv = o[k]
+        if bkv in mi:
+            m[mi[bkv]] = o
+        else:
+            mi[bkv] = len(m)
+            m.append(o)
+    return m
+
+
+def list_to_dict_with_key(lst, k):
+    """Convert a YANG styl list of objects to dict of objects.
+
+    This function converts a YANG style list of objects (dictionaries) to a plain python
+    dictionary of objects (dictionaries).  The value for the supplied key for each
+    object is used to store the object in the new diciontary.
+
+    This only works for lists of objects which are keyed on a single contained value.
+
+    Args:
+      lst: a *list* of python dictionary objects.
+      k: the key value contained in each dictionary object in the list.
+
+    Returns:
+      A dictionary of objects (dictionaries).
+    """
+    return {x[k]: x for x in (lst if lst else [])}
+
+
+def config_to_dict_with_key(c, ck, k):
+    """Convert the config item from a list of objects to dict.
+
+    Use :py:func:`list_to_dict_with_key` to convert the list of objects
+    at ``c[ck]`` to a dict of the objects using the key ``k``.
+
+    Args:
+      c: config dictionary
+      ck: The key identifying the list of objects from ``c``.
+      k: The key to pass to :py:func:`list_to_dict_with_key`.
+
+    Returns:
+      A dictionary of objects (dictionaries).
+    """
+    c[ck] = list_to_dict_with_key(c.get(ck, []), k)
+    return c[ck]
+
+
+@overload
+def config_subst(config: str, **kwargs) -> str:
+    ...
+
+
+@overload
+def config_subst(config: Iterable, **kwargs) -> Iterable:
+    ...
+
+
+def config_subst(config: Iterable, **kwargs) -> Iterable:
+    if isinstance(config, str):
+        if "%RUNDIR%/%NAME%" in config:
+            config = config.replace("%RUNDIR%/%NAME%", "%RUNDIR%")
+            logging.warning(
+                "config '%RUNDIR%/%NAME%' should be changed to '%RUNDIR%' only, "
+                "converting automatically for now."
+            )
+        for name, value in kwargs.items():
+            config = config.replace(f"%{name.upper()}%", str(value))
+    elif isinstance(config, Iterable):
+        try:
+            return {k: config_subst(config[k], **kwargs) for k in config}
+        except (KeyError, TypeError):
+            return [config_subst(x, **kwargs) for x in config]
+    return config
+
+
+def value_merge_deepcopy(s1, s2):
+    """Merge values using deepcopy.
+
+    Create a deepcopy of the result of merging the values from dicts ``s1`` and ``s2``.
+    If a key exists in both ``s1`` and ``s2`` the value from ``s2`` is used."
+    """
+    d = {}
+    for k, v in s1.items():
+        if k in s2:
+            d[k] = deepcopy(s2[k])
+        else:
+            d[k] = deepcopy(v)
+    return d
+
+
+def merge_kind_config(kconf, config):
+    mergekeys = kconf.get("merge", [])
+    config = deepcopy(config)
+    new = deepcopy(kconf)
+    for k in new:
+        if k not in config:
+            continue
+
+        if k not in mergekeys:
+            new[k] = config[k]
+        elif isinstance(new[k], list):
+            new[k].extend(config[k])
+        elif isinstance(new[k], dict):
+            new[k] = {**new[k], **config[k]}
+        else:
+            new[k] = config[k]
+    for k in config:
+        if k not in new:
+            new[k] = config[k]
+    return new
+
+
+def cli_opt_list(option_list):
+    if not option_list:
+        return []
+    if isinstance(option_list, str):
+        return [x for x in option_list.split(",") if x]
+    return [x for x in option_list if x]
+
+
+def name_in_cli_opt_str(name, option_list):
+    ol = cli_opt_list(option_list)
+    return name in ol or "all" in ol
+
+
+class ConfigOptionsProxy:
+    """Proxy options object to fill in for any missing pytest config."""
+
+    class DefNoneObject:
+        """An object that returns None for any attribute access."""
+
+        def __getattr__(self, attr):
+            return None
+
+    def __init__(self, pytestconfig=None):
+        if isinstance(pytestconfig, ConfigOptionsProxy):
+            self.config = pytestconfig.config
+            self.option = self.config.option
+        else:
+            self.config = pytestconfig
+        if self.config:
+            self.option = self.config.option
+        else:
+            self.option = ConfigOptionsProxy.DefNoneObject()
+
+    def getoption(self, opt, default=None):
+        if not self.config:
+            return default
+
+        try:
+            value = self.config.getoption(opt)
+            return value if value is not None else default
+        except ValueError:
+            return default
+
+    def get_option(self, opt, default=None):
+        return self.getoption(opt, default)
+
+    def get_option_list(self, opt):
+        value = self.get_option(opt, "")
+        return cli_opt_list(value)
+
+    def name_in_option_list(self, name, opt):
+        optlist = self.get_option_list(opt)
+        return "all" in optlist or name in optlist
diff --git a/tests/topotests/munet/kinds.yaml b/tests/topotests/munet/kinds.yaml
new file mode 100644 (file)
index 0000000..0c278d3
--- /dev/null
@@ -0,0 +1,84 @@
+version: 1
+kinds:
+  - name: frr
+    cap-add:
+      # Zebra requires these
+      - NET_ADMIN
+      - NET_RAW
+      - SYS_ADMIN
+      - AUDIT_WRITE             # needed for ssh pty allocation
+  - name: ceos
+    init: false
+    shell: false
+    merge: ["env"]
+    # Should we cap-drop some of these in privileged mode?
+    # ceos kind is special. munet will add args to /sbin/init for each
+    # environment variable of the form `systemd.setenv=ENVNAME=VALUE` for each
+    # environment varialbe named ENVNAME with a value of `VALUE`. If cmd: is
+    # changed to anything but `/sbin/init` munet will not do this.
+    cmd: /sbin/init
+    privileged: true
+    env:
+      - name: "EOS_PLATFORM"
+        value: "ceoslab"
+      - name: "container"
+        value: "docker"
+      - name: "ETBA"
+        value: "4"
+      - name: "SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT"
+        value: "1"
+      - name: "INTFTYPE"
+        value: "eth"
+      - name: "MAPETH0"
+        value: "1"
+      - name: "MGMT_INTF"
+        value: "eth0"
+      - name: "CEOS"
+        value: "1"
+
+    # cap-add:
+    #   # cEOS requires these, except GNMI still doesn't work
+    #   # - NET_ADMIN
+    #   # - NET_RAW
+    #   # - SYS_ADMIN
+    #   # - SYS_RESOURCE            # Required for the CLI
+
+    # All Caps
+    #   - AUDIT_CONTROL
+    #   - AUDIT_READ
+    #   - AUDIT_WRITE
+    #   - BLOCK_SUSPEND
+    #   - CHOWN
+    #   - DAC_OVERRIDE
+    #   - DAC_READ_SEARCH
+    #   - FOWNER
+    #   - FSETID
+    #   - IPC_LOCK
+    #   - IPC_OWNER
+    #   - KILL
+    #   - LEASE
+    #   - LINUX_IMMUTABLE
+    #   - MKNOD
+    #   - NET_ADMIN
+    #   - NET_BIND_SERVICE
+    #   - NET_BROADCAST
+    #   - NET_RAW
+    #   - SETFCAP
+    #   - SETGID
+    #   - SETPCAP
+    #   - SETUID
+    #   - SYSLOG
+    #   - SYS_ADMIN
+    #   - SYS_BOOT
+    #   - SYS_CHROOT
+    #   - SYS_MODULE
+    #   - SYS_NICE
+    #   - SYS_PACCT
+    #   - SYS_PTRACE
+    #   - SYS_RAWIO
+    #   - SYS_RESOURCE
+    #   - SYS_TIME
+    #   - SYS_TTY_CONFIG
+    #   - WAKE_ALARM
+    #   - MAC_ADMIN  - Smack project?
+    #   - MAC_OVERRIDE - Smack project?
diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py
new file mode 100644 (file)
index 0000000..417f745
--- /dev/null
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# June 10 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""A module that gives access to linux unshare system call."""
+
+import ctypes  # pylint: disable=C0415
+import ctypes.util  # pylint: disable=C0415
+import errno
+import functools
+import os
+
+
+libc = None
+
+
+def raise_oserror(enum):
+    s = errno.errorcode[enum] if enum in errno.errorcode else str(enum)
+    error = OSError(s)
+    error.errno = enum
+    error.strerror = s
+    raise error
+
+
+def _load_libc():
+    global libc  # pylint: disable=W0601,W0603
+    if libc:
+        return
+    lcpath = ctypes.util.find_library("c")
+    libc = ctypes.CDLL(lcpath, use_errno=True)
+
+
+def pause():
+    if not libc:
+        _load_libc()
+    libc.pause()
+
+
+MS_RDONLY = 1
+MS_NOSUID = 1 << 1
+MS_NODEV = 1 << 2
+MS_NOEXEC = 1 << 3
+MS_SYNCHRONOUS = 1 << 4
+MS_REMOUNT = 1 << 5
+MS_MANDLOCK = 1 << 6
+MS_DIRSYNC = 1 << 7
+MS_NOSYMFOLLOW = 1 << 8
+MS_NOATIME = 1 << 10
+MS_NODIRATIME = 1 << 11
+MS_BIND = 1 << 12
+MS_MOVE = 1 << 13
+MS_REC = 1 << 14
+MS_SILENT = 1 << 15
+MS_POSIXACL = 1 << 16
+MS_UNBINDABLE = 1 << 17
+MS_PRIVATE = 1 << 18
+MS_SLAVE = 1 << 19
+MS_SHARED = 1 << 20
+MS_RELATIME = 1 << 21
+MS_KERNMOUNT = 1 << 22
+MS_I_VERSION = 1 << 23
+MS_STRICTATIME = 1 << 24
+MS_LAZYTIME = 1 << 25
+
+
+def mount(source, target, fs, flags=0, options=""):
+    if not libc:
+        _load_libc()
+    libc.mount.argtypes = (
+        ctypes.c_char_p,
+        ctypes.c_char_p,
+        ctypes.c_char_p,
+        ctypes.c_ulong,
+        ctypes.c_char_p,
+    )
+    fsenc = fs.encode() if fs else None
+    optenc = options.encode() if options else None
+    ret = libc.mount(source.encode(), target.encode(), fsenc, flags, optenc)
+    if ret < 0:
+        err = ctypes.get_errno()
+        raise OSError(
+            err,
+            f"Error mounting {source} ({fs}) on {target}"
+            f" with options '{options}': {os.strerror(err)}",
+        )
+
+
+# unmout options
+MNT_FORCE = 0x1
+MNT_DETACH = 0x2
+MNT_EXPIRE = 0x4
+UMOUNT_NOFOLLOW = 0x8
+
+
+def umount(target, options):
+    if not libc:
+        _load_libc()
+    libc.umount.argtypes = (ctypes.c_char_p, ctypes.c_uint)
+
+    ret = libc.umount(target.encode(), int(options))
+    if ret < 0:
+        err = ctypes.get_errno()
+        raise OSError(
+            err,
+            f"Error umounting {target} with options '{options}': {os.strerror(err)}",
+        )
+
+
+def pidfd_open(pid, flags=0):
+    if hasattr(os, "pidfd_open") and os.pidfd_open is not pidfd_open:
+        return os.pidfd_open(pid, flags)  # pylint: disable=no-member
+
+    if not libc:
+        _load_libc()
+
+    try:
+        pfof = libc.pidfd_open
+    except AttributeError:
+        __NR_pidfd_open = 434
+        _pidfd_open = libc.syscall
+        _pidfd_open.restype = ctypes.c_int
+        _pidfd_open.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.c_uint
+        pfof = functools.partial(_pidfd_open, __NR_pidfd_open)
+
+    fd = pfof(int(pid), int(flags))
+    if fd == -1:
+        raise_oserror(ctypes.get_errno())
+
+    return fd
+
+
+if not hasattr(os, "pidfd_open"):
+    os.pidfd_open = pidfd_open
+
+
+def setns(fd, nstype):  # noqa: D402
+    """See setns(2) manpage."""
+    if not libc:
+        _load_libc()
+
+    if libc.setns(int(fd), int(nstype)) == -1:
+        raise_oserror(ctypes.get_errno())
+
+
+def unshare(flags):  # noqa: D402
+    """See unshare(2) manpage."""
+    if not libc:
+        _load_libc()
+
+    if libc.unshare(int(flags)) == -1:
+        raise_oserror(ctypes.get_errno())
+
+
+CLONE_NEWTIME = 0x00000080
+CLONE_VM = 0x00000100
+CLONE_FS = 0x00000200
+CLONE_FILES = 0x00000400
+CLONE_SIGHAND = 0x00000800
+CLONE_PIDFD = 0x00001000
+CLONE_PTRACE = 0x00002000
+CLONE_VFORK = 0x00004000
+CLONE_PARENT = 0x00008000
+CLONE_THREAD = 0x00010000
+CLONE_NEWNS = 0x00020000
+CLONE_SYSVSEM = 0x00040000
+CLONE_SETTLS = 0x00080000
+CLONE_PARENT_SETTID = 0x00100000
+CLONE_CHILD_CLEARTID = 0x00200000
+CLONE_DETACHED = 0x00400000
+CLONE_UNTRACED = 0x00800000
+CLONE_CHILD_SETTID = 0x01000000
+CLONE_NEWCGROUP = 0x02000000
+CLONE_NEWUTS = 0x04000000
+CLONE_NEWIPC = 0x08000000
+CLONE_NEWUSER = 0x10000000
+CLONE_NEWPID = 0x20000000
+CLONE_NEWNET = 0x40000000
+CLONE_IO = 0x80000000
+
+clone_flag_names = {
+    CLONE_NEWTIME: "CLONE_NEWTIME",
+    CLONE_VM: "CLONE_VM",
+    CLONE_FS: "CLONE_FS",
+    CLONE_FILES: "CLONE_FILES",
+    CLONE_SIGHAND: "CLONE_SIGHAND",
+    CLONE_PIDFD: "CLONE_PIDFD",
+    CLONE_PTRACE: "CLONE_PTRACE",
+    CLONE_VFORK: "CLONE_VFORK",
+    CLONE_PARENT: "CLONE_PARENT",
+    CLONE_THREAD: "CLONE_THREAD",
+    CLONE_NEWNS: "CLONE_NEWNS",
+    CLONE_SYSVSEM: "CLONE_SYSVSEM",
+    CLONE_SETTLS: "CLONE_SETTLS",
+    CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID",
+    CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID",
+    CLONE_DETACHED: "CLONE_DETACHED",
+    CLONE_UNTRACED: "CLONE_UNTRACED",
+    CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID",
+    CLONE_NEWCGROUP: "CLONE_NEWCGROUP",
+    CLONE_NEWUTS: "CLONE_NEWUTS",
+    CLONE_NEWIPC: "CLONE_NEWIPC",
+    CLONE_NEWUSER: "CLONE_NEWUSER",
+    CLONE_NEWPID: "CLONE_NEWPID",
+    CLONE_NEWNET: "CLONE_NEWNET",
+    CLONE_IO: "CLONE_IO",
+}
+
+
+def clone_flag_string(flags):
+    ns = [v for k, v in clone_flag_names.items() if k & flags]
+    if ns:
+        return "|".join(ns)
+    return "None"
+
+
+namespace_files = {
+    CLONE_NEWUSER: "ns/user",
+    CLONE_NEWCGROUP: "ns/cgroup",
+    CLONE_NEWIPC: "ns/ipc",
+    CLONE_NEWUTS: "ns/uts",
+    CLONE_NEWNET: "ns/net",
+    CLONE_NEWPID: "ns/pid_for_children",
+    CLONE_NEWNS: "ns/mnt",
+    CLONE_NEWTIME: "ns/time_for_children",
+}
+
+PR_SET_PDEATHSIG = 1
+PR_GET_PDEATHSIG = 2
+PR_SET_NAME = 15
+PR_GET_NAME = 16
+
+
+def set_process_name(name):
+    if not libc:
+        _load_libc()
+
+    # Why does uncommenting this cause failure?
+    # libc.prctl.argtypes = (
+    #     ctypes.c_int,
+    #     ctypes.c_ulong,
+    #     ctypes.c_ulong,
+    #     ctypes.c_ulong,
+    #     ctypes.c_ulong,
+    # )
+
+    s = ctypes.create_string_buffer(bytes(name, encoding="ascii"))
+    sr = ctypes.byref(s)
+    libc.prctl(PR_SET_NAME, sr, 0, 0, 0)
+
+
+def set_parent_death_signal(signum):
+    if not libc:
+        _load_libc()
+
+    # Why does uncommenting this cause failure?
+    libc.prctl.argtypes = (
+        ctypes.c_int,
+        ctypes.c_ulong,
+        ctypes.c_ulong,
+        ctypes.c_ulong,
+        ctypes.c_ulong,
+    )
+
+    libc.prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0)
diff --git a/tests/topotests/munet/logconf-mutest.yaml b/tests/topotests/munet/logconf-mutest.yaml
new file mode 100644 (file)
index 0000000..b450fb9
--- /dev/null
@@ -0,0 +1,84 @@
+version: 1
+formatters:
+  brief:
+    format: '%(levelname)5s: %(message)s'
+  operfmt:
+    class: munet.mulog.ColorFormatter
+    format: ' ------| %(message)s'
+  exec:
+    format: '%(asctime)s %(levelname)5s: %(name)s: %(message)s'
+  output:
+    format: '%(asctime)s %(levelname)5s: OUTPUT: %(message)s'
+  results:
+    # format: '%(asctime)s %(levelname)5s: %(message)s'
+    format: '%(message)s'
+
+handlers:
+  console:
+    level: WARNING
+    class: logging.StreamHandler
+    formatter: brief
+    stream: ext://sys.stderr
+  info_console:
+    level: INFO
+    class: logging.StreamHandler
+    formatter: brief
+    stream: ext://sys.stderr
+  oper_console:
+    level: DEBUG
+    class: logging.StreamHandler
+    formatter: operfmt
+    stream: ext://sys.stderr
+  exec:
+    level: DEBUG
+    class: logging.FileHandler
+    formatter: exec
+    filename: mutest-exec.log
+    mode: w
+  output:
+    level: DEBUG
+    class: munet.mulog.MultiFileHandler
+    root_path: "mutest.output"
+    formatter: output
+    filename: mutest-output.log
+    mode: w
+  results:
+    level: INFO
+    class: munet.mulog.MultiFileHandler
+    root_path: "mutest.results"
+    new_handler_level: DEBUG
+    formatter: results
+    filename: mutest-results.log
+    mode: w
+
+root:
+  level: DEBUG
+  handlers: [ "console", "exec" ]
+
+loggers:
+  # These are some loggers that get used...
+  # munet:
+  #   level: DEBUG
+  #   propagate: true
+  # munet.base.commander
+  #   level: DEBUG
+  #   propagate: true
+  # mutest.error:
+  #   level: DEBUG
+  #   propagate: true
+  mutest.output:
+    level: DEBUG
+    handlers: ["output", "exec"]
+    propagate: false
+  mutest.results:
+    level: DEBUG
+    handlers: [ "info_console", "exec", "output", "results" ]
+    # We don't propagate this b/c we want a lower level accept on the console
+    # Instead we use info_console and exec to cover what root would log to.
+    propagate: false
+  # This is used to debug the operation of mutest
+  mutest.oper:
+    # Records are emitted at DEBUG so this will normally filter everything
+    level: INFO
+    handlers: [ "oper_console" ]
+    propagate: false
diff --git a/tests/topotests/munet/logconf.yaml b/tests/topotests/munet/logconf.yaml
new file mode 100644 (file)
index 0000000..430ee20
--- /dev/null
@@ -0,0 +1,32 @@
+version: 1
+formatters:
+  brief:
+    format: '%(asctime)s: %(levelname)s: %(message)s'
+  precise:
+    format: '%(asctime)s %(levelname)s: %(name)s: %(message)s'
+
+handlers:
+  console:
+    class: logging.StreamHandler
+    formatter: brief
+    level: INFO
+    stream: ext://sys.stderr
+  file:
+    class: logging.FileHandler
+    formatter: precise
+    level: DEBUG
+    filename: munet-exec.log
+    mode: w
+
+root:
+  level: DEBUG
+  handlers: [ "console", "file" ]
+
+# these are some loggers that get used.
+# loggers:
+#   munet:
+#     level: DEBUG
+#     propagate: true
+#   munet.base.commander
+#     level: DEBUG
+#      propagate: true
diff --git a/tests/topotests/munet/mucmd.py b/tests/topotests/munet/mucmd.py
new file mode 100644 (file)
index 0000000..5518c6d
--- /dev/null
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 5 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A command that allows external command execution inside nodes."""
+import argparse
+import json
+import os
+import subprocess
+import sys
+
+from pathlib import Path
+
+
+def newest_file_in(filename, paths, has_sibling=None):
+    new = None
+    newst = None
+    items = (x for y in paths for x in Path(y).rglob(filename))
+    for e in items:
+        st = os.stat(e)
+        if has_sibling and not e.parent.joinpath(has_sibling).exists():
+            continue
+        if not new or st.st_mtime_ns > newst.st_mtime_ns:
+            new = e
+            newst = st
+            continue
+    return new, newst
+
+
+def main(*args):
+    ap = argparse.ArgumentParser(args)
+    ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
+    ap.add_argument("node", nargs="?", help="node to enter or run command inside")
+    ap.add_argument(
+        "shellcmd",
+        nargs=argparse.REMAINDER,
+        help="optional shell-command to execute on NODE",
+    )
+    args = ap.parse_args()
+    if args.rundir:
+        configpath = Path(args.rundir).joinpath("config.json")
+    else:
+        configpath, _ = newest_file_in(
+            "config.json",
+            ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"],
+            has_sibling=args.node,
+        )
+        print(f'Using "{configpath}"')
+
+    if not configpath.exists():
+        print(f'"{configpath}" not found')
+        return 1
+    rundir = configpath.parent
+
+    nodes = []
+    config = json.load(open(configpath, encoding="utf-8"))
+    nodes = list(config.get("topology", {}).get("nodes", []))
+    envcfg = config.get("mucmd", {}).get("env", {})
+
+    # If args.node is not a node it's part of shellcmd
+    if args.node and args.node not in nodes:
+        if args.node != ".":
+            args.shellcmd[0:0] = [args.node]
+        args.node = None
+
+    if args.node:
+        name = args.node
+        nodedir = rundir.joinpath(name)
+        if not nodedir.exists():
+            print('"{name}" node doesn\'t exist in "{rundir}"')
+            return 1
+        rundir = nodedir
+    else:
+        name = "munet"
+    pidpath = rundir.joinpath("nspid")
+    pid = open(pidpath, encoding="ascii").read().strip()
+
+    env = {**os.environ}
+    env["MUNET_NODENAME"] = name
+    env["MUNET_RUNDIR"] = str(rundir)
+
+    for k in envcfg:
+        envcfg[k] = envcfg[k].replace("%NAME%", str(name))
+        envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir))
+
+    # Can't use -F if it's a new pid namespace
+    ecmd = "/usr/bin/nsenter"
+    eargs = [ecmd]
+
+    output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8")
+    if " -a," in output:
+        eargs.append("-a")
+    else:
+        # -U doesn't work
+        for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]:
+            if f" {flag}," in output:
+                eargs.append(flag)
+    eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children")
+    eargs.append(f"--wd={rundir}")
+    eargs.extend(["-t", pid])
+    eargs += args.shellcmd
+    # print("Using ", eargs)
+    return os.execvpe(ecmd, eargs, {**env, **envcfg})
+
+
+if __name__ == "__main__":
+    exit_status = main()
+    sys.exit(exit_status)
diff --git a/tests/topotests/munet/mulog.py b/tests/topotests/munet/mulog.py
new file mode 100644 (file)
index 0000000..f840eae
--- /dev/null
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 4 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Utilities for logging in munet."""
+
+import logging
+
+from pathlib import Path
+
+
+class MultiFileHandler(logging.FileHandler):
+    """A logging handler that logs to new files based on the logger name.
+
+    The MultiFileHandler operates as a FileHandler with additional functionality. In
+    addition to logging to the specified logging file MultiFileHandler also creates new
+    FileHandlers for child loggers based on a root logging name path.
+
+    The ``root_path`` determines when to create a new FileHandler. For each received log
+    record, ``root_path`` is removed from the logger name of the record if present, and
+    the resulting channel path (if any) determines the directory for a new log file to
+    also emit the record to. The new file path is constructed by starting with the
+    directory ``filename`` resides in, then joining the path determined above after
+    converting "." to "/" and finally by adding back the basename of ``filename``.
+
+      record logger path => mutest.output.testingfoo
+      root_path => mutest.output
+      base filename => /tmp/mutest/mutest-exec.log
+      new logfile => /tmp/mutest/testingfoo/mutest-exec.log
+
+    All messages are also emitted to the common FileLogger for ``filename``.
+
+    If a log record is from a logger that does not start with ``root_path`` no file is
+    created and the normal emit occurs.
+
+    Args:
+        root_path: the logging path of the root level for this handler.
+        new_handler_level: logging level for newly created handlers
+        log_dir: the log directory to put log files in.
+        filename: the base log file.
+    """
+
+    def __init__(self, root_path, filename=None, **kwargs):
+        self.__root_path = root_path
+        self.__basename = Path(filename).name
+        if root_path[-1] != ".":
+            self.__root_path += "."
+        self.__root_pathlen = len(self.__root_path)
+        self.__kwargs = kwargs
+        self.__log_dir = Path(filename).absolute().parent
+        self.__log_dir.mkdir(parents=True, exist_ok=True)
+        self.__filenames = {}
+        self.__added = set()
+
+        if "new_handler_level" not in kwargs:
+            self.__new_handler_level = logging.NOTSET
+        else:
+            new_handler_level = kwargs["new_handler_level"]
+            del kwargs["new_handler_level"]
+            self.__new_handler_level = new_handler_level
+
+        super().__init__(filename=filename, **kwargs)
+
+        if self.__new_handler_level is None:
+            self.__new_handler_level = self.level
+
+    def __log_filename(self, name):
+        if name in self.__filenames:
+            return self.__filenames[name]
+
+        if not name.startswith(self.__root_path):
+            newname = None
+        else:
+            newname = name[self.__root_pathlen :]
+            newname = Path(newname.replace(".", "/"))
+            newname = self.__log_dir.joinpath(newname)
+            newname = newname.joinpath(self.__basename)
+            self.__filenames[name] = newname
+
+        self.__filenames[name] = newname
+        return newname
+
+    def emit(self, record):
+        newname = self.__log_filename(record.name)
+        if newname:
+            if newname not in self.__added:
+                self.__added.add(newname)
+                h = logging.FileHandler(filename=newname, **self.__kwargs)
+                h.setLevel(self.__new_handler_level)
+                h.setFormatter(self.formatter)
+                logging.getLogger(record.name).addHandler(h)
+                h.emit(record)
+        super().emit(record)
+
+
+class ColorFormatter(logging.Formatter):
+    """A formatter that adds color sequences based on level."""
+
+    def __init__(self, fmt=None, datefmt=None, style="%", **kwargs):
+        grey = "\x1b[90m"
+        yellow = "\x1b[33m"
+        red = "\x1b[31m"
+        bold_red = "\x1b[31;1m"
+        reset = "\x1b[0m"
+        # basefmt = " ------| %(message)s "
+
+        self.formatters = {
+            logging.DEBUG: logging.Formatter(grey + fmt + reset),
+            logging.INFO: logging.Formatter(grey + fmt + reset),
+            logging.WARNING: logging.Formatter(yellow + fmt + reset),
+            logging.ERROR: logging.Formatter(red + fmt + reset),
+            logging.CRITICAL: logging.Formatter(bold_red + fmt + reset),
+        }
+        # Why are we even bothering?
+        super().__init__(fmt, datefmt, style, **kwargs)
+
+    def format(self, record):
+        formatter = self.formatters.get(record.levelno)
+        return formatter.format(record)
diff --git a/tests/topotests/munet/munet-schema.json b/tests/topotests/munet/munet-schema.json
new file mode 100644 (file)
index 0000000..a1dcd87
--- /dev/null
@@ -0,0 +1,654 @@
+{
+  "title": "labn-munet-config",
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "description": "Generated by pyang from module labn-munet-config",
+  "type": "object",
+  "properties": {
+    "cli": {
+      "type": "object",
+      "properties": {
+        "commands": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "properties": {
+              "exec": {
+                "type": "string"
+              },
+              "exec-kind": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "kind": {
+                      "type": "string"
+                    },
+                    "exec": {
+                      "type": "string"
+                    }
+                  }
+                }
+              },
+              "format": {
+                "type": "string"
+              },
+              "help": {
+                "type": "string"
+              },
+              "interactive": {
+                "type": "boolean"
+              },
+              "kinds": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "name": {
+                "type": "string"
+              },
+              "new-window": {
+                "type": "boolean"
+              },
+              "top-level": {
+                "type": "boolean"
+              }
+            }
+          }
+        }
+      }
+    },
+    "kinds": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "properties": {
+          "merge": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "cap-add": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "cap-remove": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "cmd": {
+            "type": "string"
+          },
+          "cleanup-cmd": {
+            "type": "string"
+          },
+          "ready-cmd": {
+            "type": "string"
+          },
+          "image": {
+            "type": "string"
+          },
+          "server": {
+            "type": "string"
+          },
+          "server-port": {
+            "type": "number"
+          },
+          "qemu": {
+            "type": "object",
+            "properties": {
+              "bios": {
+                "type": "string"
+              },
+              "disk": {
+                "type": "string"
+              },
+              "kerenel": {
+                "type": "string"
+              },
+              "initrd": {
+                "type": "string"
+              },
+              "kvm": {
+                "type": "boolean"
+              },
+              "ncpu": {
+                "type": "integer"
+              },
+              "memory": {
+                "type": "string"
+              },
+              "root": {
+                "type": "string"
+              },
+              "cmdline-extra": {
+                "type": "string"
+              },
+              "extra-args": {
+                "type": "string"
+              },
+              "console": {
+                "type": "object",
+                "properties": {
+                  "user": {
+                    "type": "string"
+                  },
+                  "password": {
+                    "type": "string"
+                  },
+                  "expects": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  "sends": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  "timeout": {
+                    "type": "integer"
+                  }
+                }
+              }
+            }
+          },
+          "connections": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "to": {
+                  "type": "string"
+                },
+                "ip": {
+                  "type": "string"
+                },
+                "ipv6": {
+                  "type": "string"
+                },
+                "name": {
+                  "type": "string"
+                },
+                "hostintf": {
+                  "type": "string"
+                },
+                "physical": {
+                  "type": "string"
+                },
+                "remote-name": {
+                  "type": "string"
+                },
+                "driver": {
+                  "type": "string"
+                },
+                "delay": {
+                  "type": "integer"
+                },
+                "jitter": {
+                  "type": "integer"
+                },
+                "jitter-correlation": {
+                  "type": "string"
+                },
+                "loss": {
+                  "type": "integer"
+                },
+                "loss-correlation": {
+                  "type": "string"
+                },
+                "rate": {
+                  "type": "object",
+                  "properties": {
+                    "rate": {
+                      "oneOf": [
+                        {
+                          "type": "integer"
+                        },
+                        {
+                          "type": "string"
+                        }
+                      ]
+                    },
+                    "limit": {
+                      "oneOf": [
+                        {
+                          "type": "integer"
+                        },
+                        {
+                          "type": "string"
+                        }
+                      ]
+                    },
+                    "burst": {
+                      "oneOf": [
+                        {
+                          "type": "integer"
+                        },
+                        {
+                          "type": "string"
+                        }
+                      ]
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "env": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "name": {
+                  "type": "string"
+                },
+                "value": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "gdb-cmd": {
+            "type": "string"
+          },
+          "gdb-target-cmds": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "gdb-run-cmds": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          },
+          "init": {
+            "oneOf": [
+              {
+                "type": "boolean"
+              },
+              {
+                "type": "string"
+              }
+            ]
+          },
+          "mounts": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "properties": {
+                "destination": {
+                  "type": "string"
+                },
+                "source": {
+                  "type": "string"
+                },
+                "tmpfs-size": {
+                  "type": "string"
+                },
+                "type": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "name": {
+            "type": "string"
+          },
+          "podman": {
+            "type": "object",
+            "properties": {
+              "extra-args": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          },
+          "privileged": {
+            "type": "boolean"
+          },
+          "shell": {
+            "oneOf": [
+              {
+                "type": "boolean"
+              },
+              {
+                "type": "string"
+              }
+            ]
+          },
+          "volumes": {
+            "type": "array",
+            "items": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
+    "topology": {
+      "type": "object",
+      "properties": {
+        "dns-network": {
+          "type": "string"
+        },
+        "ipv6-enable": {
+          "type": "boolean"
+        },
+        "networks-autonumber": {
+          "type": "boolean"
+        },
+        "networks": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "properties": {
+              "name": {
+                "type": "string"
+              },
+              "ip": {
+                "type": "string"
+              },
+              "ipv6": {
+                "type": "string"
+              }
+            }
+          }
+        },
+        "nodes": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "properties": {
+              "id": {
+                "type": "integer"
+              },
+              "kind": {
+                "type": "string"
+              },
+              "cap-add": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "cap-remove": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "cmd": {
+                "type": "string"
+              },
+              "cleanup-cmd": {
+                "type": "string"
+              },
+              "ready-cmd": {
+                "type": "string"
+              },
+              "image": {
+                "type": "string"
+              },
+              "server": {
+                "type": "string"
+              },
+              "server-port": {
+                "type": "number"
+              },
+              "qemu": {
+                "type": "object",
+                "properties": {
+                  "bios": {
+                    "type": "string"
+                  },
+                  "disk": {
+                    "type": "string"
+                  },
+                  "kerenel": {
+                    "type": "string"
+                  },
+                  "initrd": {
+                    "type": "string"
+                  },
+                  "kvm": {
+                    "type": "boolean"
+                  },
+                  "ncpu": {
+                    "type": "integer"
+                  },
+                  "memory": {
+                    "type": "string"
+                  },
+                  "root": {
+                    "type": "string"
+                  },
+                  "cmdline-extra": {
+                    "type": "string"
+                  },
+                  "extra-args": {
+                    "type": "string"
+                  },
+                  "console": {
+                    "type": "object",
+                    "properties": {
+                      "user": {
+                        "type": "string"
+                      },
+                      "password": {
+                        "type": "string"
+                      },
+                      "expects": {
+                        "type": "array",
+                        "items": {
+                          "type": "string"
+                        }
+                      },
+                      "sends": {
+                        "type": "array",
+                        "items": {
+                          "type": "string"
+                        }
+                      },
+                      "timeout": {
+                        "type": "integer"
+                      }
+                    }
+                  }
+                }
+              },
+              "connections": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "to": {
+                      "type": "string"
+                    },
+                    "ip": {
+                      "type": "string"
+                    },
+                    "ipv6": {
+                      "type": "string"
+                    },
+                    "name": {
+                      "type": "string"
+                    },
+                    "hostintf": {
+                      "type": "string"
+                    },
+                    "physical": {
+                      "type": "string"
+                    },
+                    "remote-name": {
+                      "type": "string"
+                    },
+                    "driver": {
+                      "type": "string"
+                    },
+                    "delay": {
+                      "type": "integer"
+                    },
+                    "jitter": {
+                      "type": "integer"
+                    },
+                    "jitter-correlation": {
+                      "type": "string"
+                    },
+                    "loss": {
+                      "type": "integer"
+                    },
+                    "loss-correlation": {
+                      "type": "string"
+                    },
+                    "rate": {
+                      "type": "object",
+                      "properties": {
+                        "rate": {
+                          "oneOf": [
+                            {
+                              "type": "integer"
+                            },
+                            {
+                              "type": "string"
+                            }
+                          ]
+                        },
+                        "limit": {
+                          "oneOf": [
+                            {
+                              "type": "integer"
+                            },
+                            {
+                              "type": "string"
+                            }
+                          ]
+                        },
+                        "burst": {
+                          "oneOf": [
+                            {
+                              "type": "integer"
+                            },
+                            {
+                              "type": "string"
+                            }
+                          ]
+                        }
+                      }
+                    }
+                  }
+                }
+              },
+              "env": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "name": {
+                      "type": "string"
+                    },
+                    "value": {
+                      "type": "string"
+                    }
+                  }
+                }
+              },
+              "gdb-cmd": {
+                "type": "string"
+              },
+              "gdb-target-cmds": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "gdb-run-cmds": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              },
+              "init": {
+                "oneOf": [
+                  {
+                    "type": "boolean"
+                  },
+                  {
+                    "type": "string"
+                  }
+                ]
+              },
+              "mounts": {
+                "type": "array",
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "destination": {
+                      "type": "string"
+                    },
+                    "source": {
+                      "type": "string"
+                    },
+                    "tmpfs-size": {
+                      "type": "string"
+                    },
+                    "type": {
+                      "type": "string"
+                    }
+                  }
+                }
+              },
+              "name": {
+                "type": "string"
+              },
+              "podman": {
+                "type": "object",
+                "properties": {
+                  "extra-args": {
+                    "type": "array",
+                    "items": {
+                      "type": "string"
+                    }
+                  }
+                }
+              },
+              "privileged": {
+                "type": "boolean"
+              },
+              "shell": {
+                "oneOf": [
+                  {
+                    "type": "boolean"
+                  },
+                  {
+                    "type": "string"
+                  }
+                ]
+              },
+              "volumes": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "version": {
+      "type": "integer"
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/topotests/munet/mutest/__main__.py b/tests/topotests/munet/mutest/__main__.py
new file mode 100644 (file)
index 0000000..c870311
--- /dev/null
@@ -0,0 +1,445 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# December 2 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+"""Command to execute mutests."""
+
+import asyncio
+import logging
+import os
+import subprocess
+import sys
+import time
+
+from argparse import ArgumentParser
+from argparse import Namespace
+from copy import deepcopy
+from pathlib import Path
+from typing import Union
+
+from munet import parser
+from munet.base import Bridge
+from munet.base import get_event_loop
+from munet.mutest import userapi as uapi
+from munet.native import L3NodeMixin
+from munet.native import Munet
+from munet.parser import async_build_topology
+from munet.parser import get_config
+
+
+# We want all but critical to fit in 5 characters for alignment
+logging.addLevelName(logging.WARNING, "WARN")
+root_logger = logging.getLogger("")
+exec_formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s")
+
+
+async def get_unet(config: dict, croot: Path, rundir: Path, unshare: bool = False):
+    """Create and run a new Munet topology.
+
+    The topology is built from the given ``config`` to run inside the path indicated
+    by ``rundir``. If ``unshare`` is True then the process will unshare into it's
+    own private namespace.
+
+    Args:
+        config: a config dictionary obtained from ``munet.parser.get_config``. This
+          value will be modified and stored in the built ``Munet`` object.
+        croot: common root of all tests, used to search for ``kinds.yaml`` files.
+        rundir: the path to the run directory for this topology.
+        unshare: True to unshare the process into it's own private namespace.
+
+    Yields:
+        Munet: The constructed and running topology.
+    """
+    tasks = []
+    unet = None
+    try:
+        try:
+            unet = await async_build_topology(
+                config, rundir=str(rundir), unshare_inline=unshare
+            )
+        except Exception as error:
+            logging.debug("unet build failed: %s", error, exc_info=True)
+            raise
+        try:
+            tasks = await unet.run()
+        except Exception as error:
+            logging.debug("unet run failed: %s", error, exc_info=True)
+            raise
+        logging.debug("unet topology running")
+        try:
+            yield unet
+        except Exception as error:
+            logging.error("unet fixture: yield unet unexpected exception: %s", error)
+            raise
+    except KeyboardInterrupt:
+        logging.info("Received keyboard while building topology")
+        raise
+    finally:
+        if unet:
+            await unet.async_delete()
+
+        # No one ever awaits these so cancel them
+        logging.debug("unet fixture: cleanup")
+        for task in tasks:
+            task.cancel()
+
+        # Reset the class variables so auto number is predictable
+        logging.debug("unet fixture: resetting ords to 1")
+        L3NodeMixin.next_ord = 1
+        Bridge.next_ord = 1
+
+
+def common_root(path1: Union[str, Path], path2: Union[str, Path]) -> Path:
+    """Find the common root between 2 paths.
+
+    Args:
+        path1: Path
+        path2: Path
+    Returns:
+        Path: the shared root components between ``path1`` and ``path2``.
+
+    Examples:
+        >>> common_root("/foo/bar/baz", "/foo/bar/zip/zap")
+        PosixPath('/foo/bar')
+        >>> common_root("/foo/bar/baz", "/fod/bar/zip/zap")
+        PosixPath('/')
+    """
+    apath1 = Path(path1).absolute().parts
+    apath2 = Path(path2).absolute().parts
+    alen = min(len(apath1), len(apath2))
+    common = None
+    for a, b in zip(apath1[:alen], apath2[:alen]):
+        if a != b:
+            break
+        common = common.joinpath(a) if common else Path(a)
+    return common
+
+
+async def collect(args: Namespace):
+    """Collect test files.
+
+    Files must match the pattern ``mutest_*.py``, and their containing
+    directory must have a munet config file present. This function also changes
+    the current directory to the common parent of all the tests, and paths are
+    returned relative to the common directory.
+
+    Args:
+      args: argparse results
+
+    Returns:
+      (commondir, tests, configs): where ``commondir`` is the path representing
+        the common parent directory of all the testsd, ``tests`` is a
+        dictionary of lists of test files, keyed on their containing directory
+        path, and ``configs`` is a dictionary of config dictionaries also keyed
+        on its containing directory path. The directory paths are relative to a
+        common ancestor.
+    """
+    file_select = args.file_select
+    upaths = args.paths if args.paths else ["."]
+    globpaths = set()
+    for upath in (Path(x) for x in upaths):
+        if upath.is_file():
+            paths = {upath.absolute()}
+        else:
+            paths = {x.absolute() for x in Path(upath).rglob(file_select)}
+        globpaths |= paths
+    tests = {}
+    configs = {}
+
+    # Find the common root
+    # We don't actually need this anymore, the idea was prefix test names
+    # with uncommon paths elements to automatically differentiate them.
+    common = None
+    sortedpaths = []
+    for path in sorted(globpaths):
+        sortedpaths.append(path)
+        dirpath = path.parent
+        common = common_root(common, dirpath) if common else dirpath
+
+    ocwd = Path().absolute()
+    try:
+        os.chdir(common)
+        # Work with relative paths to the common directory
+        for path in (x.relative_to(common) for x in sortedpaths):
+            dirpath = path.parent
+            if dirpath not in configs:
+                try:
+                    configs[dirpath] = get_config(search=[dirpath])
+                except FileNotFoundError:
+                    logging.warning(
+                        "Skipping '%s' as munet.{yaml,toml,json} not found in '%s'",
+                        path,
+                        dirpath,
+                    )
+                    continue
+            if dirpath not in tests:
+                tests[dirpath] = []
+            tests[dirpath].append(path.absolute())
+    finally:
+        os.chdir(ocwd)
+    return common, tests, configs
+
+
+async def execute_test(
+    unet: Munet,
+    test: Path,
+    args: Namespace,
+    test_num: int,
+    exec_handler: logging.Handler,
+) -> (int, int, int, Exception):
+    """Execute a test case script.
+
+    Using the built and running topology in ``unet`` for targets
+    execute the test case script file ``test``.
+
+    Args:
+        unet: a running topology.
+        test: path to the test case script file.
+        args: argparse results.
+        test_num: the number of this test case in the run.
+        exec_handler: exec file handler to add to test loggers which do not propagate.
+    """
+    test_name = testname_from_path(test)
+
+    # Get test case loggers
+    logger = logging.getLogger(f"mutest.output.{test_name}")
+    reslog = logging.getLogger(f"mutest.results.{test_name}")
+    logger.addHandler(exec_handler)
+    reslog.addHandler(exec_handler)
+
+    # We need to send an info level log to cause the speciifc handler to be
+    # created, otherwise all these debug ones don't get through
+    reslog.info("")
+
+    # reslog.debug("START: %s:%s from %s", test_num, test_name, test.stem)
+    # reslog.debug("-" * 70)
+
+    targets = dict(unet.hosts.items())
+    targets["."] = unet
+
+    tc = uapi.TestCase(
+        str(test_num), test_name, test, targets, logger, reslog, args.full_summary
+    )
+    passed, failed, e = tc.execute()
+
+    run_time = time.time() - tc.info.start_time
+
+    status = "PASS" if not (failed or e) else "FAIL"
+
+    # Turn off for now
+    reslog.debug("-" * 70)
+    reslog.debug(
+        "stats: %d steps, %d pass, %d fail, %s abort, %4.2fs elapsed",
+        passed + failed,
+        passed,
+        failed,
+        1 if e else 0,
+        run_time,
+    )
+    reslog.debug("-" * 70)
+    reslog.debug("END: %s %s:%s\n", status, test_num, test_name)
+
+    return passed, failed, e
+
+
+def testname_from_path(path: Path) -> str:
+    """Return test name based on the path to the test file.
+
+    Args:
+       path: path to the test file.
+
+    Returns:
+       str: the name of the test.
+    """
+    return str(Path(path).stem).replace("/", ".")
+
+
+def print_header(reslog, unet):
+    targets = dict(unet.hosts.items())
+    nmax = max(len(x) for x in targets)
+    nmax = max(nmax, len("TARGET"))
+    sum_fmt = uapi.TestCase.sum_fmt.format(nmax)
+    reslog.info(sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION")
+    reslog.info("-" * 70)
+
+
+async def run_tests(args):
+    reslog = logging.getLogger("mutest.results")
+
+    common, tests, configs = await collect(args)
+    results = []
+    errlog = logging.getLogger("mutest.error")
+    reslog = logging.getLogger("mutest.results")
+    printed_header = False
+    tnum = 0
+    start_time = time.time()
+    try:
+        for dirpath in tests:
+            test_files = tests[dirpath]
+            for test in test_files:
+                tnum += 1
+                config = deepcopy(configs[dirpath])
+                test_name = testname_from_path(test)
+                rundir = args.rundir.joinpath(test_name)
+
+                # Add an test case exec file handler to the root logger and result
+                # logger
+                exec_path = rundir.joinpath("mutest-exec.log")
+                exec_path.parent.mkdir(parents=True, exist_ok=True)
+                exec_handler = logging.FileHandler(exec_path, "w")
+                exec_handler.setFormatter(exec_formatter)
+                root_logger.addHandler(exec_handler)
+
+                try:
+                    async for unet in get_unet(config, common, rundir):
+                        if not printed_header:
+                            print_header(reslog, unet)
+                            printed_header = True
+                        passed, failed, e = await execute_test(
+                            unet, test, args, tnum, exec_handler
+                        )
+                except KeyboardInterrupt as error:
+                    errlog.warning("KeyboardInterrupt while running test %s", test_name)
+                    passed, failed, e = 0, 0, error
+                    raise
+                except Exception as error:
+                    logging.error(
+                        "Error executing test %s: %s", test, error, exc_info=True
+                    )
+                    errlog.error(
+                        "Error executing test %s: %s", test, error, exc_info=True
+                    )
+                    passed, failed, e = 0, 0, error
+                finally:
+                    # Remove the test case exec file handler form the root logger.
+                    root_logger.removeHandler(exec_handler)
+                    results.append((test_name, passed, failed, e))
+
+    except KeyboardInterrupt:
+        pass
+
+    run_time = time.time() - start_time
+    tnum = 0
+    tpassed = 0
+    tfailed = 0
+    texc = 0
+
+    spassed = 0
+    sfailed = 0
+
+    for result in results:
+        _, passed, failed, e = result
+        tnum += 1
+        spassed += passed
+        sfailed += failed
+        if e:
+            texc += 1
+        if failed or e:
+            tfailed += 1
+        else:
+            tpassed += 1
+
+    reslog.info("")
+    reslog.info(
+        "run stats: %s steps, %s pass, %s fail, %s abort, %4.2fs elapsed",
+        spassed + sfailed,
+        spassed,
+        sfailed,
+        texc,
+        run_time,
+    )
+    reslog.info("-" * 70)
+
+    tnum = 0
+    for result in results:
+        test_name, passed, failed, e = result
+        tnum += 1
+        s = "FAIL" if failed or e else "PASS"
+        reslog.info(" %s  %s:%s", s, tnum, test_name)
+
+    reslog.info("-" * 70)
+    reslog.info(
+        "END RUN: %s test scripts, %s passed, %s failed", tnum, tpassed, tfailed
+    )
+
+    return 1 if tfailed else 0
+
+
+async def async_main(args):
+    status = 3
+    try:
+        # For some reson we are not catching exceptions raised inside
+        status = await run_tests(args)
+    except KeyboardInterrupt:
+        logging.info("Exiting (async_main), received KeyboardInterrupt in main")
+    except Exception as error:
+        logging.info(
+            "Exiting (async_main), unexpected exception %s", error, exc_info=True
+        )
+    logging.debug("async_main returns %s", status)
+    return status
+
+
+def main():
+    ap = ArgumentParser()
+    ap.add_argument(
+        "--dist",
+        type=int,
+        nargs="?",
+        const=-1,
+        default=0,
+        action="store",
+        metavar="NUM-THREADS",
+        help="Run in parallel, value is num. of threads or no value for auto",
+    )
+    ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc")
+    ap.add_argument(
+        "--file-select", default="mutest_*.py", help="shell glob for finding tests"
+    )
+    ap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)")
+    ap.add_argument(
+        "-V",
+        "--full-summary",
+        action="store_true",
+        help="print full summary headers from docstrings",
+    )
+    ap.add_argument(
+        "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose"
+    )
+    ap.add_argument("paths", nargs="*", help="Paths to collect tests from")
+    args = ap.parse_args()
+
+    rundir = args.rundir if args.rundir else "/tmp/mutest"
+    args.rundir = Path(rundir)
+    os.environ["MUNET_RUNDIR"] = rundir
+    subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+
+    config = parser.setup_logging(args, config_base="logconf-mutest")
+    # Grab the exec formatter from the logging config
+    if fconfig := config.get("formatters", {}).get("exec"):
+        global exec_formatter  # pylint: disable=W291,W0603
+        exec_formatter = logging.Formatter(
+            fconfig.get("format"), fconfig.get("datefmt")
+        )
+
+    loop = None
+    status = 4
+    try:
+        loop = get_event_loop()
+        status = loop.run_until_complete(async_main(args))
+    except KeyboardInterrupt:
+        logging.info("Exiting (main), received KeyboardInterrupt in main")
+    except Exception as error:
+        logging.info("Exiting (main), unexpected exception %s", error, exc_info=True)
+    finally:
+        if loop:
+            loop.close()
+
+    sys.exit(status)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py
new file mode 100644 (file)
index 0000000..1df8c0d
--- /dev/null
@@ -0,0 +1,1111 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright 2017, 2022, LabN Consulting, L.L.C.
+"""Mutest is a simple send/expect based testing framework.
+
+This module implements the basic send/expect functionality for mutest.  The test
+developer first creates a munet topology (:ref:`munet-config`) and then writes test
+scripts ("test cases") which are composed of calls to the functions defined below
+("steps").  In short these are:
+
+Send/Expect functions:
+
+    - :py:func:`step`
+
+    - :py:func:`step_json`
+
+    - :py:func:`match_step`
+
+    - :py:func:`match_step_json`
+
+    - :py:func:`wait_step`
+
+    - :py:func:`wait_step_json`
+
+Control/Utility functions:
+
+    - :py:func:`script_dir`
+
+    - :py:func:`include`
+
+    - :py:func:`log`
+
+    - :py:func:`test`
+
+Test scripts are located by the :command:`mutest` command by their name.  The name of a
+test script should take the form ``mutest_TESTNAME.py`` where ``TESTNAME`` is replaced
+with a user chosen name for the test case.
+
+Here's a simple example test script which first checks that a specific forwarding entry
+is in the FIB for the IP destination ``10.0.1.1``.  Then it checks repeatedly for up to
+10 seconds for a second forwarding entry in the FIB for the IP destination ``10.0.2.1``.
+
+.. code-block:: python
+
+    match_step("r1", 'vtysh -c "show ip fib 10.0.1.1"', "Routing entry for 10.0.1.0/24",
+               "Check for FIB entry for 10.0.1.1")
+
+    wait_step("r1",
+              'vtysh -c "show ip fib 10.0.2.1"',
+              "Routing entry for 10.0.2.0/24",
+              desc="Check for FIB entry for 10.0.2.1",
+              timeout=10)
+
+Notice that the call arguments can be specified by their correct position in the list or
+using keyword names, and they can also be specified over multiple lines if preferred.
+
+All of the functions are documented and defined below.
+"""
+
+# pylint: disable=global-statement
+
+import functools
+import json
+import logging
+import pprint
+import re
+import time
+
+from pathlib import Path
+from typing import Any
+from typing import Union
+
+from deepdiff import DeepDiff as json_cmp
+
+from munet.base import Commander
+
+
+class TestCaseInfo:
+    """Object to hold nestable TestCase Results."""
+
+    def __init__(self, tag: str, name: str, path: Path):
+        self.path = path.absolute()
+        self.tag = tag
+        self.name = name
+        self.steps = 0
+        self.passed = 0
+        self.failed = 0
+        self.start_time = time.time()
+        self.step_start_time = self.start_time
+        self.run_time = None
+
+    def __repr__(self):
+        return (
+            f"TestCaseInfo({self.tag} {self.name} steps {self.steps} "
+            f"p {self.passed} f {self.failed} path {self.path})"
+        )
+
+
+class TestCase:
+    """A mutest testcase.
+
+    This is normally meant to be used internally by the mutest command to
+    implement the user API. See README-mutest.org for usage details on the
+    user API.
+
+    Args:
+        tag: identity of the test in a run. (x.x...)
+        name: the name of the test case
+        path: the test file that is being executed.
+        targets: a dictionary of objects which implement ``cmd_nostatus(str)``
+        output_logger: a logger for output and other messages from the test.
+        result_logger: a logger to output the results of test steps to.
+        full_summary: if True then print entire doctstring instead of
+          only the first line in the results report
+
+    Attributes:
+        tag: identity of the test in a run
+        name: the name of the test
+        targets: dictionary of targets.
+
+        steps: total steps executed so far.
+        passed: number of passing steps.
+        failed: number of failing steps.
+
+        last: the last command output.
+        last_m: the last result of re.search during a matching step on the output with
+            newlines converted to spaces.
+
+    :meta private:
+    """
+
+    # sum_hfmt = "{:5.5s} {:4.4s} {:>6.6s} {}"
+    # sum_dfmt = "{:5s} {:4.4s} {:^6.6s} {}"
+    sum_fmt = "%-8.8s %4.4s %{}s %6s  %s"
+
+    def __init__(
+        self,
+        tag: int,
+        name: str,
+        path: Path,
+        targets: dict,
+        output_logger: logging.Logger = None,
+        result_logger: logging.Logger = None,
+        full_summary: bool = False,
+    ):
+
+        self.info = TestCaseInfo(tag, name, path)
+        self.__saved_info = []
+        self.__short_doc_header = not full_summary
+
+        self.__space_before_result = False
+
+        # we are only ever in a section once, an include ends a section
+        # so are never in section+include, and another section ends a
+        # section, so we don't need __in_section to be save in the
+        # TestCaseInfo struct.
+        self.__in_section = False
+
+        self.targets = targets
+
+        self.last = ""
+        self.last_m = None
+
+        self.rlog = result_logger
+        self.olog = output_logger
+        self.logf = functools.partial(self.olog.log, logging.INFO)
+
+        oplog = logging.getLogger("mutest.oper")
+        self.oplogf = oplog.debug
+        self.oplogf("new TestCase: tag: %s name: %s path: %s", tag, name, path)
+
+        # find the longerst target name and make target field that wide
+        nmax = max(len(x) for x in targets)
+        nmax = max(nmax, len("TARGET"))
+        self.sum_fmt = TestCase.sum_fmt.format(nmax)
+
+        # Let's keep this out of summary for now
+        self.rlog.debug(self.sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION")
+        self.rlog.debug("-" * 70)
+
+    @property
+    def tag(self):
+        return self.info.tag
+
+    @property
+    def name(self):
+        return self.info.name
+
+    @property
+    def steps(self):
+        return self.info.steps
+
+    @property
+    def passed(self):
+        return self.info.passed
+
+    @property
+    def failed(self):
+        return self.info.failed
+
+    def execute(self):
+        """Execute the test case.
+
+        :meta private:
+        """
+        assert TestCase.g_tc is None
+        self.oplogf("execute")
+        try:
+            TestCase.g_tc = self
+            e = self.__exec_script(self.info.path, True, False)
+        except BaseException:
+            self.__end_test()
+            raise
+        return *self.__end_test(), e
+
+    def __del__(self):
+        if TestCase.g_tc is self:
+            logging.error("Internal error, TestCase.__end_test() was not called!")
+            TestCase.g_tc = None
+
+    def __push_execinfo(self, path: Path):
+        self.oplogf(
+            "__push_execinfo: path: %s current top is %s",
+            path,
+            pprint.pformat(self.info),
+        )
+        newname = self.name + path.stem
+        self.info.steps += 1
+        self.__saved_info.append(self.info)
+        tag = f"{self.info.tag}.{self.info.steps}"
+        self.info = TestCaseInfo(tag, newname, path)
+        self.oplogf("__push_execinfo: now on top: %s", pprint.pformat(self.info))
+
+    def __pop_execinfo(self):
+        # do something with tag?
+        finished_info = self.info
+        self.info = self.__saved_info.pop()
+        self.oplogf("  __pop_execinfo: poppped: %s", pprint.pformat(finished_info))
+        self.oplogf("  __pop_execinfo: now on top: %s", pprint.pformat(self.info))
+        return finished_info
+
+    def __print_header(self, tag, header, add_newline=False):
+        # self.olog.info(self.sum_fmt, tag, "", "", "", header)
+        self.olog.info("== %s ==", f"TEST: {tag}. {header}")
+        if add_newline:
+            self.rlog.info("")
+        self.rlog.info("%s. %s", tag, header)
+
+    def __exec_script(self, path, print_header, add_newline):
+
+        # Below was the original method to avoid the global TestCase
+        # variable; however, we need global functions so we can import them
+        # into test scripts. Without imports pylint will complain about undefined
+        # functions and the resulting christmas tree of warnings is annoying.
+        #
+        # pylint: disable=possibly-unused-variable,exec-used,redefined-outer-name
+        # include = self.include
+        # log = self.logf
+        # match_step = self.match_step
+        # match_step_json = self.match_step_json
+        # step = self.step
+        # step_json = self.step_json
+        # test = self.test
+        # wait_step = self.wait_step
+        # wait_step_json = self.wait_step_json
+
+        name = f"{path.stem}{self.tag}"
+        name = re.sub(r"\W|^(?=\d)", "_", name)
+
+        _ok_result = "marker"
+        try:
+            self.oplogf("__exec_script: path %s", path)
+            script = open(path, "r", encoding="utf-8").read()
+
+            # Load the script into a function.
+            script = script.strip()
+            s2 = (
+                # f"async def _{name}(ok_result):\n"
+                f"def _{name}(ok_result):\n"
+                + " "
+                + script.replace("\n", "\n ")
+                + "\n return ok_result\n"
+                + "\n"
+            )
+            exec(s2)
+
+            # Extract any docstring as a title.
+            if print_header:
+                title = locals()[f"_{name}"].__doc__.lstrip()
+                if self.__short_doc_header and (title := title.lstrip()):
+                    if (idx := title.find("\n")) != -1:
+                        title = title[:idx].strip()
+                if not title:
+                    title = f"Test from file: {self.info.path.name}"
+                self.__print_header(self.info.tag, title, add_newline)
+            self.__space_before_result = False
+
+            # Execute the function.
+            result = locals()[f"_{name}"](_ok_result)
+
+            # Here's where we can do async in the future if we want.
+            # result = await locals()[f"_{name}"](_ok_result)
+        except Exception as error:
+            logging.error(
+                "Unexpected exception executing %s: %s", name, error, exc_info=True
+            )
+            return error
+        else:
+            if result is not _ok_result:
+                logging.info("%s returned early, result: %s", name, result)
+            else:
+                self.oplogf("__exec_script: name %s completed normally", name)
+        return None
+
+    def __post_result(self, target, success, rstr, logstr=None):
+        self.oplogf(
+            "__post_result: target: %s success %s rstr %s", target, success, rstr
+        )
+        if success:
+            self.info.passed += 1
+            status = "PASS"
+            outlf = self.logf
+            reslf = self.rlog.info
+        else:
+            self.info.failed += 1
+            status = "FAIL"
+            outlf = self.olog.warning
+            reslf = self.rlog.warning
+
+        self.info.steps += 1
+        if logstr is not None:
+            outlf("R:%d %s: %s" % (self.steps, status, logstr))
+
+        run_time = time.time() - self.info.step_start_time
+
+        stepstr = f"{self.tag}.{self.steps}"
+        rtimes = _delta_time_str(run_time)
+
+        if self.__space_before_result:
+            self.rlog.info("")
+            self.__space_before_result = False
+
+        reslf(self.sum_fmt, stepstr, status, target, rtimes, rstr)
+
+        # start counting for next step now
+        self.info.step_start_time = time.time()
+
+    def __end_test(self) -> (int, int):
+        """End the test log final results.
+
+        Returns:
+            number of steps, number passed, number failed, run time.
+        """
+        self.oplogf("__end_test: __in_section: %s", self.__in_section)
+        if self.__in_section:
+            self.__end_section()
+
+        passed, failed = self.info.passed, self.info.failed
+
+        # No close for loggers
+        # self.olog.close()
+        # self.rlog.close()
+        self.olog = None
+        self.rlog = None
+
+        assert (
+            TestCase.g_tc == self
+        ), "TestCase global unexpectedly someon else in __end_test"
+        TestCase.g_tc = None
+
+        self.info.run_time = time.time() - self.info.start_time
+        return passed, failed
+
+    def _command(
+        self,
+        target: str,
+        cmd: str,
+    ) -> str:
+        """Execute a ``cmd`` and return result.
+
+        Args:
+            target: the target to execute the command on.
+            cmd: string to execut on the target.
+        """
+        out = self.targets[target].cmd_nostatus(cmd, warn=False)
+        self.last = out = out.rstrip()
+        report = out if out else "<no output>"
+        self.logf("COMMAND OUTPUT:\n%s", report)
+        return out
+
+    def _command_json(
+        self,
+        target: str,
+        cmd: str,
+    ) -> dict:
+        """Execute a json ``cmd`` and return json result.
+
+        Args:
+            target: the target to execute the command on.
+            cmd: string to execut on the target.
+        """
+        out = self.targets[target].cmd_nostatus(cmd, warn=False)
+        self.last = out = out.rstrip()
+        try:
+            js = json.loads(out)
+        except Exception as error:
+            js = {}
+            self.olog.warning(
+                "JSON load failed. Check command output is in JSON format: %s",
+                error,
+            )
+        self.logf("COMMAND OUTPUT:\n%s", out)
+        return js
+
+    def _match_command(
+        self,
+        target: str,
+        cmd: str,
+        match: str,
+        expect_fail: bool,
+        flags: int,
+    ) -> (bool, Union[str, list]):
+        """Execute a ``cmd`` and check result.
+
+        Args:
+            target: the target to execute the command on.
+            cmd: string to execute on the target.
+            match: regex to ``re.search()`` for in output.
+            expect_fail: if True then succeed when the regexp doesn't match.
+            flags: python regex flags to modify matching behavior
+
+        Returns:
+            (success, matches): if the match fails then "matches" will be None,
+            otherwise if there were matching groups then groups() will be returned in
+            ``matches`` otherwise group(0) (i.e., the matching text).
+        """
+        out = self._command(target, cmd)
+        search = re.search(match, out, flags)
+        self.last_m = search
+        if search is None:
+            success = expect_fail
+            ret = None
+        else:
+            success = not expect_fail
+            ret = search.groups()
+            if not ret:
+                ret = search.group(0)
+
+            level = logging.DEBUG if success else logging.WARNING
+            self.olog.log(level, "matched:%s:", ret)
+        return success, ret
+
+    def _match_command_json(
+        self,
+        target: str,
+        cmd: str,
+        match: Union[str, dict],
+        expect_fail: bool,
+    ) -> Union[str, dict]:
+        """Execute a json ``cmd`` and check result.
+
+        Args:
+            target: the target to execute the command on.
+            cmd: string to execut on the target.
+            match: A json ``str`` or object (``dict``) to compare against the json
+                output from ``cmd``.
+            expect_fail: if True then succeed when the json doesn't match.
+        """
+        js = self._command_json(target, cmd)
+        try:
+            expect = json.loads(match)
+        except Exception as error:
+            expect = {}
+            self.olog.warning(
+                "JSON load failed. Check match value is in JSON format: %s", error
+            )
+
+        if json_diff := json_cmp(expect, js):
+            success = expect_fail
+            if not success:
+                self.logf("JSON DIFF:%s:" % json_diff)
+            return success, json_diff
+
+        success = not expect_fail
+        return success, js
+
+    def _wait(
+        self,
+        target: str,
+        cmd: str,
+        match: Union[str, dict],
+        is_json: bool,
+        timeout: float,
+        interval: float,
+        expect_fail: bool,
+        flags: int,
+    ) -> Union[str, dict]:
+        """Execute a command repeatedly waiting for result until timeout."""
+        startt = time.time()
+        endt = startt + timeout
+
+        success = False
+        ret = None
+        while not success and time.time() < endt:
+            if is_json:
+                success, ret = self._match_command_json(target, cmd, match, expect_fail)
+            else:
+                success, ret = self._match_command(
+                    target, cmd, match, expect_fail, flags
+                )
+            if not success:
+                time.sleep(interval)
+        return success, ret
+
+    # ---------------------
+    # Public APIs for User
+    # ---------------------
+
+    def include(self, pathname: str, new_section: bool = False):
+        """See :py:func:`~munet.mutest.userapi.include`.
+
+        :meta private:
+        """
+        path = Path(pathname)
+        path = self.info.path.parent.joinpath(path)
+
+        self.oplogf(
+            "include: new path: %s create section: %s currently __in_section: %s",
+            path,
+            new_section,
+            self.__in_section,
+        )
+
+        if new_section:
+            self.oplogf("include: starting new exec section")
+            self.__start_exec_section(path)
+            our_info = self.info
+            # Note we do *not* mark __in_section True
+        else:
+            # swap the current path inside the top info
+            old_path = self.info.path
+            self.info.path = path
+            self.oplogf("include: swapped info path: new %s old %s", path, old_path)
+
+        self.__exec_script(path, print_header=new_section, add_newline=new_section)
+
+        if new_section:
+            # Something within the section creating include has also created a section
+            # end it, sections do not cross section creating file boundaries
+            if self.__in_section:
+                self.oplogf(
+                    "include done: path: %s __in_section calling __end_section", path
+                )
+                self.__end_section()
+
+            # We should now be back to the info we started with, b/c we don't actually
+            # start a new section (__in_section) that then could have been ended inside
+            # the included file.
+            assert our_info == self.info
+
+            self.oplogf(
+                "include done: path: %s new_section calling __end_section", path
+            )
+            self.__end_section()
+        else:
+            # The current top path could be anything due to multiple inline includes as
+            # well as section swap in and out. Forcibly return the top path to the file
+            # we are returning to
+            self.info.path = old_path
+            self.oplogf("include: restored info path: %s", old_path)
+
+    def __end_section(self):
+        self.oplogf("__end_section: __in_section: %s", self.__in_section)
+        info = self.__pop_execinfo()
+        passed, failed = info.passed, info.failed
+        self.info.passed += passed
+        self.info.failed += failed
+        self.__space_before_result = True
+        self.oplogf("__end_section setting __in_section to False")
+        self.__in_section = False
+
+    def __start_exec_section(self, path):
+        self.oplogf("__start_exec_section: __in_section: %s", self.__in_section)
+        if self.__in_section:
+            self.__end_section()
+
+        self.__push_execinfo(path)
+        self.__space_before_result = False
+        self.oplogf("NOT setting __in_section to True")
+        assert not self.__in_section
+
+    def section(self, desc: str):
+        """See :py:func:`~munet.mutest.userapi.section`.
+
+        :meta private:
+        """
+        self.oplogf("section: __in_section: %s", self.__in_section)
+        # Grab path before we pop the current info off the top
+        path = self.info.path
+        old_steps = self.info.steps
+
+        if self.__in_section:
+            self.__end_section()
+
+        self.__push_execinfo(path)
+        add_nl = self.info.steps <= old_steps
+
+        self.__space_before_result = False
+        self.__in_section = True
+        self.oplogf("   section setting __in_section to True")
+        self.__print_header(self.info.tag, desc, add_nl)
+
+    def step(self, target: str, cmd: str) -> str:
+        """See :py:func:`~munet.mutest.userapi.step`.
+
+        :meta private:
+        """
+        self.logf(
+            "#%s.%s:%s:STEP:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+        )
+        return self._command(target, cmd)
+
+    def step_json(self, target: str, cmd: str) -> dict:
+        """See :py:func:`~munet.mutest.userapi.step_json`.
+
+        :meta private:
+        """
+        self.logf(
+            "#%s.%s:%s:STEP_JSON:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+        )
+        return self._command_json(target, cmd)
+
+    def match_step(
+        self,
+        target: str,
+        cmd: str,
+        match: str,
+        desc: str = "",
+        expect_fail: bool = False,
+        flags: int = re.DOTALL,
+    ) -> (bool, Union[str, list]):
+        """See :py:func:`~munet.mutest.userapi.match_step`.
+
+        :meta private:
+        """
+        self.logf(
+            "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+            match,
+            desc,
+            expect_fail,
+            flags,
+        )
+        success, ret = self._match_command(target, cmd, match, expect_fail, flags)
+        if desc:
+            self.__post_result(target, success, desc)
+        return success, ret
+
+    def test_step(self, expr_or_value: Any, desc: str, target: str = "") -> bool:
+        """See :py:func:`~munet.mutest.userapi.test`.
+
+        :meta private:
+        """
+        success = bool(expr_or_value)
+        self.__post_result(target, success, desc)
+        return success
+
+    def match_step_json(
+        self,
+        target: str,
+        cmd: str,
+        match: Union[str, dict],
+        desc: str = "",
+        expect_fail: bool = False,
+    ) -> (bool, Union[str, dict]):
+        """See :py:func:`~munet.mutest.userapi.match_step_json`.
+
+        :meta private:
+        """
+        self.logf(
+            "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+            match,
+            desc,
+            expect_fail,
+        )
+        success, ret = self._match_command_json(target, cmd, match, expect_fail)
+        if desc:
+            self.__post_result(target, success, desc)
+        return success, ret
+
+    def wait_step(
+        self,
+        target: str,
+        cmd: str,
+        match: Union[str, dict],
+        desc: str = "",
+        timeout=10,
+        interval=0.5,
+        expect_fail: bool = False,
+        flags: int = re.DOTALL,
+    ) -> (bool, Union[str, list]):
+        """See :py:func:`~munet.mutest.userapi.wait_step`.
+
+        :meta private:
+        """
+        if interval is None:
+            interval = min(timeout / 20, 0.25)
+        self.logf(
+            "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+            match,
+            timeout,
+            interval,
+            desc,
+            expect_fail,
+            flags,
+        )
+        success, ret = self._wait(
+            target, cmd, match, False, timeout, interval, expect_fail, flags
+        )
+        if desc:
+            self.__post_result(target, success, desc)
+        return success, ret
+
+    def wait_step_json(
+        self,
+        target: str,
+        cmd: str,
+        match: Union[str, dict],
+        desc: str = "",
+        timeout=10,
+        interval=None,
+        expect_fail: bool = False,
+    ) -> (bool, Union[str, dict]):
+        """See :py:func:`~munet.mutest.userapi.wait_step_json`.
+
+        :meta private:
+        """
+        if interval is None:
+            interval = min(timeout / 20, 0.25)
+        self.logf(
+            "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s",
+            self.tag,
+            self.steps + 1,
+            self.info.path,
+            target,
+            cmd,
+            match,
+            timeout,
+            interval,
+            desc,
+            expect_fail,
+        )
+        success, ret = self._wait(
+            target, cmd, match, True, timeout, interval, expect_fail, 0
+        )
+        if desc:
+            self.__post_result(target, success, desc)
+        return success, ret
+
+
+# A non-rentrant global to allow for simplified operations
+TestCase.g_tc = None
+
+# pylint: disable=protected-access
+
+
+def _delta_time_str(run_time: float) -> str:
+    if run_time < 0.0001:
+        return "0.0"
+    if run_time < 0.001:
+        return f"{run_time:1.4f}"
+    if run_time < 0.01:
+        return f"{run_time:2.3f}"
+    if run_time < 0.1:
+        return f"{run_time:3.2f}"
+    if run_time < 100:
+        return f"{run_time:4.1f}"
+    return f"{run_time:5f}s"
+
+
+def section(desc: str):
+    """Start a new section for steps, with a description.
+
+    This starts a new section of tests. The result is basically
+    the same as doing a non-inline include. The current test number
+    is used to form a new sub-set of test steps. So if the current
+    test number is 2.3, a section will now number subsequent steps
+    2.3.1, 2.3.2, ...
+
+    A subsequent :py:func:`section` or non-inline :py:func:`include`
+    call ends the current section and advances the base test number.
+
+    Args:
+        desc: the description for the new section.
+    """
+    TestCase.g_tc.section(desc)
+
+
+def log(fmt, *args, **kwargs):
+    """Log a message in the testcase output log."""
+    return TestCase.g_tc.logf(fmt, *args, **kwargs)
+
+
+def include(pathname: str, new_section=False):
+    """Include a file as part of testcase.
+
+    Args:
+        pathname: the file to include.
+        new_section: if a new section should be created, otherwise
+          commands are executed inline.
+    """
+    return TestCase.g_tc.include(pathname, new_section)
+
+
+def script_dir() -> Path:
+    """The pathname to the directory containing the current script file.
+
+    When an include() is called the script_dir is updated to be current with the
+    includeded file, and is reverted to the previous value when the include completes.
+    """
+    return TestCase.g_tc.info.path.parent
+
+
+def get_target(name: str) -> Commander:
+    """Get the target object with the given ``name``."""
+    return TestCase.g_tc.targets[name]
+
+
+def step(target: str, cmd: str) -> str:
+    """Execute a ``cmd`` on a ``target`` and return the output.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execute on the target.
+
+    Returns:
+        Returns the ``str`` output of the ``cmd``.
+    """
+    return TestCase.g_tc.step(target, cmd)
+
+
+def step_json(target: str, cmd: str) -> dict:
+    """Execute a json ``cmd`` on a ``target`` and return the json object.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execute on the target.
+
+    Returns:
+        Returns the json object after parsing the ``cmd`` output.
+
+        If json parse fails, a warning is logged and an empty ``dict`` is used.
+    """
+    return TestCase.g_tc.step_json(target, cmd)
+
+
+def test_step(expr_or_value: Any, desc: str, target: str = "") -> bool:
+    """Evaluates ``expr_or_value`` and posts a result base on it bool(expr).
+
+    If ``expr_or_value`` evaluates to a positive result (i.e., True, non-zero, non-None,
+    non-empty string, non-empty list, etc..) then a PASS result is recorded, otherwise
+    record a FAIL is recorded.
+
+    Args:
+        expr: an expression or value to evaluate
+        desc: description of this test step.
+        target: optional target to associate with this test in the result string.
+
+    Returns:
+        A bool indicating the test PASS or FAIL result.
+    """
+    return TestCase.g_tc.test_step(expr_or_value, desc, target)
+
+
+def match_step(
+    target: str,
+    cmd: str,
+    match: str,
+    desc: str = "",
+    expect_fail: bool = False,
+    flags: int = re.DOTALL,
+) -> (bool, Union[str, list]):
+    """Execute a ``cmd`` on a ``target`` check result.
+
+    Execute ``cmd`` on ``target`` and check if the regexp in ``match``
+    matches or doesn't match (according to the ``expect_fail`` value) the
+    ``cmd`` output.
+
+    If the ``match`` regexp includes groups and if the match succeeds
+    the group values will be returned in a list, otherwise the command
+    output is returned.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execut on the ``target``.
+        match: regex to match against output.
+        desc: description of test, if no description then no result is logged.
+        expect_fail: if True then succeed when the regexp doesn't match.
+        flags: python regex flags to modify matching behavior
+
+    Returns:
+        Returns a 2-tuple. The first value is a bool indicating ``success``.
+        The second value will be a list from ``re.Match.groups()`` if non-empty,
+        otherwise ``re.Match.group(0)`` if there was a match otherwise None.
+    """
+    return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags)
+
+
+def match_step_json(
+    target: str,
+    cmd: str,
+    match: Union[str, dict],
+    desc: str = "",
+    expect_fail: bool = False,
+) -> (bool, Union[str, dict]):
+    """Execute a ``cmd`` on a ``target`` check result.
+
+    Execute ``cmd`` on ``target`` and check if the json object in ``match``
+    matches or doesn't match (according to the ``expect_fail`` value) the
+    json output from ``cmd``.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execut on the ``target``.
+        match: A json ``str`` or object (``dict``) to compare against the json
+            output from ``cmd``.
+        desc: description of test, if no description then no result is logged.
+        expect_fail: if True then succeed if the a json doesn't match.
+
+    Returns:
+        Returns a 2-tuple. The first value is a bool indicating ``success``. The
+        second value is a ``str`` diff if there is a difference found in the json
+        compare, otherwise the value is the json object (``dict``) from the ``cmd``.
+
+        If json parse fails, a warning is logged and an empty ``dict`` is used.
+    """
+    return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail)
+
+
+def wait_step(
+    target: str,
+    cmd: str,
+    match: Union[str, dict],
+    desc: str = "",
+    timeout: float = 10.0,
+    interval: float = 0.5,
+    expect_fail: bool = False,
+    flags: int = re.DOTALL,
+) -> (bool, Union[str, list]):
+    """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result.
+
+    Execute ``cmd`` on ``target``, every ``interval`` seconds for up to ``timeout``
+    seconds until the output of ``cmd`` does or doesn't match (according to the
+    ``expect_fail`` value) the ``match`` value.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execut on the ``target``.
+        match: regexp to match against output.
+        timeout: The number of seconds to repeat the ``cmd`` looking for a match
+            (or non-match if ``expect_fail`` is True).
+        interval: The number of seconds between running the ``cmd``. If not
+            specified the value is calculated from the timeout value so that on
+            average the cmd will execute 10 times. The minimum calculated interval
+            is .25s, shorter values can be passed explicitly.
+        desc: description of test, if no description then no result is logged.
+        expect_fail: if True then succeed when the regexp *doesn't* match.
+        flags: python regex flags to modify matching behavior
+
+    Returns:
+        Returns a 2-tuple. The first value is a bool indicating ``success``.
+        The second value will be a list from ``re.Match.groups()`` if non-empty,
+        otherwise ``re.Match.group(0)`` if there was a match otherwise None.
+    """
+    return TestCase.g_tc.wait_step(
+        target, cmd, match, desc, timeout, interval, expect_fail, flags
+    )
+
+
+def wait_step_json(
+    target: str,
+    cmd: str,
+    match: Union[str, dict],
+    desc: str = "",
+    timeout=10,
+    interval=None,
+    expect_fail: bool = False,
+) -> (bool, Union[str, dict]):
+    """Execute a cmd repeatedly and wait for matching result.
+
+    Execute ``cmd`` on ``target``, every ``interval`` seconds until
+    the output of ``cmd`` matches or doesn't match (according to the
+    ``expect_fail`` value) ``match``, for up to ``timeout`` seconds.
+
+    ``match`` is a regular expression to search for in the output of ``cmd`` when
+    ``is_json`` is False.
+
+    When ``is_json`` is True ``match`` must be a json object or a ``str`` which
+    parses into a json object. Likewise, the ``cmd`` output is parsed into a json
+    object and then a comparison is done between the two json objects.
+
+    Args:
+        target: the target to execute the ``cmd`` on.
+        cmd: string to execut on the ``target``.
+        match: A json object or str representation of one to compare against json
+            output from ``cmd``.
+        desc: description of test, if no description then no result is logged.
+        timeout: The number of seconds to repeat the ``cmd`` looking for a match
+            (or non-match if ``expect_fail`` is True).
+        interval: The number of seconds between running the ``cmd``. If not
+            specified the value is calculated from the timeout value so that on
+            average the cmd will execute 10 times. The minimum calculated interval
+            is .25s, shorter values can be passed explicitly.
+        expect_fail: if True then succeed if the a json doesn't match.
+
+    Returns:
+        Returns a 2-tuple. The first value is a bool indicating ``success``.
+        The second value is a ``str`` diff if there is a difference found in the
+        json compare, otherwise the value is a json object (dict) from the ``cmd``
+        output.
+
+        If json parse fails, a warning is logged and an empty ``dict`` is used.
+    """
+    return TestCase.g_tc.wait_step_json(
+        target, cmd, match, desc, timeout, interval, expect_fail
+    )
+
+
+def luInclude(filename, CallOnFail=None):
+    """Backward compatible API, do not use in new tests."""
+    return include(filename)
+
+
+def luLast(usenl=False):
+    """Backward compatible API, do not use in new tests."""
+    del usenl
+    return TestCase.g_tc.last_m
+
+
+def luCommand(
+    target,
+    cmd,
+    regexp=".",
+    op="none",
+    result="",
+    ltime=10,
+    returnJson=False,
+    wait_time=0.5,
+):
+    """Backward compatible API, do not use in new tests.
+
+    Only non-json is verified to any degree of confidence by code inspection.
+
+    For non-json should return match.group() if match else return bool(op == "fail").
+
+    For json if no diff return the json else diff return bool(op == "jsoncmp_fail")
+     bug if no json from output (fail parse) could maybe generate diff, which could
+     then return
+    """
+    if op == "wait":
+        if returnJson:
+            return wait_step_json(target, cmd, regexp, result, ltime, wait_time)
+
+        success, _ = wait_step(target, cmd, regexp, result, ltime, wait_time)
+        match = luLast()
+        if success and match is not None:
+            return match.group()
+        return success
+
+    if op == "none":
+        if returnJson:
+            return step_json(target, cmd)
+        return step(target, cmd)
+
+    if returnJson and op in ("jsoncmp_fail", "jsoncmp_pass"):
+        expect_fail = op == "jsoncmp_fail"
+        return match_step_json(target, cmd, regexp, result, expect_fail)
+
+    assert not returnJson
+    assert op in ("fail", "pass")
+    expect_fail = op == "fail"
+    success, _ = match_step(target, cmd, regexp, result, expect_fail)
+    match = luLast()
+    if success and match is not None:
+        return match.group()
+    return success
diff --git a/tests/topotests/munet/mutestshare.py b/tests/topotests/munet/mutestshare.py
new file mode 100644 (file)
index 0000000..95ffa74
--- /dev/null
@@ -0,0 +1,254 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# January 28 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""A tiny init for namespaces in python inspired by the C program tini."""
+import argparse
+import errno
+import logging
+import os
+import shlex
+import signal
+import subprocess
+import sys
+import threading
+import time
+
+from signal import Signals as S
+
+from . import linux
+from .base import commander
+
+
+child_pid = -1
+very_verbose = False
+restore_signals = set()
+
+
+def vdebug(*args, **kwargs):
+    if very_verbose:
+        logging.debug(*args, **kwargs)
+
+
+def exit_with_status(pid, status):
+    try:
+        ec = status >> 8 if bool(status & 0xFF00) else status | 0x80
+        logging.debug("reaped our child, exiting %s", ec)
+        sys.exit(ec)
+    except ValueError:
+        vdebug("pid %s didn't actually exit", pid)
+
+
+def waitpid(tag):
+    logging.debug("%s: waitid for exiting processes", tag)
+    idobj = os.waitid(os.P_ALL, 0, os.WEXITED)
+    pid = idobj.si_pid
+    status = idobj.si_status
+    if pid == child_pid:
+        exit_with_status(pid, status)
+    else:
+        logging.debug("%s: reaped zombie pid %s with status %s", tag, pid, status)
+
+
+def new_process_group():
+    pid = os.getpid()
+    try:
+        pgid = os.getpgrp()
+        if pgid == pid:
+            logging.debug("already process group leader %s", pgid)
+        else:
+            logging.debug("creating new process group %s", pid)
+            os.setpgid(pid, 0)
+    except Exception as error:
+        logging.warning("unable to get new process group: %s", error)
+        return
+
+    # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked
+    signal.signal(S.SIGTTIN, signal.SIG_IGN)
+    signal.signal(S.SIGTTOU, signal.SIG_IGN)
+    fd = sys.stdin.fileno()
+    if not os.isatty(fd):
+        logging.debug("stdin not a tty no foregrounding required")
+    else:
+        try:
+            # This will error if our session no longer associated with controlling tty.
+            pgid = os.tcgetpgrp(fd)
+            if pgid == pid:
+                logging.debug("process group already in foreground %s", pgid)
+            else:
+                logging.debug("making us the foreground pgid backgrounding %s", pgid)
+                os.tcsetpgrp(fd, pid)
+        except OSError as error:
+            if error.errno == errno.ENOTTY:
+                logging.debug("session is no longer associated with controlling tty")
+            else:
+                logging.warning("unable to foreground pgid %s: %s", pid, error)
+    signal.signal(S.SIGTTIN, signal.SIG_DFL)
+    signal.signal(S.SIGTTOU, signal.SIG_DFL)
+
+
+def exec_child(exec_args):
+    # Restore signals to default handling:
+    for snum in restore_signals:
+        signal.signal(snum, signal.SIG_DFL)
+
+    # Create new process group.
+    new_process_group()
+
+    estring = shlex.join(exec_args)
+    try:
+        # and exec the process
+        logging.debug("child: executing '%s'", estring)
+        os.execvp(exec_args[0], exec_args)
+        # NOTREACHED
+    except Exception as error:
+        logging.warning("child: unable to execute '%s': %s", estring, error)
+        raise
+
+
+def is_creating_pid_namespace():
+    p1name = subprocess.check_output(
+        "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True
+    )
+    p2name = subprocess.check_output(
+        "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True
+    )
+    return p1name != p2name
+
+
+def restore_namespace(ppid_fd, uflags):
+    fd = ppid_fd
+    retry = 3
+    for i in range(0, retry):
+        try:
+            linux.setns(fd, uflags)
+        except OSError as error:
+            logging.warning("could not reset to old namespace fd %s: %s", fd, error)
+            if i == retry - 1:
+                raise
+            time.sleep(1)
+    os.close(fd)
+
+
+def create_thread_test():
+    def runthread(name):
+        logging.info("In thread: %s", name)
+
+    logging.info("Create thread")
+    thread = threading.Thread(target=runthread, args=(1,))
+    logging.info("Run thread")
+    thread.start()
+    logging.info("Join thread")
+    thread.join()
+
+
+def run(args):
+    del args
+    # We look for this b/c the unshare pid will share with /sibn/init
+    # nselm = "pid_for_children"
+    # nsflags.append(f"--pid={pp / nselm}")
+    # mutini now forks when created this way
+    # cmd.append("--pid")
+    # cmd.append("--fork")
+    # cmd.append("--kill-child")
+    # cmd.append("--mount-proc")
+
+    uflags = linux.CLONE_NEWPID
+    nslist = ["pid_for_children"]
+    uflags |= linux.CLONE_NEWNS
+    nslist.append("mnt")
+    uflags |= linux.CLONE_NEWNET
+    nslist.append("net")
+
+    # Before values
+    pid = os.getpid()
+    nsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist}
+
+    #
+    # UNSHARE
+    #
+    create_thread_test()
+
+    ppid = os.getppid()
+    ppid_fd = linux.pidfd_open(ppid)
+    linux.unshare(uflags)
+
+    # random syscall's fail until we fork a child to establish the new pid namespace.
+    global child_pid  # pylint: disable=global-statement
+    child_pid = os.fork()
+    if not child_pid:
+        logging.info("In child sleeping")
+        time.sleep(1200)
+        sys.exit(1)
+
+    # verify after values differ
+    nnsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist}
+    assert not {k for k in nsdict if nsdict[k] == nnsdict[k]}
+
+    # Remount / and any future mounts below it as private
+    commander.cmd_raises("mount --make-rprivate /")
+    # Mount a new /proc in our new namespace
+    commander.cmd_raises("mount -t proc proc /proc")
+
+    #
+    # In NEW NS
+    #
+
+    cid = os.fork()
+    if not cid:
+        logging.info("In second child sleeping")
+        time.sleep(4)
+        sys.exit(1)
+    logging.info("Waiting for second child")
+    os.waitpid(cid, 0)
+
+    try:
+        create_thread_test()
+    except Exception as error:
+        print(error)
+
+    #
+    # RESTORE
+    #
+
+    logging.info("In new namespace, restoring old")
+    # Make sure we can go back, not sure since this is PID namespace, but maybe
+    restore_namespace(ppid_fd, uflags)
+
+    # verify after values the same
+    nnsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist}
+    assert nsdict == nnsdict
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument(
+        "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose"
+    )
+    ap.add_argument("rest", nargs=argparse.REMAINDER)
+    args = ap.parse_args()
+
+    level = logging.DEBUG if args.verbose else logging.INFO
+    if args.verbose > 1:
+        global very_verbose  # pylint: disable=global-statement
+        very_verbose = True
+    logging.basicConfig(
+        level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s"
+    )
+
+    status = 4
+    try:
+        run(args)
+    except KeyboardInterrupt:
+        logging.info("exiting (main), received KeyboardInterrupt in main")
+    except Exception as error:
+        logging.info("exiting (main), unexpected exception %s", error, exc_info=True)
+
+    sys.exit(status)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/topotests/munet/mutini.py b/tests/topotests/munet/mutini.py
new file mode 100755 (executable)
index 0000000..e5f9931
--- /dev/null
@@ -0,0 +1,432 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# January 28 2023, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2023, LabN Consulting, L.L.C.
+#
+"""A tiny init for namespaces in python inspired by the C program tini."""
+
+
+# pylint: disable=global-statement
+import argparse
+import errno
+import logging
+import os
+import re
+import shlex
+import signal
+import subprocess
+import sys
+
+from signal import Signals as S
+
+
+try:
+    from munet import linux
+except ModuleNotFoundError:
+    # We cannot use relative imports and still run this module directly as a script, and
+    # there are some use cases where we want to run this file as a script.
+    sys.path.append(os.path.dirname(os.path.realpath(__file__)))
+    import linux
+
+
+class g:
+    """Global variables for our program."""
+
+    child_pid = -1
+    orig_pid = os.getpid()
+    exit_signal = False
+    pid_status_cache = {}
+    restore_signals = set()
+    very_verbose = False
+
+
+unshare_flags = {
+    "C": linux.CLONE_NEWCGROUP,
+    "i": linux.CLONE_NEWIPC,
+    "m": linux.CLONE_NEWNS,
+    "n": linux.CLONE_NEWNET,
+    "p": linux.CLONE_NEWPID,
+    "u": linux.CLONE_NEWUTS,
+    "T": linux.CLONE_NEWTIME,
+}
+
+
+ignored_signals = {
+    S.SIGTTIN,
+    S.SIGTTOU,
+}
+abort_signals = {
+    S.SIGABRT,
+    S.SIGBUS,
+    S.SIGFPE,
+    S.SIGILL,
+    S.SIGKILL,
+    S.SIGSEGV,
+    S.SIGSTOP,
+    S.SIGSYS,
+    S.SIGTRAP,
+}
+no_prop_signals = abort_signals | ignored_signals | {S.SIGCHLD}
+
+
+def vdebug(*args, **kwargs):
+    if g.very_verbose:
+        logging.debug(*args, **kwargs)
+
+
+def get_pid_status_item(status, stat):
+    m = re.search(rf"(?:^|\n){stat}:\t(.*)(?:\n|$)", status)
+    return m.group(1).strip() if m else None
+
+
+def pget_pid_status_item(pid, stat):
+    if pid not in g.pid_status_cache:
+        with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f:
+            g.pid_status_cache[pid] = f.read().strip()
+    return get_pid_status_item(g.pid_status_cache[pid], stat).strip()
+
+
+def get_pid_name(pid):
+    try:
+        return get_pid_status_item(g.pid_status_cache[pid], "Name")
+    except Exception:
+        return str(pid)
+
+
+# def init_get_child_pids():
+#     """Return list of "children" pids.
+#     We consider any process with a 0 parent pid to also be our child as it
+#     nsentered our pid namespace from an external parent.
+#     """
+#     g.pid_status_cache.clear()
+#     pids = (int(x) for x in os.listdir("/proc") if x.isdigit() and x != "1")
+#     return (
+#         x for x in pids if x == g.child_pid or pget_pid_status_item(x, "PPid") == "0"
+#     )
+
+
+def exit_with_status(status):
+    if os.WIFEXITED(status):
+        ec = os.WEXITSTATUS(status)
+    elif os.WIFSIGNALED(status):
+        ec = 0x80 | os.WTERMSIG(status)
+    else:
+        ec = 255
+    logging.debug("exiting with code %s", ec)
+    sys.exit(ec)
+
+
+def waitpid(tag):
+    logging.debug("%s: waitid for exiting process", tag)
+    idobj = os.waitid(os.P_ALL, 0, os.WEXITED)
+    pid = idobj.si_pid
+    status = idobj.si_status
+
+    if pid != g.child_pid:
+        pidname = get_pid_name(pid)
+        logging.debug(
+            "%s: reaped zombie %s (%s) w/ status %s", tag, pid, pidname, status
+        )
+        return
+
+    logging.debug("reaped child with status %s", status)
+    exit_with_status(status)
+    # NOTREACHED
+
+
+def sig_trasmit(signum, _):
+    signame = signal.Signals(signum).name
+    if g.child_pid == -1:
+        # We've received a signal after setting up to be init proc
+        # but prior to fork or fork returning with child pid
+        logging.debug("received %s prior to child exec, exiting", signame)
+        sys.exit(0x80 | signum)
+
+    try:
+        os.kill(g.child_pid, signum)
+    except OSError as error:
+        if error.errno != errno.ESRCH:
+            logging.error(
+                "error forwarding signal %s to child, exiting: %s", signum, error
+            )
+            sys.exit(0x80 | signum)
+        logging.debug("child pid %s exited prior to signaling", g.child_pid)
+
+
+def sig_sigchld(signum, _):
+    assert signum == S.SIGCHLD
+    try:
+        waitpid("SIGCHLD")
+    except ChildProcessError as error:
+        logging.warning("got SIGCHLD but no pid to wait on: %s", error)
+
+
+def setup_init_signals():
+    valid = set(signal.valid_signals())
+    named = set(x.value for x in signal.Signals)
+    for snum in sorted(named):
+        if snum not in valid:
+            continue
+        if S.SIGRTMIN <= snum <= S.SIGRTMAX:
+            continue
+
+        sname = signal.Signals(snum).name
+        if snum == S.SIGCHLD:
+            vdebug("installing local handler for %s", sname)
+            signal.signal(snum, sig_sigchld)
+            g.restore_signals.add(snum)
+        elif snum in ignored_signals:
+            vdebug("installing ignore handler for %s", sname)
+            signal.signal(snum, signal.SIG_IGN)
+            g.restore_signals.add(snum)
+        elif snum in abort_signals:
+            vdebug("leaving default handler for %s", sname)
+            # signal.signal(snum, signal.SIG_DFL)
+        else:
+            vdebug("installing trasmit signal handler for %s", sname)
+            try:
+                signal.signal(snum, sig_trasmit)
+                g.restore_signals.add(snum)
+            except OSError as error:
+                logging.warning(
+                    "failed installing signal handler for %s: %s", sname, error
+                )
+
+
+def new_process_group():
+    """Create and lead a new process group.
+
+    This function will create a new process group if we are not yet leading one, and
+    additionally foreground said process group in our session. This foregrounding
+    action is copied from tini, and I believe serves a purpose when serving as init
+    for a container (e.g., podman).
+    """
+    pid = os.getpid()
+    try:
+        pgid = os.getpgrp()
+        if pgid == pid:
+            logging.debug("already process group leader %s", pgid)
+        else:
+            logging.debug("creating new process group %s", pid)
+            os.setpgid(pid, 0)
+    except Exception as error:
+        logging.warning("unable to get new process group: %s", error)
+        return
+
+    # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked
+    signal.signal(S.SIGTTIN, signal.SIG_IGN)
+    signal.signal(S.SIGTTOU, signal.SIG_IGN)
+    fd = sys.stdin.fileno()
+    if not os.isatty(fd):
+        logging.debug("stdin not a tty no foregrounding required")
+    else:
+        try:
+            # This will error if our session no longer associated with controlling tty.
+            pgid = os.tcgetpgrp(fd)
+            if pgid == pid:
+                logging.debug("process group already in foreground %s", pgid)
+            else:
+                logging.debug("making us the foreground pgid backgrounding %s", pgid)
+                os.tcsetpgrp(fd, pid)
+        except OSError as error:
+            if error.errno == errno.ENOTTY:
+                logging.debug("session is no longer associated with controlling tty")
+            else:
+                logging.warning("unable to foreground pgid %s: %s", pid, error)
+    signal.signal(S.SIGTTIN, signal.SIG_DFL)
+    signal.signal(S.SIGTTOU, signal.SIG_DFL)
+
+
+def is_creating_pid_namespace():
+    p1name = subprocess.check_output(
+        "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True
+    )
+    p2name = subprocess.check_output(
+        "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True
+    )
+    return p1name != p2name
+
+
+def be_init(new_pg, exec_args):
+    #
+    # Arrange for us to be killed when our parent dies, this will subsequently also kill
+    # all procs in any PID namespace we are init for.
+    #
+    logging.debug("set us to be SIGKILLed when parent exits")
+    linux.set_parent_death_signal(signal.SIGKILL)
+
+    # If we are createing a new PID namespace for children...
+    if g.orig_pid != 1:
+        logging.debug("started as pid %s", g.orig_pid)
+        # assert is_creating_pid_namespace()
+
+        # Fork to become pid 1
+        logging.debug("forking to become pid 1")
+        child_pid = os.fork()
+        if child_pid:
+            logging.debug("in parent waiting on child pid %s to exit", child_pid)
+            status = os.wait()
+            logging.debug("got child exit status %s", status)
+            exit_with_status(status)
+            # NOTREACHED
+
+        # We must be pid 1 now.
+        logging.debug("in child as pid %s", os.getpid())
+        assert os.getpid() == 1
+
+        # We need a new /proc now.
+        logging.debug("mount new /proc")
+        linux.mount("proc", "/proc", "proc")
+
+        # If the parent exists kill us using SIGKILL
+        logging.debug("set us to be SIGKILLed when parent exits")
+        linux.set_parent_death_signal(signal.SIGKILL)
+
+    if not exec_args:
+        if not new_pg:
+            logging.debug("no exec args, no new process group")
+            # # if 0 == os.getpgid(0):
+            # status = os.setpgid(0, 1)
+            # logging.debug("os.setpgid(0, 1) == %s", status)
+        else:
+            logging.debug("no exec args, creating new process group")
+            # No exec so we are the "child".
+            new_process_group()
+
+        # Reap children as init process
+        vdebug("installing local handler for SIGCHLD")
+        signal.signal(signal.SIGCHLD, sig_sigchld)
+
+        while True:
+            logging.info("init: waiting to reap zombies")
+            linux.pause()
+        # NOTREACHED
+
+    # Set (parent) signal handlers before any fork to avoid race
+    setup_init_signals()
+
+    logging.debug("forking to execute child")
+    g.child_pid = os.fork()
+    if g.child_pid == 0:
+        # In child, restore signals to default handling:
+        for snum in g.restore_signals:
+            signal.signal(snum, signal.SIG_DFL)
+
+        # XXX is a new pg right?
+        new_process_group()
+        logging.debug("child: executing '%s'", shlex.join(exec_args))
+        os.execvp(exec_args[0], exec_args)
+        # NOTREACHED
+
+    while True:
+        logging.info("parent: waiting for child pid %s to exit", g.child_pid)
+        waitpid("parent")
+
+
+def unshare(flags):
+    """Unshare into new namespaces."""
+    uflags = 0
+    for flag in flags:
+        if flag not in unshare_flags:
+            raise ValueError(f"unknown unshare flag '{flag}'")
+        uflags |= unshare_flags[flag]
+    new_pid = bool(uflags & linux.CLONE_NEWPID)
+    new_mnt = bool(uflags & linux.CLONE_NEWNS)
+
+    logging.debug("unshareing with flags: %s", linux.clone_flag_string(uflags))
+    linux.unshare(uflags)
+
+    if new_pid and not new_mnt:
+        try:
+            # If we are not creating new mount namspace, remount /proc private
+            # so that our mount of a new /proc doesn't affect parent namespace
+            logging.debug("remount /proc recursive private")
+            linux.mount("none", "/proc", None, linux.MS_REC | linux.MS_PRIVATE)
+        except OSError as error:
+            # EINVAL is OK b/c /proc not mounted may cause an error
+            if error.errno != errno.EINVAL:
+                raise
+    if new_mnt:
+        # Remount root as recursive private.
+        logging.debug("remount / recursive private")
+        linux.mount("none", "/", None, linux.MS_REC | linux.MS_PRIVATE)
+
+    # if new_pid:
+    #     logging.debug("mount new /proc")
+    #     linux.mount("proc", "/proc", "proc")
+
+    return new_pid
+
+
+def main():
+    #
+    # Parse CLI args.
+    #
+
+    ap = argparse.ArgumentParser()
+    ap.add_argument(
+        "-P",
+        "--no-proc-group",
+        action="store_true",
+        help="set to inherit the process group",
+    )
+    valid_flags = "".join(unshare_flags)
+    ap.add_argument(
+        "--unshare-flags",
+        help=(
+            f"string of unshare(1) flags. Supported values from '{valid_flags}'."
+            " 'm' will remount `/` recursive private. 'p' will remount /proc"
+            " and fork, and the child will be signaled to exit on exit of parent.."
+        ),
+    )
+    ap.add_argument(
+        "-v", dest="verbose", action="count", default=0, help="more -v's, more verbose"
+    )
+    ap.add_argument("rest", nargs=argparse.REMAINDER)
+    args = ap.parse_args()
+
+    #
+    # Setup logging.
+    #
+
+    level = logging.DEBUG if args.verbose else logging.INFO
+    if args.verbose > 1:
+        g.very_verbose = True
+    logging.basicConfig(
+        level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s"
+    )
+
+    #
+    # Run program
+    #
+
+    status = 5
+    try:
+        new_pid = False
+        if args.unshare_flags:
+            new_pid = unshare(args.unshare_flags)
+
+        if g.orig_pid != 1 and not new_pid:
+            # Simply hold the namespaces
+            while True:
+                logging.info("holding namespace waiting to be signaled to exit")
+                linux.pause()
+            # NOTREACHED
+
+        be_init(not args.no_proc_group, args.rest)
+        # NOTREACHED
+        logging.critical("Exited from be_init!")
+    except KeyboardInterrupt:
+        logging.info("exiting (main), received KeyboardInterrupt in main")
+        status = 0x80 | signal.SIGINT
+    except Exception as error:
+        logging.info("exiting (main), do to exception %s", error, exc_info=True)
+
+    sys.exit(status)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py
new file mode 100644 (file)
index 0000000..fecf709
--- /dev/null
@@ -0,0 +1,2941 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# October 1 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+# pylint: disable=protected-access
+"""A module that defines objects for standalone use."""
+import asyncio
+import errno
+import getpass
+import ipaddress
+import logging
+import os
+import random
+import re
+import shlex
+import socket
+import subprocess
+import time
+
+from . import cli
+from .base import BaseMunet
+from .base import Bridge
+from .base import Commander
+from .base import LinuxNamespace
+from .base import MunetError
+from .base import Timeout
+from .base import _async_get_exec_path
+from .base import _get_exec_path
+from .base import cmd_error
+from .base import commander
+from .base import fsafe_name
+from .base import get_exec_path_host
+from .config import config_subst
+from .config import config_to_dict_with_key
+from .config import find_matching_net_config
+from .config import find_with_kv
+from .config import merge_kind_config
+
+
+class L3ContainerNotRunningError(MunetError):
+    """Exception if no running container exists."""
+
+
+def get_loopback_ips(c, nid):
+    if ip := c.get("ip"):
+        if ip == "auto":
+            return [ipaddress.ip_interface("10.255.0.0/32") + nid]
+        if isinstance(ip, str):
+            return [ipaddress.ip_interface(ip)]
+        return [ipaddress.ip_interface(x) for x in ip]
+    return []
+
+
+def make_ip_network(net, inc):
+    n = ipaddress.ip_network(net)
+    return ipaddress.ip_network(
+        (n.network_address + inc * n.num_addresses, n.prefixlen)
+    )
+
+
+def make_ip_interface(ia, inc):
+    ia = ipaddress.ip_interface(ia)
+    # this turns into a /32 fix this
+    ia = ia + ia.network.num_addresses * inc
+    # IPv6
+    ia = ipaddress.ip_interface(str(ia).replace("/32", "/24").replace("/128", "/64"))
+    return ia
+
+
+def get_ip_network(c, brid, ipv6=False):
+    ip = c.get("ipv6" if ipv6 else "ip")
+    if ip and str(ip) != "auto":
+        try:
+            ifip = ipaddress.ip_interface(ip)
+            if ifip.ip == ifip.network.network_address:
+                return ifip.network
+            return ifip
+        except ValueError:
+            return ipaddress.ip_network(ip)
+    if ipv6:
+        return make_ip_interface("fc00::fe/64", brid)
+    return make_ip_interface("10.0.0.254/24", brid)
+
+
+def parse_pciaddr(devaddr):
+    comp = re.match(
+        "(?:([0-9A-Fa-f]{4}):)?([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}).([0-7])", devaddr
+    ).groups()
+    if comp[0] is None:
+        comp[0] = "0000"
+    return [int(x, 16) for x in comp]
+
+
+def read_int_value(path):
+    return int(open(path, encoding="ascii").read())
+
+
+def read_str_value(path):
+    return open(path, encoding="ascii").read().strip()
+
+
+def read_sym_basename(path):
+    return os.path.basename(os.readlink(path))
+
+
+async def to_thread(func):
+    """to_thread for python < 3.9."""
+    try:
+        return await asyncio.to_thread(func)
+    except AttributeError:
+        logging.warning("Using backport to_thread")
+        return await asyncio.get_running_loop().run_in_executor(None, func)
+
+
+def convert_ranges_to_bitmask(ranges):
+    bitmask = 0
+    for r in ranges.split(","):
+        if "-" not in r:
+            bitmask |= 1 << int(r)
+        else:
+            x, y = (int(x) for x in r.split("-"))
+            for b in range(x, y + 1):
+                bitmask |= 1 << b
+    return bitmask
+
+
+class L2Bridge(Bridge):
+    """A linux bridge with no IP network address."""
+
+    def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None):
+        """Create a linux Bridge."""
+        super().__init__(name=name, unet=unet, logger=logger, mtu=mtu)
+
+        self.config = config if config else {}
+
+    async def _async_delete(self):
+        self.logger.debug("%s: deleting", self)
+        await super()._async_delete()
+
+
+class L3Bridge(Bridge):
+    """A linux bridge with associated IP network address."""
+
+    def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None):
+        """Create a linux Bridge."""
+        super().__init__(name=name, unet=unet, logger=logger, mtu=mtu)
+
+        self.config = config if config else {}
+
+        self.ip_interface = get_ip_network(self.config, self.id)
+        if hasattr(self.ip_interface, "network"):
+            self.ip_address = self.ip_interface.ip
+            self.ip_network = self.ip_interface.network
+            self.cmd_raises(f"ip addr add {self.ip_interface} dev {name}")
+        else:
+            self.ip_address = None
+            self.ip_network = self.ip_interface
+
+        self.logger.debug("%s: set IPv4 network address to %s", self, self.ip_interface)
+        self.cmd_raises("sysctl -w net.ipv4.ip_forward=1")
+
+        self.ip6_interface = None
+        if self.unet.ipv6_enable:
+            self.ip6_interface = get_ip_network(self.config, self.id, ipv6=True)
+            if hasattr(self.ip6_interface, "network"):
+                self.ip6_address = self.ip6_interface.ip
+                self.ip6_network = self.ip6_interface.network
+                self.cmd_raises(f"ip addr add {self.ip6_interface} dev {name}")
+            else:
+                self.ip6_address = None
+                self.ip6_network = self.ip6_interface
+
+            self.logger.debug(
+                "%s: set IPv6 network address to %s", self, self.ip_interface
+            )
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1")
+
+        self.is_nat = self.config.get("nat", False)
+        if self.is_nat:
+            self.cmd_raises(
+                "iptables -t nat -A POSTROUTING "
+                f"-s {self.ip_network} ! -d {self.ip_network} "
+                f"! -o {self.name} -j MASQUERADE"
+            )
+
+    def get_intf_addr(self, ifname, ipv6=False):
+        # None is a valid interface, we have the same address for all interfaces
+        # just make sure they aren't asking for something we don't have.
+        if ifname is not None and ifname not in self.intfs:
+            return None
+        return self.ip6_interface if ipv6 else self.ip_interface
+
+    async def _async_delete(self):
+        self.logger.debug("%s: deleting", self)
+
+        if self.config.get("nat", False):
+            self.cmd_status(
+                "iptables -t nat -D POSTROUTING "
+                f"-s {self.ip_network} ! -d {self.ip_network} "
+                f"! -o {self.name} -j MASQUERADE"
+            )
+        await super()._async_delete()
+
+
+class NodeMixin:
+    """Node attributes and functionality."""
+
+    next_ord = 1
+
+    @classmethod
+    def _get_next_ord(cls):
+        # Do not use `cls` here b/c that makes the variable class specific
+        n = L3NodeMixin.next_ord
+        L3NodeMixin.next_ord = n + 1
+        return n
+
+    def __init__(self, *args, config=None, **kwargs):
+        """Create a Node."""
+        super().__init__(*args, **kwargs)
+
+        self.config = config if config else {}
+        config = self.config
+
+        self.id = int(config["id"]) if "id" in config else self._get_next_ord()
+
+        self.cmd_p = None
+        self.container_id = None
+        self.cleanup_called = False
+
+        # Clear and create rundir early
+        assert self.unet is not None
+        self.rundir = self.unet.rundir.joinpath(self.name)
+        commander.cmd_raises(f"rm -rf {self.rundir}")
+        commander.cmd_raises(f"mkdir -p {self.rundir}")
+
+    def _shebang_prep(self, config_key):
+        cmd = self.config.get(config_key, "").strip()
+        if not cmd:
+            return []
+
+        script_name = fsafe_name(config_key)
+
+        # shell_cmd is a union and can be boolean or string
+        shell_cmd = self.config.get("shell", "/bin/bash")
+        if not isinstance(shell_cmd, str):
+            if shell_cmd:
+                # i.e., "shell: true"
+                shell_cmd = "/bin/bash"
+            else:
+                # i.e., "shell: false"
+                shell_cmd = ""
+
+        # If we have a shell_cmd then we create a cleanup_cmds file in run_cmd
+        # and volume mounted it
+        if shell_cmd:
+            # Create cleanup cmd file
+            cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+            cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+            cmd = cmd.replace("%NAME%", str(self.name))
+            cmd += "\n"
+
+            # Write out our cleanup cmd file at this time too.
+            cmdpath = os.path.join(self.rundir, f"{script_name}.shebang")
+            with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+                cmdfile.write(f"#!{shell_cmd}\n")
+                cmdfile.write(cmd)
+                cmdfile.flush()
+            commander.cmd_raises(f"chmod 755 {cmdpath}")
+
+            if self.container_id:
+                # XXX this counts on it being mounted in container, ugly
+                cmds = [f"/tmp/{script_name}.shebang"]
+            else:
+                cmds = [cmdpath]
+        else:
+            cmds = []
+            if isinstance(cmd, str):
+                cmds.extend(shlex.split(cmd))
+            else:
+                cmds.extend(cmd)
+            cmds = [
+                x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds
+            ]
+            cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds]
+            cmds = [x.replace("%NAME%", str(self.name)) for x in cmds]
+
+        return cmds
+
+    async def _async_shebang_cmd(self, config_key, warn=True):
+        cmds = self._shebang_prep(config_key)
+        if not cmds:
+            return 0
+
+        rc, o, e = await self.async_cmd_status(cmds, warn=warn)
+        if not rc and warn and (o or e):
+            self.logger.info(
+                f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+            )
+        elif rc and warn:
+            self.logger.warning(
+                f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+            )
+        else:
+            self.logger.debug(
+                f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e)
+            )
+
+        return rc
+
+    def has_run_cmd(self) -> bool:
+        return bool(self.config.get("cmd", "").strip())
+
+    async def get_proc_child_pid(self, p):
+        # commander is right for both unshare inline (our proc pidns)
+        # and non-inline (root pidns).
+
+        # This doesn't work b/c we can't get back to the root pidns
+
+        rootcmd = self.unet.rootcmd
+        pgrep = rootcmd.get_exec_path("pgrep")
+        spid = str(p.pid)
+        for _ in Timeout(4):
+            if p.returncode is not None:
+                self.logger.debug("%s: proc %s exited before getting child", self, p)
+                return None
+
+            rc, o, e = await rootcmd.async_cmd_status(
+                [pgrep, "-o", "-P", spid], warn=False
+            )
+            if rc == 0:
+                return int(o.strip())
+
+            await asyncio.sleep(0.1)
+            self.logger.debug(
+                "%s: no child of proc %s: %s", self, p, cmd_error(rc, o, e)
+            )
+        self.logger.warning("%s: timeout getting child pid of proc %s", self, p)
+        return None
+
+    async def run_cmd(self):
+        """Run the configured commands for this node."""
+        self.logger.debug(
+            "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+        )
+
+        cmds = self._shebang_prep("cmd")
+        if not cmds:
+            return
+
+        stdout = open(os.path.join(self.rundir, "cmd.out"), "wb")
+        stderr = open(os.path.join(self.rundir, "cmd.err"), "wb")
+        self.cmd_pid = None
+        self.cmd_p = await self.async_popen(
+            cmds,
+            stdin=subprocess.DEVNULL,
+            stdout=stdout,
+            stderr=stderr,
+            start_new_session=True,  # allows us to signal all children to exit
+        )
+
+        # If our process is actually the child of an nsenter fetch its pid.
+        if self.nsenter_fork:
+            self.cmd_pid = await self.get_proc_child_pid(self.cmd_p)
+
+        self.logger.debug(
+            "%s: async_popen %s => %s (cmd_pid %s)",
+            self,
+            cmds,
+            self.cmd_p.pid,
+            self.cmd_pid,
+        )
+
+        self.pytest_hook_run_cmd(stdout, stderr)
+
+        return self.cmd_p
+
+    async def _async_cleanup_cmd(self):
+        """Run the configured cleanup commands for this node.
+
+        This function is called by subclass' async_cleanup_cmd
+        """
+        self.cleanup_called = True
+
+        return await self._async_shebang_cmd("cleanup-cmd")
+
+    def has_cleanup_cmd(self) -> bool:
+        return bool(self.config.get("cleanup-cmd", "").strip())
+
+    async def async_cleanup_cmd(self):
+        """Run the configured cleanup commands for this node."""
+        return await self._async_cleanup_cmd()
+
+    def has_ready_cmd(self) -> bool:
+        return bool(self.config.get("ready-cmd", "").strip())
+
+    async def async_ready_cmd(self):
+        """Run the configured ready commands for this node."""
+        return not await self._async_shebang_cmd("ready-cmd", warn=False)
+
+    def cmd_completed(self, future):
+        self.logger.debug("%s: cmd completed callback", self)
+        try:
+            status = future.result()
+            self.logger.debug(
+                "%s: node cmd_p completed result: %s cmd: %s", self, status, self.cmd_p
+            )
+            self.cmd_pid = None
+            self.cmd_p = None
+        except asyncio.CancelledError:
+            # Should we stop the container if we have one?
+            self.logger.debug("%s: node cmd_p.wait() canceled", future)
+
+    def pytest_hook_run_cmd(self, stdout, stderr):
+        """Handle pytest options related to running the node cmd.
+
+        This function does things such as launch tail'ing windows
+        on the given files if requested by the user.
+
+        Args:
+            stdout: file-like object with a ``name`` attribute, or a path to a file.
+            stderr: file-like object with a ``name`` attribute, or a path to a file.
+        """
+        if not self.unet:
+            return
+
+        outopt = self.unet.cfgopt.getoption("--stdout")
+        outopt = outopt if outopt is not None else ""
+        if outopt == "all" or self.name in outopt.split(","):
+            outname = stdout.name if hasattr(stdout, "name") else stdout
+            self.run_in_window(f"tail -F {outname}", title=f"O:{self.name}")
+
+        if stderr:
+            erropt = self.unet.cfgopt.getoption("--stderr")
+            erropt = erropt if erropt is not None else ""
+            if erropt == "all" or self.name in erropt.split(","):
+                errname = stderr.name if hasattr(stderr, "name") else stderr
+                self.run_in_window(f"tail -F {errname}", title=f"E:{self.name}")
+
+    def pytest_hook_open_shell(self):
+        if not self.unet:
+            return
+
+        gdbcmd = self.config.get("gdb-cmd")
+        shellopt = self.unet.cfgopt.getoption("--gdb", "")
+        should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(","))
+        use_emacs = self.unet.cfgopt.getoption("--gdb-use-emacs", False)
+
+        if should_gdb and not use_emacs:
+            cmds = self.config.get("gdb-target-cmds", [])
+            for cmd in cmds:
+                gdbcmd += f" '-ex={cmd}'"
+
+            bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",")
+            for bp in bps:
+                gdbcmd += f" '-ex=b {bp}'"
+
+            cmds = self.config.get("gdb-run-cmd", [])
+            for cmd in cmds:
+                gdbcmd += f" '-ex={cmd}'"
+
+            self.run_in_window(gdbcmd)
+        elif should_gdb and use_emacs:
+            gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ")
+            ecbin = self.get_exec_path("emacsclient")
+            # output = self.cmd_raises(
+            #     [ecbin, "--eval", f"(gdb \"{gdbcmd} -ex='p 123456'\")"]
+            # )
+            _ = self.cmd_raises([ecbin, "--eval", f'(gdb "{gdbcmd}")'])
+
+            # can't figure out how to wait until symbols are loaded, until we do we just
+            # have to wait "long enough" for the symbol load to finish :/
+            # for _ in range(100):
+            #     output = self.cmd_raises(
+            #         [
+            #             ecbin,
+            #             "--eval",
+            #             f"gdb-first-prompt",
+            #         ]
+            #     )
+            #     if output == "nil\n":
+            #         break
+            #     time.sleep(0.25)
+
+            time.sleep(10)
+
+            cmds = self.config.get("gdb-target-cmds", [])
+            for cmd in cmds:
+                # we may want to quote quotes in the cmd string
+                self.cmd_raises(
+                    [
+                        ecbin,
+                        "--eval",
+                        f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+                    ]
+                )
+
+            bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",")
+            for bp in bps:
+                cmd = f"br {bp}"
+                self.cmd_raises(
+                    [
+                        ecbin,
+                        "--eval",
+                        f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+                    ]
+                )
+
+            cmds = self.config.get("gdb-run-cmds", [])
+            for cmd in cmds:
+                # we may want to quote quotes in the cmd string
+                self.cmd_raises(
+                    [
+                        ecbin,
+                        "--eval",
+                        f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")',
+                    ]
+                )
+                gdbcmd += f" '-ex={cmd}'"
+
+        shellopt = self.unet.cfgopt.getoption("--shell")
+        shellopt = shellopt if shellopt else ""
+        if shellopt == "all" or self.name in shellopt.split(","):
+            self.run_in_window("bash")
+
+    async def _async_delete(self):
+        self.logger.debug("%s: NodeMixin sub-class _async_delete", self)
+
+        if self.cmd_p:
+            await self.async_cleanup_proc(self.cmd_p, self.cmd_pid)
+            self.cmd_p = None
+
+        # Next call users "cleanup_cmd:"
+        try:
+            if not self.cleanup_called:
+                await self.async_cleanup_cmd()
+        except Exception as error:
+            self.logger.warning(
+                "Got an error during delete from async_cleanup_cmd: %s", error
+            )
+
+        # delete the LinuxNamespace/InterfaceMixin
+        await super()._async_delete()
+
+
+class SSHRemote(NodeMixin, Commander):
+    """SSHRemote a node representing an ssh connection to something."""
+
+    def __init__(
+        self,
+        name,
+        server,
+        port=22,
+        user=None,
+        password=None,
+        idfile=None,
+        **kwargs,
+    ):
+        super().__init__(name, **kwargs)
+
+        self.logger.debug("%s: creating", self)
+
+        # Things done in LinuxNamepsace we need to replicate here.
+        self.rundir = self.unet.rundir.joinpath(self.name)
+        self.unet.cmd_raises(f"rm -rf {self.rundir}")
+        self.unet.cmd_raises(f"mkdir -p {self.rundir}")
+
+        self.mgmt_ip = None
+        self.mgmt_ip6 = None
+
+        self.port = port
+
+        if user:
+            self.user = user
+        elif "SUDO_USER" in os.environ:
+            self.user = os.environ["SUDO_USER"]
+        else:
+            self.user = getpass.getuser()
+        self.password = password
+        self.idfile = idfile
+
+        self.server = f"{self.user}@{server}"
+
+        # Setup our base `pre-cmd` values
+        #
+        # We maybe should add environment variable transfer here in particular
+        # MUNET_NODENAME. The problem is the user has to explicitly approve
+        # of SendEnv variables.
+        self.__base_cmd = [
+            get_exec_path_host("sudo"),
+            "-E",
+            f"-u{self.user}",
+            get_exec_path_host("ssh"),
+        ]
+        if port != 22:
+            self.__base_cmd.append(f"-p{port}")
+        self.__base_cmd.append("-q")
+        self.__base_cmd.append("-oStrictHostKeyChecking=no")
+        self.__base_cmd.append("-oUserKnownHostsFile=/dev/null")
+        if self.idfile:
+            self.__base_cmd.append(f"-i{self.idfile}")
+        # Would be nice but has to be accepted by server config so not very useful.
+        # self.__base_cmd.append("-oSendVar='TEST'")
+        self.__base_cmd_pty = list(self.__base_cmd)
+        self.__base_cmd_pty.append("-t")
+        self.__base_cmd.append(self.server)
+        self.__base_cmd_pty.append(self.server)
+        # self.set_pre_cmd(pre_cmd, pre_cmd_tty)
+
+        self.logger.info("%s: created", self)
+
+    def has_ready_cmd(self) -> bool:
+        return bool(self.config.get("ready-cmd", "").strip())
+
+    def _get_pre_cmd(self, use_str, use_pty, ns_only=False, **kwargs):
+        pre_cmd = []
+        if self.unet:
+            pre_cmd = self.unet._get_pre_cmd(False, use_pty, ns_only=False, **kwargs)
+        if ns_only:
+            return pre_cmd
+
+        # XXX grab the env from kwargs and add to podman exec
+        # env = kwargs.get("env", {})
+        if use_pty:
+            pre_cmd = pre_cmd + self.__base_cmd_pty
+        else:
+            pre_cmd = pre_cmd + self.__base_cmd
+        return shlex.join(pre_cmd) if use_str else list(pre_cmd)
+
+    def _get_cmd_as_list(self, cmd):
+        """Given a list or string return a list form for execution.
+
+        If cmd is a string then [cmd] is returned, for most other
+        node types ["bash", "-c", cmd] is returned but in our case
+        ssh is the shell.
+
+        Args:
+            cmd: list or string representing the command to execute.
+            str_shell: if True and `cmd` is a string then run the
+              command using bash -c
+        Returns:
+            list of commands to execute.
+        """
+        return [cmd] if isinstance(cmd, str) else cmd
+
+
+# Would maybe like to refactor this into L3 and Node
+class L3NodeMixin(NodeMixin):
+    """A linux namespace with IP attributes."""
+
+    def __init__(self, *args, unet=None, **kwargs):
+        """Create an L3Node."""
+        # logging.warning(
+        #     "L3NodeMixin: config %s unet %s kwargs %s", config, unet, kwargs
+        # )
+        super().__init__(*args, unet=unet, **kwargs)
+
+        self.mgmt_ip = None  # set in parser.py
+        self.mgmt_ip6 = None  # set in parser.py
+        self.host_intfs = {}
+        self.phy_intfs = {}
+        self.phycount = 0
+        self.phy_odrivers = {}
+        self.tapmacs = {}
+
+        self.intf_tc_count = 0
+
+        # super().__init__(name=name, **kwargs)
+
+        self.mount_volumes()
+
+        # -----------------------
+        # Setup node's networking
+        # -----------------------
+        if not unet.ipv6_enable:
+            # Disable IPv6
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0")
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
+        else:
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1")
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0")
+
+        self.next_p2p_network = ipaddress.ip_network(f"10.254.{self.id}.0/31")
+        self.next_p2p_network6 = ipaddress.ip_network(f"fcff:ffff:{self.id:02x}::/127")
+
+        self.loopback_ip = None
+        self.loopback_ips = get_loopback_ips(self.config, self.id)
+        self.loopback_ip = self.loopback_ips[0] if self.loopback_ips else None
+        if self.loopback_ip:
+            self.cmd_raises_nsonly(f"ip addr add {self.loopback_ip} dev lo")
+            self.cmd_raises_nsonly("ip link set lo up")
+            for i, ip in enumerate(self.loopback_ips[1:]):
+                self.cmd_raises_nsonly(f"ip addr add {ip} dev lo:{i}")
+
+        # -------------------
+        # Setup node's rundir
+        # -------------------
+
+        # Not host path based, but we assume same
+        self.set_ns_cwd(self.rundir)
+
+        # Save the namespace pid
+        with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+            f.write(f"{self.pid}\n")
+
+        with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+            f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+        # Create a hosts file to map our name
+        hosts_file = os.path.join(self.rundir, "hosts.txt")
+        with open(hosts_file, "w", encoding="ascii") as hf:
+            hf.write(
+                f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+            )
+        if hasattr(self, "bind_mount"):
+            self.bind_mount(hosts_file, "/etc/hosts")
+
+    async def console(
+        self,
+        concmd,
+        prompt=r"(^|\r?\n)[^#\$]*[#\$] ",
+        is_bourne=True,
+        user=None,
+        password=None,
+        expects=None,
+        sends=None,
+        use_pty=False,
+        will_echo=False,
+        logfile_prefix="console",
+        trace=True,
+        **kwargs,
+    ):
+        """Create a REPL (read-eval-print-loop) driving a console.
+
+        Args:
+            concmd: string or list to popen with, or an already open socket
+            prompt: the REPL prompt to look for, the function returns when seen
+            is_bourne: True if the console is a bourne shell
+            user: user name to log in with
+            password: password to log in with
+            expects: a list of regex other than the prompt, the standard user, or
+                password to look for. "ogin:" or "[Pp]assword:"r.
+            sends: what to send when an element of `expects` matches. Can be the
+                empty string to send nothing.
+            use_pty: true for pty based expect, otherwise uses popen (pipes/files)
+            will_echo: bash is buggy in that it echo's to non-tty unlike any other
+                sh/ksh, set this value to true if running back
+            logfile_prefix: prefix for 3 logfiles opened to track the console i/o
+            trace: trace the send/expect sequence
+            **kwargs: kwargs passed on the _spawn.
+        """
+        lfname = os.path.join(self.rundir, f"{logfile_prefix}-log.txt")
+        logfile = open(lfname, "a+", encoding="utf-8")
+        logfile.write("-- start logging for: '{}' --\n".format(concmd))
+
+        lfname = os.path.join(self.rundir, f"{logfile_prefix}-read-log.txt")
+        logfile_read = open(lfname, "a+", encoding="utf-8")
+        logfile_read.write("-- start read logging for: '{}' --\n".format(concmd))
+
+        lfname = os.path.join(self.rundir, f"{logfile_prefix}-send-log.txt")
+        logfile_send = open(lfname, "a+", encoding="utf-8")
+        logfile_send.write("-- start send logging for: '{}' --\n".format(concmd))
+
+        expects = [] if expects is None else expects
+        sends = [] if sends is None else sends
+        if user:
+            expects.append("ogin:")
+            sends.append(user + "\n")
+        if password is not None:
+            expects.append("assword:")
+            sends.append(password + "\n")
+        repl = await self.shell_spawn(
+            concmd,
+            prompt,
+            expects=expects,
+            sends=sends,
+            use_pty=use_pty,
+            will_echo=will_echo,
+            is_bourne=is_bourne,
+            logfile=logfile,
+            logfile_read=logfile_read,
+            logfile_send=logfile_send,
+            trace=trace,
+            **kwargs,
+        )
+        return repl
+
+    async def monitor(
+        self,
+        sockpath,
+        prompt=r"\(qemu\) ",
+    ):
+        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        sock.connect(sockpath)
+
+        pfx = os.path.basename(sockpath)
+
+        lfname = os.path.join(self.rundir, f"{pfx}-log.txt")
+        logfile = open(lfname, "a+", encoding="utf-8")
+        logfile.write("-- start logging for: '{}' --\n".format(sock))
+
+        lfname = os.path.join(self.rundir, f"{pfx}-read-log.txt")
+        logfile_read = open(lfname, "a+", encoding="utf-8")
+        logfile_read.write("-- start read logging for: '{}' --\n".format(sock))
+
+        p = self.spawn(sock, prompt, logfile=logfile, logfile_read=logfile_read)
+        from .base import ShellWrapper  # pylint: disable=C0415
+
+        p.send("\n")
+        return ShellWrapper(p, prompt, None, will_echo=True, escape_ansi=True)
+
+    def mount_volumes(self):
+        for m in self.config.get("volumes", []):
+            if isinstance(m, str):
+                s = m.split(":", 1)
+                if len(s) == 1:
+                    self.tmpfs_mount(s[0])
+                else:
+                    spath = s[0]
+                    if spath[0] == ".":
+                        spath = os.path.abspath(
+                            os.path.join(self.unet.config_dirname, spath)
+                        )
+                    self.bind_mount(spath, s[1])
+                continue
+            raise NotImplementedError("complex mounts for non-containers")
+
+    def get_ifname(self, netname):
+        for c in self.config["connections"]:
+            if c["to"] == netname:
+                return c["name"]
+        return None
+
+    def set_lan_addr(self, switch, cconf):
+        if ip := cconf.get("ip"):
+            ipaddr = ipaddress.ip_interface(ip)
+            assert ipaddr.version == 4
+        elif self.unet.autonumber and "ip" not in cconf:
+            self.logger.debug(
+                "%s: prefixlen of switch %s is %s",
+                self,
+                switch.name,
+                switch.ip_network.prefixlen,
+            )
+            n = switch.ip_network
+            ipaddr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen))
+        else:
+            ipaddr = None
+
+        if ip := cconf.get("ipv6"):
+            ip6addr = ipaddress.ip_interface(ip)
+            assert ipaddr.version == 6
+        elif self.unet.ipv6_enable and self.unet.autonumber and "ipv6" not in cconf:
+            self.logger.debug(
+                "%s: prefixlen of switch %s is %s",
+                self,
+                switch.name,
+                switch.ip6_network.prefixlen,
+            )
+            n = switch.ip6_network
+            ip6addr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen))
+        else:
+            ip6addr = None
+
+        dns_network = self.unet.topoconf.get("dns-network")
+        for ip in (ipaddr, ip6addr):
+            if not ip:
+                continue
+            ipcmd = "ip " if ip.version == 4 else "ip -6 "
+            if dns_network and dns_network == switch.name:
+                if ip.version == 4:
+                    self.mgmt_ip = ip.ip
+                else:
+                    self.mgmt_ip6 = ip.ip
+            ifname = cconf["name"]
+            self.set_intf_addr(ifname, ip)
+            self.logger.debug("%s: adding %s to lan intf %s", self, ip, ifname)
+            if not self.is_vm:
+                self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}")
+                if hasattr(switch, "is_nat") and switch.is_nat:
+                    swaddr = (
+                        switch.ip_address if ip.version == 4 else switch.ip6_address
+                    )
+                    self.cmd_raises(ipcmd + f"route add default via {swaddr}")
+
+    def _set_p2p_addr(self, other, cconf, occonf, ipv6=False):
+        ipkey = "ipv6" if ipv6 else "ip"
+        ipaddr = ipaddress.ip_interface(cconf[ipkey]) if cconf.get(ipkey) else None
+        oipaddr = ipaddress.ip_interface(occonf[ipkey]) if occonf.get(ipkey) else None
+        self.logger.debug(
+            "%s: set_p2p_addr %s %s %s", self, other.name, ipaddr, oipaddr
+        )
+
+        if not ipaddr and not oipaddr:
+            if self.unet.autonumber:
+                if ipv6:
+                    n = self.next_p2p_network6
+                    self.next_p2p_network6 = make_ip_network(n, 1)
+                else:
+                    n = self.next_p2p_network
+                    self.next_p2p_network = make_ip_network(n, 1)
+
+                ipaddr = ipaddress.ip_interface(n)
+                oipaddr = ipaddress.ip_interface((ipaddr.ip + 1, n.prefixlen))
+            else:
+                return
+
+        if ipaddr:
+            ifname = cconf["name"]
+            self.set_intf_addr(ifname, ipaddr)
+            self.logger.debug("%s: adding %s to p2p intf %s", self, ipaddr, ifname)
+            if "physical" not in cconf and not self.is_vm:
+                self.intf_ip_cmd(ifname, f"ip addr add {ipaddr} dev {ifname}")
+
+        if oipaddr:
+            oifname = occonf["name"]
+            other.set_intf_addr(oifname, oipaddr)
+            self.logger.debug(
+                "%s: adding %s to other p2p intf %s", other, oipaddr, oifname
+            )
+            if "physical" not in occonf and not other.is_vm:
+                other.intf_ip_cmd(oifname, f"ip addr add {oipaddr} dev {oifname}")
+
+    def set_p2p_addr(self, other, cconf, occonf):
+        self._set_p2p_addr(other, cconf, occonf, ipv6=False)
+        if self.unet.ipv6_enable:
+            self._set_p2p_addr(other, cconf, occonf, ipv6=True)
+
+    async def add_host_intf(self, hname, lname, mtu=None):
+        if hname in self.host_intfs:
+            return
+        self.host_intfs[hname] = lname
+        self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ")
+        self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}")
+        self.cmd_raises(f"ip link set {hname} name {lname}")
+        if mtu:
+            self.cmd_raises(f"ip link set {lname} mtu {mtu}")
+        self.cmd_raises(f"ip link set {lname} up")
+
+    async def rem_host_intf(self, hname):
+        lname = self.host_intfs[hname]
+        self.cmd_raises(f"ip link set {lname} down")
+        self.cmd_raises(f"ip link set {lname} name {hname}")
+        self.cmd_raises(f"ip link set {hname} netns 1")
+        del self.host_intfs[hname]
+
+    async def add_phy_intf(self, devaddr, lname):
+        """Add a physical inteface (i.e. mv it to vfio-pci driver.
+
+        This is primarily useful for Qemu, but also for things like TREX or DPDK
+        """
+        if devaddr in self.phy_intfs:
+            return
+        self.phy_intfs[devaddr] = lname
+        index = len(self.phy_intfs)
+
+        _, _, off, fun = parse_pciaddr(devaddr)
+        doffset = off * 8 + fun
+
+        is_virtual = self.unet.rootcmd.path_exists(
+            f"/sys/bus/pci/devices/{devaddr}/physfn"
+        )
+        if is_virtual:
+            pfname = self.unet.rootcmd.cmd_raises(
+                f"ls -1 /sys/bus/pci/devices/{devaddr}/physfn/net"
+            ).strip()
+            pdevaddr = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/physfn")
+            _, _, poff, pfun = parse_pciaddr(pdevaddr)
+            poffset = poff * 8 + pfun
+
+            offset = read_int_value(
+                f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_offset"
+            )
+            stride = read_int_value(
+                f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_stride"
+            )
+            vf = (doffset - offset - poffset) // stride
+            mac = f"02:cc:cc:cc:{index:02x}:{self.id:02x}"
+            # Some devices require the parent to be up (e.g., ixbge)
+            self.unet.rootcmd.cmd_raises(f"ip link set {pfname} up")
+            self.unet.rootcmd.cmd_raises(f"ip link set {pfname} vf {vf} mac {mac}")
+            self.unet.rootcmd.cmd_status(f"ip link set {pfname} vf {vf} trust on")
+            self.tapmacs[devaddr] = mac
+
+        self.logger.info("Adding physical PCI device %s as %s", devaddr, lname)
+
+        # Get interface name and set to down if present
+        ec, ifname, _ = self.unet.rootcmd.cmd_status(
+            f"ls /sys/bus/pci/devices/{devaddr}/net/", warn=False
+        )
+        ifname = ifname.strip()
+        if not ec and ifname:
+            # XXX Should only do this is the device is up, and then likewise return it
+            # up on exit self.phy_intfs_hostname[devaddr] = ifname
+            self.logger.info(
+                "Setting physical PCI device %s named %s down", devaddr, ifname
+            )
+            self.unet.rootcmd.cmd_status(
+                f"ip link set {ifname} down 2> /dev/null || true"
+            )
+
+        # Get the current bound driver, and unbind
+        try:
+            driver = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/driver")
+            driver = driver.strip()
+        except Exception:
+            driver = ""
+        if driver:
+            if driver == "vfio-pci":
+                self.logger.info(
+                    "Physical PCI device %s already bound to vfio-pci", devaddr
+                )
+                return
+            self.logger.info(
+                "Unbinding physical PCI device %s from driver %s", devaddr, driver
+            )
+            self.phy_odrivers[devaddr] = driver
+            self.unet.rootcmd.cmd_raises(
+                f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind"
+            )
+
+        # Add the device vendor and device id to vfio-pci in case it's the first time
+        vendor = read_str_value(f"/sys/bus/pci/devices/{devaddr}/vendor")
+        devid = read_str_value(f"/sys/bus/pci/devices/{devaddr}/device")
+        self.logger.info("Adding device IDs %s:%s to vfio-pci", vendor, devid)
+        ec, _, _ = self.unet.rootcmd.cmd_status(
+            f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False
+        )
+
+        if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"):
+            # Bind to vfio-pci if wasn't added with new_id
+            self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr)
+            ec, _, _ = self.unet.rootcmd.cmd_status(
+                f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/bind"
+            )
+
+    async def rem_phy_intf(self, devaddr):
+        """Remove a physical inteface (i.e. mv it away from vfio-pci driver.
+
+        This is primarily useful for Qemu, but also for things like TREX or DPDK
+        """
+        lname = self.phy_intfs.get(devaddr, "")
+        if lname:
+            del self.phy_intfs[devaddr]
+
+        # ifname = self.phy_intfs_hostname.get(devaddr, "")
+        # if ifname
+        #     del self.phy_intfs_hostname[devaddr]
+
+        driver = self.phy_odrivers.get(devaddr, "")
+        if not driver:
+            self.logger.info(
+                "Physical PCI device %s was bound to vfio-pci on entry", devaddr
+            )
+            return
+
+        self.logger.info(
+            "Unbinding physical PCI device %s from driver vfio-pci", devaddr
+        )
+        self.unet.rootcmd.cmd_status(
+            f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind"
+        )
+
+        self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver)
+        ec, _, _ = self.unet.rootcmd.cmd_status(
+            f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/bind"
+        )
+        if not ec:
+            del self.phy_odrivers[devaddr]
+
+    async def _async_delete(self):
+        self.logger.debug("%s: L3NodeMixin sub-class _async_delete", self)
+
+        # XXX do we need to run the cleanup command before these infra changes?
+
+        # remove any hostintf interfaces
+        for hname in list(self.host_intfs):
+            await self.rem_host_intf(hname)
+
+        # remove any hostintf interfaces
+        for devaddr in list(self.phy_intfs):
+            await self.rem_phy_intf(devaddr)
+
+        # delete the LinuxNamespace/InterfaceMixin
+        await super()._async_delete()
+
+
+class L3NamespaceNode(L3NodeMixin, LinuxNamespace):
+    """A namespace L3 node."""
+
+    def __init__(self, name, pid=True, **kwargs):
+        # logging.warning(
+        #     "L3NamespaceNode: name %s MRO: %s kwargs %s",
+        #     name,
+        #     L3NamespaceNode.mro(),
+        #     kwargs,
+        # )
+        super().__init__(name, pid=pid, **kwargs)
+        super().pytest_hook_open_shell()
+
+    async def _async_delete(self):
+        self.logger.debug("%s: deleting", self)
+        await super()._async_delete()
+
+
+class L3ContainerNode(L3NodeMixin, LinuxNamespace):
+    """An container (podman) based L3 node."""
+
+    def __init__(self, name, config, **kwargs):
+        """Create a Container Node."""
+        self.cont_exec_paths = {}
+        self.container_id = None
+        self.container_image = config["image"]
+        self.extra_mounts = []
+        assert self.container_image
+
+        self.cmd_p = None
+        self.__base_cmd = []
+        self.__base_cmd_pty = []
+
+        # don't we have a mutini or cat process?
+        super().__init__(
+            name=name,
+            config=config,
+            # pid=True,
+            # cgroup=True,
+            # private_mounts=["/sys/fs/cgroup:/sys/fs/cgroup"],
+            **kwargs,
+        )
+
+    @property
+    def is_container(self):
+        return True
+
+    def get_exec_path(self, binary):
+        """Return the full path to the binary executable inside the image.
+
+        `binary` :: binary name or list of binary names
+        """
+        return _get_exec_path(binary, self.cmd_status, self.cont_exec_paths)
+
+    async def async_get_exec_path(self, binary):
+        """Return the full path to the binary executable inside the image.
+
+        `binary` :: binary name or list of binary names
+        """
+        path = await _async_get_exec_path(
+            binary, self.async_cmd_status, self.cont_exec_paths
+        )
+        return path
+
+    def get_exec_path_host(self, binary):
+        """Return the full path to the binary executable on the host.
+
+        `binary` :: binary name or list of binary names
+        """
+        return get_exec_path_host(binary)
+
+    def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+        if ns_only:
+            return super()._get_pre_cmd(
+                use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+            )
+        if not self.cmd_p:
+            if self.container_id:
+                s = f"{self}: Running command in namespace b/c container exited"
+                self.logger.warning("%s", s)
+                raise L3ContainerNotRunningError(s)
+            self.logger.debug("%s: Running command in namespace b/c no container", self)
+            return super()._get_pre_cmd(
+                use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+            )
+
+        # We need to enter our namespaces when running the podman command
+        pre_cmd = super()._get_pre_cmd(
+            False, use_pty, ns_only=True, root_level=root_level, **kwargs
+        )
+
+        # XXX grab the env from kwargs and add to podman exec
+        # env = kwargs.get("env", {})
+        if use_pty:
+            pre_cmd = pre_cmd + self.__base_cmd_pty
+        else:
+            pre_cmd = pre_cmd + self.__base_cmd
+        return shlex.join(pre_cmd) if use_str else pre_cmd
+
+    def tmpfs_mount(self, inner):
+        # eventually would be nice to support live mounting
+        assert not self.container_id
+        self.logger.debug("Mounting tmpfs on %s", inner)
+        self.extra_mounts.append(f"--mount=type=tmpfs,destination={inner}")
+
+    def bind_mount(self, outer, inner):
+        # eventually would be nice to support live mounting
+        assert not self.container_id
+        # First bind the mount in the parent this allows things like /etc/hosts to work
+        # correctly when running "nsonly" commands
+        super().bind_mount(outer, inner)
+        # Then arrange for binding in the container as well.
+        self.logger.debug("Bind mounting %s on %s", outer, inner)
+        if not self.test_nsonly("-e", outer):
+            self.cmd_raises_nsonly(f"mkdir -p {outer}")
+        self.extra_mounts.append(f"--mount=type=bind,src={outer},dst={inner}")
+
+    def mount_volumes(self):
+        args = []
+        for m in self.config.get("volumes", []):
+            if isinstance(m, str):
+                s = m.split(":", 1)
+                if len(s) == 1:
+                    args.append("--mount=type=tmpfs,destination=" + m)
+                else:
+                    spath = s[0]
+                    spath = os.path.abspath(
+                        os.path.join(
+                            os.path.dirname(self.unet.config["config_pathname"]), spath
+                        )
+                    )
+                    if not self.test_nsonly("-e", spath):
+                        self.cmd_raises_nsonly(f"mkdir -p {spath}")
+                    args.append(f"--mount=type=bind,src={spath},dst={s[1]}")
+                continue
+
+        for m in self.config.get("mounts", []):
+            margs = ["type=" + m["type"]]
+            for k, v in m.items():
+                if k == "type":
+                    continue
+                if v:
+                    if k in ("src", "source"):
+                        v = os.path.abspath(
+                            os.path.join(
+                                os.path.dirname(self.unet.config["config_pathname"]), v
+                            )
+                        )
+                        if not self.test_nsonly("-e", v):
+                            self.cmd_raises_nsonly(f"mkdir -p {v}")
+                    margs.append(f"{k}={v}")
+                else:
+                    margs.append(f"{k}")
+            args.append("--mount=" + ",".join(margs))
+
+        if args:
+            # Need to work on a way to mount into live container too
+            self.extra_mounts += args
+
+    def has_run_cmd(self) -> bool:
+        return True
+
+    async def run_cmd(self):
+        """Run the configured commands for this node."""
+        self.logger.debug("%s: starting container", self.name)
+        self.logger.debug(
+            "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+        )
+
+        self.container_id = f"{self.name}-{os.getpid()}"
+        proc_path = self.unet.proc_path if self.unet else "/proc"
+        cmds = [
+            get_exec_path_host("podman"),
+            "run",
+            f"--name={self.container_id}",
+            # f"--net=ns:/proc/{self.pid}/ns/net",
+            f"--net=ns:{proc_path}/{self.pid}/ns/net",
+            f"--hostname={self.name}",
+            f"--add-host={self.name}:127.0.0.1",
+            # We can't use --rm here b/c podman fails on "stop".
+            # u"--rm",
+        ]
+
+        if self.config.get("init", True):
+            cmds.append("--init")
+
+        if self.config.get("privileged", False):
+            cmds.append("--privileged")
+            # If we don't do this then the host file system is remounted read-only on
+            # exit!
+            cmds.append("--systemd=false")
+        else:
+            cmds.extend(
+                [
+                    # "--cap-add=SYS_ADMIN",
+                    "--cap-add=NET_ADMIN",
+                    "--cap-add=NET_RAW",
+                ]
+            )
+
+        # Add volumes:
+        if self.extra_mounts:
+            cmds += self.extra_mounts
+
+        # Add environment variables:
+        envdict = self.config.get("env", {})
+        if envdict is None:
+            envdict = {}
+        for k, v in envdict.items():
+            cmds.append(f"--env={k}={v}")
+
+        # Update capabilities
+        cmds += [f"--cap-add={x}" for x in self.config.get("cap-add", [])]
+        cmds += [f"--cap-drop={x}" for x in self.config.get("cap-drop", [])]
+        # cmds += [f"--expose={x.split(':')[0]}" for x in self.config.get("ports", [])]
+        cmds += [f"--publish={x}" for x in self.config.get("ports", [])]
+
+        # Add extra flags from user:
+        if "podman" in self.config:
+            for x in self.config["podman"].get("extra-args", []):
+                cmds.append(x.strip())
+
+        # shell_cmd is a union and can be boolean or string
+        shell_cmd = self.config.get("shell", "/bin/bash")
+        if not isinstance(shell_cmd, str):
+            if shell_cmd:
+                shell_cmd = "/bin/bash"
+            else:
+                shell_cmd = ""
+
+        # Create shebang files, filled later on
+        for key in ("cleanup-cmd", "ready-cmd"):
+            shebang_cmd = self.config.get(key, "").strip()
+            if shell_cmd and shebang_cmd:
+                script_name = fsafe_name(key)
+                # Will write the file contents out when the command is run
+                shebang_cmdpath = os.path.join(self.rundir, f"{script_name}.shebang")
+                await self.async_cmd_raises_nsonly(f"touch {shebang_cmdpath}")
+                await self.async_cmd_raises_nsonly(f"chmod 755 {shebang_cmdpath}")
+                cmds += [
+                    # How can we override this?
+                    # u'--entrypoint=""',
+                    f"--volume={shebang_cmdpath}:/tmp/{script_name}.shebang",
+                ]
+
+        cmd = self.config.get("cmd", "").strip()
+
+        # See if we have a custom update for this `kind`
+        if kind := self.config.get("kind", None):
+            if kind in kind_run_cmd_update:
+                cmds, cmd = await kind_run_cmd_update[kind](self, shell_cmd, cmds, cmd)
+
+        # Create running command file
+        if shell_cmd and cmd:
+            assert isinstance(cmd, str)
+            # make cmd \n terminated for script
+            cmd = cmd.rstrip()
+            cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+            cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+            cmd = cmd.replace("%NAME%", str(self.name))
+            cmd += "\n"
+            cmdpath = os.path.join(self.rundir, "cmd.shebang")
+            with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+                cmdfile.write(f"#!{shell_cmd}\n")
+                cmdfile.write(cmd)
+                cmdfile.flush()
+            self.cmd_raises_nsonly(f"chmod 755 {cmdpath}")
+            cmds += [
+                # How can we override this?
+                # u'--entrypoint=""',
+                f"--volume={cmdpath}:/tmp/cmds.shebang",
+                self.container_image,
+                "/tmp/cmds.shebang",
+            ]
+        else:
+            # `cmd` is a direct run (no shell) cmd
+            cmds.append(self.container_image)
+            if cmd:
+                if isinstance(cmd, str):
+                    cmds.extend(shlex.split(cmd))
+                else:
+                    cmds.extend(cmd)
+
+            cmds = [
+                x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds
+            ]
+            cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds]
+            cmds = [x.replace("%NAME%", str(self.name)) for x in cmds]
+
+        stdout = open(os.path.join(self.rundir, "cmd.out"), "wb")
+        stderr = open(os.path.join(self.rundir, "cmd.err"), "wb")
+        # Using nsonly avoids using `podman exec` to execute the cmds.
+        self.cmd_p = await self.async_popen_nsonly(
+            cmds,
+            stdin=subprocess.DEVNULL,
+            stdout=stdout,
+            stderr=stderr,
+            start_new_session=True,  # keeps main tty signals away from podman
+        )
+
+        self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid)
+
+        self.pytest_hook_run_cmd(stdout, stderr)
+
+        # ---------------------------------------
+        # Now let's wait until container shows up
+        # ---------------------------------------
+        timeout = Timeout(30)
+        while self.cmd_p.returncode is None and not timeout.is_expired():
+            o = await self.async_cmd_raises_nsonly(
+                f"podman ps -q -f name={self.container_id}"
+            )
+            if o.strip():
+                break
+            elapsed = int(timeout.elapsed())
+            if elapsed <= 3:
+                await asyncio.sleep(0.1)
+            else:
+                self.logger.info("%s: run_cmd taking more than %ss", self, elapsed)
+                await asyncio.sleep(1)
+        if self.cmd_p.returncode is not None:
+            # leave self.container_id set to cause exception on use
+            self.logger.warning(
+                "%s: run_cmd exited quickly (%ss) rc: %s",
+                self,
+                timeout.elapsed(),
+                self.cmd_p.returncode,
+            )
+        elif timeout.is_expired():
+            self.logger.critical(
+                "%s: timeout (%ss) waiting for container to start",
+                self.name,
+                timeout.elapsed(),
+            )
+            assert not timeout.is_expired()
+
+        #
+        # Set our precmd for executing in the container
+        #
+        self.__base_cmd = [
+            get_exec_path_host("podman"),
+            "exec",
+            f"-eMUNET_RUNDIR={self.unet.rundir}",
+            f"-eMUNET_NODENAME={self.name}",
+            "-i",
+        ]
+        self.__base_cmd_pty = list(self.__base_cmd)  # copy list to pty
+        self.__base_cmd.append(self.container_id)  # end regular list
+        self.__base_cmd_pty.append("-t")  # add pty flags
+        self.__base_cmd_pty.append(self.container_id)  # end pty list
+        # self.set_pre_cmd(self.__base_cmd, self.__base_cmd_pty)  # set both pre_cmd
+
+        self.logger.info("%s: started container", self.name)
+
+        self.pytest_hook_open_shell()
+
+        return self.cmd_p
+
+    async def async_cleanup_cmd(self):
+        """Run the configured cleanup commands for this node."""
+        self.cleanup_called = True
+
+        if "cleanup-cmd" not in self.config:
+            return
+
+        if not self.cmd_p:
+            self.logger.warning("async_cleanup_cmd: container no longer running")
+            return
+
+        return await self._async_cleanup_cmd()
+
+    def cmd_completed(self, future):
+        try:
+            log = self.logger.debug if self.deleting else self.logger.warning
+            n = future.result()
+            if self.deleting:
+                log("contianer `cmd:` result: %s", n)
+            else:
+                log(
+                    "contianer `cmd:` exited early, "
+                    "try adding `tail -f /dev/null` to `cmd:`, result: %s",
+                    n,
+                )
+        except asyncio.CancelledError as error:
+            # Should we stop the container if we have one? or since we are canceled
+            # we know we will be deleting soon?
+            self.logger.warning(
+                "node container cmd wait() canceled: %s:%s", future, error
+            )
+        self.cmd_p = None
+
+    async def _async_delete(self):
+        self.logger.debug("%s: deleting", self)
+
+        if contid := self.container_id:
+            try:
+                if not self.cleanup_called:
+                    self.logger.debug("calling user cleanup cmd")
+                    await self.async_cleanup_cmd()
+            except Exception as error:
+                self.logger.warning(
+                    "Got an error during delete from async_cleanup_cmd: %s", error
+                )
+
+            # Clear the container_id field we want to act like a namespace now.
+            self.container_id = None
+
+            o = ""
+            e = ""
+            if self.cmd_p:
+                self.logger.debug("podman stop on container: %s", contid)
+                if (rc := self.cmd_p.returncode) is None:
+                    rc, o, e = await self.async_cmd_status_nsonly(
+                        [get_exec_path_host("podman"), "stop", "--time=2", contid]
+                    )
+                if rc and rc < 128:
+                    self.logger.warning(
+                        "%s: podman stop on cmd failed: %s",
+                        self,
+                        cmd_error(rc, o, e),
+                    )
+                else:
+                    # It's gone
+                    self.cmd_p = None
+
+            # now remove the container
+            self.logger.debug("podman rm on container: %s", contid)
+            rc, o, e = await self.async_cmd_status_nsonly(
+                [get_exec_path_host("podman"), "rm", contid]
+            )
+            if rc:
+                self.logger.warning(
+                    "%s: podman rm failed: %s", self, cmd_error(rc, o, e)
+                )
+            else:
+                self.logger.debug(
+                    "podman removed container %s: %s", contid, cmd_error(rc, o, e)
+                )
+
+        await super()._async_delete()
+
+
+class L3QemuVM(L3NodeMixin, LinuxNamespace):
+    """An VM (qemu) based L3 node."""
+
+    def __init__(self, name, config, **kwargs):
+        """Create a Container Node."""
+        self.cont_exec_paths = {}
+        self.launch_p = None
+        self.qemu_config = config["qemu"]
+        self.extra_mounts = []
+        assert self.qemu_config
+        self.cmdrepl = None
+        self.conrepl = None
+        self.is_kvm = False
+        self.monrepl = None
+        self.tapfds = {}
+        self.cpu_thread_map = {}
+
+        self.tapnames = {}
+
+        self.use_ssh = False
+        self.__base_cmd = []
+        self.__base_cmd_pty = []
+
+        super().__init__(name=name, config=config, pid=False, **kwargs)
+
+        self.sockdir = self.rundir.joinpath("s")
+        self.cmd_raises(f"mkdir -p {self.sockdir}")
+
+        self.qemu_config = config_subst(
+            self.qemu_config,
+            name=self.name,
+            rundir=os.path.join(self.rundir, self.name),
+            configdir=self.unet.config_dirname,
+        )
+        self.ssh_keyfile = self.qemu_config.get("sshkey")
+
+    @property
+    def is_vm(self):
+        return True
+
+    def __setup_ssh(self):
+        if not self.ssh_keyfile:
+            self.logger.warning("%s: No sshkey config", self)
+            return False
+        if not self.mgmt_ip and not self.mgmt_ip6:
+            self.logger.warning("%s: No mgmt IP to ssh to", self)
+            return False
+        mgmt_ip = self.mgmt_ip if self.mgmt_ip else self.mgmt_ip6
+
+        #
+        # Since we have a keyfile shouldn't need to sudo
+        # self.user = os.environ.get("SUDO_USER", "")
+        # if not self.user:
+        #     self.user = getpass.getuser()
+        # self.__base_cmd = [
+        #     get_exec_path_host("sudo"),
+        #     "-E",
+        #     f"-u{self.user}",
+        #     get_exec_path_host("ssh"),
+        # ]
+        #
+        port = 22
+        self.__base_cmd = [get_exec_path_host("ssh")]
+        if port != 22:
+            self.__base_cmd.append(f"-p{port}")
+        self.__base_cmd.append("-i")
+        self.__base_cmd.append(self.ssh_keyfile)
+        self.__base_cmd.append("-q")
+        self.__base_cmd.append("-oStrictHostKeyChecking=no")
+        self.__base_cmd.append("-oUserKnownHostsFile=/dev/null")
+        # Would be nice but has to be accepted by server config so not very useful.
+        # self.__base_cmd.append("-oSendVar='TEST'")
+        self.__base_cmd_pty = list(self.__base_cmd)
+        self.__base_cmd_pty.append("-t")
+
+        user = self.qemu_config.get("sshuser", "root")
+        self.__base_cmd.append(f"{user}@{mgmt_ip}")
+        self.__base_cmd.append("--")
+        self.__base_cmd_pty.append(f"{user}@{mgmt_ip}")
+        # self.__base_cmd_pty.append("--")
+        return True
+
+    def _get_cmd_as_list(self, cmd):
+        """Given a list or string return a list form for execution.
+
+        If cmd is a string then [cmd] is returned, for most other
+        node types ["bash", "-c", cmd] is returned but in our case
+        ssh is the shell.
+
+        Args:
+            cmd: list or string representing the command to execute.
+            str_shell: if True and `cmd` is a string then run the
+              command using bash -c
+        Returns:
+            list of commands to execute.
+        """
+        if self.use_ssh and self.launch_p:
+            return [cmd] if isinstance(cmd, str) else cmd
+        return super()._get_cmd_as_list(cmd)
+
+    def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs):
+        if ns_only:
+            return super()._get_pre_cmd(
+                use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+            )
+
+        if not self.launch_p:
+            self.logger.debug("%s: Running command in namespace b/c no VM", self)
+            return super()._get_pre_cmd(
+                use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+            )
+
+        if not self.use_ssh:
+            self.logger.debug(
+                "%s: Running command in namespace b/c no SSH configured", self
+            )
+            return super()._get_pre_cmd(
+                use_str, use_pty, ns_only=True, root_level=root_level, **kwargs
+            )
+
+        pre_cmd = self.unet._get_pre_cmd(use_str, use_pty, ns_only=True)
+
+        # This is going to run in the process namespaces.
+        # We really want it to run in the munet namespace which will
+        # be different unless unshare_inline was used.
+        #
+        # XXX grab the env from kwargs and add to podman exec
+        # env = kwargs.get("env", {})
+        if use_pty:
+            pre_cmd = pre_cmd + self.__base_cmd_pty
+        else:
+            pre_cmd = pre_cmd + self.__base_cmd
+        return shlex.join(pre_cmd) if use_str else pre_cmd
+
+    async def moncmd(self):
+        """Uses internal REPL to send cmmand to qemu monitor and get reply."""
+
+    def tmpfs_mount(self, inner):
+        # eventually would be nice to support live mounting
+        self.logger.debug("Mounting tmpfs on %s", inner)
+        self.extra_mounts.append(("", inner, ""))
+
+    #
+    # bind_mount is actually being used to mount into the namespace
+    #
+    # def bind_mount(self, outer, inner):
+    #     # eventually would be nice to support live mounting
+    #     assert not self.container_id
+    #     if self.test_host("-f", outer):
+    #         self.logger.warning("Can't bind mount files with L3QemuVM: %s", outer)
+    #         return
+    #     self.logger.debug("Bind mounting %s on %s", outer, inner)
+    #     if not self.test_host("-e", outer):
+    #         self.cmd_raises(f"mkdir -p {outer}")
+    #     self.extra_mounts.append((outer, inner, ""))
+
+    def mount_volumes(self):
+        """Mount volumes from the config."""
+        args = []
+        for m in self.config.get("volumes", []):
+            if not isinstance(m, str):
+                continue
+            s = m.split(":", 1)
+            if len(s) == 1:
+                args.append(("", s[0], ""))
+            else:
+                spath = s[0]
+                spath = os.path.abspath(
+                    os.path.join(
+                        os.path.dirname(self.unet.config["config_pathname"]), spath
+                    )
+                )
+                if not self.test_nsonly("-e", spath):
+                    self.cmd_raises_nsonly(f"mkdir -p {spath}")
+                args.append((spath, s[1], ""))
+
+        for m in self.config.get("mounts", []):
+            src = m.get("src", m.get("source", ""))
+            if src:
+                src = os.path.abspath(
+                    os.path.join(
+                        os.path.dirname(self.unet.config["config_pathname"]), src
+                    )
+                )
+                if not self.test_nsonly("-e", src):
+                    self.cmd_raises_nsonly(f"mkdir -p {src}")
+            dst = m.get("dst", m.get("destination"))
+            assert dst, "destination path required for mount"
+
+            margs = []
+            for k, v in m.items():
+                if k in ["destination", "dst", "source", "src"]:
+                    continue
+                if k == "type":
+                    assert v in ["bind", "tmpfs"]
+                    continue
+                if not v:
+                    margs.append(k)
+                else:
+                    margs.append(f"{k}={v}")
+            args.append((src, dst, ",".join(margs)))
+
+        if args:
+            self.extra_mounts += args
+
+    async def run_cmd(self):
+        """Run the configured commands for this node inside VM."""
+        self.logger.debug(
+            "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir)
+        )
+
+        cmd = self.config.get("cmd", "").strip()
+        if not cmd:
+            self.logger.debug("%s: no `cmd` to run", self)
+            return None
+
+        shell_cmd = self.config.get("shell", "/bin/bash")
+        if not isinstance(shell_cmd, str):
+            if shell_cmd:
+                shell_cmd = "/bin/bash"
+            else:
+                shell_cmd = ""
+
+        if shell_cmd:
+            cmd = cmd.rstrip()
+            cmd = f"#!{shell_cmd}\n" + cmd
+            cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+            cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+            cmd = cmd.replace("%NAME%", str(self.name))
+            cmd += "\n"
+
+            # Write a copy to the rundir
+            cmdpath = os.path.join(self.rundir, "cmd.shebang")
+            with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile:
+                cmdfile.write(cmd)
+            commander.cmd_raises(f"chmod 755 {cmdpath}")
+
+            # Now write a copy inside the VM
+            self.conrepl.cmd_status("cat > /tmp/cmd.shebang << EOF\n" + cmd + "\nEOF")
+            self.conrepl.cmd_status("chmod 755 /tmp/cmd.shebang")
+            cmds = "/tmp/cmd.shebang"
+        else:
+            cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname))
+            cmd = cmd.replace("%RUNDIR%", str(self.rundir))
+            cmd = cmd.replace("%NAME%", str(self.name))
+            cmds = cmd
+
+        # class future_proc:
+        #     """Treat awaitable minimally as a proc."""
+        #     def __init__(self, aw):
+        #         self.aw = aw
+        #         # XXX would be nice to have a real value here
+        #         self.returncode = 0
+        #     async def wait(self):
+        #         if self.aw:
+        #             return await self.aw
+        #         return None
+
+        class now_proc:
+            """Treat awaitable minimally as a proc."""
+
+            def __init__(self, output):
+                self.output = output
+                self.returncode = 0
+
+            async def wait(self):
+                return self.output
+
+        if self.cmdrepl:
+            # self.cmd_p = future_proc(
+            #     # We need our own console here b/c this is async and not returning
+            #     # immediately
+            #     # self.cmdrepl.run_command(cmds, timeout=120, async_=True)
+            #     self.cmdrepl.run_command(cmds, timeout=120)
+            # )
+
+            # When run_command supports async_ arg we can use the above...
+            self.cmd_p = now_proc(self.cmdrepl.run_command(cmds, timeout=120))
+
+            # stdout and err both combined into logfile from the spawned repl
+            stdout = os.path.join(self.rundir, "_cmdcon-log.txt")
+            self.pytest_hook_run_cmd(stdout, None)
+        else:
+            # If we only have a console we can't run in parallel, so run to completion
+            self.cmd_p = now_proc(self.conrepl.run_command(cmds, timeout=120))
+
+        return self.cmd_p
+
+    # InterfaceMixin override
+    # We need a name unique in the shared namespace.
+    def get_ns_ifname(self, ifname):
+        return self.name + ifname
+
+    async def add_host_intf(self, hname, lname, mtu=None):
+        # L3QemuVM needs it's own add_host_intf for macvtap, We need to create the tap
+        # in the host then move that interface so that the ifindex/devfile are
+        # different.
+
+        if hname in self.host_intfs:
+            return
+
+        self.host_intfs[hname] = lname
+        index = len(self.host_intfs)
+
+        tapindex = self.unet.tapcount
+        self.unet.tapcount = self.unet.tapcount + 1
+
+        tapname = f"tap{tapindex}"
+        self.tapnames[hname] = tapname
+
+        mac = f"02:bb:bb:bb:{index:02x}:{self.id:02x}"
+        self.tapmacs[hname] = mac
+
+        self.unet.rootcmd.cmd_raises(
+            f"ip link add link {hname} name {tapname} type macvtap"
+        )
+        if mtu:
+            self.unet.rootcmd.cmd_raises(f"ip link set {tapname} mtu {mtu}")
+        self.unet.rootcmd.cmd_raises(f"ip link set {tapname} address {mac} up")
+        ifindex = self.unet.rootcmd.cmd_raises(
+            f"cat /sys/class/net/{tapname}/ifindex"
+        ).strip()
+        # self.unet.rootcmd.cmd_raises(f"ip link set {tapname} netns {self.pid}")
+
+        tapfile = f"/dev/tap{ifindex}"
+        fd = os.open(tapfile, os.O_RDWR)
+        self.tapfds[hname] = fd
+        self.logger.info(
+            "%s: Add host intf: created macvtap interface %s (%s) on %s fd %s",
+            self,
+            tapname,
+            tapfile,
+            hname,
+            fd,
+        )
+
+    async def rem_host_intf(self, hname):
+        tapname = self.tapnames[hname]
+        self.unet.rootcmd.cmd_raises(f"ip link set {tapname} down")
+        self.unet.rootcmd.cmd_raises(f"ip link delete {tapname} type macvtap")
+        del self.tapnames[hname]
+        del self.host_intfs[hname]
+
+    async def create_tap(self, index, ifname, mtu=None, driver="virtio-net-pci"):
+        # XXX we shouldn't be doign a tap on a bridge with a veth
+        # we should just be using a tap created earlier which was connected to the
+        # bridge. Except we need to handle the case of p2p qemu <-> namespace
+        #
+        ifname = self.get_ns_ifname(ifname)
+        brname = f"{self.name}br{index}"
+
+        tapindex = self.unet.tapcount
+        self.unet.tapcount += 1
+
+        mac = f"02:aa:aa:aa:{index:02x}:{self.id:02x}"
+        # nic = "tap,model=virtio-net-pci"
+        # qemu -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11
+        self.cmd_raises(f"ip address flush dev {ifname}")
+        self.cmd_raises(f"ip tuntap add tap{tapindex} mode tap")
+        self.cmd_raises(f"ip link add name {brname} type bridge")
+        self.cmd_raises(f"ip link set dev {ifname} master {brname}")
+        self.cmd_raises(f"ip link set dev tap{tapindex} master {brname}")
+        if mtu:
+            self.cmd_raises(f"ip link set dev tap{tapindex} mtu {mtu}")
+            self.cmd_raises(f"ip link set dev {ifname} mtu {mtu}")
+        self.cmd_raises(f"ip link set dev tap{tapindex} up")
+        self.cmd_raises(f"ip link set dev {ifname} up")
+        self.cmd_raises(f"ip link set dev {brname} up")
+        dev = f"{driver},netdev=n{index},mac={mac}"
+        return [
+            "-netdev",
+            f"tap,id=n{index},ifname=tap{tapindex},script=no,downscript=no",
+            "-device",
+            dev,
+        ]
+
+    async def mount_mounts(self):
+        """Mount any shared directories."""
+        self.logger.info("Mounting shared directories")
+        con = self.conrepl
+        for i, m in enumerate(self.extra_mounts):
+            outer, mp, uargs = m
+            if not outer:
+                con.cmd_raises(f"mkdir -p {mp}")
+                margs = f"-o {uargs}" if uargs else ""
+                con.cmd_raises(f"mount {margs} -t tmpfs tmpfs {mp}")
+                continue
+
+            uargs = "" if uargs is None else uargs
+            margs = "trans=virtio"
+            if uargs:
+                margs += f",{uargs}"
+            self.logger.info("Mounting %s on %s with %s", outer, mp, margs)
+            con.cmd_raises(f"mkdir -p {mp}")
+            con.cmd_raises(f"mount -t 9p -o {margs} shared{i} {mp}")
+
+    async def renumber_interfaces(self):
+        """Re-number the interfaces.
+
+        After VM comes up need to renumber the interfaces now on the inside.
+        """
+        self.logger.info("Renumbering interfaces")
+        con = self.conrepl
+        con.cmd_raises("sysctl -w net.ipv4.ip_forward=1")
+        if self.unet.ipv6_enable:
+            self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1")
+        for ifname in sorted(self.intfs):
+            conn = find_with_kv(self.config.get("connections"), "name", ifname)
+            to = conn["to"]
+            switch = self.unet.switches.get(to)
+            mtu = conn.get("mtu")
+            if not mtu and switch:
+                mtu = switch.config.get("mtu")
+            if mtu:
+                con.cmd_raises(f"ip link set {ifname} mtu {mtu}")
+            con.cmd_raises(f"ip link set {ifname} up")
+            # In case there was some preconfig e.g., cloud-init
+            con.cmd_raises(f"ip -4 addr flush dev {ifname}")
+            sw_is_nat = switch and hasattr(switch, "is_nat") and switch.is_nat
+            if ifaddr := self.get_intf_addr(ifname, ipv6=False):
+                con.cmd_raises(f"ip addr add {ifaddr} dev {ifname}")
+                if sw_is_nat:
+                    # In case there was some preconfig e.g., cloud-init
+                    con.cmd_raises("ip route flush exact default")
+                    con.cmd_raises(f"ip route add default via {switch.ip_address}")
+            if ifaddr := self.get_intf_addr(ifname, ipv6=True):
+                con.cmd_raises(f"ip -6 addr add {ifaddr} dev {ifname}")
+                if sw_is_nat:
+                    # In case there was some preconfig e.g., cloud-init
+                    con.cmd_raises("ip -6 route flush exact default")
+                    con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}")
+        con.cmd_raises("ip link set lo up")
+
+        if self.unet.cfgopt.getoption("--coverage"):
+            con.cmd_raises("mount -t debugfs none /sys/kernel/debug")
+
+    async def gather_coverage_data(self):
+        con = self.conrepl
+
+        gcda = "/sys/kernel/debug/gcov"
+        tmpdir = con.cmd_raises("mktemp -d").strip()
+        dest = "/gcov-data.tgz"
+        con.cmd_raises(rf"find {gcda} -type d -exec mkdir -p {tmpdir}/{{}} \;")
+        con.cmd_raises(
+            rf"find {gcda} -name '*.gcda' -exec sh -c 'cat < $0 > {tmpdir}/$0' {{}} \;"
+        )
+        con.cmd_raises(
+            rf"find {gcda} -name '*.gcno' -exec sh -c 'cp -d $0 {tmpdir}/$0' {{}} \;"
+        )
+        con.cmd_raises(rf"tar cf - -C {tmpdir} sys | gzip -c > {dest}")
+        con.cmd_raises(rf"rm -rf {tmpdir}")
+        self.logger.info("Saved coverage data in VM at %s", dest)
+        if self.use_ssh:
+            ldest = os.path.join(self.rundir, "gcov-data.tgz")
+            self.cmd_raises(["/bin/cat", dest], stdout=open(ldest, "wb"))
+            self.logger.info("Saved coverage data on host at %s", ldest)
+
+    async def _opencons(
+        self,
+        *cnames,
+        prompt=None,
+        is_bourne=True,
+        user="root",
+        password="",
+        expects=None,
+        sends=None,
+        timeout=-1,
+    ):
+        """Open consoles based on socket file names."""
+        timeo = Timeout(timeout)
+        cons = []
+        for cname in cnames:
+            sockpath = os.path.join(self.sockdir, cname)
+            connected = False
+            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            while self.launch_p.returncode is None and not timeo.is_expired():
+                try:
+                    sock.connect(sockpath)
+                    connected = True
+                    break
+                except OSError as error:
+                    if error.errno == errno.ENOENT:
+                        self.logger.debug("waiting for console socket: %s", sockpath)
+                    else:
+                        self.logger.warning(
+                            "can't open console socket: %s", error.strerror
+                        )
+                        raise
+                elapsed = int(timeo.elapsed())
+                if elapsed <= 3:
+                    await asyncio.sleep(0.25)
+                else:
+                    self.logger.info(
+                        "%s: launch (qemu) taking more than %ss", self, elapsed
+                    )
+                    await asyncio.sleep(1)
+
+            if connected:
+                if prompt is None:
+                    prompt = r"(^|\r\n)[^#\$]*[#\$] "
+                cons.append(
+                    await self.console(
+                        sock,
+                        prompt=prompt,
+                        is_bourne=is_bourne,
+                        user=user,
+                        password=password,
+                        use_pty=False,
+                        logfile_prefix=cname,
+                        will_echo=True,
+                        expects=expects,
+                        sends=sends,
+                        timeout=timeout,
+                        trace=True,
+                    )
+                )
+            elif self.launch_p.returncode is not None:
+                self.logger.warning(
+                    "%s: launch (qemu) exited quickly (%ss) rc: %s",
+                    self,
+                    timeo.elapsed(),
+                    self.launch_p.returncode,
+                )
+                raise Exception("Qemu launch exited early")
+            elif timeo.is_expired():
+                self.logger.critical(
+                    "%s: timeout (%ss) waiting for qemu to start",
+                    self,
+                    timeo.elapsed(),
+                )
+                assert not timeo.is_expired()
+
+        return cons
+
+    async def set_cpu_affinity(self, afflist):
+        for i, aff in enumerate(afflist):
+            if not aff:
+                continue
+            # affmask = convert_ranges_to_bitmask(aff)
+            if i not in self.cpu_thread_map:
+                logging.warning("affinity %s given for missing vcpu %s", aff, i)
+                continue
+            logging.info("setting vcpu %s affinity to %s", i, aff)
+            tid = self.cpu_thread_map[i]
+            self.cmd_raises_nsonly(f"taskset -cp {aff} {tid}")
+
+    async def launch(self):
+        """Launch qemu."""
+        self.logger.info("%s: Launch Qemu", self)
+
+        qc = self.qemu_config
+        cc = qc.get("console", {})
+        bootd = "d" if "iso" in qc else "c"
+        # args = [get_exec_path_host("qemu-system-x86_64"),
+        #         "-nodefaults", "-boot", bootd]
+        args = [get_exec_path_host("qemu-system-x86_64"), "-boot", bootd]
+
+        args += ["-machine", "q35"]
+
+        if qc.get("kvm"):
+            rc, _, e = await self.async_cmd_status_nsonly("ls -l /dev/kvm")
+            if rc:
+                self.logger.warning("Can't enable KVM no /dev/kvm: %s", e)
+            else:
+                # [args += ["-enable-kvm", "-cpu", "host"]
+                # uargs += ["-accel", "kvm", "-cpu", "Icelake-Server-v5"]
+                args += ["-accel", "kvm", "-cpu", "host"]
+
+        if ncpu := qc.get("ncpu"):
+            # args += ["-smp", f"sockets={ncpu}"]
+            args += ["-smp", f"cores={ncpu}"]
+            # args += ["-smp", f"{ncpu},sockets={ncpu},cores=1,threads=1"]
+
+        args.extend(["-m", str(qc.get("memory", "512M"))])
+
+        if "bios" in qc:
+            if qc["bios"] == "open-firmware":
+                args.extend(["-bios", "/usr/share/qemu/OVMF.fd"])
+            else:
+                args.extend(["-bios", qc["bios"]])
+        if "kernel" in qc:
+            args.extend(["-kernel", qc["kernel"]])
+        if "initrd" in qc:
+            args.extend(["-initrd", qc["initrd"]])
+        if "iso" in qc:
+            args.extend(["-cdrom", qc["iso"]])
+
+        # we only have append if we have a kernel
+        if "kernel" in qc:
+            args.append("-append")
+            root = qc.get("root", "/dev/ram0")
+            # Only 1 serial console the other ports (ttyS[123] hvc[01]) should have
+            # gettys in inittab
+            append = f"root={root} rw console=ttyS0"
+            if "cmdline-extra" in qc:
+                append += f" {qc['cmdline-extra']}"
+            args.append(append)
+
+        if "extra-args" in qc:
+            if isinstance(qc["extra-args"], list):
+                args.extend(qc["extra-args"])
+            else:
+                args.extend(shlex.split(qc["extra-args"]))
+
+        # Walk the list of connections in order so we attach them the same way
+        pass_fds = []
+        nnics = 0
+        pciaddr = 3
+        for index, conn in enumerate(self.config["connections"]):
+            devaddr = conn.get("physical", "")
+            hostintf = conn.get("hostintf", "")
+            if devaddr:
+                # if devaddr in self.tapmacs:
+                #     mac = f",mac={self.tapmacs[devaddr]}"
+                # else:
+                #     mac = ""
+                args += ["-device", f"vfio-pci,host={devaddr},addr={pciaddr}"]
+            elif hostintf:
+                fd = self.tapfds[hostintf]
+                mac = self.tapmacs[hostintf]
+                args += [
+                    "-nic",
+                    f"tap,model=virtio-net-pci,mac={mac},fd={fd},addr={pciaddr}",
+                ]
+                pass_fds.append(fd)
+                nnics += 1
+            elif not hostintf:
+                driver = conn.get("driver", "virtio-net-pci")
+                mtu = conn.get("mtu")
+                if not mtu and conn["to"] in self.unet.switches:
+                    mtu = self.unet.switches[conn["to"]].config.get("mtu")
+                tapargs = await self.create_tap(
+                    index, conn["name"], mtu=mtu, driver=driver
+                )
+                tapargs[-1] += f",addr={pciaddr}"
+                args += tapargs
+                nnics += 1
+            pciaddr += 1
+        if not nnics:
+            args += ["-nic", "none"]
+
+        dtpl = qc.get("disk-template")
+        diskpath = disk = qc.get("disk")
+        if dtpl and not disk:
+            disk = qc["disk"] = f"{self.name}-{os.path.basename(dtpl)}"
+            diskpath = os.path.join(self.rundir, disk)
+            if self.path_exists(diskpath):
+                logging.debug("Disk '%s' file exists, using.", diskpath)
+            else:
+                dtplpath = os.path.abspath(
+                    os.path.join(
+                        os.path.dirname(self.unet.config["config_pathname"]), dtpl
+                    )
+                )
+                logging.info("Create disk '%s' from template '%s'", diskpath, dtplpath)
+                self.cmd_raises(
+                    f"qemu-img create -f qcow2 -F qcow2 -b {dtplpath} {diskpath}"
+                )
+
+        if diskpath:
+            args.extend(
+                ["-drive", f"file={diskpath},if=none,id=sata-disk0,format=qcow2"]
+            )
+            args.extend(["-device", "ahci,id=ahci"])
+            args.extend(["-device", "ide-hd,bus=ahci.0,drive=sata-disk0"])
+
+        use_stdio = cc.get("stdio", True)
+        has_cmd = self.config.get("cmd")
+        use_cmdcon = has_cmd and use_stdio
+
+        #
+        # Any extra serial/console ports beyond thw first, require entries in
+        # inittab to have getty running on them, modify inittab
+        #
+        # Use -serial stdio for output only, and as the first serial console
+        # which kernel uses for printk, as it has serious issues with dropped
+        # input chars for some reason.
+        #
+        # 4 serial ports (max), we'll add extra ports using virtual consoles.
+        _sd = self.sockdir
+        if use_stdio:
+            args += ["-serial", "stdio"]
+        args += ["-serial", f"unix:{_sd}/_console,server,nowait"]
+        if use_cmdcon:
+            args += [
+                "-serial",
+                f"unix:{_sd}/_cmdcon,server,nowait",
+            ]
+        args += [
+            "-serial",
+            f"unix:{_sd}/console,server,nowait",
+            # A 2 virtual consoles - /dev/hvc[01]
+            # Requires CONFIG_HVC_DRIVER=y CONFIG_VIRTIO_CONSOLE=y
+            "-device",
+            "virtio-serial",  # serial console bus
+            "-chardev",
+            f"socket,path={_sd}/vcon0,server=on,wait=off,id=vcon0",
+            "-chardev",
+            f"socket,path={_sd}/vcon1,server=on,wait=off,id=vcon1",
+            "-device",
+            "virtconsole,chardev=vcon0",
+            "-device",
+            "virtconsole,chardev=vcon1",
+            # 2 monitors
+            "-monitor",
+            f"unix:{_sd}/_monitor,server,nowait",
+            "-monitor",
+            f"unix:{_sd}/monitor,server,nowait",
+            "-gdb",
+            f"unix:{_sd}/gdbserver,server,nowait",
+        ]
+
+        for i, m in enumerate(self.extra_mounts):
+            args += [
+                "-virtfs",
+                f"local,path={m[0]},mount_tag=shared{i},security_model=passthrough",
+            ]
+
+        args += ["-nographic"]
+
+        #
+        # Launch Qemu
+        #
+
+        stdout = open(os.path.join(self.rundir, "qemu.out"), "wb")
+        stderr = open(os.path.join(self.rundir, "qemu.err"), "wb")
+        self.launch_p = await self.async_popen(
+            args,
+            stdin=subprocess.DEVNULL,
+            stdout=stdout,
+            stderr=stderr,
+            pass_fds=pass_fds,
+            # We don't need this here b/c we are only ever running qemu and that's all
+            # we need to kill for cleanup
+            # XXX reconcile this
+            start_new_session=True,  # allows us to signal all children to exit
+        )
+
+        self.pytest_hook_run_cmd(stdout, stderr)
+
+        # We've passed these on, so don't need these open here anymore.
+        for fd in pass_fds:
+            os.close(fd)
+
+        self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid)
+
+        confiles = ["_console"]
+        if use_cmdcon:
+            confiles.append("_cmdcon")
+
+        #
+        # Connect to the console socket, retrying
+        #
+        prompt = cc.get("prompt")
+        cons = await self._opencons(
+            *confiles,
+            prompt=prompt,
+            is_bourne=not bool(prompt),
+            user=cc.get("user", "root"),
+            password=cc.get("password", ""),
+            expects=cc.get("expects"),
+            sends=cc.get("sends"),
+            timeout=int(cc.get("timeout", 60)),
+        )
+        self.conrepl = cons[0]
+        if use_cmdcon:
+            self.cmdrepl = cons[1]
+        self.monrepl = await self.monitor(os.path.join(self.sockdir, "_monitor"))
+
+        # the monitor output has super annoying ANSI escapes in it
+
+        output = self.monrepl.cmd_nostatus("info status")
+        self.logger.info("VM status: %s", output)
+
+        output = self.monrepl.cmd_nostatus("info kvm")
+        self.logger.info("KVM status: %s", output)
+
+        #
+        # Set thread affinity
+        #
+        output = self.monrepl.cmd_nostatus("info cpus")
+        matches = re.findall(r"CPU #(\d+): *thread_id=(\d+)", output)
+        self.cpu_thread_map = {int(k): int(v) for k, v in matches}
+        if cpuaff := self.qemu_config.get("cpu-affinity"):
+            await self.set_cpu_affinity(cpuaff)
+
+        self.is_kvm = "disabled" not in output
+
+        if qc.get("unix-os", True):
+            await self.renumber_interfaces()
+
+        if self.extra_mounts:
+            await self.mount_mounts()
+
+        self.use_ssh = bool(self.ssh_keyfile)
+        if self.use_ssh:
+            self.use_ssh = self.__setup_ssh()
+
+        self.pytest_hook_open_shell()
+
+        return self.launch_p
+
+    def launch_completed(self, future):
+        self.logger.debug("%s: launch (qemu) completed called", self)
+        self.use_ssh = False
+        try:
+            n = future.result()
+            self.logger.debug("%s: node launch (qemu) completed result: %s", self, n)
+        except asyncio.CancelledError as error:
+            self.logger.debug(
+                "%s: node launch (qemu) cmd wait() canceled: %s", future, error
+            )
+
+    async def cleanup_qemu(self):
+        """Launch qemu."""
+        if self.launch_p:
+            await self.async_cleanup_proc(self.launch_p)
+
+    async def async_cleanup_cmd(self):
+        """Run the configured cleanup commands for this node."""
+        self.cleanup_called = True
+
+        if "cleanup-cmd" not in self.config:
+            return
+
+        if not self.launch_p:
+            self.logger.warning("async_cleanup_cmd: qemu no longer running")
+            return
+
+        raise NotImplementedError("Needs to be like run_cmd")
+        # return await self._async_cleanup_cmd()
+
+    async def _async_delete(self):
+        self.logger.debug("%s: deleting", self)
+
+        # Need to cleanup early b/c it is running on the VM
+        if self.cmd_p:
+            await self.async_cleanup_proc(self.cmd_p)
+            self.cmd_p = None
+
+        try:
+            # Need to cleanup early b/c it is running on the VM
+            if not self.cleanup_called:
+                await self.async_cleanup_cmd()
+        except Exception as error:
+            self.logger.warning(
+                "Got an error during delete from async_cleanup_cmd: %s", error
+            )
+
+        try:
+            if not self.launch_p:
+                self.logger.warning("async_delete: qemu is not running")
+            else:
+                await self.cleanup_qemu()
+        except Exception as error:
+            self.logger.warning("%s: failued to cleanup qemu process: %s", self, error)
+
+        await super()._async_delete()
+
+
+class Munet(BaseMunet):
+    """Munet."""
+
+    def __init__(
+        self,
+        rundir=None,
+        config=None,
+        pid=True,
+        logger=None,
+        **kwargs,
+    ):
+        # logging.warning("Munet")
+
+        if not rundir:
+            rundir = "/tmp/munet"
+
+        if logger is None:
+            logger = logging.getLogger("munet.unet")
+
+        super().__init__("munet", pid=pid, rundir=rundir, logger=logger, **kwargs)
+
+        self.built = False
+        self.tapcount = 0
+
+        self.cmd_raises(f"mkdir -p {self.rundir} && chmod 755 {self.rundir}")
+        self.set_ns_cwd(self.rundir)
+
+        if not config:
+            config = {}
+        self.config = config
+        if "config_pathname" in config:
+            self.config_pathname = os.path.realpath(config["config_pathname"])
+            self.config_dirname = os.path.dirname(self.config_pathname)
+        else:
+            self.config_pathname = ""
+            self.config_dirname = ""
+
+        # Done in BaseMunet now
+        # # We need some way to actually get back to the root namespace
+        # if not self.isolated:
+        #     self.rootcmd = commander
+        # else:
+        #     spid = str(pid)
+        #     nsflags = (f"--mount={self.proc_path / spid / 'ns/mnt'}",
+        #                f"--net={self.proc_path / spid / 'ns/net'}",
+        #                f"--uts={self.proc_path / spid / 'ns/uts'}",
+        #                f"--ipc={self.proc_path / spid / 'ns/ipc'}",
+        #                f"--cgroup={self.proc_path / spid / 'ns/cgroup'}",
+        #                f"--pid={self.proc_path / spid / 'ns/net'}",
+        #     self.rootcmd = SharedNamespace("host", pid=1, nsflags=nsflags)
+
+        # Save the namespace pid
+        with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f:
+            f.write(f"{self.pid}\n")
+
+        with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f:
+            f.write(f'{" ".join([str(x) for x in self.pids])}\n')
+
+        hosts_file = os.path.join(self.rundir, "hosts.txt")
+        with open(hosts_file, "w", encoding="ascii") as hf:
+            hf.write(
+                f"""127.0.0.1\tlocalhost {self.name}
+::1\tip6-localhost ip6-loopback
+fe00::0\tip6-localnet
+ff00::0\tip6-mcastprefix
+ff02::1\tip6-allnodes
+ff02::2\tip6-allrouters
+"""
+            )
+        self.bind_mount(hosts_file, "/etc/hosts")
+
+        # Common CLI commands for any topology
+        cdict = {
+            "commands": [
+                {
+                    "name": "pcap",
+                    "format": "pcap NETWORK",
+                    "help": (
+                        "capture packets from NETWORK into file capture-NETWORK.pcap"
+                        " the command is run within a new window which also shows"
+                        " packet summaries. NETWORK can also be an interface specified"
+                        " as HOST:INTF. To capture inside the host namespace."
+                    ),
+                    "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap",
+                    "top-level": True,
+                    "new-window": {"background": True},
+                },
+                {
+                    "name": "nsterm",
+                    "format": "nsterm HOST [HOST ...]",
+                    "help": (
+                        "open terminal[s] in the namespace only"
+                        " (outside containers or VM), * for all"
+                    ),
+                    "exec": "bash",
+                    "new-window": {"ns_only": True},
+                },
+                {
+                    "name": "term",
+                    "format": "term HOST [HOST ...]",
+                    "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all",
+                    "exec": "bash",
+                    "new-window": True,
+                },
+                {
+                    "name": "xterm",
+                    "format": "xterm HOST [HOST ...]",
+                    "help": "open XTerm[s] on HOST[S], * for all",
+                    "exec": "bash",
+                    "new-window": {
+                        "forcex": True,
+                    },
+                },
+                {
+                    "name": "sh",
+                    "format": "[HOST ...] sh <SHELL-COMMAND>",
+                    "help": "execute <SHELL-COMMAND> on hosts",
+                    "exec": "{}",
+                },
+                {
+                    "name": "shi",
+                    "format": "[HOST ...] shi <INTERACTIVE-COMMAND>",
+                    "help": "execute <INTERACTIVE-COMMAND> on HOST[s]",
+                    "exec": "{}",
+                    "interactive": True,
+                },
+                {
+                    "name": "stdout",
+                    "exec": (
+                        "[ -e %RUNDIR%/qemu.out ] && tail -F %RUNDIR%/qemu.out "
+                        "|| tail -F %RUNDIR%/cmd.out"
+                    ),
+                    "format": "stdout HOST [HOST ...]",
+                    "help": "tail -f on the stdout of the qemu/cmd for this node",
+                    "new-window": True,
+                },
+                {
+                    "name": "stderr",
+                    "exec": (
+                        "[ -e %RUNDIR%/qemu.err ] && tail -F %RUNDIR%/qemu.err "
+                        "|| tail -F %RUNDIR%/cmd.err"
+                    ),
+                    "format": "stderr HOST [HOST ...]",
+                    "help": "tail -f on the stdout of the qemu/cmd for this node",
+                    "new-window": True,
+                },
+            ]
+        }
+
+        cli.add_cli_config(self, cdict)
+
+        if "cli" in config:
+            cli.add_cli_config(self, config["cli"])
+
+        if "topology" not in self.config:
+            self.config["topology"] = {}
+
+        self.topoconf = self.config["topology"]
+        self.ipv6_enable = self.topoconf.get("ipv6-enable", False)
+
+        if self.isolated:
+            if not self.ipv6_enable:
+                # Disable IPv6
+                self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0")
+                self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
+            else:
+                self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1")
+                self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0")
+
+        # we really need overlay, but overlay-layers (used by overlay-images)
+        # counts on things being present in overlay so this temp stuff doesn't work.
+        # if self.isolated:
+        #     # Let's hide podman details
+        #     self.tmpfs_mount("/var/lib/containers/storage/overlay-containers")
+
+        shellopt = self.cfgopt.getoption("--shell")
+        shellopt = shellopt if shellopt else ""
+        if shellopt == "all" or "." in shellopt.split(","):
+            self.run_in_window("bash")
+
+    def __del__(self):
+        """Catch case of build object but not async_deleted."""
+        if hasattr(self, "built"):
+            if not self.deleting:
+                logging.critical(
+                    "Munet object deleted without calling `async_delete` for cleanup."
+                )
+        s = super()
+        if hasattr(s, "__del__"):
+            s.__del__(self)
+
+    async def _async_build(self, logger=None):
+        """Build the topology based on config."""
+        if self.built:
+            self.logger.warning("%s: is already built", self)
+            return
+
+        self.built = True
+
+        # Allow for all networks to be auto-numbered
+        topoconf = self.topoconf
+        autonumber = self.autonumber
+        ipv6_enable = self.ipv6_enable
+
+        # ---------------------------------------------
+        # Merge Kinds and perform variable substitution
+        # ---------------------------------------------
+
+        kinds = self.config.get("kinds", {})
+
+        for name, conf in config_to_dict_with_key(topoconf, "networks", "name").items():
+            if kind := conf.get("kind"):
+                if kconf := kinds[kind]:
+                    conf = merge_kind_config(kconf, conf)
+            conf = config_subst(
+                conf, name=name, rundir=self.rundir, configdir=self.config_dirname
+            )
+            if "ip" not in conf and autonumber:
+                conf["ip"] = "auto"
+            if "ipv6" not in conf and autonumber and ipv6_enable:
+                conf["ipv6"] = "auto"
+            topoconf["networks"][name] = conf
+            self.add_network(name, conf, logger=logger)
+
+        for name, conf in config_to_dict_with_key(topoconf, "nodes", "name").items():
+            if kind := conf.get("kind"):
+                if kconf := kinds[kind]:
+                    conf = merge_kind_config(kconf, conf)
+
+            config_to_dict_with_key(
+                conf, "env", "name"
+            )  # convert list of env objects to dict
+
+            conf = config_subst(
+                conf,
+                name=name,
+                rundir=os.path.join(self.rundir, name),
+                configdir=self.config_dirname,
+            )
+            topoconf["nodes"][name] = conf
+            self.add_l3_node(name, conf, logger=logger)
+
+        # ------------------
+        # Create connections
+        # ------------------
+
+        # Go through all connections and name them so they are sane to the user
+        # otherwise when we do p2p links the names/ords skip around based oddly
+        for name, node in self.hosts.items():
+            nconf = node.config
+            if "connections" not in nconf:
+                continue
+            nconns = []
+            for cconf in nconf["connections"]:
+                # Replace string only with a dictionary
+                if isinstance(cconf, str):
+                    splitconf = cconf.split(":", 1)
+                    cconf = {"to": splitconf[0]}
+                    if len(splitconf) == 2:
+                        cconf["name"] = splitconf[1]
+                # Allocate a name if not already assigned
+                if "name" not in cconf:
+                    cconf["name"] = node.get_next_intf_name()
+                nconns.append(cconf)
+            nconf["connections"] = nconns
+
+        for name, node in self.hosts.items():
+            nconf = node.config
+            if "connections" not in nconf:
+                continue
+            for cconf in nconf["connections"]:
+                # Eventually can add support for unconnected intf here.
+                if "to" not in cconf:
+                    continue
+                to = cconf["to"]
+                if to in self.switches:
+                    switch = self.switches[to]
+                    swconf = find_matching_net_config(name, cconf, switch.config)
+                    await self.add_native_link(switch, node, swconf, cconf)
+                elif cconf["name"] not in node.intfs:
+                    # Only add the p2p interface if not already there.
+                    other = self.hosts[to]
+                    oconf = find_matching_net_config(name, cconf, other.config)
+                    await self.add_native_link(node, other, cconf, oconf)
+
+    @property
+    def autonumber(self):
+        return self.topoconf.get("networks-autonumber", False)
+
+    @autonumber.setter
+    def autonumber(self, value):
+        self.topoconf["networks-autonumber"] = bool(value)
+
+    async def add_native_link(self, node1, node2, c1=None, c2=None):
+        """Add a link between switch and node or 2 nodes."""
+        isp2p = False
+
+        c1 = {} if c1 is None else c1
+        c2 = {} if c2 is None else c2
+
+        if node1.name in self.switches:
+            assert node2.name in self.hosts
+        elif node2.name in self.switches:
+            assert node1.name in self.hosts
+            node1, node2 = node2, node1
+            c1, c2 = c2, c1
+        else:
+            # p2p link
+            assert node1.name in self.hosts
+            assert node1.name in self.hosts
+            isp2p = True
+
+        if "name" not in c1:
+            c1["name"] = node1.get_next_intf_name()
+        if1 = c1["name"]
+
+        if "name" not in c2:
+            c2["name"] = node2.get_next_intf_name()
+        if2 = c2["name"]
+
+        do_add_link = True
+        for n, c in ((node1, c1), (node2, c2)):
+            if "hostintf" in c:
+                await n.add_host_intf(c["hostintf"], c["name"], mtu=c.get("mtu"))
+                do_add_link = False
+            elif "physical" in c:
+                await n.add_phy_intf(c["physical"], c["name"])
+                do_add_link = False
+        if do_add_link:
+            assert "hostintf" not in c1
+            assert "hostintf" not in c2
+            assert "physical" not in c1
+            assert "physical" not in c2
+
+            if isp2p:
+                mtu1 = c1.get("mtu")
+                mtu2 = c2.get("mtu")
+                mtu = mtu1 if mtu1 else mtu2
+                if mtu1 and mtu2 and mtu1 != mtu2:
+                    self.logger.error("mtus differ for add_link %s != %s", mtu1, mtu2)
+            else:
+                mtu = c2.get("mtu")
+
+            super().add_link(node1, node2, if1, if2, mtu=mtu)
+
+        if isp2p:
+            node1.set_p2p_addr(node2, c1, c2)
+        else:
+            node2.set_lan_addr(node1, c2)
+
+        if "physical" not in c1 and not node1.is_vm:
+            node1.set_intf_constraints(if1, **c1)
+        if "physical" not in c2 and not node2.is_vm:
+            node2.set_intf_constraints(if2, **c2)
+
+    def add_l3_node(self, name, config=None, **kwargs):
+        """Add a node to munet."""
+        if config and config.get("image"):
+            cls = L3ContainerNode
+        elif config and config.get("qemu"):
+            cls = L3QemuVM
+        elif config and config.get("server"):
+            cls = SSHRemote
+            kwargs["server"] = config["server"]
+            kwargs["port"] = int(config.get("server-port", 22))
+            if "ssh-identity-file" in config:
+                kwargs["idfile"] = config.get("ssh-identity-file")
+            if "ssh-user" in config:
+                kwargs["user"] = config.get("ssh-user")
+            if "ssh-password" in config:
+                kwargs["password"] = config.get("ssh-password")
+        else:
+            cls = L3NamespaceNode
+        return super().add_host(name, cls=cls, config=config, **kwargs)
+
+    def add_network(self, name, config=None, **kwargs):
+        """Add a l2 or l3 switch to munet."""
+        if config is None:
+            config = {}
+
+        cls = L3Bridge if config.get("ip") else L2Bridge
+        mtu = kwargs.get("mtu", config.get("mtu"))
+        return super().add_switch(name, cls=cls, config=config, mtu=mtu, **kwargs)
+
+    async def run(self):
+        tasks = []
+
+        hosts = self.hosts.values()
+        launch_nodes = [x for x in hosts if hasattr(x, "launch")]
+        launch_nodes = [x for x in launch_nodes if x.config.get("qemu")]
+        run_nodes = [x for x in hosts if hasattr(x, "has_run_cmd") and x.has_run_cmd()]
+        ready_nodes = [
+            x for x in hosts if hasattr(x, "has_ready_cmd") and x.has_ready_cmd()
+        ]
+
+        pcapopt = self.cfgopt.getoption("--pcap")
+        pcapopt = pcapopt if pcapopt else ""
+        if pcapopt == "all":
+            pcapopt = self.switches.keys()
+        if pcapopt:
+            for pcap in pcapopt.split(","):
+                if ":" in pcap:
+                    host, intf = pcap.split(":")
+                    pcap = f"{host}-{intf}"
+                    host = self.hosts[host]
+                else:
+                    host = self
+                    intf = pcap
+                host.run_in_window(
+                    f"tshark -s 9200 -i {intf} -P -w capture-{pcap}.pcap",
+                    background=True,
+                    title=f"cap:{pcap}",
+                )
+
+        if launch_nodes:
+            # would like a info when verbose here.
+            logging.debug("Launching nodes")
+            await asyncio.gather(*[x.launch() for x in launch_nodes])
+
+        # Watch for launched processes to exit
+        for node in launch_nodes:
+            task = asyncio.create_task(
+                node.launch_p.wait(), name=f"Node-{node.name}-launch"
+            )
+            task.add_done_callback(node.launch_completed)
+            tasks.append(task)
+
+        if run_nodes:
+            # would like a info when verbose here.
+            logging.debug("Running `cmd` on nodes")
+            await asyncio.gather(*[x.run_cmd() for x in run_nodes])
+
+        # Watch for run_cmd processes to exit
+        for node in run_nodes:
+            task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd")
+            task.add_done_callback(node.cmd_completed)
+            tasks.append(task)
+
+        # Wait for nodes to be ready
+        if ready_nodes:
+
+            async def wait_until_ready(x):
+                while not await x.async_ready_cmd():
+                    logging.debug("Waiting for ready on: %s", x)
+                    await asyncio.sleep(0.25)
+                logging.debug("%s is ready!", x)
+
+            logging.debug("Waiting for ready on nodes: %s", ready_nodes)
+            _, pending = await asyncio.wait(
+                [wait_until_ready(x) for x in ready_nodes], timeout=30
+            )
+            if pending:
+                logging.warning("Timeout waiting for ready: %s", pending)
+                for nr in pending:
+                    nr.cancel()
+                raise asyncio.TimeoutError()
+            logging.debug("All nodes ready")
+
+        return tasks
+
+    async def _async_delete(self):
+        from .testing.util import async_pause_test  # pylint: disable=C0415
+
+        self.logger.debug("%s: deleting.", self)
+
+        if self.cfgopt.getoption("--coverage"):
+            nodes = (
+                x for x in self.hosts.values() if hasattr(x, "gather_coverage_data")
+            )
+            try:
+                await asyncio.gather(*(x.gather_coverage_data() for x in nodes))
+            except Exception as error:
+                logging.warning("Error gathering coverage data: %s", error)
+
+        pause = bool(self.cfgopt.getoption("--pause-at-end"))
+        pause = pause or bool(self.cfgopt.getoption("--pause"))
+        if pause:
+            try:
+                await async_pause_test("Before MUNET delete")
+            except KeyboardInterrupt:
+                print("^C...continuing")
+            except Exception as error:
+                self.logger.error("\n...continuing after error: %s", error)
+
+        # XXX should we cancel launch and run tasks?
+
+        try:
+            await super()._async_delete()
+        except Exception as error:
+            self.logger.error("Error cleaning up: %s", error, exc_info=True)
+            raise
+
+
+async def run_cmd_update_ceos(node, shell_cmd, cmds, cmd):
+    cmd = cmd.strip()
+    if shell_cmd or cmd != "/sbin/init":
+        return cmds, cmd
+
+    #
+    # Add flash dir and mount it
+    #
+    flashdir = os.path.join(node.rundir, "flash")
+    node.cmd_raises_nsonly(f"mkdir -p {flashdir} && chmod 775 {flashdir}")
+    cmds += [f"--volume={flashdir}:/mnt/flash"]
+
+    #
+    # Startup config (if not present already)
+    #
+    if startup_config := node.config.get("startup-config", None):
+        dest = os.path.join(flashdir, "startup-config")
+        if os.path.exists(dest):
+            node.logger.info("Skipping copy of startup-config, already present")
+        else:
+            source = os.path.join(node.unet.config_dirname, startup_config)
+            node.cmd_raises_nsonly(f"cp {source} {dest} && chmod 664 {dest}")
+
+    #
+    # system mac address (if not present already
+    #
+    dest = os.path.join(flashdir, "system_mac_address")
+    if os.path.exists(dest):
+        node.logger.info("Skipping system-mac generation, already present")
+    else:
+        random_arista_mac = "00:1c:73:%02x:%02x:%02x" % (
+            random.randint(0, 255),
+            random.randint(0, 255),
+            random.randint(0, 255),
+        )
+        system_mac = node.config.get("system-mac", random_arista_mac)
+        with open(dest, "w", encoding="ascii") as f:
+            f.write(system_mac + "\n")
+        node.cmd_raises_nsonly(f"chmod 664 {dest}")
+
+    args = []
+
+    # Pass special args for the environment variables
+    if "env" in node.config:
+        args += [f"systemd.setenv={k}={v}" for k, v in node.config["env"].items()]
+
+    return cmds, [cmd] + args
+
+
+# XXX this is only used by the container code
+kind_run_cmd_update = {"ceos": run_cmd_update_ceos}
diff --git a/tests/topotests/munet/parser.py b/tests/topotests/munet/parser.py
new file mode 100644 (file)
index 0000000..4fc0c75
--- /dev/null
@@ -0,0 +1,374 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# September 30 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+"""A module that implements the standalone parser."""
+import asyncio
+import importlib.resources
+import json
+import logging
+import logging.config
+import os
+import subprocess
+import sys
+import tempfile
+
+from pathlib import Path
+
+
+try:
+    import jsonschema  # pylint: disable=C0415
+    import jsonschema.validators  # pylint: disable=C0415
+
+    from jsonschema.exceptions import ValidationError  # pylint: disable=C0415
+except ImportError:
+    jsonschema = None
+
+from .config import list_to_dict_with_key
+from .native import Munet
+
+
+def get_schema():
+    if get_schema.schema is None:
+        with importlib.resources.path("munet", "munet-schema.json") as datapath:
+            search = [str(datapath.parent)]
+        get_schema.schema = get_config(basename="munet-schema", search=search)
+    return get_schema.schema
+
+
+get_schema.schema = None
+
+project_root_contains = [
+    ".git",
+    "pyproject.toml",
+    "tox.ini",
+    "setup.cfg",
+    "setup.py",
+    "pytest.ini",
+    ".projectile",
+]
+
+
+def is_project_root(path: Path) -> bool:
+
+    for contains in project_root_contains:
+        if path.joinpath(contains).exists():
+            return True
+    return False
+
+
+def find_project_root(config_path: Path, project_root=None):
+    if project_root is not None:
+        project_root = Path(project_root)
+        if project_root in config_path.parents:
+            return project_root
+        logging.warning(
+            "project_root %s is not a common ancestor of config file %s",
+            project_root,
+            config_path,
+        )
+        return config_path.parent
+    for ppath in config_path.parents:
+        if is_project_root(ppath):
+            return ppath
+    return config_path.parent
+
+
+def get_config(pathname=None, basename="munet", search=None, logf=logging.debug):
+
+    cwd = os.getcwd()
+
+    if not search:
+        search = [cwd]
+    elif isinstance(search, (str, Path)):
+        search = [search]
+
+    if pathname:
+        pathname = os.path.join(cwd, pathname)
+        if not os.path.exists(pathname):
+            raise FileNotFoundError(pathname)
+    else:
+        for d in search:
+            logf("%s", f'searching in "{d}" for "{basename}".{{yaml, toml, json}}')
+            for ext in ("yaml", "toml", "json"):
+                pathname = os.path.join(d, basename + "." + ext)
+                if os.path.exists(pathname):
+                    logf("%s", f'Found "{pathname}"')
+                    break
+            else:
+                continue
+            break
+        else:
+            raise FileNotFoundError(basename + ".{json,toml,yaml} in " + f"{search}")
+
+    _, ext = pathname.rsplit(".", 1)
+
+    if ext == "json":
+        config = json.load(open(pathname, encoding="utf-8"))
+    elif ext == "toml":
+        import toml  # pylint: disable=C0415
+
+        config = toml.load(pathname)
+    elif ext == "yaml":
+        import yaml  # pylint: disable=C0415
+
+        config = yaml.safe_load(open(pathname, encoding="utf-8"))
+    else:
+        raise ValueError("Filename does not end with (.json|.toml|.yaml)")
+
+    config["config_pathname"] = os.path.realpath(pathname)
+    return config
+
+
+def setup_logging(args, config_base="logconf"):
+    # Create rundir and arrange for future commands to run in it.
+
+    # Change CWD to the rundir prior to parsing config
+    old = os.getcwd()
+    os.chdir(args.rundir)
+    try:
+        search = [old]
+        with importlib.resources.path("munet", config_base + ".yaml") as datapath:
+            search.append(str(datapath.parent))
+
+        def logf(msg, *p, **k):
+            if args.verbose:
+                print("PRELOG: " + msg % p, **k, file=sys.stderr)
+
+        config = get_config(args.log_config, config_base, search, logf=logf)
+        pathname = config["config_pathname"]
+        del config["config_pathname"]
+
+        if "info_console" in config["handlers"]:
+            # mutest case
+            if args.verbose > 1:
+                config["handlers"]["console"]["level"] = "DEBUG"
+                config["handlers"]["info_console"]["level"] = "DEBUG"
+            elif args.verbose:
+                config["handlers"]["console"]["level"] = "INFO"
+                config["handlers"]["info_console"]["level"] = "DEBUG"
+        elif args.verbose:
+            # munet case
+            config["handlers"]["console"]["level"] = "DEBUG"
+
+        # add the rundir path to the filenames
+        for v in config["handlers"].values():
+            filename = v.get("filename")
+            if not filename:
+                continue
+            v["filename"] = os.path.join(args.rundir, filename)
+
+        logging.config.dictConfig(dict(config))
+        logging.info("Loaded logging config %s", pathname)
+
+        return config
+    finally:
+        os.chdir(old)
+
+
+def append_hosts_files(unet, netname):
+    if not netname:
+        return
+
+    entries = []
+    for name in ("munet", *list(unet.hosts)):
+        if name == "munet":
+            node = unet.switches[netname]
+            ifname = None
+        else:
+            node = unet.hosts[name]
+            if not hasattr(node, "_intf_addrs"):
+                continue
+            ifname = node.get_ifname(netname)
+
+        for b in (False, True):
+            ifaddr = node.get_intf_addr(ifname, ipv6=b)
+            if ifaddr and hasattr(ifaddr, "ip"):
+                entries.append((name, ifaddr.ip))
+
+    for name in ("munet", *list(unet.hosts)):
+        node = unet if name == "munet" else unet.hosts[name]
+        if not hasattr(node, "rundir"):
+            continue
+        with open(os.path.join(node.rundir, "hosts.txt"), "a+", encoding="ascii") as hf:
+            hf.write("\n")
+            for e in entries:
+                hf.write(f"{e[1]}\t{e[0]}\n")
+
+
+def validate_config(config, logger, args):
+    if jsonschema is None:
+        logger.debug("No validation w/o jsonschema module")
+        return True
+
+    old = os.getcwd()
+    if args:
+        os.chdir(args.rundir)
+
+    try:
+        validator = jsonschema.validators.Draft202012Validator(get_schema())
+        validator.validate(instance=config)
+        logger.debug("Validated %s", config["config_pathname"])
+        return True
+    except FileNotFoundError as error:
+        logger.info("No schema found: %s", error)
+        return False
+    except ValidationError as error:
+        logger.info("Validation failed: %s", error)
+        return False
+    finally:
+        if args:
+            os.chdir(old)
+
+
+def load_kinds(args, search=None):
+    # Change CWD to the rundir prior to parsing config
+    cwd = os.getcwd()
+    if args:
+        os.chdir(args.rundir)
+
+    args_config = args.kinds_config if args else None
+    try:
+        if search is None:
+            search = [cwd]
+        with importlib.resources.path("munet", "kinds.yaml") as datapath:
+            search.insert(0, str(datapath.parent))
+
+        configs = []
+        if args_config:
+            configs.append(get_config(args_config, "kinds", search=[]))
+        else:
+            # prefer directories at the front of the list
+            for kdir in search:
+                try:
+                    configs.append(get_config(basename="kinds", search=[kdir]))
+                except FileNotFoundError:
+                    continue
+
+        kinds = {}
+        for config in configs:
+            # XXX need to fix the issue with `connections: ["net0"]` not validating
+            # if jsonschema is not None:
+            #     validator = jsonschema.validators.Draft202012Validator(get_schema())
+            #     validator.validate(instance=config)
+
+            kinds_list = config.get("kinds", [])
+            kinds_dict = list_to_dict_with_key(kinds_list, "name")
+            if kinds_dict:
+                logging.info("Loading kinds config from %s", config["config_pathname"])
+                if "kinds" in kinds:
+                    kinds["kinds"].update(**kinds_dict)
+                else:
+                    kinds["kinds"] = kinds_dict
+
+            cli_list = config.get("cli", {}).get("commands", [])
+            if cli_list:
+                logging.info("Loading cli comands from %s", config["config_pathname"])
+                if "cli" not in kinds:
+                    kinds["cli"] = {}
+                if "commands" not in kinds["cli"]:
+                    kinds["cli"]["commands"] = []
+                kinds["cli"]["commands"].extend(cli_list)
+
+        return kinds
+    except FileNotFoundError as error:
+        # if we have kinds in args but the file doesn't exist, raise the error
+        if args_config is not None:
+            raise error
+        return {}
+    finally:
+        if args:
+            os.chdir(cwd)
+
+
+async def async_build_topology(
+    config=None,
+    logger=None,
+    rundir=None,
+    args=None,
+    unshare_inline=False,
+    pytestconfig=None,
+    search_root=None,
+    top_level_pidns=True,
+):
+
+    if not rundir:
+        rundir = tempfile.mkdtemp(prefix="unet")
+    subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True)
+
+    isolated = not args.host if args else True
+    if not config:
+        config = get_config(basename="munet")
+
+    # create search directories from common root if given
+    cpath = Path(config["config_pathname"]).absolute()
+    project_root = args.project_root if args else None
+    if not search_root:
+        search_root = find_project_root(cpath, project_root)
+    if not search_root:
+        search = [cpath.parent]
+    else:
+        search_root = Path(search_root).absolute()
+        if search_root in cpath.parents:
+            search = list(cpath.parents)
+            if remcount := len(search_root.parents):
+                search = search[0:-remcount]
+
+    # load kinds along search path and merge into config
+    kinds = load_kinds(args, search=search)
+    config_kinds_dict = list_to_dict_with_key(config.get("kinds", []), "name")
+    config["kinds"] = {**kinds.get("kinds", {}), **config_kinds_dict}
+
+    # mere CLI command from kinds into config as well.
+    kinds_cli_list = kinds.get("cli", {}).get("commands", [])
+    config_cli_list = config.get("cli", {}).get("commands", [])
+    if config_cli_list:
+        if kinds_cli_list:
+            config_cli_list.extend(list(kinds_cli_list))
+    elif kinds_cli_list:
+        if "cli" not in config:
+            config["cli"] = {}
+        if "commands" not in config["cli"]:
+            config["cli"]["commands"] = []
+        config["cli"]["commands"].extend(list(kinds_cli_list))
+
+    unet = Munet(
+        rundir=rundir,
+        config=config,
+        pytestconfig=pytestconfig,
+        isolated=isolated,
+        pid=top_level_pidns,
+        unshare_inline=args.unshare_inline if args else unshare_inline,
+        logger=logger,
+    )
+
+    try:
+        await unet._async_build(logger)  # pylint: disable=W0212
+    except Exception as error:
+        logging.critical("Failure building munet topology: %s", error, exc_info=True)
+        await unet.async_delete()
+        raise
+    except KeyboardInterrupt:
+        await unet.async_delete()
+        raise
+
+    topoconf = config.get("topology")
+    if not topoconf:
+        return unet
+
+    dns_network = topoconf.get("dns-network")
+    if dns_network:
+        append_hosts_files(unet, dns_network)
+
+    # Write our current config to the run directory
+    with open(f"{unet.rundir}/config.json", "w", encoding="utf-8") as f:
+        json.dump(unet.config, f, indent=2)
+
+    return unet
+
+
+def build_topology(config=None, logger=None, rundir=None, args=None, pytestconfig=None):
+    return asyncio.run(async_build_topology(config, logger, rundir, args, pytestconfig))
diff --git a/tests/topotests/munet/testing/__init__.py b/tests/topotests/munet/testing/__init__.py
new file mode 100644 (file)
index 0000000..63cbfab
--- /dev/null
@@ -0,0 +1 @@
+"""Sub-package supporting munet use in pytest."""
diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py
new file mode 100644 (file)
index 0000000..25039df
--- /dev/null
@@ -0,0 +1,447 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""A module that implements pytest fixtures.
+
+To use in your project, in your conftest.py add:
+
+  from munet.testing.fixtures import *
+"""
+import contextlib
+import logging
+import os
+
+from pathlib import Path
+from typing import Union
+
+import pytest
+import pytest_asyncio
+
+from ..base import BaseMunet
+from ..base import Bridge
+from ..base import get_event_loop
+from ..cleanup import cleanup_current
+from ..cleanup import cleanup_previous
+from ..native import L3NodeMixin
+from ..parser import async_build_topology
+from ..parser import get_config
+from .util import async_pause_test
+from .util import pause_test
+
+
+@contextlib.asynccontextmanager
+async def achdir(ndir: Union[str, Path], desc=""):
+    odir = os.getcwd()
+    os.chdir(ndir)
+    if desc:
+        logging.debug("%s: chdir from %s to %s", desc, odir, ndir)
+    try:
+        yield
+    finally:
+        if desc:
+            logging.debug("%s: chdir back from %s to %s", desc, ndir, odir)
+        os.chdir(odir)
+
+
+@contextlib.contextmanager
+def chdir(ndir: Union[str, Path], desc=""):
+    odir = os.getcwd()
+    os.chdir(ndir)
+    if desc:
+        logging.debug("%s: chdir from %s to %s", desc, odir, ndir)
+    try:
+        yield
+    finally:
+        if desc:
+            logging.debug("%s: chdir back from %s to %s", desc, ndir, odir)
+        os.chdir(odir)
+
+
+def get_test_logdir(nodeid=None, module=False):
+    """Get log directory relative pathname."""
+    xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "")
+    mode = os.getenv("PYTEST_XDIST_MODE", "no")
+
+    # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running
+    # may be missing "::testname" if module is True
+    if not nodeid:
+        nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
+
+    cur_test = nodeid.replace("[", "_").replace("]", "_")
+    if module:
+        idx = cur_test.rfind("::")
+        path = cur_test if idx == -1 else cur_test[:idx]
+        testname = ""
+    else:
+        path, testname = cur_test.split("::")
+        testname = testname.replace("/", ".")
+    path = path[:-3].replace("/", ".")
+
+    # We use different logdir paths based on how xdist is running.
+    if mode == "each":
+        if module:
+            return os.path.join(path, "worker-logs", xdist_worker)
+        return os.path.join(path, testname, xdist_worker)
+    assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}"
+    return path if module else os.path.join(path, testname)
+
+
+def _push_log_handler(desc, logpath):
+    logpath = os.path.abspath(logpath)
+    logging.debug("conftest: adding %s logging at %s", desc, logpath)
+    root_logger = logging.getLogger()
+    handler = logging.FileHandler(logpath, mode="w")
+    fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s")
+    handler.setFormatter(fmt)
+    root_logger.addHandler(handler)
+    return handler
+
+
+def _pop_log_handler(handler):
+    root_logger = logging.getLogger()
+    logging.debug("conftest: removing logging handler %s", handler)
+    root_logger.removeHandler(handler)
+
+
+@contextlib.contextmanager
+def log_handler(desc, logpath):
+    handler = _push_log_handler(desc, logpath)
+    try:
+        yield
+    finally:
+        _pop_log_handler(handler)
+
+
+# =================
+# Sessions Fixtures
+# =================
+
+
+@pytest.fixture(autouse=True, scope="session")
+def session_autouse():
+    if "PYTEST_TOPOTEST_WORKER" not in os.environ:
+        is_worker = False
+    elif not os.environ["PYTEST_TOPOTEST_WORKER"]:
+        is_worker = False
+    else:
+        is_worker = True
+
+    if not is_worker:
+        # This is unfriendly to multi-instance
+        cleanup_previous()
+
+    # We never pop as we want to keep logging
+    _push_log_handler("session", "/tmp/unet-test/pytest-session.log")
+
+    yield
+
+    if not is_worker:
+        cleanup_current()
+
+
+# ===============
+# Module Fixtures
+# ===============
+
+
+@pytest.fixture(autouse=True, scope="module")
+def module_autouse(request):
+    logpath = get_test_logdir(request.node.name, True)
+    logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log")
+    with log_handler("module", logpath):
+        sdir = os.path.dirname(os.path.realpath(request.fspath))
+        with chdir(sdir, "module autouse fixture"):
+            yield
+
+        if BaseMunet.g_unet:
+            raise Exception("Base Munet was not cleaned up/deleted")
+
+
+@pytest.fixture(scope="module")
+def event_loop():
+    """Create an instance of the default event loop for the session."""
+    loop = get_event_loop()
+    try:
+        logging.info("event_loop_fixture: yielding with new event loop watcher")
+        yield loop
+    finally:
+        loop.close()
+
+
+@pytest.fixture(scope="module")
+def rundir_module():
+    d = os.path.join("/tmp/unet-test", get_test_logdir(module=True))
+    logging.debug("conftest: test module rundir %s", d)
+    return d
+
+
+async def _unet_impl(
+    _rundir, _pytestconfig, unshare=None, top_level_pidns=None, param=None
+):
+    try:
+        # Default is not to unshare inline if not specified otherwise
+        unshare_default = False
+        pidns_default = True
+        if isinstance(param, (tuple, list)):
+            pidns_default = bool(param[2]) if len(param) > 2 else True
+            unshare_default = bool(param[1]) if len(param) > 1 else False
+            param = str(param[0])
+        elif isinstance(param, bool):
+            unshare_default = param
+            param = None
+        if unshare is None:
+            unshare = unshare_default
+        if top_level_pidns is None:
+            top_level_pidns = pidns_default
+
+        logging.info("unet fixture: basename=%s unshare_inline=%s", param, unshare)
+        _unet = await async_build_topology(
+            config=get_config(basename=param) if param else None,
+            rundir=_rundir,
+            unshare_inline=unshare,
+            top_level_pidns=top_level_pidns,
+            pytestconfig=_pytestconfig,
+        )
+    except Exception as error:
+        logging.debug(
+            "unet fixture: unet build failed: %s\nparam: %s",
+            error,
+            param,
+            exc_info=True,
+        )
+        pytest.skip(
+            f"unet fixture: unet build failed: {error}", allow_module_level=True
+        )
+        raise
+
+    try:
+        tasks = await _unet.run()
+    except Exception as error:
+        logging.debug("unet fixture: unet run failed: %s", error, exc_info=True)
+        await _unet.async_delete()
+        pytest.skip(f"unet fixture: unet run failed: {error}", allow_module_level=True)
+        raise
+
+    logging.debug("unet fixture: containers running")
+
+    # Pytest is supposed to always return even if exceptions
+    try:
+        yield _unet
+    except Exception as error:
+        logging.error("unet fixture: yield unet unexpected exception: %s", error)
+
+    logging.debug("unet fixture: module done, deleting unet")
+    await _unet.async_delete()
+
+    # No one ever awaits these so cancel them
+    logging.debug("unet fixture: cleanup")
+    for task in tasks:
+        task.cancel()
+
+    # Reset the class variables so auto number is predictable
+    logging.debug("unet fixture: resetting ords to 1")
+    L3NodeMixin.next_ord = 1
+    Bridge.next_ord = 1
+
+
+@pytest.fixture(scope="module")
+async def unet(request, rundir_module, pytestconfig):  # pylint: disable=W0621
+    """A unet creating fixutre.
+
+    The request param is either the basename of the config file or a tuple of the form:
+    (basename, unshare, top_level_pidns), with the second and third elements boolean and
+    optional, defaulting to False, True.
+    """
+    param = request.param if hasattr(request, "param") else None
+    sdir = os.path.dirname(os.path.realpath(request.fspath))
+    async with achdir(sdir, "unet fixture"):
+        async for x in _unet_impl(rundir_module, pytestconfig, param=param):
+            yield x
+
+
+@pytest.fixture(scope="module")
+async def unet_share(request, rundir_module, pytestconfig):  # pylint: disable=W0621
+    """A unet creating fixutre.
+
+    This share variant keeps munet from unsharing the process to a new namespace so that
+    root level commands and actions are execute on the host, normally they are executed
+    in the munet namespace which allowing things like scapy inline in tests to work.
+
+    The request param is either the basename of the config file or a tuple of the form:
+    (basename, top_level_pidns), the second value is a boolean.
+    """
+    param = request.param if hasattr(request, "param") else None
+    if isinstance(param, (tuple, list)):
+        param = (param[0], False, param[1])
+    sdir = os.path.dirname(os.path.realpath(request.fspath))
+    async with achdir(sdir, "unet_share fixture"):
+        async for x in _unet_impl(
+            rundir_module, pytestconfig, unshare=False, param=param
+        ):
+            yield x
+
+
+@pytest.fixture(scope="module")
+async def unet_unshare(request, rundir_module, pytestconfig):  # pylint: disable=W0621
+    """A unet creating fixutre.
+
+    This unshare variant has the top level munet unshare the process inline so that
+    root level commands and actions are execute in a new namespace. This allows things
+    like scapy inline in tests to work.
+
+    The request param is either the basename of the config file or a tuple of the form:
+    (basename, top_level_pidns), the second value is a boolean.
+    """
+    param = request.param if hasattr(request, "param") else None
+    if isinstance(param, (tuple, list)):
+        param = (param[0], True, param[1])
+    sdir = os.path.dirname(os.path.realpath(request.fspath))
+    async with achdir(sdir, "unet_unshare fixture"):
+        async for x in _unet_impl(
+            rundir_module, pytestconfig, unshare=True, param=param
+        ):
+            yield x
+
+
+# =================
+# Function Fixtures
+# =================
+
+
+@pytest.fixture(autouse=True, scope="function")
+async def function_autouse(request):
+    async with achdir(
+        os.path.dirname(os.path.realpath(request.fspath)), "func.fixture"
+    ):
+        yield
+
+
+@pytest.fixture(autouse=True)
+async def check_for_pause(request, pytestconfig):
+    # When we unshare inline we can't pause in the pytest_runtest_makereport hook
+    # so do it here.
+    if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline:
+        pause = bool(pytestconfig.getoption("--pause"))
+        if pause:
+            await async_pause_test(f"XXX before test '{request.node.name}'")
+    yield
+
+
+@pytest.fixture(scope="function")
+def stepf(pytestconfig):
+    class Stepnum:
+        """Track the stepnum in closure."""
+
+        num = 0
+
+        def inc(self):
+            self.num += 1
+
+    pause = pytestconfig.getoption("pause")
+    stepnum = Stepnum()
+
+    def stepfunction(desc=""):
+        desc = f": {desc}" if desc else ""
+        if pause:
+            pause_test(f"before step {stepnum.num}{desc}")
+        logging.info("STEP %s%s", stepnum.num, desc)
+        stepnum.inc()
+
+    return stepfunction
+
+
+@pytest_asyncio.fixture(scope="function")
+async def astepf(pytestconfig):
+    class Stepnum:
+        """Track the stepnum in closure."""
+
+        num = 0
+
+        def inc(self):
+            self.num += 1
+
+    pause = pytestconfig.getoption("pause")
+    stepnum = Stepnum()
+
+    async def stepfunction(desc=""):
+        desc = f": {desc}" if desc else ""
+        if pause:
+            await async_pause_test(f"before step {stepnum.num}{desc}")
+        logging.info("STEP %s%s", stepnum.num, desc)
+        stepnum.inc()
+
+    return stepfunction
+
+
+@pytest.fixture(scope="function")
+def rundir():
+    d = os.path.join("/tmp/unet-test", get_test_logdir(module=False))
+    logging.debug("conftest: test function rundir %s", d)
+    return d
+
+
+# Configure logging
+@pytest.hookimpl(hookwrapper=True, tryfirst=True)
+def pytest_runtest_setup(item):
+    d = os.path.join(
+        "/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False)
+    )
+    config = item.config
+    logging_plugin = config.pluginmanager.get_plugin("logging-plugin")
+    filename = Path(d, "pytest-exec.log")
+    logging_plugin.set_log_path(str(filename))
+    logging.debug("conftest: test function setup: rundir %s", d)
+    yield
+
+
+@pytest.fixture
+async def unet_perfunc(request, rundir, pytestconfig):  # pylint: disable=W0621
+    param = request.param if hasattr(request, "param") else None
+    async for x in _unet_impl(rundir, pytestconfig, param=param):
+        yield x
+
+
+@pytest.fixture
+async def unet_perfunc_unshare(request, rundir, pytestconfig):  # pylint: disable=W0621
+    """Build unet per test function with an optional topology basename parameter.
+
+    The fixture can be parameterized to choose different config files.
+    For example, use as follows to run the test with unet_perfunc configured
+    first with a config file named `cfg1.yaml` then with config file `cfg2.yaml`
+    (where the actual files could end with `json` or `toml` rather than `yaml`).
+
+        @pytest.mark.parametrize(
+            "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"]
+        )
+        def test_example(unet_perfunc)
+    """
+    param = request.param if hasattr(request, "param") else None
+    async for x in _unet_impl(rundir, pytestconfig, unshare=True, param=param):
+        yield x
+
+
+@pytest.fixture
+async def unet_perfunc_share(request, rundir, pytestconfig):  # pylint: disable=W0621
+    """Build unet per test function with an optional topology basename parameter.
+
+    This share variant keeps munet from unsharing the process to a new namespace so that
+    root level commands and actions are execute on the host, normally they are executed
+    in the munet namespace which allowing things like scapy inline in tests to work.
+
+    The fixture can be parameterized to choose different config files.  For example, use
+    as follows to run the test with unet_perfunc configured first with a config file
+    named `cfg1.yaml` then with config file `cfg2.yaml` (where the actual files could
+    end with `json` or `toml` rather than `yaml`).
+
+        @pytest.mark.parametrize(
+            "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"]
+        )
+        def test_example(unet_perfunc)
+    """
+    param = request.param if hasattr(request, "param") else None
+    async for x in _unet_impl(rundir, pytestconfig, unshare=False, param=param):
+        yield x
diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py
new file mode 100644 (file)
index 0000000..9b6a49a
--- /dev/null
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""A module that implements pytest hooks.
+
+To use in your project, in your conftest.py add:
+
+  from munet.testing.hooks import *
+"""
+import logging
+import os
+import sys
+import traceback
+
+import pytest
+
+from ..base import BaseMunet  # pylint: disable=import-error
+from ..cli import cli  # pylint: disable=import-error
+from .util import pause_test
+
+
+# ===================
+# Hooks (non-fixture)
+# ===================
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--cli-on-error",
+        action="store_true",
+        help="CLI on test failure",
+    )
+
+    parser.addoption(
+        "--coverage",
+        action="store_true",
+        help="Enable coverage gathering if supported",
+    )
+
+    parser.addoption(
+        "--gdb",
+        default="",
+        metavar="HOST[,HOST...]",
+        help="Comma-separated list of nodes to launch gdb on, or 'all'",
+    )
+    parser.addoption(
+        "--gdb-breakpoints",
+        default="",
+        metavar="BREAKPOINT[,BREAKPOINT...]",
+        help="Comma-separated list of breakpoints",
+    )
+    parser.addoption(
+        "--gdb-use-emacs",
+        action="store_true",
+        help="Use emacsclient to run gdb instead of a shell",
+    )
+
+    parser.addoption(
+        "--pcap",
+        default="",
+        metavar="NET[,NET...]",
+        help="Comma-separated list of networks to capture packets on, or 'all'",
+    )
+
+    parser.addoption(
+        "--pause",
+        action="store_true",
+        help="Pause after each test",
+    )
+    parser.addoption(
+        "--pause-at-end",
+        action="store_true",
+        help="Pause before taking munet down",
+    )
+    parser.addoption(
+        "--pause-on-error",
+        action="store_true",
+        help="Pause after (disables default when --shell or -vtysh given)",
+    )
+    parser.addoption(
+        "--no-pause-on-error",
+        dest="pause_on_error",
+        action="store_false",
+        help="Do not pause after (disables default when --shell or -vtysh given)",
+    )
+
+    parser.addoption(
+        "--shell",
+        default="",
+        metavar="NODE[,NODE...]",
+        help="Comma-separated list of nodes to spawn shell on, or 'all'",
+    )
+
+    parser.addoption(
+        "--stdout",
+        default="",
+        metavar="NODE[,NODE...]",
+        help="Comma-separated list of nodes to open tail-f stdout window on, or 'all'",
+    )
+
+    parser.addoption(
+        "--stderr",
+        default="",
+        metavar="NODE[,NODE...]",
+        help="Comma-separated list of nodes to open tail-f stderr window on, or 'all'",
+    )
+
+
+def pytest_configure(config):
+    if "PYTEST_XDIST_WORKER" not in os.environ:
+        os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no")
+        os.environ["PYTEST_IS_WORKER"] = ""
+        is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no"
+        is_worker = False
+    else:
+        os.environ["PYTEST_IS_WORKER"] = os.environ["PYTEST_XDIST_WORKER"]
+        is_xdist = True
+        is_worker = True
+
+    # Turn on live logging if user specified verbose and the config has a CLI level set
+    if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"):
+        if config.getoption("--log-cli-level", None) is None:
+            # By setting the CLI option to the ini value it enables log_cli=1
+            cli_level = config.getini("log_cli_level")
+            if cli_level is not None:
+                config.option.log_cli_level = cli_level
+
+    have_tmux = bool(os.getenv("TMUX", ""))
+    have_screen = not have_tmux and bool(os.getenv("STY", ""))
+    have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", ""))
+    have_windows = have_tmux or have_screen or have_xterm
+    have_windows_pause = have_tmux or have_xterm
+    xdist_no_windows = is_xdist and not is_worker and not have_windows_pause
+
+    for winopt in ["--shell", "--stdout", "--stderr"]:
+        b = config.getoption(winopt)
+        if b and xdist_no_windows:
+            pytest.exit(
+                f"{winopt} use requires byobu/TMUX/XTerm "
+                f"under dist {os.environ['PYTEST_XDIST_MODE']}"
+            )
+        elif b and not is_xdist and not have_windows:
+            pytest.exit(f"{winopt} use requires byobu/TMUX/SCREEN/XTerm")
+
+
+def pytest_runtest_makereport(item, call):
+    """Pause or invoke CLI as directed by config."""
+    isatty = sys.stdout.isatty()
+
+    pause = bool(item.config.getoption("--pause"))
+    skipped = False
+
+    if call.excinfo is None:
+        error = False
+    elif call.excinfo.typename == "Skipped":
+        skipped = True
+        error = False
+        pause = False
+    else:
+        error = True
+        modname = item.parent.module.__name__
+        exval = call.excinfo.value
+        logging.error(
+            "test %s/%s failed: %s: stdout: '%s' stderr: '%s'",
+            modname,
+            item.name,
+            exval,
+            exval.stdout if hasattr(exval, "stdout") else "NA",
+            exval.stderr if hasattr(exval, "stderr") else "NA",
+        )
+        if not pause:
+            pause = item.config.getoption("--pause-on-error")
+
+    if error and isatty and item.config.getoption("--cli-on-error"):
+        if not BaseMunet.g_unet:
+            logging.error("Could not launch CLI b/c no munet exists yet")
+        else:
+            print(f"\nCLI-ON-ERROR: {call.excinfo.typename}")
+            print(f"CLI-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}")
+            if hasattr(exval, "stdout") and exval.stdout:
+                print("stdout: " + exval.stdout.replace("\n", "\nstdout: "))
+            if hasattr(exval, "stderr") and exval.stderr:
+                print("stderr: " + exval.stderr.replace("\n", "\nstderr: "))
+            cli(BaseMunet.g_unet)
+
+    if pause:
+        if skipped:
+            item.skip_more_pause = True
+        elif hasattr(item, "skip_more_pause"):
+            pass
+        elif call.when == "setup":
+            if error:
+                item.skip_more_pause = True
+
+            # we can't asyncio.run() (which pause does) if we are unhsare_inline
+            # at this point, count on an autouse fixture to pause instead in this
+            # case
+            if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline:
+                pause_test(f"before test '{item.nodeid}'")
+
+        # check for a result to try and catch setup (or module setup) failure
+        # e.g., after a module level fixture fails, we do not want to pause on every
+        # skipped test.
+        elif call.when == "teardown" and call.excinfo:
+            logging.warning(
+                "Caught exception during teardown: %s\n:Traceback:\n%s",
+                call.excinfo,
+                "".join(traceback.format_tb(call.excinfo.tb)),
+            )
+            pause_test(f"after teardown after test '{item.nodeid}'")
+        elif call.when == "teardown" and call.result:
+            pause_test(f"after test '{item.nodeid}'")
+        elif error:
+            item.skip_more_pause = True
+            print(f"\nPAUSE-ON-ERROR: {call.excinfo.typename}")
+            print(f"PAUSE-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}")
+            if hasattr(exval, "stdout") and exval.stdout:
+                print("stdout: " + exval.stdout.replace("\n", "\nstdout: "))
+            if hasattr(exval, "stderr") and exval.stderr:
+                print("stderr: " + exval.stderr.replace("\n", "\nstderr: "))
+            pause_test(f"PAUSE-ON-ERROR: '{item.nodeid}'")
diff --git a/tests/topotests/munet/testing/util.py b/tests/topotests/munet/testing/util.py
new file mode 100644 (file)
index 0000000..a1a94bc
--- /dev/null
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 22 2022, Christian Hopps <chopps@gmail.com>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C
+#
+"""Utility functions useful when using munet testing functionailty in pytest."""
+import asyncio
+import datetime
+import functools
+import logging
+import sys
+import time
+
+from ..base import BaseMunet
+from ..cli import async_cli
+
+
+# =================
+# Utility Functions
+# =================
+
+
+async def async_pause_test(desc=""):
+    isatty = sys.stdout.isatty()
+    if not isatty:
+        desc = f" for {desc}" if desc else ""
+        logging.info("NO PAUSE on non-tty terminal%s", desc)
+        return
+
+    while True:
+        if desc:
+            print(f"\n== PAUSING: {desc} ==")
+        try:
+            user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ')
+        except EOFError:
+            print("^D...continuing")
+            break
+        user = user.strip()
+        if user == "cli":
+            await async_cli(BaseMunet.g_unet)
+        elif user == "pdb":
+            breakpoint()  # pylint: disable=W1515
+        elif user:
+            print(f'Unrecognized input: "{user}"')
+        else:
+            break
+
+
+def pause_test(desc=""):
+    asyncio.run(async_pause_test(desc))
+
+
+def retry(retry_timeout, initial_wait=0, expected=True):
+    """decorator: retry while functions return is not None or raises an exception.
+
+    * `retry_timeout`: Retry for at least this many seconds; after waiting
+                       initial_wait seconds
+    * `initial_wait`: Sleeps for this many seconds before first executing function
+    * `expected`: if False then the return logic is inverted, except for exceptions,
+                  (i.e., a non None ends the retry loop, and returns that value)
+    """
+
+    def _retry(func):
+        @functools.wraps(func)
+        def func_retry(*args, **kwargs):
+            retry_sleep = 2
+
+            # Allow the wrapped function's args to override the fixtures
+            _retry_timeout = kwargs.pop("retry_timeout", retry_timeout)
+            _expected = kwargs.pop("expected", expected)
+            _initial_wait = kwargs.pop("initial_wait", initial_wait)
+            retry_until = datetime.datetime.now() + datetime.timedelta(
+                seconds=_retry_timeout + _initial_wait
+            )
+
+            if initial_wait > 0:
+                logging.info("Waiting for [%s]s as initial delay", initial_wait)
+                time.sleep(initial_wait)
+
+            while True:
+                seconds_left = (retry_until - datetime.datetime.now()).total_seconds()
+                try:
+                    ret = func(*args, **kwargs)
+                    if _expected and ret is None:
+                        logging.debug("Function succeeds")
+                        return ret
+                    logging.debug("Function returned %s", ret)
+                except Exception as error:
+                    logging.info("Function raised exception: %s", str(error))
+                    ret = error
+
+                if seconds_left < 0:
+                    logging.info("Retry timeout of %ds reached", _retry_timeout)
+                    if isinstance(ret, Exception):
+                        raise ret
+                    return ret
+
+                logging.info(
+                    "Sleeping %ds until next retry with %.1f retry time left",
+                    retry_sleep,
+                    seconds_left,
+                )
+                time.sleep(retry_sleep)
+
+        func_retry._original = func  # pylint: disable=W0212
+        return func_retry
+
+    return _retry
index 6c7cb9624082a1f1d6a40204b8d10f9210135dd2..e6e5733010fbcab259ad268fca5b039c7776c7d1 100644 (file)
@@ -1,35 +1,35 @@
-debug ospf6 lsa all
-debug ospf6 message all
-debug ospf6 route all
-debug ospf6 spf time
-debug ospf6 spf database
-debug ospf6 zebra send
-debug ospf6 zebra recv
-
-debug ospf6 lsa router
-debug ospf6 lsa router originate
-debug ospf6 lsa router examine
-debug ospf6 lsa router flooding
-debug ospf6 lsa as-external
-debug ospf6 lsa as-external originate
-debug ospf6 lsa as-external examine
-debug ospf6 lsa as-external flooding
-debug ospf6 lsa intra-prefix
-debug ospf6 lsa intra-prefix originate
-debug ospf6 lsa intra-prefix examine
-debug ospf6 lsa intra-prefix flooding
-debug ospf6 border-routers
-debug ospf6 zebra
-debug ospf6 interface
-debug ospf6 neighbor
-debug ospf6 flooding
-debug ospf6 gr helper
-debug ospf6 spf process
-debug ospf6 route intra-area
-debug ospf6 route inter-area
-debug ospf6 abr
-debug ospf6 asbr
-debug ospf6 nssa
+!debug ospf6 lsa all
+!debug ospf6 message all
+!debug ospf6 route all
+!debug ospf6 spf time
+!debug ospf6 spf database
+!debug ospf6 zebra send
+!debug ospf6 zebra recv
+!
+!debug ospf6 lsa router
+!debug ospf6 lsa router originate
+!debug ospf6 lsa router examine
+!debug ospf6 lsa router flooding
+!debug ospf6 lsa as-external
+!debug ospf6 lsa as-external originate
+!debug ospf6 lsa as-external examine
+!debug ospf6 lsa as-external flooding
+!debug ospf6 lsa intra-prefix
+!debug ospf6 lsa intra-prefix originate
+!debug ospf6 lsa intra-prefix examine
+!debug ospf6 lsa intra-prefix flooding
+!debug ospf6 border-routers
+!debug ospf6 zebra
+!debug ospf6 interface
+!debug ospf6 neighbor
+!debug ospf6 flooding
+!debug ospf6 gr helper
+!debug ospf6 spf process
+!debug ospf6 route intra-area
+!debug ospf6 route inter-area
+!debug ospf6 abr
+!debug ospf6 asbr
+!debug ospf6 nssa
 !
 interface r1-eth0
  ipv6 ospf6 area 0
index 126934c344e0f425a57c396f7c40c216112c8260..54863382b2bcf3ec7018bf6b0f5c3687eb34831e 100644 (file)
@@ -18,7 +18,7 @@
                     "ospf": {
                         "area": "0.0.0.3",
                         "hello_interval": 1,
-                        "dead_interval": 4,
+                        "dead_interval": 10,
                         "priority": 98
                     }
                 },
@@ -27,7 +27,7 @@
                     "ospf": {
                         "area": "0.0.0.3",
                         "hello_interval": 1,
-                        "dead_interval": 4,
+                        "dead_interval": 10,
                         "priority": 99
                     }
                 },
@@ -36,7 +36,7 @@
                     "ospf": {
                         "area": "0.0.0.3",
                         "hello_interval": 1,
-                        "dead_interval": 4,
+                        "dead_interval": 10,
                         "priority": 0
                     }
                 },
@@ -45,7 +45,7 @@
                     "ospf": {
                         "area": "0.0.0.3",
                         "hello_interval": 1,
-                        "dead_interval": 4,
+                        "dead_interval": 10,
                         "priority": 0
                     }
                 }
             }
         }
     }
-}
\ No newline at end of file
+}
index 3d15e94a51e0bfcbd8d176a5ce128cf606521cc7..9d7a15833c16b3eb487f19dad13f8d0c86c61970 100644 (file)
@@ -140,7 +140,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -260,11 +260,9 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf": {
@@ -290,7 +288,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -306,7 +304,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Change the summary address mask to lower match (ex - 16 to 8)")
     ospf_summ_r1 = {
@@ -334,7 +332,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Verify that external routes(static / connected) are summarised"
@@ -350,7 +348,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Change the summary address mask to higher match (ex - 8 to 24)")
     ospf_summ_r1 = {
@@ -378,7 +376,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Verify that external routes(static / connected) are summarised"
@@ -399,7 +397,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step(" Un configure one of the summary address.")
     ospf_summ_r1 = {
@@ -432,7 +430,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     ospf_summ_r1 = {
         "r0": {
@@ -459,7 +457,7 @@ def test_ospf_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -506,11 +504,9 @@ def test_ospf_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r1 = {
         "r0": {
@@ -538,7 +534,7 @@ def test_ospf_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -554,26 +550,26 @@ def test_ospf_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step(
         "Configure route map and & rule to permit configured summary address,"
@@ -635,7 +631,7 @@ def test_ospf_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     input_dict = {
         SUMMARY["ipv4"][0]: {
@@ -650,7 +646,7 @@ def test_ospf_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -697,7 +693,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step(
         "Configure External Route summary in R0 to summarise 5"
@@ -731,7 +727,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -747,26 +743,26 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -789,19 +785,17 @@ def test_ospf_type5_summary_tc42_p0(request):
     step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Summary Route should not present in RIB"
+        "Error: Summary Route still present in RIB".format(tc_name)
     )
 
     step("show ip ospf summary should not have any summary address.")
@@ -816,9 +810,10 @@ def test_ospf_type5_summary_tc42_p0(request):
     }
     dut = "r0"
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Summary Route should not present in OSPF DB"
+        "Error: Summary still present in DB".format(tc_name)
+    )
 
     dut = "r1"
     step("All 5 routes are advertised after deletion of configured summary.")
@@ -829,7 +824,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("configure the summary again and delete static routes .")
     ospf_summ_r1 = {
@@ -857,7 +852,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     input_dict = {
         "r0": {
@@ -874,18 +869,18 @@ def test_ospf_type5_summary_tc42_p0(request):
 
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Add back static routes.")
     input_dict_static_rtes = {
@@ -901,18 +896,18 @@ def test_ospf_type5_summary_tc42_p0(request):
     dut = "r1"
 
     result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
     dut = "r1"
@@ -923,7 +918,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show configure summaries.")
 
@@ -940,7 +935,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure new static route which is matching configured summary.")
     input_dict_static_rtes = {
@@ -1004,7 +999,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Shut one of the interface")
     intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
@@ -1076,7 +1071,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
 
@@ -1087,7 +1082,7 @@ def test_ospf_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     ospf_summ_r1 = {
         "r0": {
@@ -1113,18 +1108,18 @@ def test_ospf_type5_summary_tc42_p0(request):
 
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     ospf_summ_r1 = {
         "r0": {
@@ -1188,11 +1183,9 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf": {
@@ -1224,7 +1217,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1240,7 +1233,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -1263,19 +1256,17 @@ def test_ospf_type5_summary_tc45_p0(request):
     step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Summary Routes should not be present in RIB. \n"
+        "Error: Summary Route still present in RIB".format(tc_name)
     )
 
     step("show ip ospf summary should not have any summary address.")
@@ -1292,7 +1283,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary still present in DB".format(tc_name)
 
     step("Configure Min tag value")
     ospf_summ_r1 = {
@@ -1317,7 +1308,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1333,7 +1324,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure Max Tag Value")
     ospf_summ_r1 = {
@@ -1363,7 +1354,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1382,7 +1373,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("configure new static route with different tag.")
     input_dict_static_rtes_11 = {
@@ -1403,10 +1394,9 @@ def test_ospf_type5_summary_tc45_p0(request):
     dut = "r1"
 
     result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
@@ -1418,9 +1408,10 @@ def test_ospf_type5_summary_tc45_p0(request):
         tag="88888",
         expected=False,
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1439,7 +1430,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary address")
     ospf_summ_r1 = {
@@ -1470,24 +1461,24 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that summary address is flushed from neighbor.")
 
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Configure summary first & then configure matching static route.")
 
@@ -1589,7 +1580,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -1619,11 +1610,9 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf": {
@@ -1655,7 +1644,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1691,19 +1680,17 @@ def test_ospf_type5_summary_tc45_p0(request):
     step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB "
+        "Error: Summary Route still present in RIB".format(tc_name)
     )
 
     step("show ip ospf summary should not have any summary address.")
@@ -1720,7 +1707,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary still present in DB".format(tc_name)
 
     step("Configure Min tag value")
     ospf_summ_r1 = {
@@ -1745,7 +1732,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1761,7 +1748,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure Max Tag Value")
     ospf_summ_r1 = {
@@ -1791,7 +1778,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1810,7 +1797,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("configure new static route with different tag.")
     input_dict_static_rtes_11 = {
@@ -1831,10 +1818,9 @@ def test_ospf_type5_summary_tc45_p0(request):
     dut = "r1"
 
     result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
@@ -1846,9 +1832,10 @@ def test_ospf_type5_summary_tc45_p0(request):
         tag="88888",
         expected=False,
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1867,7 +1854,7 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary address")
     ospf_summ_r1 = {
@@ -1898,24 +1885,24 @@ def test_ospf_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that summary address is flushed from neighbor.")
 
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Configure summary first & then configure matching static route.")
 
@@ -1999,7 +1986,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step(
         "Configure External Route summary in R0 to summarise 5"
@@ -2031,20 +2018,20 @@ def test_ospf_type5_summary_tc46_p0(request):
     dut = "r1"
 
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB."
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
-    step("Verify that show ip ospf summary should show the " "configured summaries.")
+    step("Verify that show ip ospf summary should show the  configured summaries.")
     input_dict = {
         SUMMARY["ipv4"][0]: {
             "summaryAddress": SUMMARY["ipv4"][0],
@@ -2055,7 +2042,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -2080,10 +2067,9 @@ def test_ospf_type5_summary_tc46_p0(request):
     step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
@@ -2091,9 +2077,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
-    )
+    ), "Testcase {} : Failed. Error: Summary Route still present in RIB".format(tc_name)
 
     step("show ip ospf summary should not have any summary address.")
     input_dict = {
@@ -2109,7 +2093,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary still present in DB".format(tc_name)
 
     step("Reconfigure summary with no advertise.")
     ospf_summ_r1 = {
@@ -2138,20 +2122,20 @@ def test_ospf_type5_summary_tc46_p0(request):
     dut = "r1"
 
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
-    step("Verify that show ip ospf summary should show the " "configured summaries.")
+    step("Verify that show ip ospf summary should show the  configured summaries.")
     input_dict = {
         SUMMARY["ipv4"][0]: {
             "summaryAddress": SUMMARY["ipv4"][0],
@@ -2162,7 +2146,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Change summary address from no advertise to advertise "
@@ -2211,7 +2195,7 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2227,26 +2211,26 @@ def test_ospf_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB"
+        "Error: Routes is present in RIB".format(tc_name)
+    )
 
     write_test_footer(tc_name)
 
@@ -2293,11 +2277,9 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r1 = {
         "r0": {
@@ -2325,7 +2307,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2341,26 +2323,26 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step(
         "configure route map and add rule to permit configured static "
@@ -2423,7 +2405,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     input_dict = {
         SUMMARY["ipv4"][0]: {
@@ -2461,18 +2443,18 @@ def test_ospf_type5_summary_tc47_p0(request):
     step("Verify that advertised summary route is flushed from neighbor.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB\n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in RIB.\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Delete the configured route map.")
 
@@ -2511,7 +2493,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     input_dict = {
         SUMMARY["ipv4"][0]: {
@@ -2526,7 +2508,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Reconfigure the route map with denying configure summary address.")
 
@@ -2570,7 +2552,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Redistribute static/connected routes without route map.")
 
@@ -2606,7 +2588,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     input_dict = {
         SUMMARY["ipv4"][0]: {
@@ -2621,7 +2603,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Configure rule to deny all the routes in route map and configure"
@@ -2672,18 +2654,18 @@ def test_ospf_type5_summary_tc47_p0(request):
     step("Verify that no summary route is originated.")
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n  Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     routemaps = {
         "r0": {
@@ -2773,7 +2755,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2789,7 +2771,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Change route map rule for 1 of the routes to deny.")
     # Create ip prefix list
@@ -2822,7 +2804,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("add rule in route map to deny configured summary address.")
     # Create ip prefix list
@@ -2855,7 +2837,7 @@ def test_ospf_type5_summary_tc47_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -2995,7 +2977,7 @@ def test_ospf_type5_summary_tc51_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -3042,11 +3024,9 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r1 = {
         "r0": {
@@ -3074,7 +3054,7 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -3090,26 +3070,26 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Reload the FRR router")
     # stop/start -> restart FRR router and verify
@@ -3130,7 +3110,7 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -3146,26 +3126,26 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("Kill OSPFd daemon on R0.")
     kill_router_daemons(tgen, "r0", ["ospfd"])
@@ -3176,7 +3156,7 @@ def test_ospf_type5_summary_tc49_p2(request):
     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(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -3194,7 +3174,7 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -3210,26 +3190,26 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     step("restart zebrad")
     kill_router_daemons(tgen, "r0", ["zebra"])
@@ -3251,7 +3231,7 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -3267,26 +3247,26 @@ def test_ospf_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
     }
     dut = "r1"
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
-    assert (
-        result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
-        tc_name, result
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB. \n Error: "
+        "Routes still present in OSPF RIB {}".format(tc_name, result)
     )
 
     result = verify_rib(
         tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
     )
-    assert (
-        result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    assert result is not True, (
+        "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n"
+        "Error: Routes still present in RIB".format(tc_name)
+    )
 
     write_test_footer(tc_name)
 
index cff59c3a40618977497ab3218195db6c5848209d..603aeadb855b7735fae6109d403495aa9d3bcf7a 100644 (file)
@@ -132,7 +132,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -235,11 +235,9 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r0 = {
         "r0": {
@@ -259,9 +257,7 @@ def test_ospf_type5_summary_tc44_p0(request):
         "route is sent to R1."
     )
 
-    step(
-        "Configure summary & redistribute static/connected route with " "metric type 2"
-    )
+    step("Configure summary & redistribute static/connected route with  metric type 2")
 
     input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}}
     dut = "r1"
@@ -272,7 +268,7 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -288,7 +284,7 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Learn type 7 lsa from neighbours")
 
@@ -312,7 +308,7 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : FailedError: Routes is missing in RIB".format(tc_name)
 
     ospf_summ_r0 = {
         "r0": {
@@ -340,7 +336,7 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     step("Verify that already originated summary is intact.")
     input_dict = {
@@ -356,7 +352,7 @@ def test_ospf_type5_summary_tc44_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : FailedError: Summary missing in OSPF DB".format(tc_name)
 
     dut = "r1"
     aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}}
index 40df0b230861da07c5f49950784570769f772642..88219b84006e7612d3e694f151348dad084bc2f1 100644 (file)
@@ -100,7 +100,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -166,7 +166,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
     step("Verify that the neighbour is not FULL between R1 and R2.")
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -192,7 +192,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -223,7 +223,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
     ospf_covergence = verify_ospf_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=10
     )
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -245,7 +245,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -260,7 +260,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
         "show ip ospf neighbor cmd."
     )
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -274,7 +274,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -314,7 +314,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request):
 
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -361,7 +361,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
     ospf_covergence = verify_ospf_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=6
     )
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -393,7 +393,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -426,7 +426,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
     ospf_covergence = verify_ospf_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=10
     )
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -454,7 +454,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -469,7 +469,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
         "show ip ospf neighbor cmd."
     )
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -483,7 +483,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -528,7 +528,7 @@ def test_ospf_authentication_md5_tc29_p1(request):
 
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -576,7 +576,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
     ospf_covergence = verify_ospf_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=10
     )
-    assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -608,7 +608,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -655,7 +655,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -687,7 +687,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -720,7 +720,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -765,7 +765,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -810,7 +810,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
index 124b36a5fa34df6db8a369dd3c57b4052a730b5f..e58f081f96366790df597f63e3dbc532789b2819 100644 (file)
@@ -111,7 +111,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -177,7 +177,7 @@ def test_ospf_chaos_tc31_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -198,7 +198,7 @@ def test_ospf_chaos_tc31_p1(request):
     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(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -208,7 +208,7 @@ def test_ospf_chaos_tc31_p1(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -217,7 +217,7 @@ def test_ospf_chaos_tc31_p1(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -227,7 +227,7 @@ def test_ospf_chaos_tc31_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -250,7 +250,7 @@ def test_ospf_chaos_tc31_p1(request):
     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(
+    assert ospf_covergence is not True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -260,7 +260,7 @@ def test_ospf_chaos_tc31_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -316,7 +316,7 @@ def test_ospf_chaos_tc32_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -338,7 +338,7 @@ def test_ospf_chaos_tc32_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -361,7 +361,7 @@ def test_ospf_chaos_tc32_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -421,7 +421,7 @@ def test_ospf_chaos_tc34_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -444,7 +444,7 @@ def test_ospf_chaos_tc34_p1(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -453,7 +453,7 @@ def test_ospf_chaos_tc34_p1(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -463,7 +463,7 @@ def test_ospf_chaos_tc34_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -488,7 +488,7 @@ def test_ospf_chaos_tc34_p1(request):
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
index d58e2503edcb61a10a5a3f4c1149765c3f54658d..aba313db9f73fe8452f40f008dffb2cfe13b13a1 100644 (file)
@@ -114,7 +114,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -168,7 +168,7 @@ def test_ospf_ecmp_tc16_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -217,7 +217,7 @@ def test_ospf_ecmp_tc16_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -227,7 +227,7 @@ def test_ospf_ecmp_tc16_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -259,7 +259,7 @@ def test_ospf_ecmp_tc16_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -286,7 +286,7 @@ def test_ospf_ecmp_tc16_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -303,7 +303,7 @@ def test_ospf_ecmp_tc16_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -343,7 +343,7 @@ def test_ospf_ecmp_tc17_p0(request):
     step("Verify that OSPF is up with 2 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -394,7 +394,7 @@ def test_ospf_ecmp_tc17_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -411,7 +411,7 @@ def test_ospf_ecmp_tc17_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
index 6a565571be3f75c75c89731ae98a1abad77d60c6..1eeb23e9f7c5ea6b6bb6fd5bc8d3b76169b43580 100644 (file)
@@ -115,7 +115,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -169,7 +169,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -222,7 +222,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
 
     dut = "r0"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -231,7 +231,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -261,7 +261,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -278,7 +278,7 @@ def test_ospf_lan_ecmp_tc18_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
index 53b1be6d716d51771f7690e4fd695e537b84319d..1358027f219ad2350e0875b06e5e39a441edc079 100644 (file)
@@ -114,7 +114,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -165,9 +165,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "DR"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "DR"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -185,9 +185,9 @@ def test_ospf_lan_tc1_p0(request):
         "r1": {
             "ospf": {
                 "neighbors": {
-                    "r0": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r0": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -197,7 +197,8 @@ def test_ospf_lan_tc1_p0(request):
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
     step(
-        "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+        "Configure DR priority 100 on R0 and clear ospf neighbors "
+        "on all the routers."
     )
 
     input_dict = {
@@ -223,9 +224,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -235,7 +236,8 @@ def test_ospf_lan_tc1_p0(request):
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
     step(
-        "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers."
+        "Configure DR priority 150 on R0 and clear ospf neighbors "
+        "on all the routers."
     )
 
     input_dict = {
@@ -261,9 +263,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -297,9 +299,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "DR"},
-                    "r2": {"state": "2-Way", "role": "DROther"},
-                    "r3": {"state": "2-Way", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "DR"},
+                    "r2": {"nbrState": "2-Way", "role": "DROther"},
+                    "r3": {"nbrState": "2-Way", "role": "DROther"},
                 }
             }
         }
@@ -336,9 +338,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -355,7 +357,7 @@ def test_ospf_lan_tc1_p0(request):
     result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r0: OSPF neighbors-hip is up \n Error: {}".format(
         tc_name, result
     )
 
@@ -368,9 +370,9 @@ def test_ospf_lan_tc1_p0(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "DR"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "DR"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -423,9 +425,9 @@ def test_ospf_lan_tc1_p0(request):
         "r1": {
             "ospf": {
                 "neighbors": {
-                    "r0": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r0": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -449,9 +451,9 @@ def test_ospf_lan_tc1_p0(request):
         "r1": {
             "ospf": {
                 "neighbors": {
-                    "r0": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r0": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -491,7 +493,7 @@ def test_ospf_lan_tc2_p0(request):
                 "s1": {
                     "ospf": {
                         "priority": 98,
-                        "timerDeadSecs": 4,
+                        "timerDeadSecs": 10,
                         "area": "0.0.0.3",
                         "mcastMemberOspfDesignatedRouters": True,
                         "mcastMemberOspfAllRouters": True,
index 0c4697cc212cbd69dc343893f4fe4c555e457e65..d669e21d4d2ef37e49ca75ba1eff11de3781785f 100644 (file)
@@ -112,7 +112,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -220,11 +220,11 @@ def test_ospf_learning_tc15_p0(request):
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
-    step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).")
+    step("Change area 1 as non nssa area (on the fly changing area  type on DUT).")
 
     for rtr in ["r1", "r2", "r3"]:
         input_dict = {
index 858412f1d35a1747fac98a16000170d8d3535efa..4f797743e7f7b91d0263de42109f5afc6d23d128 100644 (file)
@@ -411,17 +411,17 @@ def test_ospf_nbrs(tgen):
                 "neighbors": {
                     "100.1.1.1": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.2": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.3": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                 }
@@ -434,17 +434,17 @@ def test_ospf_nbrs(tgen):
                 "neighbors": {
                     "100.1.1.0": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.2": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.3": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                 }
@@ -457,17 +457,17 @@ def test_ospf_nbrs(tgen):
                 "neighbors": {
                     "100.1.1.0": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.1": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.3": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                 }
@@ -480,17 +480,17 @@ def test_ospf_nbrs(tgen):
                 "neighbors": {
                     "100.1.1.0": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.1": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                     "100.1.1.2": [
                         {
-                            "state": "Full/DROther",
+                            "nbrState": "Full/DROther",
                         }
                     ],
                 }
index dad6d915e8efa04281f0336e2955358f7460b2bc..c9f43cdfe497711cc55f2e528c8e68c3b77936d6 100644 (file)
@@ -127,7 +127,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -201,9 +201,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
 
     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"
-    )
+    step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix &  deny 10.0.20.2/32")
 
     # Create ip prefix list
     pfx_list = {
@@ -294,7 +292,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -303,7 +301,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are present in fib \n Error: {}".format(
         tc_name, result
     )
 
@@ -347,7 +345,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -356,7 +354,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -404,7 +402,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -413,7 +411,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -464,7 +462,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, retry_timeout=4, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -479,7 +477,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -499,7 +497,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -508,7 +506,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -523,7 +521,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -532,7 +530,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -553,7 +551,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: OSPF routes are present \n Error: {}".format(
         tc_name, result
     )
 
@@ -562,7 +560,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+    ), "Testcase {} : Failed \n  r1: routes are still present \n Error: {}".format(
         tc_name, result
     )
 
@@ -861,7 +859,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request):
     result = verify_prefix_lists(tgen, pfx_list)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+    ), "Testcase {} : Failed \n Prefix list not  present. Error: {}".format(
         tc_name, result
     )
 
@@ -930,7 +928,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request):
     result = verify_prefix_lists(tgen, pfx_list)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+    ), "Testcase {} : Failed \n Prefix list not  present. Error: {}".format(
         tc_name, result
     )
 
@@ -1078,7 +1076,7 @@ def test_ospf_routemaps_functionality_tc25_p0(request):
 
     # 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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
index 63c421ec8456abc6877f678eaaf136a23f89e35b..f0950a2db33e401451a85579b9af1bb8be55c850 100644 (file)
@@ -123,7 +123,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -171,7 +171,7 @@ def test_ospf_redistribution_tc5_p0(request):
 
     step("Verify that OSPF neighbors are FULL.")
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -295,7 +295,7 @@ def test_ospf_redistribution_tc6_p0(request):
 
     step("Verify that OSPF neighbors are FULL.")
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -524,7 +524,7 @@ def test_ospf_redistribution_tc8_p1(request):
     step("Verify that OSPF neighbours are reset and forms new adjacencies.")
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -558,7 +558,7 @@ def test_ospf_rfc2328_appendinxE_p0(request):
     step("Verify that OSPF neighbours are Full.")
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
index 39bbab42e7d0e3ad6791f3e0ab266412737aa7c0..757d6fb1d58151be831feffcaaa4ca441b524551 100644 (file)
@@ -108,7 +108,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -358,7 +358,7 @@ def test_ospf_p2p_tc3_p0(request):
 
     # Api call verify whether BGP is converged
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -440,7 +440,7 @@ def test_ospf_hello_tc10_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -486,7 +486,7 @@ def test_ospf_hello_tc10_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -532,7 +532,7 @@ def test_ospf_hello_tc10_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -575,7 +575,7 @@ def test_ospf_hello_tc10_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -597,7 +597,7 @@ def test_ospf_show_p1(request):
     reset_config_on_routers(tgen)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
     dut = "r1"
@@ -690,7 +690,7 @@ def test_ospf_dead_tc11_p0(request):
     result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("modify dead interval from default value to r1" "dead interval timer on r2")
+    step("modify dead interval from default value to r1 dead interval timer on r2")
 
     topo1 = {
         "r0": {
@@ -714,11 +714,11 @@ def test_ospf_dead_tc11_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
-    step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+    step("reconfigure the default dead interval timer value to  default on r1 and r2")
     topo1 = {
         "r0": {
             "links": {
@@ -755,7 +755,7 @@ def test_ospf_dead_tc11_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -797,7 +797,7 @@ def test_ospf_dead_tc11_p0(request):
 
     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(
+    assert ospf_covergence is True, "Testcase Failed \n Error  {}".format(
         ospf_covergence
     )
 
@@ -835,9 +835,7 @@ def test_ospf_dead_tc11_p0(request):
     result = create_interfaces_cfg(tgen, topo1)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Verify that timer value is deleted from intf & " "set to default value 40 sec."
-    )
+    step("Verify that timer value is deleted from intf &  set to default value 40 sec.")
     input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}}
     dut = "r1"
     result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
@@ -883,18 +881,14 @@ def test_ospf_tc4_mtu_ignore_p0(request):
 
     clear_ospf(tgen, "r0")
 
-    step(
-        "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
-    )
+    step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart  State.")
     result = verify_ospf_neighbor(tgen, topo, expected=False)
     assert result is not True, (
         "Testcase {} : Failed \n OSPF nbrs are Full "
         "instead of Exstart. Error: {}".format(tc_name, result)
     )
 
-    step(
-        "Verify that configured MTU value is updated in the show ip " "ospf interface."
-    )
+    step("Verify that configured MTU value is updated in the show ip  ospf interface.")
 
     dut = "r0"
     input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}}
@@ -951,9 +945,7 @@ def test_ospf_tc4_mtu_ignore_p0(request):
 
     clear_ospf(tgen, "r0")
 
-    step(
-        "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
-    )
+    step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart  State.")
     result = verify_ospf_neighbor(tgen, topo, expected=False)
     assert result is not True, (
         "Testcase {} : Failed \n OSPF nbrs are Full "
@@ -970,9 +962,7 @@ def test_ospf_tc4_mtu_ignore_p0(request):
     result = verify_ospf_neighbor(tgen, topo)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
-    )
+    step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.")
 
     rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf))
     rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf))
index 1c265962304dc091283bcc368dc0df08bd10c2bd..79374281cb7a3ff289c792186f888be22c9b7089 100644 (file)
@@ -119,7 +119,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -182,20 +182,20 @@ def test_ospf_gr_helper_tc1_p0(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
 
-    step("Verify that GR helper route is disabled by default to the in" "the DUT.")
+    step("Verify that GR helper route is disabled by default to the in the DUT.")
     input_dict = {
         "helperSupport": "Disabled",
         "strictLsaCheck": "Enabled",
-        "restartSupoort": "Planned and Unplanned Restarts",
+        "restartSupport": "Planned and Unplanned Restarts",
         "supportedGracePeriod": 1800,
     }
     dut = "r0"
     result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.")
+    step("Verify that DUT does not enter helper mode upon receiving the  grace lsa.")
 
     # send grace lsa
     scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
@@ -205,7 +205,7 @@ def test_ospf_gr_helper_tc1_p0(request):
     result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+    ), "Testcase {} : Failed. DUT entered helper role   \n Error: {}".format(
         tc_name, result
     )
 
@@ -220,7 +220,7 @@ def test_ospf_gr_helper_tc1_p0(request):
     input_dict = {
         "helperSupport": "Enabled",
         "strictLsaCheck": "Enabled",
-        "restartSupoort": "Planned and Unplanned Restarts",
+        "restartSupport": "Planned and Unplanned Restarts",
         "supportedGracePeriod": 1800,
     }
     dut = "r0"
@@ -234,7 +234,7 @@ def test_ospf_gr_helper_tc1_p0(request):
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
     step("Perform GR in RR.")
-    step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+    step("Verify that DUT does enter helper mode upon receiving  the grace lsa.")
     input_dict = {"activeRestarterCnt": 1}
     gracelsa_sent = False
     repeat = 0
@@ -277,7 +277,7 @@ def test_ospf_gr_helper_tc1_p0(request):
     result = create_router_ospf(tgen, topo, ospf_gr_r0)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+    step("Verify that DUT does enter helper mode upon receiving  the grace lsa.")
     input_dict = {"activeRestarterCnt": 1}
     gracelsa_sent = False
     repeat = 0
@@ -306,7 +306,7 @@ def test_ospf_gr_helper_tc1_p0(request):
     result = create_router_ospf(tgen, topo, ospf_gr_r0)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x")
+    step("Verify that GR helper router is disabled in the DUT for  router id x.x.x.x")
     input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]}
     dut = "r0"
     result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
@@ -343,7 +343,7 @@ def test_ospf_gr_helper_tc2_p0(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
     ospf_gr_r0 = {
         "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
     }
index a3ccb58d38e5726418059b8992773a5cfc695d51..46c0da309f7bae08ec5d15825f4b55b72047b924 100644 (file)
@@ -119,7 +119,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -188,10 +188,10 @@ def test_ospf_gr_helper_tc3_p1(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
-    step(
-        "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
-    )
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
+
+    step("Configure DR priority 100 on R0 and clear ospf neighbors "
+         "on all the routers.")
 
     input_dict = {
         "r0": {
@@ -216,9 +216,9 @@ def test_ospf_gr_helper_tc3_p1(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "Backup"},
-                    "r2": {"state": "Full", "role": "DROther"},
-                    "r3": {"state": "Full", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "Backup"},
+                    "r2": {"nbrState": "Full", "role": "DROther"},
+                    "r3": {"nbrState": "Full", "role": "DROther"},
                 }
             }
         }
@@ -282,10 +282,8 @@ def test_ospf_gr_helper_tc4_p1(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
-    step(
-        "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
-    )
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
+    step("Configure DR priority 0 on R0 and clear ospf neighbors  on all the routers.")
 
     input_dict = {
         "r0": {
@@ -310,9 +308,9 @@ def test_ospf_gr_helper_tc4_p1(request):
         "r0": {
             "ospf": {
                 "neighbors": {
-                    "r1": {"state": "Full", "role": "DR"},
-                    "r2": {"state": "2-Way", "role": "DROther"},
-                    "r3": {"state": "2-Way", "role": "DROther"},
+                    "r1": {"nbrState": "Full", "role": "DR"},
+                    "r2": {"nbrState": "2-Way", "role": "DROther"},
+                    "r3": {"nbrState": "2-Way", "role": "DROther"},
                 }
             }
         }
index 64aac2fba8abf5bdac25458277beae5a507075cf..3be28196d884f9eb0211d7fed03c5797d4968102 100644 (file)
@@ -119,7 +119,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -193,7 +193,7 @@ def test_ospf_gr_helper_tc7_p1(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
     ospf_gr_r0 = {
         "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
     }
@@ -221,7 +221,7 @@ def test_ospf_gr_helper_tc7_p1(request):
     result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+    ), "Testcase {} : Failed. DUT entered helper role   \n Error: {}".format(
         tc_name, result
     )
 
@@ -253,7 +253,7 @@ def test_ospf_gr_helper_tc8_p1(request):
     ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
     assert (
         ospf_covergence is True
-    ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+    ), "OSPF is not after reset config \n Error:  {}".format(ospf_covergence)
     ospf_gr_r0 = {
         "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
     }
index f82758101c0b480dde1eb749ecd0cb73ecaf31ca..0212f9da9cda101232ae34946c08556191fc76a4 100644 (file)
@@ -3,7 +3,7 @@
     "2.2.2.2":[
       {
         "converged":"Full",
-        "address":"10.0.1.2",
+        "ifaceAddress":"10.0.1.2",
         "ifaceName":"eth-rt2:10.0.1.1"
       }
     ]
index 5a0b0927020483ab95cd8ba1dbb0410d7a34234e..31146604836e711fe2633c4df4d9315e55bd415f 100644 (file)
@@ -3,14 +3,14 @@
     "1.1.1.1":[
       {
         "converged":"Full",
-        "address":"10.0.1.1",
+        "ifaceAddress":"10.0.1.1",
         "ifaceName":"eth-rt1:10.0.1.2"
       }
     ],
     "3.3.3.3":[
       {
         "converged":"Full",
-        "address":"10.0.2.3",
+        "ifaceAddress":"10.0.2.3",
         "ifaceName":"eth-rt3:10.0.2.2"
       }
     ]
index ab5e78414d1c1dcd5465c731388e405872a97712..49a019d36d28513ad5107d0af3c912f8453de126 100644 (file)
@@ -3,21 +3,21 @@
     "2.2.2.2":[
       {
         "converged":"Full",
-        "address":"10.0.2.2",
+        "ifaceAddress":"10.0.2.2",
         "ifaceName":"eth-rt2:10.0.2.3"
       }
     ],
     "4.4.4.4":[
       {
         "converged":"Full",
-        "address":"10.0.3.4",
+        "ifaceAddress":"10.0.3.4",
         "ifaceName":"eth-rt4:10.0.3.3"
       }
     ],
     "6.6.6.6":[
       {
         "converged":"Full",
-        "address":"10.0.4.6",
+        "ifaceAddress":"10.0.4.6",
         "ifaceName":"eth-rt6:10.0.4.3"
       }
     ]
index 405679c10e986cc971ab668285c9212f6726fea7..9ab49d7266fd61b9c2687e3d89a35c047dfe837e 100644 (file)
@@ -3,14 +3,14 @@
     "3.3.3.3":[
       {
         "converged":"Full",
-        "address":"10.0.3.3",
+        "ifaceAddress":"10.0.3.3",
         "ifaceName":"eth-rt3:10.0.3.4"
       }
     ],
     "5.5.5.5":[
       {
         "converged":"Full",
-        "address":"10.0.5.5",
+        "ifaceAddress":"10.0.5.5",
         "ifaceName":"eth-rt5:10.0.5.4"
       }
     ]
index 893d454368539b4e72c71f5b934e52dd9f4431a3..7d3d589772732714bdda17a5d15342c7f5ddf23b 100644 (file)
@@ -3,7 +3,7 @@
     "4.4.4.4":[
       {
         "converged":"Full",
-        "address":"10.0.5.4",
+        "ifaceAddress":"10.0.5.4",
         "ifaceName":"eth-rt4:10.0.5.5"
       }
     ]
index 564a513ac6e94ce06cb294b700c9a772f310be22..506eb4086bb4ba0810e9eac9bad10e74e9725ee3 100644 (file)
@@ -3,14 +3,14 @@
     "3.3.3.3":[
       {
         "converged":"Full",
-        "address":"10.0.4.3",
+        "ifaceAddress":"10.0.4.3",
         "ifaceName":"eth-rt3:10.0.4.6"
       }
     ],
     "7.7.7.7":[
       {
         "converged":"Full",
-        "address":"10.0.6.7",
+        "ifaceAddress":"10.0.6.7",
         "ifaceName":"eth-rt7:10.0.6.6"
       }
     ]
index bc6b60697c703e58dd393cdae5a710e509c16306..6429148004038975620dcd5f79f098223e52219a 100644 (file)
@@ -3,7 +3,7 @@
     "6.6.6.6":[
       {
         "converged":"Full",
-        "address":"10.0.6.6",
+        "ifaceAddress":"10.0.6.6",
         "ifaceName":"eth-rt6:10.0.6.7"
       }
     ]
diff --git a/tests/topotests/ospf_metric_propagation/__init__.py b/tests/topotests/ospf_metric_propagation/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/tests/topotests/ospf_metric_propagation/h1/frr.conf b/tests/topotests/ospf_metric_propagation/h1/frr.conf
new file mode 100644 (file)
index 0000000..1196a19
--- /dev/null
@@ -0,0 +1,10 @@
+!
+hostname h1
+password zebra
+log file /tmp/h1-frr.log
+!
+ip route 0.0.0.0/0 10.0.91.1
+!
+interface h1-eth0
+ ip address 10.0.91.2/24
+!
\ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/h2/frr.conf b/tests/topotests/ospf_metric_propagation/h2/frr.conf
new file mode 100644 (file)
index 0000000..f951fe6
--- /dev/null
@@ -0,0 +1,10 @@
+!
+hostname h2
+password zebra
+log file /tmp/h2-frr.log
+!
+ip route 0.0.0.0/0 10.0.94.4
+!
+interface h2-eth0
+ ip address 10.0.94.2/24
+!
\ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf
new file mode 100644 (file)
index 0000000..8523049
--- /dev/null
@@ -0,0 +1,96 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+ip forwarding
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r1-eth1 vrf blue
+ ip address 10.0.10.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+interface r1-eth2 vrf green
+ ip address 10.0.91.1/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+!
+router ospf
+  ospf router-id 10.0.255.1
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.1.0/24 area 0
+!
+router ospf vrf blue
+  ospf router-id 10.0.255.1
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.10.0/24 area 0
+!
+router ospf vrf green
+  ospf router-id 10.0.255.1
+    distance 20
+  redistribute bgp route-map costplus
+  network 10.0.91.0/24 area 0
+!
+router bgp 99
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf route-map rmap
+    import vrf route-map rmap
+    import vrf blue
+    import vrf green
+  !
+!
+router bgp 99 vrf blue
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf route-map rmap
+    import vrf route-map rmap
+    import vrf default
+    import vrf green
+  !
+router bgp 99 vrf green
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf blue
+  !
+!
+route-map rmap permit 10
+  set metric-type type-1
+  set metric +1
+  exit
+!
+ip prefix-list min seq 5 permit 10.0.80.0/24
+route-map costmax permit 20
+  set metric-type type-1
+  set metric +1
+  set metric-min 713
+  match ip address prefix-list min
+  exit
+!
+ip prefix-list max seq 10 permit 10.0.70.0/24
+route-map costplus permit 30
+  set metric-type type-1
+  set metric +1
+  set metric-max 13
+  match ip address prefix-list max
+  exit
+!
+route-map costplus permit 40
+  set metric-type type-1
+  set metric +1
+  exit
\ No newline at end of file
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
new file mode 100644 (file)
index 0000000..e3a5cc4
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":34,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.10.5",
+          "afi":"ipv4",
+          "interfaceIndex":6,
+          "interfaceName":"r1-eth1",
+          "vrf":"blue",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
new file mode 100644 (file)
index 0000000..f3597bf
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":136,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.1.2",
+          "afi":"ipv4",
+          "interfaceIndex":5,
+          "interfaceName":"r1-eth0",
+          "vrf":"default",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
new file mode 100644 (file)
index 0000000..eebcab8
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":1138,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.1.2",
+          "afi":"ipv4",
+          "interfaceIndex":5,
+          "interfaceName":"r1-eth0",
+          "vrf":"default",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
new file mode 100644 (file)
index 0000000..d0e3d81
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":1218,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.1.2",
+          "afi":"ipv4",
+          "interfaceIndex":5,
+          "interfaceName":"r1-eth0",
+          "vrf":"default",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
new file mode 100644 (file)
index 0000000..989ccf7
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":238,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.1.2",
+          "afi":"ipv4",
+          "interfaceIndex":5,
+          "interfaceName":"r1-eth0",
+          "vrf":"default",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
new file mode 100644 (file)
index 0000000..84b1188
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "10.0.94.0/24":[
+    {
+      "prefix":"10.0.94.0/24",
+      "prefixLen":24,
+      "protocol":"bgp",
+      "vrfId":9,
+      "vrfName":"green",
+      "selected":true,
+      "destSelected":true,
+      "distance":20,
+      "metric":136,
+      "installed":true,
+      "table":12,
+      "internalStatus":16,
+      "internalFlags":8,
+      "internalNextHopNum":1,
+      "internalNextHopActiveNum":1,
+      "nexthopGroupId":"*",
+      "installedNexthopGroupId":"*",
+      "uptime":"*",
+      "nexthops":[
+        {
+          "flags":3,
+          "fib":true,
+          "ip":"10.0.10.5",
+          "afi":"ipv4",
+          "interfaceIndex":6,
+          "interfaceName":"r1-eth1",
+          "vrf":"blue",
+          "active":true,
+          "weight":1
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf
new file mode 100644 (file)
index 0000000..e67a374
--- /dev/null
@@ -0,0 +1,81 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-frr.log
+ip forwarding
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth1 vrf blue
+ ip address 10.0.20.2/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r2-eth2 vrf green
+ ip address 10.0.70.2/24
+ ip ospf cost 1000
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.2
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.1.0/24 area 0
+!
+router ospf vrf blue
+  ospf router-id 10.0.255.2
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.20.0/24 area 0
+!
+
+router ospf vrf green
+  ospf router-id 10.0.255.2
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.70.0/24 area 0
+!
+
+router bgp 99
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf blue
+    import vrf green
+  !
+!
+router bgp 99 vrf blue
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf green
+  !
+router bgp 99 vrf green
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf blue
+  !
+!
+route-map rmap permit 10
+  set metric-type type-1
+  set metric +1
+  exit
+!
+route-map costplus permit 1
+  set metric-type type-1
+  set metric +1
+  exit
diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf
new file mode 100644 (file)
index 0000000..175851d
--- /dev/null
@@ -0,0 +1,79 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-frr.log
+ip forwarding
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r3-eth1 vrf blue
+ ip address 10.0.30.3/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r3-eth2 vrf green
+ ip address 10.0.80.3/24
+ ip ospf cost 1000
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.3
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.3.0/24 area 0
+!
+router ospf vrf blue
+  ospf router-id 10.0.255.3
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.30.0/24 area 0
+!
+router ospf vrf green
+  ospf router-id 10.0.255.3
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.80.0/24 area 0
+!
+router bgp 99
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf blue
+    import vrf green
+  !
+!
+router bgp 99 vrf blue
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf green
+  !
+router bgp 99 vrf green
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf blue
+  !
+!
+route-map rmap permit 10
+  set metric-type type-1
+  set metric +1
+  exit
+!
+route-map costplus permit 1
+  set metric-type type-1
+  set metric +1
+  exit
diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf
new file mode 100644 (file)
index 0000000..70a47e3
--- /dev/null
@@ -0,0 +1,78 @@
+!
+hostname r4
+password zebra
+log file /tmp/r4-frr.log
+ip forwarding
+!
+interface r4-eth0
+ ip address 10.0.3.4/24
+ ip ospf cost 100
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r4-eth1 vrf blue
+ ip address 10.0.40.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface r4-eth2 vrf green
+ ip address 10.0.94.4/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.4
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.3.0/24 area 0
+!
+router ospf vrf blue
+  ospf router-id 10.0.255.4
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.40.0/24 area 0
+!
+router ospf vrf green
+  ospf router-id 10.0.255.1
+  distance 20
+  redistribute bgp route-map costplus
+  network 10.0.94.0/24 area 0
+!
+router bgp 99
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf route-map costplus
+    import vrf route-map rmap
+    import vrf blue
+    import vrf green
+  !
+!
+router bgp 99 vrf blue
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf green
+  !
+router bgp 99 vrf green
+  no bgp ebgp-requires-policy
+  address-family ipv4 unicast
+    redistribute connected
+    redistribute ospf
+    import vrf route-map rmap
+    import vrf default
+    import vrf blue
+  !
+!
+route-map rmap permit 10
+  set metric-type type-1
+  set metric +1
+  exit
+!
+route-map costplus permit 1
+  set metric-type type-1
+  set metric +1
+  exit
diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf
new file mode 100644 (file)
index 0000000..7be9e5c
--- /dev/null
@@ -0,0 +1,27 @@
+!
+hostname ra
+password zebra
+log file /tmp/ra-frr.log
+ip forwarding
+!
+interface ra-eth0
+ ip address 10.0.50.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface ra-eth1
+ ip address 10.0.10.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface ra-eth2
+ ip address 10.0.20.5/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.5
+  network 10.0.10.0/24 area 0
+  network 10.0.20.0/24 area 0
+  network 10.0.50.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf
new file mode 100644 (file)
index 0000000..a7dbf82
--- /dev/null
@@ -0,0 +1,27 @@
+!
+hostname rb
+password zebra
+log file /tmp/rb-frr.log
+ip forwarding
+!
+interface rb-eth0
+ ip address 10.0.50.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rb-eth1
+ ip address 10.0.30.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rb-eth2
+ ip address 10.0.40.6/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.6
+  network 10.0.30.0/24 area 0
+  network 10.0.40.0/24 area 0
+  network 10.0.50.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf
new file mode 100644 (file)
index 0000000..f5a2ed7
--- /dev/null
@@ -0,0 +1,21 @@
+!
+hostname rc
+password zebra
+log file /tmp/rc-frr.log
+ip forwarding
+!
+interface rc-eth0
+ ip address 10.0.70.7/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+interface rc-eth1
+ ip address 10.0.80.7/24
+ ip ospf hello-interval 1
+ ip ospf dead-interval 30
+!
+router ospf
+  ospf router-id 10.0.255.7
+  network 10.0.70.0/24 area 0
+  network 10.0.80.0/24 area 0
+!
diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
new file mode 100644 (file)
index 0000000..4d78bd2
--- /dev/null
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_metric_propagation.py
+#
+# Copyright (c) 2023 ATCorp
+# Jafar Al-Gharaibeh
+#
+
+import os
+import sys
+import json
+from time import sleep
+from functools import partial
+import pytest
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+"""
+test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation
+"""
+
+TOPOLOGY = """
+                                      +-----+                           +-----+
+                                 eth1 |     |           eth0            |     | eth2
+                        +-------------+ rA  +---------------------------+ rB  +---------------+
+                        |          .5 |     | .5                     .6 |     | .6            |
+                        |             +--+--+     10.0.50.0/24          +--+--+ .6            |
+                        |                |.5                               |.6                |
+                        |            eth2|                             eth1|                  |
+                 10.0.10.0/24            |                                 |                  |
+                        |            10.0.20.0/24                   10.0.30.0/24          10.0.40.0/24
+                        |blue            |blue                             |blue              |blue
+                        |                |                                 |                  |
+                    eth1|.1          eth1|.2                           eth1|.3            eth1|.4
+    +-----+          +--+--+          +--+--+           +-----+          +-+---+            +-+---+         +------+
+    |     |eth0  eth2|     |   eth0   |     |eth2   eth1|     |eth2  eth3|     |   eth0     |     |eth2 eth0|      |
+    | h1  +----------+ R1  +----------+ R2  +-----------+ rC  +----------+ R3  +------------+ R4  +---------+ h2   |
+    |     |          |     |          |     |           |     |          |     |            |     |         |      |
+    +-----+.2     .1 +-----+.1      .2+-----+.2      .7 +-----+.7      .3+-----+.3        .4+-----+.4     .2+------+
+                  green                    green                      green                       green
+
+       10.0.91.0/24        10.0.1.0/24      10.0.70.0/24      10.0.80.0/24     10.0.3.0/24        10.0.94.0/24
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+    "Build function"
+
+    # Create 4 routers
+    for routern in range(1, 5):
+        tgen.add_router("r{}".format(routern))
+
+    tgen.add_router("ra")
+    tgen.add_router("rb")
+    tgen.add_router("rc")
+    tgen.add_router("h1")
+    tgen.add_router("h2")
+
+    # Interconect router 1, 2
+    switch = tgen.add_switch("s1-2")
+    switch.add_link(tgen.gears["r1"])
+    switch.add_link(tgen.gears["r2"])
+
+    # Interconect router 3, 4
+    switch = tgen.add_switch("s3-4")
+    switch.add_link(tgen.gears["r3"])
+    switch.add_link(tgen.gears["r4"])
+
+    # Interconect router a, b
+    switch = tgen.add_switch("sa-b")
+    switch.add_link(tgen.gears["ra"])
+    switch.add_link(tgen.gears["rb"])
+
+    # Interconect router 1, a
+    switch = tgen.add_switch("s1-a")
+    switch.add_link(tgen.gears["r1"])
+    switch.add_link(tgen.gears["ra"])
+
+    # Interconect router 2, a
+    switch = tgen.add_switch("s2-a")
+    switch.add_link(tgen.gears["r2"])
+    switch.add_link(tgen.gears["ra"])
+
+    # Interconect router 3, b
+    switch = tgen.add_switch("s3-b")
+    switch.add_link(tgen.gears["r3"])
+    switch.add_link(tgen.gears["rb"])
+
+    # Interconect router 4, b
+    switch = tgen.add_switch("s4-b")
+    switch.add_link(tgen.gears["r4"])
+    switch.add_link(tgen.gears["rb"])
+
+    # Interconect router 1, h1
+    switch = tgen.add_switch("s1-h1")
+    switch.add_link(tgen.gears["r1"])
+    switch.add_link(tgen.gears["h1"])
+
+    # Interconect router 4, h2
+    switch = tgen.add_switch("s4-h2")
+    switch.add_link(tgen.gears["r4"])
+    switch.add_link(tgen.gears["h2"])
+
+    # Interconect router 2, c
+    switch = tgen.add_switch("s2-c")
+    switch.add_link(tgen.gears["r2"])
+    switch.add_link(tgen.gears["rc"])
+
+    # Interconect router 3, c
+    switch = tgen.add_switch("s3-c")
+    switch.add_link(tgen.gears["r3"])
+    switch.add_link(tgen.gears["rc"])
+
+
+def setup_module(mod):
+    logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY))
+
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+
+    vrf_setup_cmds = [
+        "ip link add name blue type vrf table 11",
+        "ip link set dev blue up",
+        "ip link add name green type vrf table 12",
+        "ip link set dev green up",
+    ]
+
+    # Starting Routers
+    router_list = tgen.routers()
+
+    # Create VRFs and bind to interfaces
+    for routern in range(1, 5):
+        for cmd in vrf_setup_cmds:
+            tgen.net["r{}".format(routern)].cmd(cmd)
+    for routern in range(1, 5):
+        tgen.net["r{}".format(routern)].cmd(
+            "ip link set dev r{}-eth1 vrf blue up".format(routern)
+        )
+        tgen.net["r{}".format(routern)].cmd(
+            "ip link set dev r{}-eth2 vrf green up".format(routern)
+        )
+
+    for rname, router in router_list.items():
+        logger.info("Loading router %s" % rname)
+        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+    # Initialize all routers.
+    tgen.start_router()
+    for router in router_list.values():
+        if router.has_version("<", "4.20"):
+            tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_all_links_up():
+    "Test path R1 -> Ra -> Rb -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    r1 = tgen.gears["r1"]
+    json_file = "{}/r1/show_ip_route-1.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_down():
+    "Test path R1 -> R2 -> Ra -> Rb -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    tgen.net["r1"].cmd("ip link set dev r1-eth1 down")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-2.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_2_down():
+    "Test path R1 -> R2 -> Rc -> R3 -> Rb -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    tgen.net["r2"].cmd("ip link set dev r2-eth1 down")
+    tgen.net["r2"].cmd("ip link set dev r2-eth2 down")
+    # ospf dead-interval is set to 30 seconds, wait 35 seconds to clear the neighbor
+    sleep(35)
+    tgen.net["r2"].cmd("ip link set dev r2-eth2 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-3.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_2_3_down():
+    "Test path R1 -> R2 -> Rc -> R3  -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    tgen.net["r3"].cmd("ip link set dev r3-eth0 down")
+    tgen.net["r3"].cmd("ip link set dev r3-eth1 down")
+    # ospf dead-interval is set to 30 seconds, wait 35 seconds to clear the neighbor
+    sleep(35)
+    tgen.net["r3"].cmd("ip link set dev r3-eth0 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_2_3_4_down():
+    "Test path R1 -> R2 -> Rc -> R3  -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    tgen.net["r4"].cmd("ip link set dev r4-eth1 down")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_2_4_down():
+    "Test path R1 -> R2 -> Rc -> R3  -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    # bring link 3 back up
+    tgen.net["r3"].cmd("ip link set dev r3-eth1 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-4.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_4_down():
+    "Test path R1 -> R2 -> Ra -> Rb -> R3  -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    # bring back link 2 up
+    tgen.net["r2"].cmd("ip link set dev r2-eth1 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-5.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_4_down():
+    "Test path R1 -> Ra -> Rb -> R3  -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    # bring back link 1 up
+    tgen.net["r1"].cmd("ip link set dev r1-eth1 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-6.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_link_1_2_3_4_up():
+    "Test path R1 -> Ra -> Rb -> R4"
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of router(s) failure")
+
+    # bring back link 4 up
+    tgen.net["r4"].cmd("ip link set dev r4-eth1 up")
+    r1 = tgen.gears["r1"]
+
+    json_file = "{}/r1/show_ip_route-1.json".format(CWD)
+    expected = json.loads(open(json_file).read())
+    test_func = partial(
+        topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+
+    assertmsg = "r1 JSON output mismatches"
+    assert result is None, assertmsg
+
+
+def test_memory_leak():
+    "Run the memory leak test and report results."
+    tgen = get_topogen()
+    if not tgen.is_memleak_enabled():
+        pytest.skip("Memory leak test/report is disabled")
+
+    tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_nssa_topo1/__init__.py b/tests/topotests/ospf_nssa_topo1/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf
new file mode 100644 (file)
index 0000000..6d23c84
--- /dev/null
@@ -0,0 +1,22 @@
+password 1
+hostname rt1
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 1.1.1.1
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf
new file mode 100644 (file)
index 0000000..7ba3dc7
--- /dev/null
@@ -0,0 +1,6 @@
+log file staticd.log
+!
+hostname rt1
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..ac34417
--- /dev/null
@@ -0,0 +1,115 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..ac34417
--- /dev/null
@@ -0,0 +1,115 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..ac34417
--- /dev/null
@@ -0,0 +1,115 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..ac34417
--- /dev/null
@@ -0,0 +1,115 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..6a05555
--- /dev/null
@@ -0,0 +1,103 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..6a05555
--- /dev/null
@@ -0,0 +1,103 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..2d3c8c4
--- /dev/null
@@ -0,0 +1,91 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..6a05555
--- /dev/null
@@ -0,0 +1,103 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..f41ee3b
--- /dev/null
@@ -0,0 +1,103 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..2d3c8c4
--- /dev/null
@@ -0,0 +1,91 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.0",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.1.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf
new file mode 100644 (file)
index 0000000..1df1005
--- /dev/null
@@ -0,0 +1,18 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf
new file mode 100644 (file)
index 0000000..12884d2
--- /dev/null
@@ -0,0 +1,35 @@
+password 1
+hostname rt2
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt1
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 2.2.2.2
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf
new file mode 100644 (file)
index 0000000..b6d4233
--- /dev/null
@@ -0,0 +1,6 @@
+log file staticd.log
+!
+hostname rt2
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c094117
--- /dev/null
@@ -0,0 +1,127 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c094117
--- /dev/null
@@ -0,0 +1,127 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..a67dfb4
--- /dev/null
@@ -0,0 +1,139 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "0.0.0.0\/0":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c094117
--- /dev/null
@@ -0,0 +1,127 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c7dd93c
--- /dev/null
@@ -0,0 +1,129 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..9c3cfff
--- /dev/null
@@ -0,0 +1,117 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..f6bbdfa
--- /dev/null
@@ -0,0 +1,103 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c7dd93c
--- /dev/null
@@ -0,0 +1,129 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c7dd93c
--- /dev/null
@@ -0,0 +1,129 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..c094117
--- /dev/null
@@ -0,0 +1,127 @@
+{
+  "1.1.1.1\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":"10.0.1.1",
+        "via":"eth-rt1"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.0",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt1"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt3"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt4"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.3",
+        "via":"eth-rt3"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.4",
+        "via":"eth-rt4"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf
new file mode 100644 (file)
index 0000000..fa274eb
--- /dev/null
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt3
+ ip address 10.0.2.2/24
+!
+interface eth-rt4
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf
new file mode 100644 (file)
index 0000000..9691a7c
--- /dev/null
@@ -0,0 +1,24 @@
+password 1
+hostname rt3
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 1
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 3.3.3.3
+ area 1 nssa
+ redistribute connected
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf
new file mode 100644 (file)
index 0000000..f0edd6c
--- /dev/null
@@ -0,0 +1,8 @@
+log file staticd.log
+!
+hostname rt3
+!
+ip route 0.0.0.0/0 Null0
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..a2d078a
--- /dev/null
@@ -0,0 +1,138 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..4619067
--- /dev/null
@@ -0,0 +1,150 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..a2d078a
--- /dev/null
@@ -0,0 +1,138 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..a2d078a
--- /dev/null
@@ -0,0 +1,138 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..1038721
--- /dev/null
@@ -0,0 +1,150 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..4f8eaf1
--- /dev/null
@@ -0,0 +1,138 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..41e9f67
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..1038721
--- /dev/null
@@ -0,0 +1,150 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..4619067
--- /dev/null
@@ -0,0 +1,150 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..4619067
--- /dev/null
@@ -0,0 +1,150 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.1\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.2\/32":{
+    "routeType":"N E2",
+    "cost":20,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.2.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf
new file mode 100644 (file)
index 0000000..d943540
--- /dev/null
@@ -0,0 +1,18 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.0.2.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf
new file mode 100644 (file)
index 0000000..cba7cf7
--- /dev/null
@@ -0,0 +1,24 @@
+password 1
+hostname rt4
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 1
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 12
+!
+router ospf
+ router-id 4.4.4.4
+ area 1 nssa
+ redistribute static
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf
new file mode 100644 (file)
index 0000000..e00ee5d
--- /dev/null
@@ -0,0 +1,9 @@
+log file staticd.log
+!
+hostname rt4
+!
+ip route 172.16.1.1/32 Null0
+ip route 172.16.1.2/32 Null0
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..e57f542
--- /dev/null
@@ -0,0 +1,114 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..82a0e1a
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..e57f542
--- /dev/null
@@ -0,0 +1,114 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..e57f542
--- /dev/null
@@ -0,0 +1,114 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..5f51b3b
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..5f51b3b
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..5f51b3b
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..5f51b3b
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":20,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..82a0e1a
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref
new file mode 100644 (file)
index 0000000..82a0e1a
--- /dev/null
@@ -0,0 +1,126 @@
+{
+  "0.0.0.0\/0":{
+    "routeType":"N IA",
+    "cost":11,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "1.1.1.1\/32":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2\/32":{
+    "routeType":"N IA",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3\/32":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "4.4.4.4\/32":{
+    "routeType":"N",
+    "cost":0,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"lo"
+      }
+    ]
+  },
+  "10.0.1.0\/24":{
+    "routeType":"N IA",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.2.0\/24":{
+    "routeType":"N",
+    "cost":20,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "10.0.3.0\/24":{
+    "routeType":"N",
+    "cost":10,
+    "area":"0.0.0.1",
+    "nexthops":[
+      {
+        "ip":" ",
+        "directlyAttachedTo":"eth-rt2"
+      }
+    ]
+  },
+  "2.2.2.2":{
+    "routeType":"R ",
+    "cost":10,
+    "area":"0.0.0.1",
+    "routerType":"abr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "3.3.3.3":{
+    "routeType":"R ",
+    "cost":20,
+    "area":"0.0.0.1",
+    "routerType":"asbr",
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  },
+  "172.16.1.0\/24":{
+    "routeType":"N E2",
+    "cost":10,
+    "type2cost":1000,
+    "tag":0,
+    "nexthops":[
+      {
+        "ip":"10.0.3.2",
+        "via":"eth-rt2"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf
new file mode 100644 (file)
index 0000000..588febe
--- /dev/null
@@ -0,0 +1,18 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt2
+ ip address 10.0.3.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py
new file mode 100644 (file)
index 0000000..432ddf0
--- /dev/null
@@ -0,0 +1,416 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_ospf_nssa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_ospf_nssa_topo1.py:
+
+             +---------+
+             |   RT1   |
+             | 1.1.1.1 |
+             +---------+
+                  |eth-rt2
+                  |
+                  |10.0.1.0/24
+                  |
+                  |eth-rt1
+             +---------+
+             |   RT2   |
+             | 2.2.2.2 |
+             +---------+
+          eth-rt3|  |eth-rt4
+                 |  |
+     10.0.2.0/24 |  |  10.0.3.0/24
+       +---------+  +--------+
+       |                     |
+       |eth-rt2              |eth-rt2
+  +---------+           +---------+
+  |   RT3   |           |   RT4   |
+  | 3.3.3.3 |           | 4.4.4.4 |
+  +---------+           +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+    "Build function"
+
+    #
+    # Define FRR Routers
+    #
+    for router in ["rt1", "rt2", "rt3", "rt4"]:
+        tgen.add_router(router)
+
+    #
+    # Define connections
+    #
+    switch = tgen.add_switch("s1")
+    switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+    switch = tgen.add_switch("s2")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+    switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+    switch = tgen.add_switch("s3")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+    switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+
+
+def setup_module(mod):
+    "Sets up the pytest environment"
+    tgen = Topogen(build_topo, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    # For all registered routers, load the zebra configuration file
+    for rname, router in router_list.items():
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+        )
+        router.load_config(
+            TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+        )
+
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    "Teardown the pytest environment"
+    tgen = get_topogen()
+
+    # This function tears down the whole topology.
+    tgen.stop_topology()
+
+
+def print_cmd_result(rname, command):
+    print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def router_compare_json_output(rname, command, reference):
+    "Compare router JSON output"
+
+    logger.info('Comparing router "%s" "%s" output', rname, command)
+
+    tgen = get_topogen()
+    filename = "{}/{}/{}".format(CWD, rname, reference)
+    expected = json.loads(open(filename).read())
+
+    # Run test function until we get an result. Wait at most 60 seconds.
+    test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+    _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+    assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+    assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_rib_step1():
+    logger.info("Test (step 1): test initial network convergence")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step1/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -rt3: configure an NSSA default route
+#
+# Expected changes:
+# -rt2: add NSSA default route pointing to rt3
+#
+def test_rib_step2():
+    logger.info("Test (step 2): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Adding NSSA default on rt4")
+    tgen.net["rt3"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa default-information-originate"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step2/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -rt3: remove NSSA default route
+#
+# Expected changes:
+# -rt2: remove NSSA default route
+#
+def test_rib_step3():
+    logger.info("Test (step 3): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Removing NSSA default on rt4")
+    tgen.net["rt3"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step3/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -rt2: configure an NSSA range for 172.16.1.0/24
+#
+# Expected changes:
+# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be removed
+# -rt1: the 172.16.1.0/24 route should be added
+#
+def test_rib_step4():
+    logger.info("Test (step 4): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Configuring NSSA range on rt2")
+    tgen.net["rt2"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step4/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -rt4: remove the 172.16.1.1/32 static route
+#
+# Expected changes:
+# -None (the 172.16.1.0/24 range is still active because of 172.16.1.2/32)
+#
+def test_rib_step5():
+    logger.info("Test (step 5): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Removing first static route in rt4")
+    tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.1/32 Null0"')
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step5/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -rt4: remove the 172.16.1.2/32 static route
+#
+# Expected changes:
+# -rt1: remove the 172.16.1.0/24 route since the NSSA range is no longer active
+#
+def test_rib_step6():
+    logger.info("Test (step 6): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Removing second static route in rt4")
+    tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.2/32 Null0"')
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step6/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -rt4: readd the 172.16.1.1/32 and 172.16.1.2/32 static routes
+#
+# Expected changes:
+# -rt1: readd the 172.16.1.0/24 route since the NSSA range is active again
+#
+def test_rib_step7():
+    logger.info("Test (step 7): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Readding static routes in rt4")
+    tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.1/32 Null0"')
+    tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.2/32 Null0"')
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step7/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -rt2: update the NSSA range with a static cost
+#
+# Expected changes:
+# -rt1: update the metric of the 172.16.1.0/24 route from 20 to 1000
+#
+def test_rib_step8():
+    logger.info("Test (step 8): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Updating the NSSA range cost on rt2")
+    tgen.net["rt2"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 cost 1000"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step8/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -rt2: update the NSSA range to not advertise itself
+#
+# Expected changes:
+# -rt1: the 172.16.1.0/24 route should be removed
+#
+def test_rib_step9():
+    logger.info("Test (step 9): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Updating the NSSA range to not advertise itself")
+    tgen.net["rt2"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 not-advertise"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step9/show_ip_ospf_route.ref"
+        )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -rt2: remove the NSSA range
+#
+# Expected changes:
+# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be added
+#
+def test_rib_step10():
+    logger.info("Test (step 10): verify OSPF routes")
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Removing NSSA range on rt2")
+    tgen.net["rt2"].cmd(
+        'vtysh -c "conf t" -c "router ospf" -c "no area 1 nssa range 172.16.1.0/24"'
+    )
+
+    for rname in ["rt1", "rt2", "rt3", "rt4"]:
+        router_compare_json_output(
+            rname, "show ip ospf route json", "step10/show_ip_ospf_route.ref"
+        )
+
+
+# Memory leak test template
+def test_memory_leak():
+    "Run the memory leak test and report results."
+    tgen = get_topogen()
+    if not tgen.is_memleak_enabled():
+        pytest.skip("Memory leak test/report is disabled")
+
+    tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 5b1a53b895b33323c7238185731870c92acf29fd..d5583ac06abb3220c98277138b55d318cdc25772 100644 (file)
@@ -111,9 +111,7 @@ def ospf_unconfigure_suppress_fa(router_name, area):
 
     tgen = get_topogen()
     router = tgen.gears[router_name]
-    router.vtysh_cmd(
-        "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area)
-    )
+    router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area))
 
 
 def ospf_get_lsa_type5(router_name):
index 18aee4ffabace0e1d5b57073309a0d2fb1dbe1a7..8b2413a8940da7cae132aae42da7a8a8945834d3 100644 (file)
@@ -57,7 +57,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         }
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167773441,
+        "edge-id":"10.0.5.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
index 1ed7272560f8b87e2dd1b07135c74baad639f039..625b57d15c074fd896a429d30ec7253e49485b66 100644 (file)
@@ -57,7 +57,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167773441,
+        "edge-id":"10.0.5.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
index 0e79670c78f6cd94ece12136b66b15397d258bd2..4cfec0f608f6394526222205c3b6611f07c6966a 100644 (file)
@@ -49,7 +49,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
@@ -93,7 +93,7 @@
         }
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         }
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
index 860dcb3f216a6ce9a0a0d176228e3017c4482aec..e8e24d9805bbabf28688314b4157ac3acf57da05 100644 (file)
@@ -72,7 +72,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
index 615a691c451660b15b83cc7fc2a3064763b7195d..4713cc0115fe3b8f8717744996af5aaf9b804600 100644 (file)
@@ -72,7 +72,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
index 3b84d258081fb967dc9f745154c83df3e9d09cb1..aaac07b051691c1ceb14d006ce2cd6792c009c4e 100644 (file)
@@ -72,7 +72,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167773185,
+        "edge-id":"10.0.4.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.4",
         ]
       },
       {
-        "edge-id":167773186,
+        "edge-id":"10.0.4.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
index 83f8a1d5d60f3cb2c90d9db04648f4279e80be10..56ed1f176b166404a22e9a02acdcc8704a0240e6 100644 (file)
@@ -53,7 +53,7 @@
     ],
     "edges":[
       {
-        "edge-id":167772161,
+        "edge-id":"10.0.0.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772162,
+        "edge-id":"10.0.0.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772417,
+        "edge-id":"10.0.1.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.1",
         ]
       },
       {
-        "edge-id":167772418,
+        "edge-id":"10.0.1.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
         ]
       },
       {
-        "edge-id":167772929,
+        "edge-id":"10.0.3.1",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.3",
         }
       },
       {
-        "edge-id":167772930,
+        "edge-id":"10.0.3.2",
         "status":"Sync",
         "origin":"OSPFv2",
         "advertised-router":"10.0.255.2",
index 55166367c1e7baf48eaa31c790ecd0399392d049..b0e56e619a096fa9a88845856741ba444561e304 100644 (file)
@@ -156,7 +156,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -280,7 +280,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step(
         "Configure External Route summary in R0 to summarise 5"
@@ -314,7 +314,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -330,9 +330,9 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
     }
@@ -340,7 +340,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -349,7 +349,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -374,7 +374,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -383,9 +383,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
-    )
+    ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
 
     step("show ip ospf summary should not have any summary address.")
     input_dict = {
@@ -403,7 +401,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
 
     dut = "r1"
     step("All 5 routes are advertised after deletion of configured summary.")
@@ -414,7 +412,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("configure the summary again and delete static routes .")
     ospf_summ_r1 = {
@@ -442,7 +440,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     input_dict = {
         "r0": {
@@ -461,7 +459,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -470,7 +468,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step("Add back static routes.")
     input_dict_static_rtes = {
@@ -488,7 +486,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -497,7 +495,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
     dut = "r1"
@@ -508,7 +506,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show configure summaries.")
 
@@ -525,7 +523,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure new static route which is matching configured summary.")
     input_dict_static_rtes = {
@@ -591,7 +589,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Shut one of the interface")
     intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
@@ -663,7 +661,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
 
@@ -674,7 +672,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     ospf_summ_r1 = {
         "r0": {
@@ -702,7 +700,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -711,7 +709,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     ospf_summ_r1 = {
         "r0": {
@@ -774,11 +772,9 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf6": {
@@ -804,7 +800,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -820,7 +816,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Change the summary address mask to lower match (ex - 16 to 8)")
     ospf_summ_r1 = {
@@ -855,7 +851,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Verify that external routes(static / connected) are summarised"
@@ -871,7 +867,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Change the summary address mask to higher match (ex - 8 to 24)")
     ospf_summ_r1 = {
@@ -899,7 +895,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Verify that external routes(static / connected) are summarised"
@@ -920,7 +916,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step(" Un configure one of the summary address.")
     ospf_summ_r1 = {
@@ -955,7 +951,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     ospf_summ_r1 = {
         "r0": {
@@ -982,7 +978,7 @@ def test_ospfv3_type5_summary_tc43_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -1030,11 +1026,9 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf6": {
@@ -1066,7 +1060,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1082,7 +1076,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -1107,7 +1101,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1116,9 +1110,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
-    )
+    ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
 
     step("show ip ospf summary should not have any summary address.")
     input_dict = {
@@ -1136,7 +1128,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
 
     step("Configure Min tag value")
     ospf_summ_r1 = {
@@ -1161,7 +1153,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1177,7 +1169,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure Max Tag Value")
     ospf_summ_r1 = {
@@ -1207,7 +1199,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1226,7 +1218,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("configure new static route with different tag.")
     input_dict_static_rtes_11 = {
@@ -1251,7 +1243,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1266,7 +1258,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1287,7 +1279,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary address")
     ospf_summ_r1 = {
@@ -1318,7 +1310,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that summary address is flushed from neighbor.")
 
@@ -1326,7 +1318,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1335,7 +1327,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step("Configure summary first & then configure matching static route.")
 
@@ -1467,11 +1459,9 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
     ospf_summ_r1 = {
         "r0": {
             "ospf6": {
@@ -1503,7 +1493,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1541,7 +1531,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1550,9 +1540,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
-    )
+    ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
 
     step("show ip ospf summary should not have any summary address.")
     input_dict = {
@@ -1570,7 +1558,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
 
     step("Configure Min tag value")
     ospf_summ_r1 = {
@@ -1595,7 +1583,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries with tag.")
     input_dict = {
@@ -1611,7 +1599,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure Max Tag Value")
     ospf_summ_r1 = {
@@ -1641,7 +1629,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1660,7 +1648,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("configure new static route with different tag.")
     input_dict_static_rtes_11 = {
@@ -1685,7 +1673,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1700,7 +1688,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step(
         "Verify that boundary values tags are used for summary route"
@@ -1721,7 +1709,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary address")
     ospf_summ_r1 = {
@@ -1752,7 +1740,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that summary address is flushed from neighbor.")
 
@@ -1760,7 +1748,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1769,7 +1757,7 @@ def ospfv3_type5_summary_tc45_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step("Configure summary first & then configure matching static route.")
 
@@ -1853,7 +1841,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step(
         "Configure External Route summary in R0 to summarise 5"
@@ -1887,7 +1875,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1896,9 +1884,9 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
-    step("Verify that show ip ospf summary should show the " "configured summaries.")
+    step("Verify that show ip ospf summary should show the  configured summaries.")
     input_dict = {
         SUMMARY["ipv6"][0]: {
             "summaryAddress": SUMMARY["ipv6"][0],
@@ -1909,7 +1897,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Delete the configured summary")
     ospf_summ_r1 = {
@@ -1936,7 +1924,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -1945,9 +1933,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
-        tc_name
-    )
+    ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name)
 
     step("show ip ospf summary should not have any summary address.")
     input_dict = {
@@ -1965,7 +1951,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name)
 
     step("Reconfigure summary with no advertise.")
     ospf_summ_r1 = {
@@ -1996,7 +1982,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2005,9 +1991,9 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
-    step("Verify that show ip ospf summary should show the " "configured summaries.")
+    step("Verify that show ip ospf summary should show the  configured summaries.")
     input_dict = {
         SUMMARY["ipv6"][0]: {
             "summaryAddress": SUMMARY["ipv6"][0],
@@ -2018,7 +2004,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step(
         "Change summary address from no advertise to advertise "
@@ -2067,7 +2053,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2083,9 +2069,9 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     output = tgen.gears["r0"].vtysh_cmd(
         "show ipv6 ospf6 database as-external json", isjson=True
     )
@@ -2101,7 +2087,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2110,7 +2096,7 @@ def test_ospfv3_type5_summary_tc46_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is present in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -2157,11 +2143,9 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r1 = {
         "r0": {
@@ -2189,7 +2173,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2205,9 +2189,9 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
     }
@@ -2215,7 +2199,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2224,7 +2208,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step(
         "Configure route map and & rule to permit configured summary address,"
@@ -2287,7 +2271,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     input_dict = {
         SUMMARY["ipv6"][0]: {
@@ -2302,7 +2286,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Configure metric type as 1 in route map.")
 
@@ -2339,7 +2323,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Un configure metric type from route map.")
 
@@ -2376,7 +2360,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     step("Change rule from permit to deny in prefix list.")
     pfx_list = {
@@ -2407,7 +2391,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2416,7 +2400,7 @@ def test_ospfv3_type5_summary_tc48_p0(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -2556,7 +2540,7 @@ def test_ospfv3_type5_summary_tc51_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
     write_test_footer(tc_name)
 
@@ -2603,11 +2587,9 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
-    step(
-        "Configure External Route summary in R0 to summarise 5" " routes to one route."
-    )
+    step("Configure External Route summary in R0 to summarise 5  routes to one route.")
 
     ospf_summ_r1 = {
         "r0": {
@@ -2635,7 +2617,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2651,9 +2633,9 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
     }
@@ -2661,7 +2643,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2670,7 +2652,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     step("Reload the FRR router")
     # stop/start -> restart FRR router and verify
@@ -2691,7 +2673,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name)
 
     step("Verify that show ip ospf summary should show the summaries.")
     input_dict = {
@@ -2707,9 +2689,9 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
     assert (
         result is True
-    ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+    ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name)
 
-    step("Verify that originally advertised routes are withdraw from there" " peer.")
+    step("Verify that originally advertised routes are withdraw from there  peer.")
     input_dict = {
         "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
     }
@@ -2717,7 +2699,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+    ), "Testcase {} : Failed \n Error:  Routes still present in OSPF RIB {}".format(
         tc_name, result
     )
 
@@ -2726,7 +2708,7 @@ def test_ospfv3_type5_summary_tc49_p2(request):
     )
     assert (
         result is not True
-    ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+    ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name)
 
     write_test_footer(tc_name)
 
index 2f90c98785b394efa70bd70239ab5f83a31fefb3..58608e249b2ebd3bb10c74038965f363fd8ff35a 100644 (file)
@@ -111,7 +111,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf6_covergence
     )
 
@@ -182,7 +182,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -214,7 +214,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -244,7 +244,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=5
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -272,7 +272,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -287,7 +287,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
         "show ip ospf6 neighbor cmd."
     )
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -301,7 +301,7 @@ def test_ospf6_auth_trailer_tc1_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -348,7 +348,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -380,7 +380,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -410,7 +410,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=5
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -438,7 +438,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -453,7 +453,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
         "show ip ospf6 neighbor cmd."
     )
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -467,7 +467,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -531,7 +531,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -561,7 +561,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -580,7 +580,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=5
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -606,7 +606,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -621,7 +621,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
         "show ip ospf6 neighbor cmd."
     )
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -635,7 +635,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -699,7 +699,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -729,7 +729,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -748,7 +748,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=5
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -774,7 +774,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -789,7 +789,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
         "show ip ospf6 neighbor cmd."
     )
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -803,7 +803,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -850,7 +850,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -887,7 +887,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -919,7 +919,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -966,7 +966,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -998,7 +998,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1030,7 +1030,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1102,7 +1102,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1132,7 +1132,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1162,7 +1162,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1234,7 +1234,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1264,7 +1264,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1294,7 +1294,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1342,7 +1342,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1372,7 +1372,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request):
     ospf6_covergence = verify_ospf6_neighbor(
         tgen, topo, dut=dut, expected=False, retry_timeout=3
     )
-    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
@@ -1402,7 +1402,7 @@ def test_ospf6_auth_trailer_tc10_no_auth_trailer(request):
 
     dut = "r2"
     ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+    assert ospf6_covergence is True, "Testcase {} :Failed \n Error:  {}".format(
         tc_name, ospf6_covergence
     )
 
index 472364e84f1b93539d8e89f0935cae9c5608e9ab..0c1e3fa43e878cc6455b0225c7844ba922506cdb 100644 (file)
@@ -115,7 +115,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -257,7 +257,7 @@ def test_ospfv3_ecmp_tc16_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -352,7 +352,7 @@ def test_ospfv3_ecmp_tc16_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -390,7 +390,7 @@ def test_ospfv3_ecmp_tc17_p0(request):
     step("Verify that OSPF is up with 2 neighborship sessions.")
     dut = "r1"
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
index 6b3e16965c6fc6dc8ec567cccb54f079577d72f6..7c6773260edf8f924b7c89da2f09772597ccacac 100644 (file)
@@ -128,7 +128,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -282,7 +282,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request):
     step("Verify that OSPF is up with 8 neighborship sessions.")
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -343,7 +343,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request):
 
     dut = "r0"
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -352,7 +352,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request):
 
     dut = "r2"
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
index c0d8d718cc4345cf537d3bf8d435ca6765afd6c3..dc4ce888306e846b4860cf5d635f05db658b5705 100644 (file)
@@ -73,7 +73,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     result = verify_ospf6_neighbor(tgen, topo)
-    assert result is True, "setup_module: Failed \n Error:" " {}".format(result)
+    assert result is True, "setup_module: Failed \n Error:  {}".format(result)
 
     logger.info("Running setup_module() done")
 
index 138112775f9d5466170f9288033833d52aac7083..90548fb5ced42f3966b4a9944cf71fa410282b56 100644 (file)
@@ -138,7 +138,7 @@ def setup_module(mod):
 
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -277,7 +277,7 @@ def test_ospfv3_nssa_tc26_p0(request):
     result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+    ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result)
 
     step("Now configure area 0 on interface of r1 connecting to r2.")
 
@@ -349,7 +349,7 @@ def test_ospfv3_nssa_tc26_p0(request):
     result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+    ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result)
 
     step("Now configure area 2 on interface of r1 connecting to r2.")
 
@@ -471,7 +471,7 @@ def test_ospfv3_learning_tc15_p0(request):
     result = verify_ospf6_neighbor(tgen, topo)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).")
+    step("Change area 1 as non nssa area (on the fly changing area  type on DUT).")
 
     for rtr in ["r1", "r2", "r3"]:
         input_dict = {
index b2cd0da24e786eb93f4bc0021e75c606ef88a36f..069806a3ef46cfa548b98eafe1bb35011aeec5f2 100644 (file)
@@ -129,7 +129,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -212,9 +212,7 @@ def test_ospfv3_routemaps_functionality_tc19_p0(request):
     result = create_router_ospf(tgen, topo, ospf_red_r1)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
-    )
+    step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix &  deny 10.0.20.2/32")
 
     # Create ip prefix list
     pfx_list = {
@@ -686,7 +684,7 @@ def test_ospfv3_routemaps_functionality_tc25_p0(request):
 
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -1078,7 +1076,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request):
     result = verify_prefix_lists(tgen, pfx_list)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+    ), "Testcase {} : Failed \n Prefix list not  present. Error: {}".format(
         tc_name, result
     )
 
@@ -1147,7 +1145,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request):
     result = verify_prefix_lists(tgen, pfx_list)
     assert (
         result is not True
-    ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+    ), "Testcase {} : Failed \n Prefix list not  present. Error: {}".format(
         tc_name, result
     )
 
index 9e7f112efbc5d8aa98dd5307ca789d5483a9f3b4..645dea8dec44118ef0183d8c5a63900fd5798f88 100644 (file)
@@ -120,7 +120,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -256,7 +256,7 @@ def test_ospfv3_redistribution_tc5_p0(request):
 
     step("Verify that OSPF neighbors are FULL.")
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -616,7 +616,7 @@ def test_ospfv3_redistribution_tc8_p1(request):
     step("Verify that OSPF neighbours are reset and forms new adjacencies.")
     # Api call verify whether OSPF is converged
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
index 5d5ba1480fb1d785bd7ab3d0f98fbaa798d4e735..7199f160fe5b94e38759d2bac235269de03bdf51 100644 (file)
@@ -114,7 +114,7 @@ def setup_module(mod):
         pytest.skip(tgen.errors)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "setup_module :Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -460,7 +460,7 @@ def test_ospfv3_hello_tc10_p0(request):
 
     step("verify that ospf neighbours are  full")
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -513,7 +513,7 @@ def test_ospfv3_hello_tc10_p0(request):
 
     step("verify that ospf neighbours are  full")
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -566,7 +566,7 @@ def test_ospfv3_hello_tc10_p0(request):
 
     step("verify that ospf neighbours are  full")
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -618,7 +618,7 @@ def test_ospfv3_hello_tc10_p0(request):
 
     step("verify that ospf neighbours are  full")
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
-    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "Testcase Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -709,9 +709,7 @@ def test_ospfv3_hello_tc10_p0(request):
     result = create_interfaces_cfg(tgen, topo1)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Verify that timer value is deleted from intf & " "set to default value 40 sec."
-    )
+    step("Verify that timer value is deleted from intf &  set to default value 40 sec.")
     input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}}
     dut = "r1"
     result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
@@ -763,7 +761,7 @@ def test_ospfv3_dead_tc11_p0(request):
     result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step("modify dead interval from default value to r1" "dead interval timer on r2")
+    step("modify dead interval from default value to r1 dead interval timer on r2")
 
     topo1 = {
         "r0": {
@@ -799,7 +797,7 @@ def test_ospfv3_dead_tc11_p0(request):
     # reconfiguring deleted ospf process by resetting the configs.
     reset_config_on_routers(tgen)
 
-    step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+    step("reconfigure the default dead interval timer value to  default on r1 and r2")
     topo1 = {
         "r0": {
             "links": {
@@ -920,9 +918,7 @@ def test_ospfv3_dead_tc11_p0(request):
     result = create_interfaces_cfg(tgen, topo1)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Verify that timer value is deleted from intf & " "set to default value 40 sec."
-    )
+    step("Verify that timer value is deleted from intf &  set to default value 40 sec.")
     input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
     dut = "r1"
     result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
@@ -967,18 +963,14 @@ def test_ospfv3_tc4_mtu_ignore_p0(request):
     clear_ospf(tgen, "r0", ospf="ospf6")
     clear_ospf(tgen, "r1", ospf="ospf6")
 
-    step(
-        "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
-    )
+    step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart  State.")
     result = verify_ospf6_neighbor(tgen, topo, expected=False)
     assert result is not True, (
         "Testcase {} : Failed \n OSPF nbrs are Full "
         "instead of Exstart. Error: {}".format(tc_name, result)
     )
 
-    step(
-        "Verify that configured MTU value is updated in the show ip " "ospf interface."
-    )
+    step("Verify that configured MTU value is updated in the show ip  ospf interface.")
 
     dut = "r0"
     input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}}
@@ -1035,9 +1027,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request):
 
     clear_ospf(tgen, "r0", ospf="ospf6")
 
-    step(
-        "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
-    )
+    step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart  State.")
     result = verify_ospf6_neighbor(tgen, topo, expected=False)
     assert result is not True, (
         "Testcase {} : Failed \n OSPF nbrs are Full "
@@ -1054,9 +1044,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request):
     result = verify_ospf6_neighbor(tgen, topo)
     assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
 
-    step(
-        "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
-    )
+    step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.")
 
     rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf))
     rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf))
@@ -1091,53 +1079,6 @@ def test_ospfv3_show_p1(request):
 
     result = create_debug_log_config(tgen, input_dict)
 
-    # Code coverage steps #Do Not upstream
-    input_dict_config = {
-        "r1": {
-            "raw_config": [
-                "end",
-                "debug ospf6 event",
-                "debug ospf6 gr helper",
-                "debug ospf6 ism events",
-                "debug ospf6 ism status",
-                "debug ospf6 ism timers",
-                "debug ospf6 nsm events",
-                "debug ospf6 nsm status",
-                "debug ospf6 nsm timers ",
-                "debug ospf6 nssa",
-                "debug ospf6 lsa aggregate",
-                "debug ospf6 lsa flooding ",
-                "debug ospf6 lsa generate",
-                "debug ospf6 lsa install ",
-                "debug ospf6 lsa refresh",
-                "debug ospf6 packet all detail",
-                "debug ospf6 packet all recv",
-                "debug ospf6 packet all send",
-                "debug ospf6 packet dd detail",
-                "debug ospf6 packet dd recv",
-                "debug ospf6 packet dd send ",
-                "debug ospf6 packet hello detail",
-                "debug ospf6 packet hello recv",
-                "debug ospf6 packet hello send",
-                "debug ospf6 packet ls-ack detail",
-                "debug ospf6 packet ls-ack recv",
-                "debug ospf6 packet ls-ack send",
-                "debug ospf6 packet ls-request detail",
-                "debug ospf6 packet ls-request recv",
-                "debug ospf6 packet ls-request send",
-                "debug ospf6 packet ls-update detail",
-                "debug ospf6 packet ls-update recv",
-                "debug ospf6 packet ls-update send",
-                "debug ospf6 sr",
-                "debug ospf6 te ",
-                "debug ospf6 zebra interface",
-                "debug ospf6 zebra redistribute",
-            ]
-        }
-    }
-
-    apply_raw_config(tgen, input_dict_config)
-
     for rtr in topo["routers"]:
         clear_ospf(tgen, rtr, ospf="ospf6")
 
@@ -1234,7 +1175,7 @@ def ospfv3_router_id_tc14_p2(request):
         clear_ospf(tgen, rtr, ospf="ospf6")
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
-    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -1260,9 +1201,9 @@ def ospfv3_router_id_tc14_p2(request):
     assert result is True, "Testcase : Failed \n Error: {}".format(result)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False)
-    assert (
-        ospf_covergence is not True
-    ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence)
+    assert ospf_covergence is not True, "OSPF NBRs are up.Failed \n Error:  {}".format(
+        ospf_covergence
+    )
     topo1 = {}
     topo1 = deepcopy(topo)
 
@@ -1305,7 +1246,7 @@ def ospfv3_router_id_tc14_p2(request):
     topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4"
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
-    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:  {}".format(
         ospf_covergence
     )
 
@@ -1313,7 +1254,7 @@ def ospfv3_router_id_tc14_p2(request):
     reset_config_on_routers(tgen)
 
     ospf_covergence = verify_ospf6_neighbor(tgen, topo)
-    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+    assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:  {}".format(
         ospf_covergence
     )
 
index af83d6bea373a0029b8917596785650a6b4624ee..34ce481a3f6367157b4b33fb09dc880b02575e7e 100644 (file)
@@ -2,57 +2,57 @@
   "neighbors":{
     "192.168.0.11":[
       {
-        "priority":10,
+        "nbrPriority":10,
         "converged":"Full",
-        "address":"192.168.101.11",
+        "ifaceAddress":"192.168.101.11",
         "ifaceName":"r1-eth1:192.168.101.1",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "192.168.0.12":[
       {
-        "priority":0,
+        "nbrPriority":0,
         "converged":"Full",
-        "address":"192.168.101.12",
+        "ifaceAddress":"192.168.101.12",
         "ifaceName":"r1-eth1:192.168.101.1",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "192.168.0.13":[
       {
-        "priority":0,
+        "nbrPriority":0,
         "converged":"Full",
-        "address":"192.168.101.13",
+        "ifaceAddress":"192.168.101.13",
         "ifaceName":"r1-eth1:192.168.101.1",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "192.168.0.14":[
       {
-        "priority":0,
+        "nbrPriority":0,
         "converged":"Full",
-        "address":"192.168.101.14",
+        "ifaceAddress":"192.168.101.14",
         "ifaceName":"r1-eth1:192.168.101.1",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ],
     "192.168.0.15":[
       {
-        "priority":0,
+        "nbrPriority":0,
         "converged":"Full",
-        "address":"192.168.101.15",
+        "ifaceAddress":"192.168.101.15",
         "ifaceName":"r1-eth1:192.168.101.1",
-        "retransmitCounter":0,
-        "requestCounter":0,
-        "dbSummaryCounter":0
+        "linkStateRetransmissionListCounter":0,
+        "linkStateRequestListCounter":0,
+        "databaseSummaryListCounter":0
       }
     ]
   }
index 1e70fcc36e375ce8189a610bc0f3c07f1661b867..198098d3d3054efa3ef5f8cbcf329886810090e5 100644 (file)
@@ -4,9 +4,9 @@
     "neighbors":{
       "192.168.0.11":[
         {
-          "priority":10,
+          "nbrPriority":10,
           "converged":"Full",
-          "address":"192.168.101.11",
+          "ifaceAddress":"192.168.101.11",
           "ifaceName":"r1-eth1:192.168.101.1"
         }
       ]
index 7f2ab248cc1d00c2941910ec4146b182837320f1..6fce225ffc8e3d8eb56244e14742af7acfb10685 100644 (file)
@@ -4,9 +4,9 @@
     "neighbors":{
       "192.168.0.12":[
         {
-          "priority":10,
+          "nbrPriority":10,
           "converged":"Full",
-          "address":"192.168.101.12",
+          "ifaceAddress":"192.168.101.12",
           "ifaceName":"r1-eth3:192.168.101.1"
         }
       ]
index 6986e3051c954b20c723d60c29c346a3f19d885a..f779bf0a74f74dab434393c6c648e285c4e4aed2 100644 (file)
@@ -1,6 +1,8 @@
 # Skip pytests example directory
 [pytest]
 
+asyncio_mode = auto
+
 # We always turn this on inside conftest.py, default shown
 # addopts = --junitxml=<rundir>/topotests.xml
 
@@ -24,7 +26,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S
 junit_logging = all
 junit_log_passing_tests = true
 
-norecursedirs = .git example_test example_topojson_test lib docker
+norecursedirs = .git example_munet example_test example_topojson_test lib munet docker
 
 # Directory to store test results and run logs in, default shown
 # rundir = /tmp/topotests
diff --git a/tests/topotests/rip_allow_ecmp/__init__.py b/tests/topotests/rip_allow_ecmp/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/rip_allow_ecmp/r1/frr.conf b/tests/topotests/rip_allow_ecmp/r1/frr.conf
new file mode 100644 (file)
index 0000000..d8eb9a3
--- /dev/null
@@ -0,0 +1,9 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router rip
+ allow-ecmp
+ network 192.168.1.0/24
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/rip_allow_ecmp/r2/frr.conf b/tests/topotests/rip_allow_ecmp/r2/frr.conf
new file mode 100644 (file)
index 0000000..d7ea6f3
--- /dev/null
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/r3/frr.conf b/tests/topotests/rip_allow_ecmp/r3/frr.conf
new file mode 100644 (file)
index 0000000..2362c47
--- /dev/null
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
new file mode 100644 (file)
index 0000000..acc0aea
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIP `allow-ecmp` command works correctly.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.ripd]
+
+
+def setup_module(mod):
+    topodef = {"s1": ("r1", "r2", "r3")}
+    tgen = Topogen(topodef, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for _, (rname, router) in enumerate(router_list.items(), 1):
+        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_rip_allow_ecmp():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    r1 = tgen.gears["r1"]
+
+    def _show_rip_routes():
+        xpath = (
+            "/frr-ripd:ripd/instance[vrf='default']"
+            "/state/routes/route[prefix='10.10.10.1/32']"
+        )
+        try:
+            output = json.loads(
+                r1.vtysh_cmd(f"show yang operational-data {xpath} ripd")
+            )
+        except Exception:
+            return False
+
+        try:
+            output = output["frr-ripd:ripd"]["instance"][0]["state"]["routes"]
+        except KeyError:
+            return False
+
+        expected = {
+            "route": [
+                {
+                    "prefix": "10.10.10.1/32",
+                    "nexthops": {
+                        "nexthop": [
+                            {
+                                "nh-type": "ip4",
+                                "protocol": "rip",
+                                "rip-type": "normal",
+                                "gateway": "192.168.1.2",
+                                "from": "192.168.1.2",
+                                "tag": 0,
+                            },
+                            {
+                                "nh-type": "ip4",
+                                "protocol": "rip",
+                                "rip-type": "normal",
+                                "gateway": "192.168.1.3",
+                                "from": "192.168.1.3",
+                                "tag": 0,
+                            },
+                        ]
+                    },
+                    "metric": 2,
+                },
+            ]
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_show_rip_routes)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+    assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`"
+
+    def _show_routes():
+        output = json.loads(r1.vtysh_cmd("show ip route json"))
+        expected = {
+            "10.10.10.1/32": [
+                {
+                    "nexthops": [
+                        {
+                            "ip": "192.168.1.2",
+                            "active": True,
+                        },
+                        {
+                            "ip": "192.168.1.3",
+                            "active": True,
+                        },
+                    ]
+                }
+            ]
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_show_routes)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+    assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip route`"
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/rip_bfd_topo1/__init__.py b/tests/topotests/rip_bfd_topo1/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/tests/topotests/rip_bfd_topo1/r1/bfdd.conf b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf
new file mode 100644 (file)
index 0000000..e848bda
--- /dev/null
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+  receive-interval 1000
+  transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/ripd.conf b/tests/topotests/rip_bfd_topo1/r1/ripd.conf
new file mode 100644 (file)
index 0000000..6cef846
--- /dev/null
@@ -0,0 +1,17 @@
+interface r1-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+interface r1-eth1
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ allow-ecmp
+ network 192.168.0.1/24
+ network 192.168.1.1/24
+ redistribute connected
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/zebra.conf b/tests/topotests/rip_bfd_topo1/r1/zebra.conf
new file mode 100644 (file)
index 0000000..e3800a9
--- /dev/null
@@ -0,0 +1,11 @@
+interface r1-eth0
+ ip address 192.168.0.1/24
+exit
+!
+interface r1-eth1
+ ip address 192.168.1.1/24
+exit
+!
+interface lo
+ ip address 10.254.254.1/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/bfdd.conf b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf
new file mode 100644 (file)
index 0000000..e848bda
--- /dev/null
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+  receive-interval 1000
+  transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/ripd.conf b/tests/topotests/rip_bfd_topo1/r2/ripd.conf
new file mode 100644 (file)
index 0000000..35e4688
--- /dev/null
@@ -0,0 +1,11 @@
+interface r2-eth0
+ ip rip bfd
+exit
+!
+router rip
+ bfd default-profile slow
+ network 192.168.0.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/staticd.conf b/tests/topotests/rip_bfd_topo1/r2/staticd.conf
new file mode 100644 (file)
index 0000000..6fe9374
--- /dev/null
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r2/zebra.conf b/tests/topotests/rip_bfd_topo1/r2/zebra.conf
new file mode 100644 (file)
index 0000000..cad922f
--- /dev/null
@@ -0,0 +1,8 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.2/32
+exit
+
diff --git a/tests/topotests/rip_bfd_topo1/r3/bfdd.conf b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf
new file mode 100644 (file)
index 0000000..e848bda
--- /dev/null
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+  receive-interval 1000
+  transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/ripd.conf b/tests/topotests/rip_bfd_topo1/r3/ripd.conf
new file mode 100644 (file)
index 0000000..0df0bac
--- /dev/null
@@ -0,0 +1,11 @@
+interface r3-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ network 192.168.1.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/staticd.conf b/tests/topotests/rip_bfd_topo1/r3/staticd.conf
new file mode 100644 (file)
index 0000000..6fe9374
--- /dev/null
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r3/zebra.conf b/tests/topotests/rip_bfd_topo1/r3/zebra.conf
new file mode 100644 (file)
index 0000000..12ffeca
--- /dev/null
@@ -0,0 +1,7 @@
+interface r3-eth0
+ ip address 192.168.1.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.3/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot
new file mode 100644 (file)
index 0000000..1480a8f
--- /dev/null
@@ -0,0 +1,58 @@
+## Color coding:
+#########################
+##  Main FRR: #f08080  red
+##  Switches: #d0e0d0  gray
+##  RIP:      #19e3d9  Cyan
+##  RIPng:    #fcb314  dark yellow
+##  OSPFv2:   #32b835  Green
+##  OSPFv3:   #19e3d9  Cyan
+##  ISIS IPv4 #fcb314  dark yellow
+##  ISIS IPv6 #9a81ec  purple
+##  BGP IPv4  #eee3d3  beige
+##  BGP IPv6  #fdff00  yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+       label="rip_bfd_topo1";
+
+       # Routers
+       r1 [
+               shape=doubleoctagon,
+               label="r1",
+               fillcolor="#f08080",
+               style=filled,
+       ];
+       r2 [
+               shape=doubleoctagon
+               label="r2",
+               fillcolor="#f08080",
+               style=filled,
+       ];
+       r3 [
+               shape=doubleoctagon
+               label="r3",
+               fillcolor="#f08080",
+               style=filled,
+       ];
+
+       # Switches
+       s1 [
+               shape=oval,
+               label="s1\n192.168.0.0/24",
+               fillcolor="#d0e0d0",
+               style=filled,
+       ];
+       s2 [
+               shape=oval,
+               label="s1\n192.168.1.0/24",
+               fillcolor="#d0e0d0",
+               style=filled,
+       ];
+
+       # Connections
+       r1 -- s1 [label="r1-eth0\n.1"];
+       r2 -- s1 [label="r2-eth0\n.2"];
+
+       r1 -- s2 [label="r1-eth1\n.1"];
+       r3 -- s2 [label="r1-eth0\n.2"];
+}
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png
new file mode 100644 (file)
index 0000000..e5e362e
Binary files /dev/null and b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png differ
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py
new file mode 100644 (file)
index 0000000..71c9093
--- /dev/null
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_rip_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_rip_bfd_topo1.py: Test RIP BFD integration.
+"""
+
+import sys
+import re
+import pytest
+
+from functools import partial
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+    pytest.mark.bfdd,
+    pytest.mark.ripd,
+]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+    "Setup/Teardown the environment and provide tgen argument to tests"
+
+    topodef = {
+        "s1": ("r1", "r2"),
+        "s2": ("r1", "r3")
+    }
+    tgen = Topogen(topodef, request.module.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for router_name, router in router_list.items():
+        router.load_config(TopoRouter.RD_BFD, "bfdd.conf")
+        router.load_config(TopoRouter.RD_RIP, "ripd.conf")
+        router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+        if router_name in ["r2", "r3"]:
+            router.load_config(TopoRouter.RD_STATIC, "staticd.conf")
+
+    tgen.start_router()
+    yield tgen
+    tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+    "Test if routers is still running otherwise skip tests"
+    if tgen.routers_have_failure():
+        pytest.skip("skipped because of previous test failure")
+
+
+def show_rip_json(router):
+    "Get router 'show ip rip' JSON output"
+    output = router.vtysh_cmd("show ip rip")
+    routes = output.splitlines()[6:]
+    json = {}
+
+    for route in routes:
+        match = re.match(
+            r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route)
+        if match is None:
+            continue
+
+        route_entry = {
+                "code": match[1],
+                "subCode": match[2],
+                "nextHop": match[4],
+                "metric": int(match[5]),
+                "from": match[6],
+        }
+
+        if json.get(match[3]) is None:
+            json[match[3]] = []
+
+        json[match[3]].append(route_entry)
+
+    return json
+
+
+def expect_routes(router, routes, time_amount):
+    "Expect 'routes' in 'router'."
+
+    def test_function():
+        "Internal test function."
+        return topotest.json_cmp(show_rip_json(router), routes)
+
+    _, result = topotest.run_and_expect(test_function,
+                                        None,
+                                        count=time_amount,
+                                        wait=1)
+    assert result is None, "Unexpected routing table in {}".format(
+        router.name)
+
+
+def expect_bfd_peers(router, peers):
+    "Expect 'peers' in 'router' BFD status."
+    test_func = partial(
+        topotest.router_json_cmp,
+        router,
+        "show bfd peers json",
+        peers,
+    )
+    _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+    assert result is None, "{} BFD peer status mismatch".format(router)
+
+
+def test_rip_convergence(tgen):
+    "Test that RIP learns the neighbor routes."
+
+    expect_routes(
+        tgen.gears["r1"], {
+            "10.254.254.2/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.2"
+            }],
+            "10.254.254.3/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.1.2"
+            }],
+            "10.254.254.100/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.2",
+            }, {
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.1.2",
+            }]
+        }, 40)
+
+    expect_bfd_peers(tgen.gears["r1"], [{
+        "peer": "192.168.0.2",
+        "status": "up",
+        "receive-interval": 1000,
+        "transmit-interval": 1000,
+    }, {
+        "peer": "192.168.1.2",
+        "status": "up",
+        "receive-interval": 1000,
+        "transmit-interval": 1000,
+    }])
+
+    expect_routes(
+        tgen.gears["r2"], {
+            "10.254.254.1/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.1"
+            }],
+            "10.254.254.3/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.1"
+            }],
+            "10.254.254.100/32": [{
+                "code": "S",
+                "subCode": "r",
+                "from": "self"
+            }]
+        }, 40)
+
+    expect_bfd_peers(tgen.gears["r2"], [{
+        "peer": "192.168.0.1",
+        "status": "up",
+        "receive-interval": 1000,
+        "transmit-interval": 1000,
+    }])
+
+    expect_routes(
+        tgen.gears["r3"], {
+            "10.254.254.1/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.1.1"
+            }],
+            "10.254.254.2/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.1.1"
+            }],
+            "10.254.254.100/32": [{
+                "code": "S",
+                "subCode": "r",
+                "from": "self"
+            }]
+        }, 40)
+
+    expect_bfd_peers(tgen.gears["r3"], [{
+        "peer": "192.168.1.1",
+        "status": "up",
+        "receive-interval": 1000,
+        "transmit-interval": 1000,
+    }])
+
+
+def test_rip_bfd_convergence(tgen):
+    "Test that RIP drop the gone neighbor routes."
+
+    tgen.gears["r3"].link_enable("r3-eth0", False)
+
+    expect_routes(
+        tgen.gears["r1"], {
+            "10.254.254.2/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.2"
+            }],
+            "10.254.254.3/32": None,
+            "10.254.254.100/32": [{
+                "code": "R",
+                "subCode": "n",
+                "from": "192.168.0.2",
+            }]
+        }, 6)
+
+    expect_routes(
+        tgen.gears["r3"], {
+            "10.254.254.1/32": None,
+            "10.254.254.2/32": None,
+            "10.254.254.100/32": [{
+                "code": "S",
+                "subCode": "r",
+                "from": "self"
+            }]
+        }, 6)
+
+
+def test_memory_leak(tgen):
+    "Run the memory leak test and report results."
+
+    if not tgen.is_memleak_enabled():
+        pytest.skip("Memory leak test/report is disabled")
+
+    tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/rip_passive_interface/__init__.py b/tests/topotests/rip_passive_interface/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/topotests/rip_passive_interface/r1/frr.conf b/tests/topotests/rip_passive_interface/r1/frr.conf
new file mode 100644 (file)
index 0000000..d8eb9a3
--- /dev/null
@@ -0,0 +1,9 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+router rip
+ allow-ecmp
+ network 192.168.1.0/24
+ timers basic 5 15 10
+exit
diff --git a/tests/topotests/rip_passive_interface/r2/frr.conf b/tests/topotests/rip_passive_interface/r2/frr.conf
new file mode 100644 (file)
index 0000000..d7ea6f3
--- /dev/null
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_passive_interface/r3/frr.conf b/tests/topotests/rip_passive_interface/r3/frr.conf
new file mode 100644 (file)
index 0000000..2362c47
--- /dev/null
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r3-eth0
+ ip address 192.168.1.3/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_passive_interface/test_rip_passive_interface.py b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py
new file mode 100644 (file)
index 0000000..c2b28c4
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if RIP `passive-interface default` and `no passive-interface IFNAME`
+combination works as expected.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.ripd]
+
+
+def setup_module(mod):
+    topodef = {"s1": ("r1", "r2", "r3")}
+    tgen = Topogen(topodef, mod.__name__)
+    tgen.start_topology()
+
+    router_list = tgen.routers()
+
+    for _, (rname, router) in enumerate(router_list.items(), 1):
+        router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+    tgen.start_router()
+
+
+def teardown_module(mod):
+    tgen = get_topogen()
+    tgen.stop_topology()
+
+
+def test_rip_passive_interface():
+    tgen = get_topogen()
+
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    r1 = tgen.gears["r1"]
+    r2 = tgen.gears["r2"]
+
+    def _show_routes(nh_num):
+        output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.1/32 json"))
+        expected = {
+            "10.10.10.1/32": [
+                {
+                    "internalNextHopNum": nh_num,
+                    "internalNextHopActiveNum": nh_num,
+                }
+            ]
+        }
+        return topotest.json_cmp(output, expected)
+
+    test_func = functools.partial(_show_routes, 2)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+    assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+    step("Configure `passive-interface default` on r2")
+    r2.vtysh_cmd(
+        """
+    configure terminal
+        router rip
+            passive-interface default
+    """
+    )
+
+    test_func = functools.partial(_show_routes, 1)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+    assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 1"
+
+    step("Configure `no passive-interface r2-eth0` on r2 towards r1")
+    r2.vtysh_cmd(
+        """
+    configure terminal
+        router rip
+            no passive-interface r2-eth0
+    """
+    )
+
+    test_func = functools.partial(_show_routes, 2)
+    _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+    assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2"
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 31ad46ab2ecafbfe2bc062351af3f5f56be35a9b..19fff1a8289e2b8fb6eb5315725b2be0d0df26b1 100644 (file)
@@ -18,5 +18,5 @@ Routing Protocol is "rip"
     r1-eth3
   Routing Information Sources:
     Gateway          BadPackets BadRoutes  Distance Last Update
-    193.1.1.2                0         0       120   XX:XX:XX
+    193.1.1.2                 0         0       120    XX:XX:XX
   Distance: (default is 120)
index 99841a62b0ed921d7378a70fc425834d660a9af1..468b7aece85ff6f1085a6632406f1ddce517e9f4 100644 (file)
@@ -14,6 +14,6 @@ Routing Protocol is "rip"
     193.1.2.0/24
   Routing Information Sources:
     Gateway          BadPackets BadRoutes  Distance Last Update
-    193.1.1.1                0         0       120   XX:XX:XX
-    193.1.2.2                0         0       120   XX:XX:XX
+    193.1.1.1                 0         0       120    XX:XX:XX
+    193.1.2.2                 0         0       120    XX:XX:XX
   Distance: (default is 120)
index 040d3c32a1a0a23cb713501382bdb9722447f149..f423e83f24de25ff20c72b82935e92c1ab5fff84 100644 (file)
@@ -12,5 +12,5 @@ Routing Protocol is "rip"
     193.1.2.0/24
   Routing Information Sources:
     Gateway          BadPackets BadRoutes  Distance Last Update
-    193.1.2.1                0         0       120   XX:XX:XX
+    193.1.2.1                 0         0       120    XX:XX:XX
   Distance: (default is 120)
index 6137471ea69096795015fd037dd6b94568c202ef..e2863218b0c1546f49e5d3e045c15d293f82e126 100644 (file)
@@ -51,7 +51,8 @@ def config_macvlan(tgen, r_str, device, macvlan):
 
 def setup_module(mod):
     "Sets up the pytest environment"
-    topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")}
+    # 8 links to 8 switches on r1
+    topodef = {"s{}".format(x): ("r1",) for x in range(1, 9)}
     tgen = Topogen(topodef, mod.__name__)
     tgen.start_topology()
 
diff --git a/tools/emacs.dir-locals.el b/tools/emacs.dir-locals.el
new file mode 100644 (file)
index 0000000..b2d7cf3
--- /dev/null
@@ -0,0 +1,8 @@
+;;; Directory Local Variables
+;;; For more information see (info "(emacs) Directory Variables")
+;;; Match project coding conventions
+
+((c-mode . ((indent-tabs-mode . t)
+            (show-trailing-whitespace . t)
+            (c-basic-offset . 8)))
+ (json-mode . ((js-indent-level 4))))
index 490e519ae9d7e47e5b655551a1ceec2479dc1ccd..0e0aec98392d68315b94ab3297b0eb3909fbfab3 100755 (executable)
@@ -788,6 +788,8 @@ def bgp_delete_nbr_remote_as_line(lines_to_add):
     # remote-as config.
 
     pg_dict = dict()
+    found_pg_cmd = False
+
     # Find all peer-group commands; create dict of each peer-group
     # to store assoicated neighbor as value
     for ctx_keys, line in lines_to_add:
@@ -809,6 +811,10 @@ def bgp_delete_nbr_remote_as_line(lines_to_add):
                 }
                 found_pg_cmd = True
 
+    # Do nothing if there is no any "peer-group"
+    if found_pg_cmd is False:
+        return
+
     # Find peer-group with remote-as command, also search neighbor
     # associated to peer-group and store into peer-group dict
     for ctx_keys, line in lines_to_add:
@@ -850,7 +856,7 @@ def bgp_delete_nbr_remote_as_line(lines_to_add):
                 for pg in pg_dict[ctx_keys[0]]:
                     if pg_dict[ctx_keys[0]][pg]["remoteas"] == True:
                         for nbr in pg_dict[ctx_keys[0]][pg]["nbr"]:
-                            if re_nbr_rmtas.group(1) in nbr:
+                            if re_nbr_rmtas.group(1) == nbr:
                                 lines_to_del_from_add.append((ctx_keys, line))
 
     for ctx_keys, line in lines_to_del_from_add:
index 32ee4bb136a59c2eccad6478992f5746a7323888..5245c74689241fb1d4180fd69184115674043f46 100644 (file)
@@ -6,11 +6,12 @@
  */
 #include <zebra.h>
 
+#include <getopt.h>
+
 #include <lib/version.h>
 
 #include "lib/command.h"
 #include "lib/filter.h"
-#include "lib/getopt.h"
 #include "lib/if.h"
 #include "lib/libfrr.h"
 #include "lib/log.h"
index 0a164ec16d5b3cbf6515ab63740a1d1cda39480d..04f7ff65e9f64f2026772761d898e63943b9e05f 100644 (file)
@@ -49,19 +49,6 @@ char *vtysh_pager_name = NULL;
 /* VTY should add timestamp */
 bool vtysh_add_timestamp;
 
-/* VTY shell client structure */
-struct vtysh_client {
-       int fd;
-       const char *name;
-       int flag;
-       char path[MAXPATHLEN];
-       struct vtysh_client *next;
-
-       struct event *log_reader;
-       int log_fd;
-       uint32_t lost_msgs;
-};
-
 static bool stderr_tty;
 static bool stderr_stdout_same;
 
@@ -119,6 +106,10 @@ static void vtysh_pager_envdef(bool fallback)
 
 /* --- */
 
+/*
+ * When updating this array, remember to change the array size here and in
+ * vtysh.h
+ */
 struct vtysh_client vtysh_client[] = {
        {.name = "mgmtd", .flag = VTYSH_MGMTD},
        {.name = "zebra", .flag = VTYSH_ZEBRA},
@@ -1184,6 +1175,13 @@ static struct cmd_node isis_node = {
        .parent_node = CONFIG_NODE,
        .prompt = "%s(config-router)# ",
 };
+
+static struct cmd_node isis_flex_algo_node = {
+       .name = "isis-flex-algo",
+       .node = ISIS_FLEX_ALGO_NODE,
+       .parent_node = ISIS_NODE,
+       .prompt = "%s(config-router-flex-algo)# ",
+};
 #endif /* HAVE_ISISD */
 
 #ifdef HAVE_FABRICD
@@ -2104,6 +2102,14 @@ DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd,
        vty->node = ISIS_NODE;
        return CMD_SUCCESS;
 }
+
+DEFUNSH(VTYSH_ISISD, isis_flex_algo, isis_flex_algo_cmd, "flex-algo (128-255)",
+       "Flexible Algorithm\n"
+       "Flexible Algorithm Number\n")
+{
+       vty->node = ISIS_FLEX_ALGO_NODE;
+       return CMD_SUCCESS;
+}
 #endif /* HAVE_ISISD */
 
 #ifdef HAVE_FABRICD
@@ -2587,6 +2593,18 @@ DEFUNSH(VTYSH_ISISD, vtysh_quit_isisd, vtysh_quit_isisd_cmd, "quit",
 {
        return vtysh_exit_isisd(self, vty, argc, argv);
 }
+
+DEFUNSH(VTYSH_ISISD, vtysh_exit_isis_flex_algo, vtysh_exit_isis_flex_algo_cmd,
+       "exit", "Exit current mode and down to previous mode\n")
+{
+       return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_ISISD, vtysh_quit_isis_flex_algo, vtysh_quit_isis_flex_algo_cmd,
+       "quit", "Exit current mode and down to previous mode\n")
+{
+       return vtysh_exit_isisd(self, vty, argc, argv);
+}
 #endif /* HAVE_ISISD */
 
 #if HAVE_BFDD > 0
@@ -3545,7 +3563,7 @@ DEFUN (vtysh_copy_to_running,
        int ret;
        const char *fname = argv[1]->arg;
 
-       ret = vtysh_read_config(fname, true);
+       ret = vtysh_apply_config(fname, true, false);
 
        /* Return to enable mode - the 'read_config' api leaves us up a level */
        vtysh_execute_no_pager("enable");
@@ -4711,6 +4729,12 @@ void vtysh_init_vty(void)
        install_element(ISIS_NODE, &vtysh_exit_isisd_cmd);
        install_element(ISIS_NODE, &vtysh_quit_isisd_cmd);
        install_element(ISIS_NODE, &vtysh_end_all_cmd);
+
+       install_node(&isis_flex_algo_node);
+       install_element(ISIS_NODE, &isis_flex_algo_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &vtysh_exit_isis_flex_algo_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &vtysh_quit_isis_flex_algo_cmd);
+       install_element(ISIS_FLEX_ALGO_NODE, &vtysh_end_all_cmd);
 #endif /* HAVE_ISISD */
 
        /* fabricd */
index cb6357f5e6525f734b8c8d0f58c38ad0daa22c07..1c2cca9d902cf37b4f76fd114fec37f733fce47a 100644 (file)
@@ -98,7 +98,7 @@ void config_add_line(struct list *, const char *);
 
 int vtysh_mark_file(const char *filename);
 
-int vtysh_read_config(const char *filename, bool dry_run);
+int vtysh_apply_config(const char *config_file_path, bool dry_run, bool fork);
 int vtysh_write_config_integrated(void);
 
 void vtysh_config_parse_line(void *, const char *);
@@ -119,4 +119,18 @@ extern int user_mode;
 
 extern bool vtysh_add_timestamp;
 
+struct vtysh_client {
+       int fd;
+       const char *name;
+       int flag;
+       char path[MAXPATHLEN];
+       struct vtysh_client *next;
+
+       struct event *log_reader;
+       int log_fd;
+       uint32_t lost_msgs;
+};
+
+extern struct vtysh_client vtysh_client[22];
+
 #endif /* VTYSH_H */
index 905761a01110a1b179bf1418b1a15d9e54fce6d2..2949faa4275bc1527c6682d6d9cd3c1faef2749f 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <zebra.h>
+#include <sys/wait.h>
 
 #include "command.h"
 #include "linklist.h"
@@ -625,18 +626,20 @@ static int vtysh_read_file(FILE *confp, bool dry_run)
        return (ret);
 }
 
-/* Read up configuration file from config_default_dir. */
-int vtysh_read_config(const char *config_default_dir, bool dry_run)
+/*
+ * Read configuration file and send it to all connected daemons
+ */
+static int vtysh_read_config(const char *config_file_path, bool dry_run)
 {
        FILE *confp = NULL;
        bool save;
        int ret;
 
-       confp = fopen(config_default_dir, "r");
+       confp = fopen(config_file_path, "r");
        if (confp == NULL) {
                fprintf(stderr,
                        "%% Can't open configuration file %s due to '%s'.\n",
-                       config_default_dir, safe_strerror(errno));
+                       config_file_path, safe_strerror(errno));
                return CMD_ERR_NO_FILE;
        }
 
@@ -648,7 +651,103 @@ int vtysh_read_config(const char *config_default_dir, bool dry_run)
 
        vtysh_add_timestamp = save;
 
-       return (ret);
+       return ret;
+}
+
+int vtysh_apply_config(const char *config_file_path, bool dry_run, bool do_fork)
+{
+       /*
+        * We need to apply the whole config file to all daemons. Instead of
+        * having one client talk to N daemons, we fork N times and let each
+        * child handle one daemon.
+        */
+       pid_t fork_pid = getpid();
+       int status = 0;
+       int ret;
+       int my_client_type;
+       char my_client[64];
+
+       if (do_fork) {
+               for (unsigned int i = 0; i < array_size(vtysh_client); i++) {
+                       /* Store name of client this fork will handle */
+                       strlcpy(my_client, vtysh_client[i].name,
+                               sizeof(my_client));
+                       my_client_type = vtysh_client[i].flag;
+                       fork_pid = fork();
+
+                       /* If child, break */
+                       if (fork_pid == 0)
+                               break;
+               }
+
+               /* parent, wait for children */
+               if (fork_pid != 0) {
+                       int keep_status = 0;
+
+                       fprintf(stdout,
+                               "Waiting for children to finish applying config...\n");
+                       while (wait(&status) > 0) {
+                               if (!keep_status && WEXITSTATUS(status))
+                                       keep_status = WEXITSTATUS(status);
+                       }
+
+                       /*
+                        * This will return the first status received
+                        * that failed( if that happens ).  This is
+                        * good enough for the moment
+                        */
+                       return keep_status;
+               }
+
+               /*
+                * children, grow up to be cowboys
+                */
+               for (unsigned int i = 0; i < array_size(vtysh_client); i++) {
+                       if (my_client_type != vtysh_client[i].flag) {
+                               struct vtysh_client *cl;
+
+                               /*
+                                * If this is a client we aren't responsible
+                                * for, disconnect
+                                */
+                               for (cl = &vtysh_client[i]; cl; cl = cl->next) {
+                                       if (cl->fd >= 0)
+                                               close(cl->fd);
+                                       cl->fd = -1;
+                               }
+                       } else if (vtysh_client[i].fd == -1 &&
+                                  vtysh_client[i].next == NULL) {
+                               /*
+                                * If this is the client we are responsible
+                                * for, but we aren't already connected to that
+                                * client, that means the client isn't up in
+                                * the first place and we can exit early
+                                */
+                               exit(0);
+                       }
+               }
+
+               fprintf(stdout, "[%d|%s] sending configuration\n", getpid(),
+                       my_client);
+       }
+
+       ret = vtysh_read_config(config_file_path, dry_run);
+
+       if (ret) {
+               if (do_fork)
+                       fprintf(stderr,
+                               "[%d|%s] Configuration file[%s] processing failure: %d\n",
+                               getpid(), my_client, frr_config, ret);
+               else
+                       fprintf(stderr,
+                               "Configuration file[%s] processing failure: %d\n",
+                               frr_config, ret);
+       } else if (do_fork) {
+               fprintf(stderr, "[%d|%s] done\n", getpid(), my_client);
+               exit(0);
+       }
+
+       return ret;
 }
 
 /* We don't write vtysh specific into file from vtysh. vtysh.conf should
index c22889a4f6cd785963019c67f4cb523e3bbe7a48..860d79d5f9adaf02f137a5f17710803f56d580d8 100644 (file)
@@ -7,7 +7,6 @@
 
 #include <sys/un.h>
 #include <setjmp.h>
-#include <sys/wait.h>
 #include <pwd.h>
 #include <sys/file.h>
 #include <unistd.h>
@@ -178,6 +177,8 @@ static void usage(int status)
                       "-u  --user               Run as an unprivileged user\n"
                       "-w, --writeconfig        Write integrated config (frr.conf) and exit\n"
                       "-H, --histfile           Override history file\n"
+                      "-t, --timestamp          Print a timestamp before going to shell or reading the configuration\n"
+                      "    --no-fork            Don't fork clients to handle daemons (slower for large configs)\n"
                       "-h, --help               Display this help and exit\n\n"
                       "Note that multiple commands may be executed from the command\n"
                       "line by passing multiple -c args, or by embedding linefeed\n"
@@ -191,6 +192,7 @@ static void usage(int status)
 /* VTY shell options, we use GNU getopt library. */
 #define OPTION_VTYSOCK 1000
 #define OPTION_CONFDIR 1001
+#define OPTION_NOFORK 1002
 struct option longopts[] = {
        {"boot", no_argument, NULL, 'b'},
        /* For compatibility with older zebra/quagga versions */
@@ -210,6 +212,7 @@ struct option longopts[] = {
        {"pathspace", required_argument, NULL, 'N'},
        {"user", no_argument, NULL, 'u'},
        {"timestamp", no_argument, NULL, 't'},
+       {"no-fork", no_argument, NULL, OPTION_NOFORK},
        {0}};
 
 bool vtysh_loop_exited;
@@ -321,6 +324,7 @@ int main(int argc, char **argv, char **env)
        int dryrun = 0;
        int boot_flag = 0;
        bool ts_flag = false;
+       bool no_fork = false;
        const char *daemon_name = NULL;
        const char *inputfile = NULL;
        struct cmd_rec {
@@ -392,6 +396,9 @@ int main(int argc, char **argv, char **env)
                        ditch_suid = 1; /* option disables SUID */
                        snprintf(sysconfdir, sizeof(sysconfdir), "%s/", optarg);
                        break;
+               case OPTION_NOFORK:
+                       no_fork = true;
+                       break;
                case 'N':
                        if (strchr(optarg, '/') || strchr(optarg, '.')) {
                                fprintf(stderr,
@@ -440,6 +447,10 @@ int main(int argc, char **argv, char **env)
                }
        }
 
+       /* No need for forks if we're talking to 1 daemon */
+       if (daemon_name)
+               no_fork = true;
+
        if (ditch_suid) {
                elevuid = realuid;
                elevgid = realgid;
@@ -483,7 +494,7 @@ int main(int argc, char **argv, char **env)
                /* Read vtysh configuration file before connecting to daemons.
                 * (file may not be readable to calling user in SUID mode) */
                suid_on();
-               vtysh_read_config(vtysh_config, dryrun);
+               vtysh_apply_config(vtysh_config, dryrun, false);
                suid_off();
        }
        /* Error code library system */
@@ -502,9 +513,9 @@ int main(int argc, char **argv, char **env)
        /* Start execution only if not in dry-run mode */
        if (dryrun && !cmd) {
                if (inputfile) {
-                       ret = vtysh_read_config(inputfile, dryrun);
+                       ret = vtysh_apply_config(inputfile, dryrun, false);
                } else {
-                       ret = vtysh_read_config(frr_config, dryrun);
+                       ret = vtysh_apply_config(frr_config, dryrun, false);
                }
 
                exit(ret);
@@ -583,10 +594,17 @@ int main(int argc, char **argv, char **env)
                return vtysh_write_config_integrated();
        }
 
-       if (inputfile) {
+       if (boot_flag)
+               inputfile = frr_config;
+
+       if (inputfile || boot_flag) {
                vtysh_flock_config(inputfile);
-               ret = vtysh_read_config(inputfile, dryrun);
+               ret = vtysh_apply_config(inputfile, dryrun, !no_fork);
                vtysh_unflock_config();
+
+               if (no_error)
+                       ret = 0;
+
                exit(ret);
        }
 
@@ -703,23 +721,6 @@ int main(int argc, char **argv, char **env)
                exit(0);
        }
 
-       /* Boot startup configuration file. */
-       if (boot_flag) {
-               vtysh_flock_config(frr_config);
-               ret = vtysh_read_config(frr_config, dryrun);
-               vtysh_unflock_config();
-               if (ret) {
-                       fprintf(stderr,
-                               "Configuration file[%s] processing failure: %d\n",
-                               frr_config, ret);
-                       if (no_error)
-                               exit(0);
-                       else
-                               exit(ret);
-               } else
-                       exit(0);
-       }
-
        vtysh_readline_init();
 
        vty_hello(vty);
index 666f2bb23561e73adf1dd477d5367e860964769b..23e5b0227ca9d4692fd95bac08473d250d9c9fc7 100644 (file)
@@ -186,6 +186,12 @@ module frr-bgp-route-map {
       "Set BGP extended community attribute";
   }
 
+  identity set-extcommunity-nt {
+    base frr-route-map:rmap-set-type;
+    description
+      "Set BGP extended community attribute";
+  }
+
   identity set-extcommunity-soo {
     base frr-route-map:rmap-set-type;
     description
@@ -786,6 +792,17 @@ module frr-bgp-route-map {
       }
     }
 
+    case extcommunity-nt {
+      when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-nt')";
+      description
+        "Value of the ext-community";
+      leaf extcommunity-nt {
+        type string;
+        description
+          "Set BGP ext-community node-target attribute";
+      }
+    }
+
     case extcommunity-soo {
       when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-soo')";
       description
diff --git a/yang/frr-if-rmap.yang b/yang/frr-if-rmap.yang
new file mode 100644 (file)
index 0000000..0fa2c5e
--- /dev/null
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: BSD-2-Clause
+module frr-if-rmap {
+  yang-version 1.1;
+  namespace "http://frrouting.org/yang/frr-if-rmap";
+  prefix frr-if-map;
+
+  import frr-interface {
+    prefix frr-interface;
+  }
+
+  import frr-route-map {
+    prefix frr-route-map;
+  }
+
+  organization
+    "FRRouting";
+  contact
+    "FRR Users List:       <mailto:frog@lists.frrouting.org>
+     FRR Development List: <mailto:dev@lists.frrouting.org>";
+  description
+    "This module defines route map settings
+
+     Copyright 2023 LabN Consulting L.L.C
+
+     Redistribution and use in source and binary forms, with or without
+     modification, are permitted provided that the following conditions
+     are met:
+
+     1. Redistributions of source code must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+
+     2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+     \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
+
+  revision 2023-04-09 {
+    description
+      "Initial revision";
+    reference "FRRouting";
+  }
+
+  grouping if-route-maps-group {
+    description "Grouping for interface route maps";
+
+    container if-route-maps {
+      description "Collection of interface route-maps";
+
+      list if-route-map {
+        must "in-route-map or out-route-map";
+        key "interface";
+        description "Collection of route-maps for an interface";
+
+        leaf "interface" {
+          type frr-interface:interface-ref;
+          description "The interface the route maps are associated with";
+        }
+        leaf "in-route-map" {
+          type frr-route-map:route-map-name;
+          description "Name of the ingress route map";
+        }
+        leaf "out-route-map" {
+          type frr-route-map:route-map-name;
+          description "Name of the egress route map";
+        }
+      }
+    }
+  }
+}
index 66ec6a410d5b682c6086183ebc7b1d26573dec46..ae69d53ccc0ec9603dbe13226fc14fd05dd1aecb 100644 (file)
@@ -1212,6 +1212,20 @@ module frr-isisd {
           "Advertise prefixes of passive interfaces only";
       }
 
+      leaf admin-group-send-zero {
+        type boolean;
+        default "false";
+        description
+          "Allow sending the default admin-group value of 0x00000000";
+      }
+
+      leaf asla-legacy-flag {
+        type boolean;
+        default "false";
+        description
+          "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV.";
+      }
+
       container lsp {
         description
           "Configuration of Link-State Packets (LSP) parameters";
@@ -1594,6 +1608,13 @@ module frr-isisd {
           "Log changes to the IS-IS adjacencies in this area.";
       }
 
+      leaf log-pdu-drops {
+        type boolean;
+        default "false";
+        description
+          "Log any dropped PDUs in this area.";
+      }
+
       container mpls-te {
         presence "Present if MPLS-TE is enabled.";
         description
@@ -1616,6 +1637,107 @@ module frr-isisd {
         }
       }
 
+      container flex-algos {
+        description
+          "Flex-Algo Table";
+        list flex-algo {
+          key "flex-algo";
+          description
+            "Configuration for an IS-IS Flex-Algo";
+          leaf advertise-definition {
+            type boolean;
+            description
+              "If TRUE, Flex-Algo definition is advertised";
+          }
+          container affinity-include-alls {
+            description
+              "Set the include-all affinity";
+            leaf-list affinity-include-all {
+              type string;
+              max-elements "256";
+              description
+                "Array of Attribute Names";
+            }
+          }
+          container affinity-include-anies {
+            description
+              "Set the include-any affinity";
+            leaf-list affinity-include-any {
+              type string;
+              max-elements "256";
+              description
+                "Array of Attribute Names";
+            }
+          }
+          container affinity-exclude-anies {
+            description
+              "Set the exclude-any affinity";
+            leaf-list affinity-exclude-any {
+              type string;
+              max-elements "256";
+              description
+                "Array of Attribute Names";
+            }
+          }
+          leaf prefix-metric {
+            type empty;
+            description
+              "Use Flex-algo Prefix Metric";
+          }
+          leaf metric-type {
+            default "igp";
+            description
+              "Set the Flex-Algo metric-type";
+                   type enumeration {
+                     enum "igp" {
+                       value 0;
+                       description
+                         "IGP Metric";
+                     }
+                     enum "min-uni-link-delay" {
+                       value 1;
+                       description
+                         "RFC 8570 Sec 4.2 Min Unidirectional Link Delay";
+                     }
+                     enum "te-default" {
+                       value 2;
+                       description
+                         "RFC 5305 Sec 3.7 Traffic Engineering Default Metric";
+                     }
+                   }
+          }
+          leaf priority {
+            type uint32 {
+              range "0..255";
+            }
+            description
+              "Set the Flex-Algo priority";
+          }
+          leaf dplane-sr-mpls {
+            type empty;
+            description
+                 "Advertise and participate in the Flex-Algo Segment-Routing MPLS data-plane";
+          }
+          leaf dplane-srv6 {
+            type empty;
+            description
+                 "Advertise and participate in the Flex-Algo Segment-Routing SRv6 data-plane";
+          }
+          leaf dplane-ip {
+            type empty;
+            description
+                 "Advertise and participate in the Flex-Algo IP data-plane";
+          }
+          leaf flex-algo {
+            type uint32 {
+              range "128..255";
+            }
+            description
+              "Flex-Algo";
+          }
+        }
+      }
+
       container segment-routing {
         description
           "Segment Routing global configuration.";
@@ -1744,6 +1866,87 @@ module frr-isisd {
             }
           }
         }
+        container algorithm-prefix-sids {
+          description
+            "Algorithm SID Table";
+          list algorithm-prefix-sid {
+            key "prefix algo";
+            description
+              "Assign prefix SID for algorithm to an
+               interface, ISISPHPFlag will be rejected
+               if set to disable, ISISEXPLICITNULLFlag
+               will override the value of ISISPHPFlag";
+            leaf algo {
+              type uint32 {
+                range "128..255";
+              }
+              description
+                "Algorithm";
+            }
+            leaf prefix {
+              type inet:ip-prefix;
+              mandatory true;
+              description
+                "Connected prefix sid.";
+            }
+            leaf sid-value-type {
+              type enumeration {
+                enum "index" {
+                  value 0;
+                  description
+                    "The value will be interpreted as an index.";
+                }
+                enum "absolute" {
+                  value 1;
+                  description
+                    "The value will become interpreted as an absolute
+                     value.";
+                }
+              }
+              default "index";
+              description
+                "This leaf defines how value must be interpreted.";
+            }
+            leaf sid-value {
+              type uint32 {
+                range "0..1048575";
+              }
+              mandatory true;
+              description
+                "Value associated with prefix. The value must be
+                 interpreted in the context of sid-value-type.";
+            }
+            leaf last-hop-behavior {
+              type enumeration {
+                enum "explicit-null" {
+                  value 0;
+                  description
+                    "Use explicit-null for the SID.";
+                }
+                enum "no-php" {
+                  value 1;
+                  description
+                    "Do not use Penultimate Hop Popping (PHP)
+                     for the SID.";
+                }
+                enum "php" {
+                  value 2;
+                  description
+                    "Use PHP for the SID.";
+                }
+              }
+              default "php";
+              description
+                "Configure last hop behavior.";
+            }
+            leaf n-flag-clear {
+              type boolean;
+              default "false";
+              description
+                "Not a node SID";
+            }
+          }
+        }
       }
 
       container mpls {
index 746bf35d28fe286b80ba87c67cd67f6dc87d22d8..a4bf50d958be3807c341eb3428f573d75e1e5109 100644 (file)
@@ -10,9 +10,18 @@ module frr-ripd {
   import ietf-yang-types {
     prefix yang;
   }
+  import frr-if-rmap {
+    prefix frr-if-rmap;
+  }
+  import frr-bfdd {
+    prefix frr-bfdd;
+  }
   import frr-interface {
     prefix frr-interface;
   }
+  import frr-nexthop {
+    prefix frr-nexthop;
+  }
   import frr-vrf {
     prefix frr-vrf;
   }
@@ -60,6 +69,7 @@ module frr-ripd {
     description
       "Changed interface references to use
       frr-interface:interface-ref typedef";
+    reference "FRRouting";
   }
   revision 2017-12-06 {
     description
@@ -69,10 +79,35 @@ module frr-ripd {
        RFC 2453: RIP Version 2.";
   }
 
+  typedef rip-route-type {
+    type enumeration {
+      enum normal {
+        value 0;
+        description "Normal RIP route type.";
+      }
+      enum static {
+        value 1;
+        description "Static RIP route type.";
+      }
+      enum default {
+        value 2;
+        description "Default RIP route type.";
+      }
+      enum redistribute {
+        value 3;
+        description "Redistribute RIP route type.";
+      }
+      enum interface {
+        value 4;
+        description "Interface RIP route type.";
+      }
+    }
+    description
+      "Types of RIP routes.";
+  }
+
   container ripd {
-    /*
-     * Routing instance configuration.
-     */
+    description "rip routing instance data";
     list instance {
       key "vrf";
       description
@@ -229,9 +264,9 @@ module frr-ripd {
           "Redistributes routes learned from other routing protocols.";
         leaf protocol {
           type frr-route-types:frr-route-types-v4;
+          must '. != "rip"';
           description
             "Routing protocol.";
-          must '. != "rip"';
         }
         leaf route-map {
           type frr-route-map:route-map-ref;
@@ -253,6 +288,9 @@ module frr-ripd {
              is 0.";
         }
       }
+
+      uses frr-if-rmap:if-route-maps-group;
+
       leaf-list static-route {
         type inet:ipv4-prefix;
         description
@@ -291,11 +329,8 @@ module frr-ripd {
         }
       }
       container version {
+        description "version of rip";
         leaf receive {
-          must
-            '(. = "1" and ../send = "1") or ' +
-            '(. = "2" and ../send = "2") or ' +
-            '(. = "1-2" and ../send = "2")';
           type enumeration {
             enum "1" {
               value 1;
@@ -313,15 +348,15 @@ module frr-ripd {
                 "Accept both RIPv1 and RIPv2 updates.";
             }
           }
+          must
+            '(. = "1" and ../send = "1") or ' +
+            '(. = "2" and ../send = "2") or ' +
+            '(. = "1-2" and ../send = "2")';
           default "1-2";
           description
             "Advertisement reception - Version control.";
         }
         leaf send {
-          must
-            '(../receive = "1" and . = "1") or ' +
-            '(../receive = "2" and . = "2") or ' +
-            '(../receive = "1-2" and . = "2")';
           type enumeration {
             enum "1" {
               value 1;
@@ -334,12 +369,22 @@ module frr-ripd {
                 "Send RIPv2 updates only.";
             }
           }
+          must
+            '(../receive = "1" and . = "1") or ' +
+            '(../receive = "2" and . = "2") or ' +
+            '(../receive = "1-2" and . = "2")';
           default "2";
           description
             "Advertisement transmission - Version control.";
         }
       }
 
+      leaf default-bfd-profile {
+        description
+          "Use this BFD profile for all peers by default.";
+        type frr-bfdd:profile-ref;
+      }
+
       /*
        * Operational data.
        */
@@ -399,23 +444,81 @@ module frr-ripd {
                  separated by the slash (/) character. The range of
                  values for the prefix-length is 0 to 32.";
             }
+            container nexthops {
+              description "container of nexthops";
+              list nexthop {
+                description "A list of nexthop objects.";
+                leaf nh-type {
+                  type frr-nexthop:nexthop-type;
+                  mandatory true;
+                  description
+                    "The nexthop type.";
+                }
+                leaf protocol {
+                  type frr-route-types:frr-route-types-v4;
+                  description
+                    "The protocol originating this route.";
+                }
+                leaf rip-type {
+                  type rip-route-type;
+                  description
+                    "The RIP type of route.";
+                }
+                leaf gateway {
+                  type inet:ipv4-address;
+                  description
+                    "The nexthop gateway address.";
+                }
+                leaf interface {
+                  type frr-interface:interface-ref;
+                  description
+                    "The nexthop egress interface.";
+                }
+                leaf from {
+                  type inet:ipv4-address;
+                  description
+                    "The nexthop gateway address.";
+                }
+                leaf tag {
+                  type uint32;
+                  default "0";
+                  description
+                    "Route tag";
+                }
+                leaf external-metric {
+                  type uint32;
+                  description
+                    "External metric if learned from external protocol.";
+                }
+                leaf expire-time {
+                  type uint32;
+                  description
+                    "Seconds before route expires.";
+                }
+              }
+            }
+            leaf metric {
+              type uint8 {
+                range "0..16";
+              }
+              description
+                "Route metric.";
+            }
+            /*
+             * Replaced by container `nexthops` above.
+             */
             leaf next-hop {
               type inet:ipv4-address;
+              status deprecated;
               description
                 "Next hop IPv4 address.";
             }
             leaf interface {
               type frr-interface:interface-ref;
+              status deprecated;
               description
                 "The interface that the route uses.";
             }
-            leaf metric {
-              type uint8 {
-                range "0..16";
-              }
-              description
-                "Route metric.";
-            }
           }
         }
       }
@@ -426,6 +529,7 @@ module frr-ripd {
    * Per-interface configuration data
    */
   augment "/frr-interface:lib/frr-interface:interface" {
+    description "rip interface data";
     container rip {
       description
         "RIP interface parameters.";
@@ -583,6 +687,24 @@ module frr-ripd {
             "Key-chain name.";
         }
       }
+
+      container bfd-monitoring {
+        presence
+          "Present if BFD is configured for RIP peers in this interface.";
+
+        leaf enable {
+          type boolean;
+          description
+            "Enable/disable BFD monitoring.";
+          default false;
+        }
+
+        leaf profile {
+          type frr-bfdd:profile-ref;
+          description
+            "BFD profile to use.";
+        }
+      }
     }
   }
 
index d7de4c398a3516a84239daa670f5cf8bb8186790..7b2b135fb5695c71f15202ac174dd7d185d86ec1 100644 (file)
@@ -10,6 +10,9 @@ module frr-ripngd {
   import ietf-yang-types {
     prefix yang;
   }
+  import frr-if-rmap {
+    prefix frr-if-rmap;
+  }
   import frr-interface {
     prefix frr-interface;
   }
@@ -196,6 +199,9 @@ module frr-ripngd {
              is 0.";
         }
       }
+
+      uses frr-if-rmap:if-route-maps-group;
+
       leaf-list static-route {
         type inet:ipv6-prefix;
         description
index 224b2caef777d77097bc49fe62b8217c519527e6..7cb13b60f2fc2b271e4946ea7a61979b02774f95 100644 (file)
@@ -160,6 +160,18 @@ module frr-route-map {
       "Set prefix/route metric";
   }
 
+  identity set-min-metric {
+    base rmap-set-type;
+    description
+      "Set minimum prefix/route metric";
+  }
+
+    identity set-max-metric {
+    base rmap-set-type;
+    description
+      "Set maximum prefix/route metric";
+  }
+
   identity set-tag {
     base rmap-set-type;
     description
@@ -346,6 +358,39 @@ module frr-route-map {
           }
         }
 
+        case set-min-metric {
+          when "derived-from-or-self(../action, 'set-min-metric')";
+          choice minimun-metric-value {
+            description
+              "Mimimum metric to set or use";
+            case min-metric {
+              leaf min-metric {
+                type uint32 {
+                  range "0..4294967295";
+                }
+                description
+                  "Use the following mimumn metric value";
+              }
+            }
+          }
+        }
+
+        case set-max-metric {
+          when "derived-from-or-self(../action, 'set-max-metric')";
+          choice maximum-metric-value {
+            description
+              "Maximum metric to set or use";
+            case max-metric {
+              leaf max-metric {
+                type uint32 {
+                  range "0..4294967295";
+                }
+                description
+                  "Use the following maximum metric value";
+              }
+            }
+          }
+        }
         case set-tag {
           when "derived-from-or-self(../action, 'set-tag')";
           leaf tag {
index 82a6a01474a46a1906c980c5a4e2a9c35b00f4b1..eb17c38dbc01796ff02c4fdab4d87ea12e8345f9 100644 (file)
@@ -24,6 +24,7 @@ dist_yangmodels_DATA += yang/frr-filter.yang
 dist_yangmodels_DATA += yang/frr-module-translator.yang
 dist_yangmodels_DATA += yang/frr-nexthop.yang
 dist_yangmodels_DATA += yang/frr-test-module.yang
+dist_yangmodels_DATA += yang/frr-if-rmap.yang
 dist_yangmodels_DATA += yang/frr-interface.yang
 dist_yangmodels_DATA += yang/frr-route-map.yang
 dist_yangmodels_DATA += yang/frr-zebra-route-map.yang
index b13daa3a97bc6d1309cab3ac866c0282b237a98b..fc140b07a34dbe1ba398f383c5eeb6423f338632 100644 (file)
@@ -645,7 +645,7 @@ static int zd_dpdk_init(void)
        zd_dpdk_vty_init();
 
        frr_with_privs (&zserv_privs) {
-               rc = rte_eal_init(ARRAY_SIZE(argv), argv);
+               rc = rte_eal_init(array_size(argv), argv);
        }
        if (rc < 0) {
                zlog_warn("EAL init failed %s", rte_strerror(rte_errno));
index 3a325df06c45bfd5a7d7126c405ae6eb5e148f1e..fcb692b715fe589c74324c8cb528af8d2f92f6aa 100644 (file)
@@ -2323,8 +2323,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
                        zlog_debug("RTM_DELLINK for %s(%u)", name,
                                   ifp->ifindex);
 
-               UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
-
                if (IS_ZEBRA_IF_BOND(ifp))
                        zebra_l2if_update_bond(ifp, false);
                if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
index 03376afc09f7d3938e610aa7dfcd4a14df0380bc..231ddc51dbec169052480665ab7648778a3b9761 100644 (file)
@@ -137,6 +137,8 @@ static int if_zebra_new_hook(struct interface *ifp)
        zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC;
        zebra_if->shutdown = IF_ZEBRA_DATA_OFF;
 
+       zebra_if->link_nsid = NS_UNKNOWN;
+
        zebra_if_nhg_dependents_init(zebra_if);
 
        zebra_ptm_if_init(zebra_if);
@@ -305,6 +307,14 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns,
        return NULL;
 }
 
+struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex)
+{
+       struct zebra_ns *zns;
+
+       zns = zebra_ns_lookup(ns_id);
+       return zns ? if_lookup_by_index_per_ns(zns, ifindex) : NULL;
+}
+
 const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex)
 {
        struct interface *ifp;
@@ -790,6 +800,8 @@ void if_delete_update(struct interface **pifp)
        if (ifp->vrf->vrf_id && !vrf_is_backend_netns())
                if_handle_vrf_change(ifp, VRF_DEFAULT);
 
+       UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
+
        /* Reset some zebra interface params to default values. */
        zif = ifp->info;
        if (zif) {
@@ -830,6 +842,9 @@ void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id)
        /* This is to issue an UPDATE or a DELETE, as appropriate. */
        zebra_interface_vrf_update_del(ifp, vrf_id);
 
+       if (if_is_vrf(ifp))
+               return;
+
        /* update VRF */
        if_update_to_new_vrf(ifp, vrf_id);
 
@@ -991,7 +1006,6 @@ void if_up(struct interface *ifp, bool install_connected)
 {
        struct zebra_if *zif;
        struct interface *link_if;
-       struct zebra_vrf *zvrf = ifp->vrf->info;
 
        zif = ifp->info;
        zif->up_count++;
@@ -1024,8 +1038,7 @@ void if_up(struct interface *ifp, bool install_connected)
                link_if = ifp;
                zebra_vxlan_svi_up(ifp, link_if);
        } else if (IS_ZEBRA_IF_VLAN(ifp)) {
-               link_if = if_lookup_by_index_per_ns(zvrf->zns,
-                                                   zif->link_ifindex);
+               link_if = zif->link;
                if (link_if)
                        zebra_vxlan_svi_up(ifp, link_if);
        } else if (IS_ZEBRA_IF_MACVLAN(ifp)) {
@@ -1049,7 +1062,6 @@ void if_down(struct interface *ifp)
 {
        struct zebra_if *zif;
        struct interface *link_if;
-       struct zebra_vrf *zvrf = ifp->vrf->info;
 
        zif = ifp->info;
        zif->down_count++;
@@ -1068,8 +1080,7 @@ void if_down(struct interface *ifp)
                link_if = ifp;
                zebra_vxlan_svi_down(ifp, link_if);
        } else if (IS_ZEBRA_IF_VLAN(ifp)) {
-               link_if = if_lookup_by_index_per_ns(zvrf->zns,
-                                                   zif->link_ifindex);
+               link_if = zif->link;
                if (link_if)
                        zebra_vxlan_svi_down(ifp, link_if);
        } else if (IS_ZEBRA_IF_MACVLAN(ifp)) {
@@ -1109,6 +1120,7 @@ void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,
        if (IS_ZEBRA_IF_VETH(ifp))
                return;
        zif = (struct zebra_if *)ifp->info;
+       zif->link_nsid = ns_id;
        zif->link_ifindex = link_ifindex;
        zif->link = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
                                              link_ifindex);
@@ -1145,8 +1157,8 @@ void zebra_if_update_all_links(struct zebra_ns *zns)
 
                /* update SVI linkages */
                if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) {
-                       zif->link = if_lookup_by_index_per_ns(
-                               zns, zif->link_ifindex);
+                       zif->link = if_lookup_by_index_per_nsid(
+                               zif->link_nsid, zif->link_ifindex);
                        if (IS_ZEBRA_DEBUG_KERNEL)
                                zlog_debug("interface %s/%d's lower fixup to %s/%d",
                                                ifp->name, ifp->ifindex,
index 4c6ebaa11dd784731ae082f0c5313a04b7adc431..e5545d6ba0895826ccdda777ee94050ef7116fb5 100644 (file)
@@ -195,6 +195,7 @@ struct zebra_if {
        struct list *mac_list;
 
        /* Link fields - for sub-interfaces. */
+       ns_id_t link_nsid;
        ifindex_t link_ifindex;
        struct interface *link;
 
@@ -259,6 +260,8 @@ extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t);
 extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *,
                                                  const char *);
 extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *);
+extern struct interface *if_lookup_by_index_per_nsid(ns_id_t nsid,
+                                                    uint32_t ifindex);
 extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int);
 
 extern void if_unlink_per_ns(struct interface *);
index ba43ae910b175538e51aa9caffdafc975c5343c2..81a30664453a5e43ebd247a7ae8a919bb7817afc 100644 (file)
@@ -175,11 +175,6 @@ static void sigint(void)
        if (zrouter.lsp_process_q)
                work_queue_free_and_null(&zrouter.lsp_process_q);
 
-       vrf_terminate();
-
-       ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
-       zebra_ns_notify_close();
-
        access_list_reset();
        prefix_list_reset();
        /*
@@ -190,6 +185,8 @@ static void sigint(void)
         */
        zebra_routemap_finish();
 
+       rib_update_finish();
+
        list_delete(&zrouter.client_list);
 
        /* Indicate that all new dplane work has been enqueued. When that
@@ -207,6 +204,11 @@ void zebra_finalize(struct event *dummy)
 {
        zlog_info("Zebra final shutdown");
 
+       vrf_terminate();
+
+       ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
+       zebra_ns_notify_close();
+
        /* Stop dplane thread and finish any cleanup */
        zebra_dplane_shutdown();
 
index 0e897881e440d4cb0c7774b967a0c187f4271ef7..a56bb05d684ef3731ce7e43788cc99d6c01745ea 100644 (file)
@@ -180,7 +180,7 @@ struct route_entry {
  * sub-queue 9: any other origin (if any) typically those that
  *              don't generate routes
  */
-#define MQ_SIZE 10
+#define MQ_SIZE 11
 struct meta_queue {
        struct list *subq[MQ_SIZE];
        uint32_t size; /* sum of lengths of all subqueues */
@@ -323,6 +323,7 @@ enum rib_update_event {
        RIB_UPDATE_OTHER,
        RIB_UPDATE_MAX
 };
+void rib_update_finish(void);
 
 int route_entry_update_nhe(struct route_entry *re,
                           struct nhg_hash_entry *new_nhghe);
@@ -601,6 +602,12 @@ static inline struct nexthop_group *rib_get_fib_backup_nhg(
        return &(re->fib_backup_ng);
 }
 
+extern void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto,
+                                   uint8_t instance);
+
+extern int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto,
+                         uint8_t instance);
+
 extern void zebra_vty_init(void);
 
 extern pid_t pid;
index fb1ebc682737d76fcf7e0a27006ebc9b95c69409..f6c1fdd78e43a9f6a24744f28d60d286c9278610 100644 (file)
@@ -3745,6 +3745,11 @@ dplane_route_update_internal(struct route_node *rn,
                                                 NEXTHOP_FLAG_FIB);
                        }
 
+                       if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && re &&
+                           (old_re != re) &&
+                           !CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))
+                               SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+
                        dplane_ctx_free(&ctx);
                        return ZEBRA_DPLANE_REQUEST_SUCCESS;
                }
@@ -5375,8 +5380,7 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link,
                atomic_fetch_add_explicit(
                        &zdplane_info.dg_gre_set_errors, 1,
                        memory_order_relaxed);
-               if (ctx)
-                       dplane_ctx_free(&ctx);
+               dplane_ctx_free(&ctx);
                result = ZEBRA_DPLANE_REQUEST_FAILURE;
        }
        return result;
@@ -6853,10 +6857,6 @@ void zebra_dplane_shutdown(void)
 
        zdplane_info.dg_run = false;
 
-       if (zdplane_info.dg_t_update)
-               event_cancel_async(zdplane_info.dg_t_update->master,
-                                  &zdplane_info.dg_t_update, NULL);
-
        frr_pthread_stop(zdplane_info.dg_pthread, NULL);
 
        /* Destroy pthread */
index ccc1b61e066c283a6265b22a59bbc3b8ffb1fa0b..6d5cd66143d3cbbe9100e9d131147233096aca4d 100644 (file)
@@ -1038,12 +1038,11 @@ int zebra_evpn_macip_send_msg_to_client(vni_t vni,
                char flag_buf[MACIP_BUF_SIZE];
 
                zlog_debug(
-                       "Send MACIP %s f %s MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s",
+                       "Send MACIP %s f %s state %u MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s",
                        (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
                        zclient_evpn_dump_macip_flags(flags, flag_buf,
                                                      sizeof(flag_buf)),
-                       macaddr, ip, seq, vni,
-                       es ? es->esi_str : "-",
+                       state, macaddr, ip, seq, vni, es ? es->esi_str : "-",
                        zebra_route_string(client->proto));
        }
 
@@ -1341,16 +1340,26 @@ int zebra_evpn_mac_send_add_to_client(vni_t vni, const struct ethaddr *macaddr,
 int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr,
                                      uint32_t flags, bool force)
 {
+       int state = ZEBRA_NEIGH_ACTIVE;
+
        if (!force) {
                if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE)
                    && !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE))
                        /* the host was not advertised - nothing  to delete */
                        return 0;
+
+               /* MAC is LOCAL and DUP_DETECTED, this local mobility event
+                * is not known to bgpd. Upon receiving local delete
+                * ask bgp to reinstall the best route (remote entry).
+                */
+               if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL) &&
+                   CHECK_FLAG(flags, ZEBRA_MAC_DUPLICATE))
+                       state = ZEBRA_NEIGH_INACTIVE;
        }
 
        return zebra_evpn_macip_send_msg_to_client(
-               vni, macaddr, NULL, 0 /* flags */, 0 /* seq */,
-               ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL);
+               vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, state, NULL,
+               ZEBRA_MACIP_DEL);
 }
 
 /*
@@ -1685,6 +1694,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn,
        struct zebra_mac *mac;
        bool inform_bgp = false;
        bool inform_dataplane = false;
+       bool mac_inactive = false;
        bool seq_change = false;
        bool es_change = false;
        uint32_t tmp_seq;
@@ -1701,6 +1711,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn,
                 */
                inform_bgp = true;
                inform_dataplane = true;
+               mac_inactive = true;
 
                /* create the MAC and associate it with the dest ES */
                mac = zebra_evpn_mac_add(zevpn, macaddr);
@@ -1812,6 +1823,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn,
                if (es_change) {
                        inform_bgp = true;
                        inform_dataplane = true;
+                       mac_inactive = true;
                }
 
                /* if peer-flag is being set notify dataplane that the
@@ -1867,9 +1879,9 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn,
                 * the activity as we are yet to establish activity
                 * locally
                 */
-               zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */,
-                                              false /* force_clear_static */,
-                                              __func__);
+               zebra_evpn_sync_mac_dp_install(
+                       mac, mac_inactive /* set_inactive */,
+                       false /* force_clear_static */, __func__);
        }
 
        return mac;
@@ -2432,7 +2444,7 @@ int zebra_evpn_del_local_mac(struct zebra_evpn *zevpn, struct zebra_mac *mac,
 
        /* Remove MAC from BGP. */
        zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags,
-                                         false /* force */);
+                                         clear_static /* force */);
 
        zebra_evpn_es_mac_deref_entry(mac);
 
index bf488ca31640c82885d1e31e18da76ff31a4611b..49120c2877cce3608d3d1c18e35f1d76d8ecdd67 100644 (file)
@@ -3157,6 +3157,9 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty,
                                json_array_string_add(json_flags, "local");
                        if (es->flags & ZEBRA_EVPNES_REMOTE)
                                json_array_string_add(json_flags, "remote");
+                       if (es->flags & ZEBRA_EVPNES_LOCAL &&
+                           !(es->flags & ZEBRA_EVPNES_NON_DF))
+                               json_array_string_add(json_flags, "df");
                        if (es->flags & ZEBRA_EVPNES_NON_DF)
                                json_array_string_add(json_flags, "nonDF");
                        if (es->flags & ZEBRA_EVPNES_BYPASS)
index 96d598f7c424b33bd3b5e995bdf01aa5d40c968e..cf2056b7acfd1cfd89088417bc2bddaab818da8d 100644 (file)
@@ -42,8 +42,8 @@ static struct zserv *zebra_gr_find_stale_client(struct zserv *client);
 static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread);
 static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info);
 static void zebra_gr_process_client_stale_routes(struct zserv *client,
-                                                vrf_id_t vrf_id);
-
+                                                struct client_gr_info *info);
+static void zebra_gr_delete_stale_route_table_afi(struct event *event);
 /*
  * Debug macros.
  */
@@ -53,7 +53,6 @@ static void zebra_gr_process_client_stale_routes(struct zserv *client,
                        zlog_debug(msg, ##__VA_ARGS__);                        \
        } while (0)
 
-
 /*
  * Client connection functions
  */
@@ -82,11 +81,12 @@ void zebra_gr_stale_client_cleanup(struct list *client_list)
                        if (info->t_stale_removal != NULL) {
                                EVENT_OFF(info->t_stale_removal);
                                info->t_stale_removal = NULL;
+                               info->do_delete = true;
                                /* Process the stale routes */
                                event_execute(
                                        zrouter.master,
                                        zebra_gr_route_stale_delete_timer_expiry,
-                                       info, 1);
+                                       info, 0);
                        }
                }
        }
@@ -101,6 +101,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client)
 
        info = XCALLOC(MTYPE_ZEBRA_GR, sizeof(struct client_gr_info));
 
+       info->stale_client_ptr = client;
+
        TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info);
        return info;
 }
@@ -108,8 +110,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client)
 /*
  * A helper function to delete and destroy client info.
  */
-static void zebra_gr_client_info_delte(struct zserv *client,
-                                      struct client_gr_info *info)
+static void zebra_gr_client_info_delete(struct zserv *client,
+                                       struct client_gr_info *info)
 {
        struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
 
@@ -117,8 +119,6 @@ static void zebra_gr_client_info_delte(struct zserv *client,
 
        EVENT_OFF(info->t_stale_removal);
 
-       XFREE(MTYPE_ZEBRA_GR, info->current_prefix);
-
        LOG_GR("%s: Instance info is being deleted for client %s vrf %s(%u)",
               __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf),
               info->vrf_id);
@@ -164,7 +164,6 @@ int32_t zebra_gr_client_disconnect(struct zserv *client)
                                zebra_gr_route_stale_delete_timer_expiry, info,
                                info->stale_removal_time,
                                &info->t_stale_removal);
-                       info->current_afi = AFI_IP;
                        info->stale_client_ptr = client;
                        info->stale_client = true;
                        LOG_GR("%s: Client %s vrf %s(%u) Stale timer update to %d",
@@ -287,31 +286,65 @@ void zebra_gr_client_reconnect(struct zserv *client)
        zserv_client_delete(old_client);
 }
 
+struct zebra_gr_afi_clean {
+       struct client_gr_info *info;
+       afi_t afi;
+       uint8_t proto;
+       uint8_t instance;
+
+       struct event *t_gac;
+};
+
 /*
  * Functions to deal with capabilities
  */
 
 /*
- * Update the graceful restart information
- * for the client instance.
- * This function handles all the capabilities that are received.
+ * Function to decode and call appropriate functions
+ * to handle client capabilities.
  */
-static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
+void zread_client_capabilities(ZAPI_HANDLER_ARGS)
 {
+       struct zapi_cap api;
        struct client_gr_info *info = NULL;
+       struct stream *s;
+       struct vrf *vrf;
+
+       s = msg;
+
+       if (zapi_capabilities_decode(s, &api)) {
+               LOG_GR("%s: Error in reading capabilities for client %s",
+                      __func__, zebra_route_string(client->proto));
+               return;
+       }
+
+       vrf = vrf_lookup_by_id(api.vrf_id);
+
+       /*
+        * If this ever matters uncomment and add safi to the
+        * arrays as needed to track
+        */
+       if (api.safi != SAFI_UNICAST)
+               return;
+
+       /* GR only for dynamic clients */
+       if (client->proto <= ZEBRA_ROUTE_CONNECT) {
+               LOG_GR("%s: GR capabilities for client %s not supported",
+                      __func__, zebra_route_string(client->proto));
+               return;
+       }
 
        /* Find the bgp information for the specified vrf id */
        TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
-               if (info->vrf_id == api->vrf_id)
+               if (info->vrf_id == api.vrf_id)
                        break;
        }
 
-
        /*
         * If the command is delete, then cancel the stale timer and
         * delete the bgp info
         */
-       switch (api->cap) {
+       switch (api.cap) {
        case ZEBRA_CLIENT_GR_DISABLE:
                if (!info)
                        return;
@@ -323,7 +356,7 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
                if ((info->gr_enable) && (client->gr_instance_count > 0))
                        client->gr_instance_count--;
 
-               zebra_gr_client_info_delte(client, info);
+               zebra_gr_client_info_delete(client, info);
                break;
        case ZEBRA_CLIENT_GR_CAPABILITIES:
                /* Allocate bgp info */
@@ -332,18 +365,16 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
 
                /* Update other parameters */
                if (!info->gr_enable) {
-                       struct vrf *vrf = vrf_lookup_by_id(api->vrf_id);
-
                        client->gr_instance_count++;
 
                        LOG_GR("%s: Cient %s vrf %s(%u) GR enabled count %d",
                               __func__, zebra_route_string(client->proto),
-                              VRF_LOGNAME(vrf), api->vrf_id,
+                              VRF_LOGNAME(vrf), api.vrf_id,
                               client->gr_instance_count);
 
-                       info->capabilities = api->cap;
-                       info->stale_removal_time = api->stale_removal_time;
-                       info->vrf_id = api->vrf_id;
+                       info->capabilities = api.cap;
+                       info->stale_removal_time = api.stale_removal_time;
+                       info->vrf_id = api.vrf_id;
                        info->gr_enable = true;
                }
                break;
@@ -353,106 +384,53 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
 
                /* Update the stale removal timer */
                if (info && info->t_stale_removal == NULL) {
-                       struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
 
                        LOG_GR("%s: vrf %s(%u) Stale time: %d is now update to: %d",
                               __func__, VRF_LOGNAME(vrf), info->vrf_id,
                               info->stale_removal_time,
-                              api->stale_removal_time);
+                              api.stale_removal_time);
 
-                       info->stale_removal_time = api->stale_removal_time;
+                       info->stale_removal_time = api.stale_removal_time;
                }
 
                break;
        case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE:
                if (!info) {
-                       LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d",
-                              __func__, zebra_route_string(client->proto),
-                              api->afi, api->safi);
-               } else {
-                       struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
-
-                       LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d",
+                       LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d, no Graceful Restart communication, returning",
                               __func__, zebra_route_string(client->proto),
-                              VRF_LOGNAME(vrf), info->vrf_id, api->afi,
-                              api->safi);
-                       info->route_sync[api->afi][api->safi] = true;
+                              api.afi, api.safi);
+                       return;
                }
+
+               LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d",
+                      __func__, zebra_route_string(client->proto),
+                      VRF_LOGNAME(vrf), info->vrf_id, api.afi, api.safi);
+               info->route_sync[api.afi] = true;
+
+               /*
+                * Schedule for after anything already in the meta Q
+                */
+               rib_add_gr_run(api.afi, api.vrf_id, client->proto,
+                              client->instance);
+               zebra_gr_process_client_stale_routes(client, info);
                break;
        case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING:
                if (!info) {
                        LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d",
                               __func__, zebra_route_string(client->proto),
-                              api->afi, api->safi);
+                              api.afi, api.safi);
                } else {
-                       struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
-
                        LOG_GR("%s: Client %s vrf %s(%u) route update pending for AFI %d, SAFI %d",
                               __func__, zebra_route_string(client->proto),
-                              VRF_LOGNAME(vrf), info->vrf_id, api->afi,
-                              api->safi);
+                              VRF_LOGNAME(vrf), info->vrf_id, api.afi,
+                              api.safi);
 
-                       info->af_enabled[api->afi][api->safi] = true;
+                       info->af_enabled[api.afi] = true;
                }
                break;
        }
 }
 
-/*
- * Handler for capabilities that are received from client.
- */
-static void zebra_client_capabilities_handler(struct zserv *client,
-                                             struct zapi_cap *api)
-{
-       switch (api->cap) {
-       case ZEBRA_CLIENT_GR_CAPABILITIES:
-       case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING:
-       case ZEBRA_CLIENT_GR_DISABLE:
-       case ZEBRA_CLIENT_RIB_STALE_TIME:
-               /*
-                * For all the cases we need to update the client info.
-                */
-               zebra_client_update_info(client, api);
-               break;
-       case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE:
-               /*
-                * After client info has been updated delete all
-                * stale routes
-                */
-               zebra_client_update_info(client, api);
-               zebra_gr_process_client_stale_routes(client, api->vrf_id);
-               break;
-       }
-}
-
-/*
- * Function to decode and call appropriate functions
- * to handle client capabilities.
- */
-void zread_client_capabilities(ZAPI_HANDLER_ARGS)
-{
-       struct zapi_cap api;
-       struct stream *s;
-
-       s = msg;
-
-       if (zapi_capabilities_decode(s, &api)) {
-               LOG_GR("%s: Error in reading capabilities for client %s",
-                      __func__, zebra_route_string(client->proto));
-               return;
-       }
-
-       /* GR only for dynamic clients */
-       if (client->proto <= ZEBRA_ROUTE_CONNECT) {
-               LOG_GR("%s: GR capabilities for client %s not supported",
-                      __func__, zebra_route_string(client->proto));
-               return;
-       }
-       /* Call the capabilities handler */
-       zebra_client_capabilities_handler(client, &api);
-}
-
-
 /*
  * Stale route handling
  */
@@ -470,10 +448,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread)
 
        client = (struct zserv *)info->stale_client_ptr;
 
-       /* Set the flag to indicate all stale route deletion */
-       if (thread->u.val == 1)
-               info->do_delete = true;
-
        cnt = zebra_gr_delete_stale_routes(info);
 
        /* Restart the timer */
@@ -492,8 +466,6 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread)
                       __func__, zebra_route_string(client->proto),
                       VRF_LOGNAME(vrf), info->vrf_id);
 
-               XFREE(MTYPE_ZEBRA_GR, info->current_prefix);
-               info->current_afi = 0;
                zebra_gr_delete_stale_client(info);
        }
 }
@@ -502,14 +474,13 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread)
 /*
  * Function to process to check if route entry is stale
  * or has been updated.
+ *
+ * Returns true when a node is deleted else false
  */
-static void zebra_gr_process_route_entry(struct zserv *client,
+static bool zebra_gr_process_route_entry(struct zserv *client,
                                         struct route_node *rn,
                                         struct route_entry *re)
 {
-       if ((client == NULL) || (rn == NULL) || (re == NULL))
-               return;
-
        /* If the route is not refreshed after restart, delete the entry */
        if (re->uptime < client->restart_time) {
                if (IS_ZEBRA_DEBUG_RIB)
@@ -517,7 +488,62 @@ static void zebra_gr_process_route_entry(struct zserv *client,
                                   __func__, zebra_route_string(client->proto),
                                   &rn->p);
                rib_delnode(rn, re);
+
+               return true;
        }
+
+       return false;
+}
+
+static void zebra_gr_delete_stale_route_table_afi(struct event *event)
+{
+       struct zebra_gr_afi_clean *gac = EVENT_ARG(event);
+       struct route_table *table;
+       struct route_node *rn;
+       struct route_entry *re, *next;
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(gac->info->vrf_id);
+       int32_t n = 0;
+
+       if (!zvrf)
+               goto done;
+
+       table = zvrf->table[gac->afi][SAFI_UNICAST];
+       if (!table)
+               goto done;
+
+       for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
+               RNODE_FOREACH_RE_SAFE (rn, re, next) {
+                       if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
+                               continue;
+
+                       /* If the route refresh is received
+                        * after restart then do not delete
+                        * the route
+                        */
+
+                       if (re->type == gac->proto &&
+                           re->instance == gac->instance &&
+                           zebra_gr_process_route_entry(
+                                   gac->info->stale_client_ptr, rn, re))
+                               n++;
+
+                       /* If the max route count is reached
+                        * then timer thread will be restarted
+                        * Store the current prefix and afi
+                        */
+                       if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) &&
+                           (gac->info->do_delete == false)) {
+                               event_add_timer(
+                                       zrouter.master,
+                                       zebra_gr_delete_stale_route_table_afi,
+                                       gac, ZEBRA_DEFAULT_STALE_UPDATE_DELAY,
+                                       &gac->t_gac);
+                       }
+               }
+       }
+
+done:
+       XFREE(MTYPE_ZEBRA_GR, gac);
 }
 
 /*
@@ -528,19 +554,11 @@ static void zebra_gr_process_route_entry(struct zserv *client,
 static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info,
                                           struct zebra_vrf *zvrf)
 {
-       struct route_node *rn, *curr;
-       struct route_entry *re;
-       struct route_entry *next;
-       struct route_table *table;
-       int32_t n = 0;
-       afi_t afi, curr_afi;
+       afi_t afi;
        uint8_t proto;
        uint16_t instance;
        struct zserv *s_client;
 
-       if ((info == NULL) || (zvrf == NULL))
-               return -1;
-
        s_client = info->stale_client_ptr;
        if (s_client == NULL) {
                LOG_GR("%s: Stale client %s(%u) not present", __func__,
@@ -550,69 +568,18 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info,
 
        proto = s_client->proto;
        instance = s_client->instance;
-       curr_afi = info->current_afi;
 
        LOG_GR("%s: Client %s %s(%u) stale routes are being deleted", __func__,
               zebra_route_string(proto), zvrf->vrf->name, zvrf->vrf->vrf_id);
 
        /* Process routes for all AFI */
-       for (afi = curr_afi; afi < AFI_MAX; afi++) {
-               table = zvrf->table[afi][SAFI_UNICAST];
+       for (afi = AFI_IP; afi < AFI_MAX; afi++) {
 
-               if (table) {
-                       /*
-                        * If the current prefix is NULL then get the first
-                        * route entry in the table
-                        */
-                       if (info->current_prefix == NULL) {
-                               rn = route_top(table);
-                               if (rn == NULL)
-                                       continue;
-                               curr = rn;
-                       } else
-                               /* Get the next route entry */
-                               curr = route_table_get_next(
-                                       table, info->current_prefix);
-
-                       for (rn = curr; rn; rn = srcdest_route_next(rn)) {
-                               RNODE_FOREACH_RE_SAFE (rn, re, next) {
-                                       if (CHECK_FLAG(re->status,
-                                                      ROUTE_ENTRY_REMOVED))
-                                               continue;
-                                       /* If the route refresh is received
-                                        * after restart then do not delete
-                                        * the route
-                                        */
-                                       if (re->type == proto
-                                           && re->instance == instance) {
-                                               zebra_gr_process_route_entry(
-                                                       s_client, rn, re);
-                                               n++;
-                                       }
-
-                                       /* If the max route count is reached
-                                        * then timer thread will be restarted
-                                        * Store the current prefix and afi
-                                        */
-                                       if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT)
-                                           && (info->do_delete == false)) {
-                                               info->current_afi = afi;
-                                               info->current_prefix = XCALLOC(
-                                                       MTYPE_ZEBRA_GR,
-                                                       sizeof(struct prefix));
-                                               prefix_copy(
-                                                       info->current_prefix,
-                                                       &rn->p);
-                                               return n;
-                                       }
-                               }
-                       }
-               }
                /*
-                * Reset the current prefix to indicate processing completion
-                * of the current AFI
+                * Schedule for immediately after anything in the
+                * meta-Q
                 */
-               XFREE(MTYPE_ZEBRA_GR, info->current_prefix);
+               rib_add_gr_run(afi, info->vrf_id, proto, instance);
        }
        return 0;
 }
@@ -623,21 +590,13 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info,
  */
 static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info)
 {
-       struct vrf *vrf;
        struct zebra_vrf *zvrf;
        uint64_t cnt = 0;
 
        if (info == NULL)
                return -1;
 
-       /* Get the current VRF */
-       vrf = vrf_lookup_by_id(info->vrf_id);
-       if (vrf == NULL) {
-               LOG_GR("%s: Invalid VRF specified %u", __func__, info->vrf_id);
-               return -1;
-       }
-
-       zvrf = vrf->info;
+       zvrf = zebra_vrf_lookup_by_id(info->vrf_id);
        if (zvrf == NULL) {
                LOG_GR("%s: Invalid VRF entry %u", __func__, info->vrf_id);
                return -1;
@@ -652,49 +611,63 @@ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info)
  * and cancels the stale timer
  */
 static void zebra_gr_process_client_stale_routes(struct zserv *client,
-                                                vrf_id_t vrf_id)
+                                                struct client_gr_info *info)
 {
-       struct client_gr_info *info = NULL;
        afi_t afi;
-       safi_t safi;
-
-       TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
-               if (info->vrf_id == vrf_id)
-                       break;
-       }
 
        if (info == NULL)
                return;
 
        /* Check if route update completed for all AFI, SAFI */
-       FOREACH_AFI_SAFI_NSF (afi, safi) {
-               if (info->af_enabled[afi][safi]) {
-                       if (!info->route_sync[afi][safi]) {
-                               struct vrf *vrf = vrf_lookup_by_id(vrf_id);
-
-                               LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d, SAFI %d",
-                                      __func__,
-                                      zebra_route_string(client->proto),
-                                      VRF_LOGNAME(vrf), info->vrf_id, afi,
-                                      safi);
-                               return;
-                       }
+       for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+               if (info->af_enabled[afi] && !info->route_sync[afi]) {
+                       struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
+
+                       LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d",
+                              __func__, zebra_route_string(client->proto),
+                              VRF_LOGNAME(vrf), info->vrf_id, afi);
+                       return;
                }
        }
 
        /*
         * Route update completed for all AFI, SAFI
-        * Cancel the stale timer and process the routes
+        * Cancel the stale timer, routes are already being processed
         */
        if (info->t_stale_removal) {
-               struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+               struct vrf *vrf = vrf_lookup_by_id(info->vrf_id);
 
                LOG_GR("%s: Client %s canceled stale delete timer vrf %s(%d)",
                       __func__, zebra_route_string(client->proto),
                       VRF_LOGNAME(vrf), info->vrf_id);
                EVENT_OFF(info->t_stale_removal);
-               event_execute(zrouter.master,
-                             zebra_gr_route_stale_delete_timer_expiry, info,
-                             0);
        }
 }
+
+void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto,
+                            uint8_t instance)
+{
+       struct zserv *client = zserv_find_client(proto, instance);
+       struct client_gr_info *info = NULL;
+       struct zebra_gr_afi_clean *gac;
+
+       if (client == NULL)
+               return;
+
+       TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
+               if (info->vrf_id == vrf_id)
+                       break;
+       }
+
+       if (info == NULL)
+               return;
+
+       gac = XCALLOC(MTYPE_ZEBRA_GR, sizeof(*gac));
+       gac->info = info;
+       gac->afi = afi;
+       gac->proto = proto;
+       gac->instance = instance;
+
+       event_add_event(zrouter.master, zebra_gr_delete_stale_route_table_afi,
+                       gac, 0, &gac->t_gac);
+}
index 1b2f7a02d41589008b1683a09ff752b54b32dbc7..4d09474b7f2287b25e191d43c45776510944fd84 100644 (file)
@@ -53,7 +53,7 @@ struct zebra_ptm_cb {
         (protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS ||  \
         (protocol) == ZEBRA_ROUTE_PIM ||                                      \
         (protocol) == ZEBRA_ROUTE_OPENFABRIC ||                               \
-        (protocol) == ZEBRA_ROUTE_STATIC)
+        (protocol) == ZEBRA_ROUTE_STATIC || (protocol) == ZEBRA_ROUTE_RIP)
 
 void zebra_ptm_init(void);
 void zebra_ptm_finish(void);
index 7a9d0c0ed6219749f242ff3966d023fb8fc4937f..adcaf640445fa3dc8b70df06e7dc2d7f35f2f0ae 100644 (file)
@@ -64,7 +64,12 @@ DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
 DEFINE_HOOK(rib_shutdown, (struct route_node * rn), (rn));
 
 
-/* Meta Q's specific names */
+/*
+ * Meta Q's specific names
+ *
+ * If you add something here ensure that you
+ * change MQ_SIZE as well over in rib.h
+ */
 enum meta_queue_indexes {
        META_QUEUE_NHG,
        META_QUEUE_EVPN,
@@ -76,6 +81,7 @@ enum meta_queue_indexes {
        META_QUEUE_NOTBGP,
        META_QUEUE_BGP,
        META_QUEUE_OTHER,
+       META_QUEUE_GR_RUN,
 };
 
 /* Each route type's string and default distance value. */
@@ -250,6 +256,8 @@ static const char *subqueue2str(enum meta_queue_indexes index)
                return "BGP Routes";
        case META_QUEUE_OTHER:
                return "Other Routes";
+       case META_QUEUE_GR_RUN:
+               return "Graceful Restart";
        }
 
        return "Unknown";
@@ -3089,6 +3097,23 @@ static void process_subq_early_route(struct listnode *lnode)
                process_subq_early_route_add(ere);
 }
 
+struct meta_q_gr_run {
+       afi_t afi;
+       vrf_id_t vrf_id;
+       uint8_t proto;
+       uint8_t instance;
+};
+
+static void process_subq_gr_run(struct listnode *lnode)
+{
+       struct meta_q_gr_run *gr_run = listgetdata(lnode);
+
+       zebra_gr_process_client(gr_run->afi, gr_run->vrf_id, gr_run->proto,
+                               gr_run->instance);
+
+       XFREE(MTYPE_WQ_WRAPPER, gr_run);
+}
+
 /*
  * Examine the specified subqueue; process one entry and return 1 if
  * there is a node, return 0 otherwise.
@@ -3122,6 +3147,9 @@ static unsigned int process_subq(struct list *subq,
        case META_QUEUE_OTHER:
                process_subq_route(lnode, qindex);
                break;
+       case META_QUEUE_GR_RUN:
+               process_subq_gr_run(lnode);
+               break;
        }
 
        list_delete_node(subq, lnode);
@@ -3727,6 +3755,23 @@ static void early_route_meta_queue_free(struct meta_queue *mq, struct list *l,
        }
 }
 
+static void rib_meta_queue_gr_run_free(struct meta_queue *mq, struct list *l,
+                                      struct zebra_vrf *zvrf)
+{
+       struct meta_q_gr_run *gr_run;
+       struct listnode *node, *nnode;
+
+       for (ALL_LIST_ELEMENTS(l, node, nnode, gr_run)) {
+               if (zvrf && zvrf->vrf->vrf_id != gr_run->vrf_id)
+                       continue;
+
+               XFREE(MTYPE_WQ_WRAPPER, gr_run);
+               node->data = NULL;
+               list_delete_node(l, node);
+               mq->size--;
+       }
+}
+
 void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf)
 {
        enum meta_queue_indexes i;
@@ -3754,6 +3799,9 @@ void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf)
                case META_QUEUE_OTHER:
                        rib_meta_queue_free(mq, mq->subq[i], zvrf);
                        break;
+               case META_QUEUE_GR_RUN:
+                       rib_meta_queue_gr_run_free(mq, mq->subq[i], zvrf);
+                       break;
                }
                if (!zvrf)
                        list_delete(&mq->subq[i]);
@@ -4094,6 +4142,17 @@ void _route_entry_dump(const char *func, union prefixconstptr pp,
        zlog_debug("%s: dump complete", straddr);
 }
 
+static int rib_meta_queue_gr_run_add(struct meta_queue *mq, void *data)
+{
+       listnode_add(mq->subq[META_QUEUE_GR_RUN], data);
+       mq->size++;
+
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+               zlog_debug("Graceful Run adding");
+
+       return 0;
+}
+
 static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data)
 {
        struct zebra_early_route *ere = data;
@@ -4110,6 +4169,20 @@ static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data)
        return 0;
 }
 
+int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, uint8_t instance)
+{
+       struct meta_q_gr_run *gr_run;
+
+       gr_run = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(*gr_run));
+
+       gr_run->afi = afi;
+       gr_run->proto = proto;
+       gr_run->vrf_id = vrf_id;
+       gr_run->instance = instance;
+
+       return mq_add_handler(gr_run, rib_meta_queue_gr_run_add);
+}
+
 struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type,
                                              uint8_t instance, uint32_t flags,
                                              uint32_t nhe_id,
@@ -4410,6 +4483,22 @@ static void rib_update_handler(struct event *thread)
  */
 static struct event *t_rib_update_threads[RIB_UPDATE_MAX];
 
+void rib_update_finish(void)
+{
+       int i;
+
+       for (i = RIB_UPDATE_KERNEL; i < RIB_UPDATE_MAX; i++) {
+               if (event_is_scheduled(t_rib_update_threads[i])) {
+                       struct rib_update_ctx *ctx;
+
+                       ctx = EVENT_ARG(t_rib_update_threads[i]);
+
+                       rib_update_ctx_fini(&ctx);
+                       EVENT_OFF(t_rib_update_threads[i]);
+               }
+       }
+}
+
 /* Schedule a RIB update event for all vrfs */
 void rib_update(enum rib_update_event event)
 {
@@ -4418,6 +4507,9 @@ void rib_update(enum rib_update_event event)
        if (event_is_scheduled(t_rib_update_threads[event]))
                return;
 
+       if (zebra_router_in_shutdown())
+               return;
+
        ctx = rib_update_ctx_init(0, event);
 
        event_add_event(zrouter.master, rib_update_handler, ctx, 0,
index 3bbcd38d1cfe9479293c92bfbc6f3b5b206e67df..28b83ce8b6f6e139f45b47c8a3c0334cbbb95210 100644 (file)
@@ -1532,11 +1532,15 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
                seg6local_context2str(buf, sizeof(buf),
                                      &nexthop->nh_srv6->seg6local_ctx,
                                      nexthop->nh_srv6->seg6local_action);
-               vty_out(vty, ", seg6local %s %s",
-                       seg6local_action2str(
-                               nexthop->nh_srv6->seg6local_action),
-                       buf);
-               vty_out(vty, ", seg6 %pI6", &nexthop->nh_srv6->seg6_segs);
+               if (nexthop->nh_srv6->seg6local_action !=
+                   ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
+                       vty_out(vty, ", seg6local %s %s",
+                               seg6local_action2str(
+                                       nexthop->nh_srv6->seg6local_action),
+                               buf);
+               if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any))
+                       vty_out(vty, ", seg6 %pI6",
+                               &nexthop->nh_srv6->seg6_segs);
        }
 
        if (nexthop->weight)
index d9a7ee465a9fcb2d8f86cf612f13deb863681ef2..142501b14994168e08158bb9439f53bc7b301552 100644 (file)
@@ -1041,7 +1041,7 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object)
        }
        alist = access_list_lookup(AFI_IP, (char *)rule);
        if (alist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -1104,7 +1104,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
        }
        plist = prefix_list_lookup(AFI_IP, (char *)rule);
        if (plist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -1145,7 +1145,7 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix,
 
        alist = access_list_lookup(afi, (char *)rule);
        if (alist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
@@ -1207,7 +1207,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix,
 
        plist = prefix_list_lookup(afi, (char *)rule);
        if (plist == NULL) {
-               if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))
+               if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)))
                        zlog_debug(
                                "%s: Prefix List %s specified does not exist defaulting to NO_MATCH",
                                __func__, (char *)rule);
index c370ad9169d836c80cd4599cf409aa1528d4a24e..36290f99e0d1d9d3e2dd2c52a773f446e247d6d3 100644 (file)
@@ -2582,19 +2582,18 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
        struct zebra_mac *zrmac = NULL;
        json_object *json = NULL;
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        zl3vni = zl3vni_lookup(l3vni);
        if (!zl3vni) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni);
                return;
@@ -2603,7 +2602,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
        zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
        if (!zrmac) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty,
                                "%% Requested RMAC doesn't exist in L3-VNI %u\n",
@@ -2624,13 +2623,18 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
        struct rmac_walk_ctx wctx;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
 
        zl3vni = zl3vni_lookup(l3vni);
        if (!zl3vni) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
                return;
@@ -2639,9 +2643,6 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json)
        if (!num_rmacs)
                return;
 
-       if (use_json)
-               json = json_object_new_object();
-
        memset(&wctx, 0, sizeof(wctx));
        wctx.vty = vty;
        wctx.json = json;
@@ -2663,15 +2664,14 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json)
        json_object *json = NULL;
        void *args[2];
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        args[0] = vty;
        args[1] = json;
        hash_iterate(zrouter.l3vni_table,
@@ -2690,15 +2690,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni,
        struct zebra_neigh *n = NULL;
        json_object *json = NULL;
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        /* If vni=0 passed, assume svd lookup */
        if (!l3vni)
                n = svd_nh_lookup(ip);
@@ -2799,15 +2798,14 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json)
        json_object *json = NULL;
        void *args[2];
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        args[0] = vty;
        args[1] = json;
        hash_iterate(zrouter.l3vni_table,
@@ -2828,24 +2826,23 @@ void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json)
        json_object *json = NULL;
        struct zebra_l3vni *zl3vni = NULL;
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
 
        zl3vni = zl3vni_lookup(vni);
        if (!zl3vni) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        args[0] = vty;
        args[1] = json;
        zl3vni_print(zl3vni, (void *)args);
@@ -2900,12 +2897,18 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
        struct neigh_walk_ctx wctx;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
+
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
@@ -2914,9 +2917,6 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
        if (!num_neigh)
                return;
 
-       if (use_json)
-               json = json_object_new_object();
-
        /* Since we have IPv6 addresses to deal with which can vary widely in
         * size, we try to be a bit more elegant in display by first computing
         * the maximum width.
@@ -2951,12 +2951,14 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
        json_object *json = NULL;
        void *args[3];
 
-       if (!is_evpn_enabled())
-               return;
-
        if (use_json)
                json = json_object_new_object();
 
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
+               return;
+       }
+
        args[0] = vty;
        args[1] = json;
        args[2] = (void *)(ptrdiff_t)print_dup;
@@ -2979,12 +2981,14 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty,
        json_object *json = NULL;
        void *args[3];
 
-       if (!is_evpn_enabled())
-               return;
-
        if (use_json)
                json = json_object_new_object();
 
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
+               return;
+       }
+
        args[0] = vty;
        args[1] = json;
        args[2] = (void *)(ptrdiff_t)print_dup;
@@ -3008,12 +3012,18 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
        struct zebra_neigh *n;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
+
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
@@ -3026,8 +3036,6 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
                                vni);
                return;
        }
-       if (use_json)
-               json = json_object_new_object();
 
        zebra_evpn_print_neigh(n, vty, json);
 
@@ -3048,12 +3056,18 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        struct neigh_walk_ctx wctx;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
+
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
@@ -3062,9 +3076,6 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        if (!num_neigh)
                return;
 
-       if (use_json)
-               json = json_object_new_object();
-
        memset(&wctx, 0, sizeof(wctx));
        wctx.zevpn = zevpn;
        wctx.vty = vty;
@@ -3094,12 +3105,20 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
        struct neigh_walk_ctx wctx;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
 
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
-               vty_out(vty, "%% VNI %u does not exist\n", vni);
+               if (use_json)
+                       vty_json(vty, json);
+               else
+                       vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
        }
 
@@ -3111,9 +3130,6 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty,
        if (!num_neigh)
                return;
 
-       if (use_json)
-               json = json_object_new_object();
-
        /* Since we have IPv6 addresses to deal with which can vary widely in
         * size, we try to be a bit more elegant in display by first computing
         * the maximum width.
@@ -3155,8 +3171,12 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
        json_object *json = NULL;
        json_object *json_mac = NULL;
 
-       if (!is_evpn_enabled())
+       if (!is_evpn_enabled()) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
                return;
+       }
+
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
@@ -3218,13 +3238,13 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
        struct mac_walk_ctx wctx;
        json_object *json = NULL;
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
-       if (use_json)
-               json = json_object_new_object();
 
        memset(&wctx, 0, sizeof(wctx));
        wctx.vty = vty;
@@ -3246,13 +3266,13 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty,
        struct mac_walk_ctx wctx;
        json_object *json = NULL;
 
+       if (use_json)
+               json = json_object_new_object();
+
        if (!is_evpn_enabled()) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
+               vty_json(vty, json);
                return;
        }
-       if (use_json)
-               json = json_object_new_object();
 
        memset(&wctx, 0, sizeof(wctx));
        wctx.vty = vty;
@@ -3275,12 +3295,14 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
        struct mac_walk_ctx wctx;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
-               return;
-
        if (use_json)
                json = json_object_new_object();
 
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
+               return;
+       }
+
        memset(&wctx, 0, sizeof(wctx));
        wctx.vty = vty;
        wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
@@ -3303,13 +3325,18 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
        struct zebra_mac *mac;
        json_object *json = NULL;
 
-       if (!is_evpn_enabled())
+       if (use_json)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
 
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty, "%% VNI %u does not exist\n", vni);
                return;
@@ -3317,7 +3344,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
        mac = zebra_evpn_mac_lookup(zevpn, macaddr);
        if (!mac) {
                if (use_json)
-                       vty_out(vty, "{}\n");
+                       vty_json(vty, json);
                else
                        vty_out(vty,
                                "%% Requested MAC does not exist in VNI %u\n",
@@ -3325,10 +3352,8 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
                return;
        }
 
-       if (use_json)
-               json = json_object_new_object();
-
        zebra_evpn_print_mac(mac, vty, json);
+
        if (use_json)
                vty_json(vty, json);
 }
@@ -3693,8 +3718,11 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
        json_object *json = NULL;
        json_object *json_mac = NULL;
 
-       if (!is_evpn_enabled())
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
+
        zevpn = zebra_evpn_lookup(vni);
        if (!zevpn) {
                if (use_json)
@@ -3745,12 +3773,14 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
        struct zebra_l3vni *zl3vni = NULL;
        struct zebra_evpn *zevpn = NULL;
 
-       if (!is_evpn_enabled())
-               return;
-
        if (use_json)
                json = json_object_new_object();
 
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
+               return;
+       }
+
        args[0] = vty;
        args[1] = json;
 
@@ -3787,8 +3817,13 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
        json_object *json = NULL;
        struct zebra_vrf *zvrf = NULL;
 
-       if (!is_evpn_enabled())
+       if (uj)
+               json = json_object_new_object();
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
                return;
+       }
 
        zvrf = zebra_vrf_get_evpn();
 
@@ -3797,7 +3832,6 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj)
        num_vnis = num_l2vnis + num_l3vnis;
 
        if (uj) {
-               json = json_object_new_object();
                json_object_string_add(json, "advertiseGatewayMacip",
                                       zvrf->advertise_gw_macip ? "Yes" : "No");
                json_object_string_add(json, "advertiseSviMacip",
@@ -3860,12 +3894,15 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
        json_object *json = NULL;
        void *args[2];
 
-       if (!is_evpn_enabled())
-               return;
-
        if (use_json)
                json = json_object_new_object();
-       else
+
+       if (!is_evpn_enabled()) {
+               vty_json(vty, json);
+               return;
+       }
+
+       if (!use_json)
                vty_out(vty, "%-10s %-4s %-21s %-8s %-8s %-15s %-37s\n", "VNI",
                        "Type", "VxLAN IF", "# MACs", "# ARPs",
                        "# Remote VTEPs", "Tenant VRF");
@@ -3943,8 +3980,11 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf,
        struct zebra_ns *zns = NULL;
        struct zebra_evpn_show zes;
 
-       if (!is_evpn_enabled())
+       if (!is_evpn_enabled()) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
                return;
+       }
 
        zns = zebra_ns_lookup(NS_DEFAULT);
        if (!zns)
index 70707866ee286092a81865de8881c8e1527ea4fc..6abd49310c5acae58fa6febdfa8e75d0d7ffaae2 100644 (file)
@@ -1152,10 +1152,6 @@ static void zebra_show_stale_client_detail(struct vty *vty,
                                                        info->t_stale_removal));
                                }
                        }
-                       vty_out(vty, "Current AFI : %d\n", info->current_afi);
-                       if (info->current_prefix)
-                               vty_out(vty, "Current prefix : %pFX\n",
-                                       info->current_prefix);
                }
        }
        vty_out(vty, "\n");
index aa58a3a29948c5942c78416caa833314c3712098..90aa4d53f46d647e02c7c530b9d1b00fb96bc3cc 100644 (file)
@@ -51,9 +51,6 @@ struct client_gr_info {
        /* VRF for which GR enabled */
        vrf_id_t vrf_id;
 
-       /* AFI */
-       afi_t current_afi;
-
        /* Stale time and GR cap */
        uint32_t stale_removal_time;
        enum zserv_client_capabilities capabilities;
@@ -64,11 +61,10 @@ struct client_gr_info {
        bool stale_client;
 
        /* Route sync and enable flags for AFI/SAFI */
-       bool af_enabled[AFI_MAX][SAFI_MAX];
-       bool route_sync[AFI_MAX][SAFI_MAX];
+       bool af_enabled[AFI_MAX];
+       bool route_sync[AFI_MAX];
 
        /* Book keeping */
-       struct prefix *current_prefix;
        void *stale_client_ptr;
        struct event *t_stale_removal;