diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 3430d8a28200..232b1c3934d5 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -912,7 +912,7 @@ Opaque LSA - *ospfd* supports Opaque LSA (:rfc:`2370`) as partial support for + *ospfd* supports Opaque LSA (:rfc:`5250`) as partial support for MPLS Traffic Engineering LSAs. The opaque-lsa capability must be enabled in the configuration. An alternate command could be "mpls-te on" (:ref:`ospf-traffic-engineering`). Note that FRR @@ -920,6 +920,18 @@ Opaque LSA extensions that are used with MPLS-TE; it does not support a complete RSVP-TE solution. +.. clicmd:: ip ospf capability opaque [A.B.C.D] + + Enable or disable OSPF LSA database exchange and flooding on an interface. + The default is that opaque capability is enabled as long as the opaque + capability is enabled with the :clicmd:`capability opaque` command at the + OSPF instance level (using the command above). Note that disabling opaque + LSA support on an interface will impact the applications using opaque LSAs + if the opaque LSAs are not received on other flooding paths by all the + OSPF routers using those applications. For example, OSPF Graceful Restart + uses opaque-link LSAs and disabling support on an interface will disable + graceful restart signaling on that interface. + .. clicmd:: show ip ospf [vrf ] database (opaque-link|opaque-area|opaque-external) .. clicmd:: show ip ospf [vrf ] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID diff --git a/lib/libospf.h b/lib/libospf.h index 9eaca9a1a8dc..e3c1adb810f2 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -69,6 +69,7 @@ extern "C" { #define OSPF_MTU_IGNORE_DEFAULT 0 #define OSPF_FAST_HELLO_DEFAULT 0 #define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false +#define OSPF_OPAQUE_CAPABLE_DEFAULT true #define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ #define OSPF_AREA_RANGE_COST_UNSPEC -1U diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index a4d0f77faffa..d327617f49be 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -568,6 +568,15 @@ int ospf_flood_through_interface(struct ospf_interface *oi, if (!ospf_if_is_enable(oi)) return 0; + if (IS_OPAQUE_LSA(lsa->data->type) && + !OSPF_IF_PARAM(oi, opaque_capable)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping interface %s (%s) with opaque disabled.", + __func__, IF_NAME(oi), ospf_get_name(oi->ospf)); + return 0; + } + /* If flood reduction is configured, set the DC bit on the lsa. */ if (IS_LSA_SELF(lsa)) { if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) { diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index 2a346f2388c1..c23c42052fc7 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -773,8 +773,15 @@ static void ospf_gr_prepare(void) } /* Send a Grace-LSA to all neighbors. */ - for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) - ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false); + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) { + if (OSPF_IF_PARAM(oi, opaque_capable)) + ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, + false); + else + zlog_debug( + "GR: skipping grace LSA on interface %s (%s) with opaque capability disabled", + IF_NAME(oi), ospf_get_name(oi->ospf)); + } /* Record end of the grace period in non-volatile memory. */ ospf_gr_nvm_update(ospf, true); diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 2c66cb3cfc9f..a867d9aaf6d3 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -538,6 +538,7 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, auth_crypt); UNSET_IF_PARAM(oip, auth_type); UNSET_IF_PARAM(oip, if_area); + UNSET_IF_PARAM(oip, opaque_capable); oip->auth_crypt = list_new(); @@ -546,6 +547,7 @@ static struct ospf_if_params *ospf_new_if_params(void) oip->ptp_dmvpn = 0; oip->p2mp_delay_reflood = OSPF_P2MP_DELAY_REFLOOD_DEFAULT; + oip->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; return oip; } @@ -575,19 +577,20 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) oip = rn->info; route_unlock_node(rn); - if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) - && !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) - && !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) - && !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) - && !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) - && !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) - && !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) - && !OSPF_IF_PARAM_CONFIGURED(oip, priority) - && !OSPF_IF_PARAM_CONFIGURED(oip, type) - && !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) - && !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) - && !OSPF_IF_PARAM_CONFIGURED(oip, if_area) - && listcount(oip->auth_crypt) == 0) { + if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) && + !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) && + !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) && + !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) && + !OSPF_IF_PARAM_CONFIGURED(oip, priority) && + !OSPF_IF_PARAM_CONFIGURED(oip, type) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) && + !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && + !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) && + listcount(oip->auth_crypt) == 0) { ospf_del_if_params(ifp, oip); rn->info = NULL; route_unlock_node(rn); @@ -693,6 +696,9 @@ int ospf_if_new_hook(struct interface *ifp) SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_type); IF_DEF_PARAMS(ifp)->auth_type = OSPF_AUTH_NOTSET; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), opaque_capable); + IF_DEF_PARAMS(ifp)->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; + rc = ospf_opaque_new_if(ifp); return rc; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index ec1afa1b8bfe..38ec45c7578d 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -112,6 +112,9 @@ struct ospf_if_params { /* point-to-multipoint delayed reflooding configuration */ bool p2mp_delay_reflood; + + /* Opaque LSA capability at interface level (see RFC5250) */ + DECLARE_IF_PARAM(bool, opaque_capable); }; enum { MEMBER_ALLROUTERS = 0, diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 6894c6a009c4..27f47a6d79a3 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -1851,9 +1851,9 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t) return; } - if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) - || !ospf_if_is_enable(oi) - || ospf_nbr_count_opaque_capable(oi) == 0) { + if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) || + ospf_nbr_count_opaque_capable(oi) == 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index d010b8b6e60b..739fedb673ef 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -953,8 +953,9 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, } #endif /* REJECT_IF_TBIT_ON */ - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) - && CHECK_FLAG(hello->options, OSPF_OPTION_O)) { + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable) && + CHECK_FLAG(hello->options, OSPF_OPTION_O)) { /* * This router does know the correct usage of O-bit * the bit should be set in DD packet only. @@ -1362,8 +1363,9 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, } #endif /* REJECT_IF_TBIT_ON */ - if (CHECK_FLAG(dd->options, OSPF_OPTION_O) - && !CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (CHECK_FLAG(dd->options, OSPF_OPTION_O) && + (!CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable))) { /* * This node is not configured to handle O-bit, for now. * Clear it to ignore unsupported capability proposed by @@ -1448,7 +1450,8 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, /* This is where the real Options are saved */ nbr->options = dd->options; - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Neighbor[%pI4] is %sOpaque-capable.", @@ -3435,7 +3438,8 @@ static int ospf_make_db_desc(struct ospf_interface *oi, /* Set Options. */ options = OPTIONS(oi); - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) SET_FLAG(options, OSPF_OPTION_O); if (OSPF_FR_CONFIG(oi->ospf, oi->area)) SET_FLAG(options, OSPF_OPTION_DC); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index ff17b147e420..d945391d8fa7 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -3810,6 +3810,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, lookup_msg(ospf_ism_state_msg, oi->state, NULL)); json_object_int_add(json_oi, "priority", PRIORITY(oi)); + json_object_boolean_add( + json_interface_sub, "opaqueCapable", + OSPF_IF_PARAM(oi, opaque_capable)); } else { vty_out(vty, " Area %s\n", ospf_area_desc_string(oi->area)); @@ -3829,6 +3832,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, OSPF_IF_PARAM(oi, transmit_delay), lookup_msg(ospf_ism_state_msg, oi->state, NULL), PRIORITY(oi)); + if (!OSPF_IF_PARAM(oi, opaque_capable)) + vty_out(vty, + " Opaque LSA capability disabled on interface\n"); } /* Show DR information. */ @@ -9799,6 +9805,59 @@ DEFUN (no_ip_ospf_mtu_ignore, return CMD_SUCCESS; } +DEFPY(ip_ospf_capability_opaque, ip_ospf_capability_opaque_addr_cmd, + "[no] ip ospf capability opaque [A.B.C.D]$ip_addr", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disable OSPF capability on this interface\n" + "Disable OSPF opaque LSA capability on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct route_node *rn; + bool old_opaque_capable; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + if (ip_addr.s_addr != INADDR_ANY) { + params = ospf_get_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + + old_opaque_capable = params->opaque_capable; + params->opaque_capable = (no) ? false : true; + if (params->opaque_capable != OSPF_OPAQUE_CAPABLE_DEFAULT) + SET_IF_PARAM(params, opaque_capable); + else { + UNSET_IF_PARAM(params, opaque_capable); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + } + + /* + * If there is a change to the opaque capability, flap the interface + * to reset all the neighbor adjacencies. + */ + if (params->opaque_capable != old_opaque_capable) { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi && (oi->state > ISM_Down) && + (ip_addr.s_addr == INADDR_ANY || + IPV4_ADDR_SAME(&oi->address->u.prefix4, + &ip_addr))) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + } + return CMD_SUCCESS; +} + DEFUN (ospf_max_metric_router_lsa_admin, ospf_max_metric_router_lsa_admin_cmd, @@ -12163,6 +12222,21 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) if (params && params->ldp_sync_info) ospf_ldp_sync_if_write_config(vty, params); + /* Capability opaque print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, opaque_capable) && + params->opaque_capable != + OSPF_OPAQUE_CAPABLE_DEFAULT) { + if (params->opaque_capable == false) + vty_out(vty, + " no ip ospf capability opaque"); + else + vty_out(vty, + " ip ospf capability opaque"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + while (1) { if (rn == NULL) rn = route_top(IF_OIFS_PARAMS(ifp)); @@ -12972,6 +13046,9 @@ static void ospf_vty_if_init(void) install_element(INTERFACE_NODE, &ip_ospf_passive_cmd); install_element(INTERFACE_NODE, &no_ip_ospf_passive_cmd); + /* "ip ospf capability opaque" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_capability_opaque_addr_cmd); + /* These commands are compatibitliy for previous version. */ install_element(INTERFACE_NODE, &ospf_authentication_key_cmd); install_element(INTERFACE_NODE, &ospf_message_digest_key_cmd); diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index 39ebbcfb6285..1036af20d297 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -102,6 +102,28 @@ def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json" return str(result) if result else None +@retry(retry_timeout=15) +def verify_ospf_interface(tgen, dut, input_dict, cmd="show ip ospf interface json"): + del tgen + show_ospf_json = run_frr_cmd(dut, cmd, isjson=True) + if not bool(show_ospf_json): + return "ospf is not running" + result = json_cmp(show_ospf_json, input_dict) + return str(result) if result else None + + +@retry(retry_timeout=45) +def verify_ospf_neighbor( + tgen, dut, input_dict, cmd="show ip ospf neighbor detail json" +): + del tgen + show_ospf_json = run_frr_cmd(dut, cmd, isjson=True) + if not bool(show_ospf_json): + return "ospf is not running" + result = json_cmp(show_ospf_json, input_dict) + return str(result) if result else None + + def myreadline(f): buf = b"" while True: @@ -1142,6 +1164,356 @@ def test_ospf_opaque_restart(tgen): _test_opaque_add_restart_add(tgen, apibin) +def _test_opaque_interface_disable(tgen, apibin): + "Test disabling opaque capability on an interface" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + tc_name = "opaque_interface_disable" + + p = None + pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+") + try: + # STEP 1 in test_ospf_opaque_interface_disable and STEP 51 Overall + step("Disable OSPF opaque LSA Copability on r1's interface to r2") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque") + time.sleep(15) + + # STEP 2 in test_ospf_opaque_interface_disable and STEP 52 Overall + step("Verify the r1 configuration of 'no ip ospf capability opaque'") + no_capability_opaque_cfg = ( + tgen.net["r1"] + .cmd( + 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"' + ) + .rstrip() + ) + assertmsg = ( + "'no ip ospf capability opaque' applied, but not present in configuration" + ) + assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg + + # STEP 3 in test_ospf_opaque_interface_disable and STEP 53 Overall + step("Verify the ospf opaque option is not applied to the r1 interface") + input_dict = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.1", + "ospfIfType": "Broadcast", + "opaqueCapable": False, + } + } + } + result = verify_ospf_interface(tgen, r1, input_dict) + assert result is None + + # STEP 4 in test_ospf_opaque_interface_disable and STEP 54 Overall + step("Verify that the neighbor options don't include opaque") + r1_without_opaque = { + "neighbors": { + "2.0.0.0": [ + { + "optionsList": "*|-|-|-|-|-|E|-", + } + ] + } + } + r2_without_opaque = { + "neighbors": { + "1.0.0.0": [ + { + "optionsList": "*|-|-|-|-|-|E|-", + } + ] + } + } + result = verify_ospf_neighbor(tgen, r1, r1_without_opaque) + assert result is None + result = verify_ospf_neighbor(tgen, r2, r2_without_opaque) + assert result is None + + # STEP 5 in test_ospf_opaque_interface_disable and STEP 55 Overall + step( + "Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration" + ) + rc, _, _ = tgen.net["r2"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" + assert rc, assertmsg + + # STEP 6 in test_ospf_opaque_interface_disable and STEP 56 Overall + step("Verify the ospf opaque option is applied to the r2 interface") + input_dict = { + "interfaces": { + "r2-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.2", + "ospfIfType": "Broadcast", + "opaqueCapable": True, + } + } + } + result = verify_ospf_interface(tgen, r2, input_dict) + assert result is None + + # STEP 7 in test_ospf_opaque_interface_disable and STEP 57 Overall + step("Install opaque LSAs on r1") + pread = r2.popen( + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], + encoding=None, # don't buffer + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + p = r1.popen( + [ + apibin, + "-v", + "add,9,10.0.1.1,230,1,feedaceedeadbeef", + "add,10,1.2.3.4,231,1,feedaceecafebeef", + "add,11,232,1,feedaceebaddbeef", + "wait,20", + ] + ) + opaque_input_dict = { + "areas": { + "1.2.3.4": { + "linkLocalOpaqueLsa": [ + { + "lsId": "230.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "linkLocalOpaqueLsaCount": 1, + "areaLocalOpaqueLsa": [ + { + "lsId": "231.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "areaLocalOpaqueLsaCount": 1, + }, + }, + "asExternalOpaqueLsa": [ + { + "lsId": "232.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "asExternalOpaqueLsaCount": 1, + } + no_opaque_input_dict = { + "areas": { + "1.2.3.4": { + "linkLocalOpaqueLsaCount": 1, + "areaLocalOpaqueLsaCount": 1, + }, + }, + "asExternalOpaqueLsaCount": 1, + } + + # STEP 8 in test_ospf_opaque_interface_disable and STEP 58 Overall + step("Check that LSAs are added on r1 but not r2") + json_cmd = "show ip ospf database json" + assert verify_ospf_database(tgen, r1, opaque_input_dict, json_cmd) is None + assert ( + verify_ospf_database(tgen, r2, no_opaque_input_dict, json_cmd) is not None + ) + + # STEP 9 in test_ospf_opaque_interface_disable and STEP 59 Overall + step("Enable OSPF opaque LSA Copability on r1's interface to r2") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque") + time.sleep(15) + + # STEP 10 in test_ospf_opaque_interface_disable and STEP 60 Overall + step("Verify no r1 configuration of 'no ip ospf capability opaque'") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration" + assert rc, assertmsg + + # STEP 11 in test_ospf_opaque_interface_disable and STEP 61 Overall + step("Verify the ospf opaque option is applied to the r1 interface") + input_dict = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.1", + "ospfIfType": "Broadcast", + "opaqueCapable": True, + } + } + } + result = verify_ospf_interface(tgen, r1, input_dict) + assert result is None + + # STEP 12 in test_ospf_opaque_interface_disable and STEP 62 Overall + step("Verify that the neighbor options include opaque") + r1_with_opaque = { + "neighbors": { + "2.0.0.0": [ + { + "optionsList": "*|O|-|-|-|-|E|-", + } + ] + } + } + r2_with_opaque = { + "neighbors": { + "1.0.0.0": [ + { + "optionsList": "*|O|-|-|-|-|E|-", + } + ] + } + } + result = verify_ospf_neighbor(tgen, r1, r1_with_opaque) + assert result is None + result = verify_ospf_neighbor(tgen, r2, r2_with_opaque) + assert result is None + + # STEP 13 in test_ospf_opaque_interface_disable and STEP 63 Overall + step("Check that LSAs are now added to r2") + json_cmd = "show ip ospf database json" + assert verify_ospf_database(tgen, r2, opaque_input_dict, json_cmd) is None + + # STEP 14 in test_ospf_opaque_interface_disable and STEP 64 Overall + step( + "Disable Opaque Capability on r2's interface to r1 using the interface address" + ) + r2.vtysh_multicmd( + "conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2" + ) + + # STEP 15 in test_ospf_opaque_interface_disable and STEP 65 Overall + step("Clear the OSPF process on r2 to clear the OSPF LSDB") + r2.vtysh_multicmd("clear ip ospf process") + time.sleep(15) + + # STEP 16 in test_ospf_opaque_interface_disable and STEP 66 Overall + step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'") + no_capability_opaque_cfg = ( + tgen.net["r2"] + .cmd_nostatus( + 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"' + ) + .rstrip() + ) + assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration" + assert ( + no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2" + ), assertmsg + + # STEP 17 in test_ospf_opaque_interface_disable and STEP 67 Overall + step("Verify the ospf opaque option is not applied to the r2 interface") + input_dict = { + "interfaces": { + "r2-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.2", + "ospfIfType": "Broadcast", + "opaqueCapable": False, + } + } + } + result = verify_ospf_interface(tgen, r2, input_dict) + assert result is None + + # STEP 18 in test_ospf_opaque_interface_disable and STEP 68 Overall + step("Verify that the neighbor options don't include opaque") + result = verify_ospf_neighbor(tgen, r1, r1_without_opaque) + assert result is None + result = verify_ospf_neighbor(tgen, r2, r2_without_opaque) + assert result is None + + # STEP 19 in test_ospf_opaque_interface_disable and STEP 69 Overall + step("Verify that r1 still has the opaque LSAs") + assert verify_ospf_database(tgen, r1, opaque_input_dict, json_cmd) is None + + # STEP 20 in test_ospf_opaque_interface_disable and STEP 70 Overall + step("Verify that r2 doesn't have the opaque LSAs") + assert ( + verify_ospf_database(tgen, r2, no_opaque_input_dict, json_cmd) is not None + ) + + # STEP 21 in test_ospf_opaque_interface_disable and STEP 71 Overall + step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ") + r2.vtysh_multicmd( + "conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2" + ) + time.sleep(15) + + # STEP 22 in test_ospf_opaque_interface_disable and STEP 72 Overall + step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'") + rc, _, _ = tgen.net["r2"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" + assert rc, assertmsg + + # STEP 23 in test_ospf_opaque_interface_disable and STEP 73 Overall + step("Verify the ospf opaque option is applied to the r2 interface") + input_dict = { + "interfaces": { + "r2-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.2", + "ospfIfType": "Broadcast", + "opaqueCapable": True, + } + } + } + result = verify_ospf_interface(tgen, r2, input_dict) + assert result is None + + # STEP 24 in test_ospf_opaque_interface_disable and STEP 74 Overall + step("Verify that the neighbor options include opaque") + result = verify_ospf_neighbor(tgen, r1, r1_with_opaque) + assert result is None + result = verify_ospf_neighbor(tgen, r2, r2_with_opaque) + assert result is None + + # STEP 25 in test_ospf_opaque_interface_disable and STEP 75 Overall + step("Verify that r2 now has the opaque LSAs") + assert verify_ospf_database(tgen, r2, opaque_input_dict, json_cmd) is None + + except Exception: + if p: + p.terminate() + if p.wait(): + comm_error(p) + p = None + raise + finally: + if pread: + pread.terminate() + pread.wait() + if p: + p.terminate() + p.wait() + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_interface_disable(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_interface_disable(tgen, apibin) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args))