diff options
| author | Acee <aceelindem@gmail.com> | 2023-06-08 16:49:34 -0400 | 
|---|---|---|
| committer | Acee <aceelindem@gmail.com> | 2023-06-28 13:03:48 -0400 | 
| commit | e72549c8f6f7b25738f8d4c913db025a7906558e (patch) | |
| tree | 4bccfc18bd16b621f4c862255ad63f73161e0d7c | |
| parent | f34076da81303313422c6beba1506a691a5e2fb6 (diff) | |
    ospfd: Configurable interface-level 'capability opaque' support
    Add support for "[no] ip ospf capbility opaque" at the interface
    level with the default being capability opaque enabled. The command
    "no ip ospf capability opaque" will disable opaque LSA database
    exchange and flooding on the interface. A change in configuration
    will result in the interface being flapped to update our options
    for neighbors but no attempt will be made to purge existing LSAs
    as in dense topologies, these may received by neighbors through
    different interfaces.
    Topotests are added to test both the configuration and the LSA
    opaque flooding suppression.
Signed-off-by: Acee <aceelindem@gmail.com>
| -rw-r--r-- | doc/user/ospfd.rst | 14 | ||||
| -rw-r--r-- | lib/libospf.h | 1 | ||||
| -rw-r--r-- | ospfd/ospf_flood.c | 9 | ||||
| -rw-r--r-- | ospfd/ospf_gr.c | 11 | ||||
| -rw-r--r-- | ospfd/ospf_interface.c | 32 | ||||
| -rw-r--r-- | ospfd/ospf_interface.h | 3 | ||||
| -rw-r--r-- | ospfd/ospf_opaque.c | 6 | ||||
| -rw-r--r-- | ospfd/ospf_packet.c | 16 | ||||
| -rw-r--r-- | ospfd/ospf_vty.c | 79 | ||||
| -rw-r--r-- | tests/topotests/ospfapi/test_ospf_clientapi.py | 439 | 
10 files changed, 585 insertions, 25 deletions
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 3430d8a282..232b1c3934 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 <NAME|all>] database (opaque-link|opaque-area|opaque-external)  .. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID diff --git a/lib/libospf.h b/lib/libospf.h index 9eaca9a1a8..e3c1adb810 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 a4d0f77faf..d327617f49 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 2a346f2388..c23c42052f 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 2c66cb3cfc..a867d9aaf6 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 ec1afa1b8b..38ec45c757 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 6894c6a009..27f47a6d79 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 d010b8b6e6..739fedb673 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 ff17b147e4..e7b578f743 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,61 @@ 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; +	bool opaque_capable_change; + +	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; +        opaque_capable_change = (old_opaque_capable != params->opaque_capable); +	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 (opaque_capable_change) { +		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 +12224,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 +13048,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 39ebbcfb62..7a7ea85e2f 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -17,6 +17,7 @@ import subprocess  import sys  import time  from datetime import datetime, timedelta +from functools import partial  import pytest  from lib.common_config import ( @@ -31,6 +32,12 @@ from lib.micronet import Timeout, comm_error  from lib.topogen import Topogen, TopoRouter  from lib.topotest import interface_set_status, json_cmp +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +  pytestmark = [pytest.mark.ospfd]  CWD = os.path.dirname(os.path.realpath(__file__)) @@ -1142,6 +1149,438 @@ 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 56 in CI tests +        step("Disable OSPF opaque LSA Copability on r1's interface to r2") +        r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque") +        time.sleep(15) + +        # STEP 2 in test_ospf_opaque_interface_disable and STEP 57 in CI tests +        step("Verify the r1 configuration of 'no ip ospf capability opaque'") +        no_capability_opaque_cfg = ( +            tgen.net["r1"] +            .cmd( +                'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"' +            ) +            .rstrip() +        ) +        assertmsg = ( +            "'no ip ospf capability opaque' applied, but not present in configuration" +        ) +        assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg + +        # STEP 3 in test_ospf_opaque_interface_disable and STEP 58 in CI tests +        step("Verify the ospf opaque option is not applied to the r1 interface") +        r1_interface_without_opaque = { +            "interfaces": { +                "r1-eth0": { +                    "ifUp": True, +                    "ospfEnabled": True, +                    "ipAddress": "10.0.1.1", +                    "ospfIfType": "Broadcast", +                    "opaqueCapable": False, +                } +            } +        } +        r1_interface_with_opaque = { +            "interfaces": { +                "r1-eth0": { +                    "ifUp": True, +                    "ospfEnabled": True, +                    "ipAddress": "10.0.1.1", +                    "ospfIfType": "Broadcast", +                    "opaqueCapable": True, +                } +            } +        } +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" +        assert result is None, assertmsg + +        r1_neighbor_without_opaque = { +            "neighbors": { +                "2.0.0.0": [ +                    { +                        "optionsList": "*|-|-|-|-|-|E|-", +                    } +                ] +            } +        } +        r2_neighbor_without_opaque = { +            "neighbors": { +                "1.0.0.0": [ +                    { +                        "optionsList": "*|-|-|-|-|-|E|-", +                    } +                ] +            } +        } +        # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests +        step("Verify that the r1 neighbor options don't include opaque") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF neighbor has opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests +        step("Verify that the r1 neighbor options don't include opaque") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF neighbor has opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 6 in test_ospf_opaque_interface_disable and STEP 61 in CI tests +        step( +            "Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration" +        ) +        rc, _, _ = tgen.net["r2"].cmd_status( +            "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False +        ) +        assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" +        assert rc, assertmsg + +        # STEP 7 in test_ospf_opaque_interface_disable and STEP 62 in CI tests +        step("Verify the ospf opaque option is applied to the r2 interface") +        r2_interface_without_opaque = { +            "interfaces": { +                "r2-eth0": { +                    "ifUp": True, +                    "ospfEnabled": True, +                    "ipAddress": "10.0.1.2", +                    "ospfIfType": "Broadcast", +                    "opaqueCapable": False, +                } +            } +        } +        r2_interface_with_opaque = { +            "interfaces": { +                "r2-eth0": { +                    "ifUp": True, +                    "ospfEnabled": True, +                    "ipAddress": "10.0.1.2", +                    "ospfIfType": "Broadcast", +                    "opaqueCapable": True, +                } +            } +        } +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF interface has opaque capability disabled" +        assert result is None, assertmsg + +        # STEP 8 in test_ospf_opaque_interface_disable and STEP 63 in CI tests +        step("Install opaque LSAs on r1") +        pread = r2.popen( +            ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], +            encoding=None,  # don't buffer +            stdin=subprocess.DEVNULL, +            stdout=subprocess.PIPE, +            stderr=subprocess.STDOUT, +        ) +        p = r1.popen( +            [ +                apibin, +                "-v", +                "add,9,10.0.1.1,230,1,feedaceedeadbeef", +                "add,10,1.2.3.4,231,1,feedaceecafebeef", +                "add,11,232,1,feedaceebaddbeef", +                "wait,20", +            ] +        ) +        opaque_LSAs_in_database = { +            "areas": { +                "1.2.3.4": { +                    "linkLocalOpaqueLsa": [ +                        { +                            "lsId": "230.0.0.1", +                            "advertisedRouter": "1.0.0.0", +                            "sequenceNumber": "80000001", +                        }, +                    ], +                    "linkLocalOpaqueLsaCount": 1, +                    "areaLocalOpaqueLsa": [ +                        { +                            "lsId": "231.0.0.1", +                            "advertisedRouter": "1.0.0.0", +                            "sequenceNumber": "80000001", +                        }, +                    ], +                    "areaLocalOpaqueLsaCount": 1, +                }, +            }, +            "asExternalOpaqueLsa": [ +                { +                    "lsId": "232.0.0.1", +                    "advertisedRouter": "1.0.0.0", +                    "sequenceNumber": "80000001", +                }, +            ], +            "asExternalOpaqueLsaCount": 1, +        } +        opaque_area_empty_database = { +            "routerId":"2.0.0.0", +            "areaLocalOpaqueLsa":{ +                "areas":{ +                    "1.2.3.4":[ +                    ] +                } +            } +        } + +        # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests +        step("Check that LSAs are added on r1") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF database doesn't contain opaque LSAs" +        assert result is None, assertmsg + +        # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests +        step("Check that LSAs are not added on r2") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", +            opaque_area_empty_database, True +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF area database contains opaque LSAs" +        assert result is None, assertmsg + +        # STEP 11 in test_ospf_opaque_interface_disable and STEP 66 in CI tests +        step("Enable OSPF opaque LSA Copability on r1's interface to r2") +        r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque") +        time.sleep(15) + +        # STEP 12 in test_ospf_opaque_interface_disable and STEP 67 in CI tests +        step("Verify no r1 configuration of 'no ip ospf capability opaque'") +        rc, _, _ = tgen.net["r1"].cmd_status( +            "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False +        ) +        assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration" +        assert rc, assertmsg + +        # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests +        step("Verify the ospf opaque option is applied to the r1 interface") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" +        assert result is None, assertmsg + +        r1_neighbor_with_opaque = { +            "neighbors": { +                "2.0.0.0": [ +                    { +                        "optionsList": "*|O|-|-|-|-|E|-", +                    } +                ] +            } +        } +        r2_neighbor_with_opaque = { +            "neighbors": { +                "1.0.0.0": [ +                    { +                        "optionsList": "*|O|-|-|-|-|E|-", +                    } +                ] +            } +        } +        # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests +        step("Verify that the r1 neighbor options include opaque") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests +        step("Verify that the r2 neighbor options include opaque") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests +        step("Check that LSAs are now added to r2") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF database doesn't contains opaque LSAs" +        assert result is None, assertmsg + +        # STEP 17 in test_ospf_opaque_interface_disable and STEP 72 in CI tests +        step( +            "Disable Opaque Capability on r2's interface to r1 using the interface address" +        ) +        r2.vtysh_multicmd( +            "conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2" +        ) + +        # STEP 18 in test_ospf_opaque_interface_disable and STEP 73 in CI tests +        step("Clear the OSPF process on r2 to clear the OSPF LSDB") +        r2.vtysh_multicmd("clear ip ospf process") +        time.sleep(15) + +        # STEP 19 in test_ospf_opaque_interface_disable and STEP 74 in CI tests +        step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'") +        no_capability_opaque_cfg = ( +            tgen.net["r2"] +            .cmd_nostatus( +                'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"' +            ) +            .rstrip() +        ) +        assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration" +        assert ( +            no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2" +        ), assertmsg + +        # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests +        step("Verify the ospf opaque option is not applied to the r2 interface") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" +        assert result is None, assertmsg + +        # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests +        step("Verify that the r1 neighbor options don't include opaque") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF neighbor has opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests +        step("Verify that the r2 neighbor options don't include opaque") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF neighbor has opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests +        step("Verify that r1 still has the opaque LSAs") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF database doesn't contain opaque LSAs" +        assert result is None, assertmsg + +        # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests +        step("Verify that r2 doesn't have the opaque LSAs") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", +            opaque_area_empty_database, True +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF area database contains opaque LSAs" +        assert result is None, assertmsg + +        # STEP 25 in test_ospf_opaque_interface_disable and STEP 80 in CI tests +        step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ") +        r2.vtysh_multicmd( +            "conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2" +        ) +        time.sleep(15) + +        # STEP 26 in test_ospf_opaque_interface_disable and STEP 81 in CI tests +        step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'") +        rc, _, _ = tgen.net["r2"].cmd_status( +            "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False +        ) +        assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" +        assert rc, assertmsg + +        # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests +        step("Verify the ospf opaque option is applied to the r2 interface") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF interface doesn't have opaque capability disabled" +        assert result is None, assertmsg + +        # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests +        step("Verify that the r2 neighbor options include opaque") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests +        step("Verify that the r1 neighbor options include opaque") +        test_func = partial( +            topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" +        assert result is None, assertmsg + +        # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests +        step("Verify that r2 now has the opaque LSAs") +        test_func = partial( +            topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database +        ) +        _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) +        assertmsg = "r2 OSPF database doesn't contain opaque LSAs" +        assert result is None, assertmsg + +    except Exception: +        if p: +            p.terminate() +            if p.wait(): +                comm_error(p) +            p = None +        raise +    finally: +        if pread: +            pread.terminate() +            pread.wait() +        if p: +            p.terminate() +            p.wait() + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_interface_disable(tgen): +    apibin = os.path.join(CLIENTDIR, "ospfclient.py") +    rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) +    logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) +    _test_opaque_interface_disable(tgen, apibin) + +  if __name__ == "__main__":      args = ["-s"] + sys.argv[1:]      sys.exit(pytest.main(args))  | 
