diff options
-rw-r--r-- | .travis.yml | 16 | ||||
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/developer/building-libyang.rst | 2 | ||||
-rw-r--r-- | docker/centos-7/Dockerfile | 6 | ||||
-rw-r--r-- | docker/centos-8/Dockerfile | 6 | ||||
-rw-r--r-- | isisd/isis_sr.c | 9 | ||||
-rw-r--r-- | lib/northbound_db.c | 17 | ||||
-rw-r--r-- | lib/northbound_grpc.cpp | 100 | ||||
-rw-r--r-- | lib/xref.h | 1 | ||||
-rw-r--r-- | tests/topotests/lib/common_config.py | 1 | ||||
-rw-r--r-- | tests/topotests/lib/ospf.py | 12 | ||||
-rw-r--r-- | tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py | 85 | ||||
-rw-r--r-- | tests/topotests/ospf_basic_functionality/test_ospf_single_area.py | 148 |
14 files changed, 337 insertions, 73 deletions
diff --git a/.travis.yml b/.travis.yml index 010292bb6..32b686c00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,14 @@ services: - docker jobs: include: - # - script: - # - docker/centos-7/build.sh - # - docker images - # name: centos7 - # - script: - # - docker/centos-8/build.sh - # - docker images - # name: centos8 + - script: + - docker/centos-7/build.sh + - docker images + name: centos7 + - script: + - docker/centos-8/build.sh + - docker images + name: centos8 - script: - sudo apt install -y linux-modules-extra-$(uname -r) - docker build -t frr-ubuntu18:latest -f docker/ubuntu18-ci/Dockerfile . diff --git a/Makefile.am b/Makefile.am index a5101df2f..a38029dcf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,11 @@ AM_CFLAGS = \ $(SAN_FLAGS) \ $(WERROR) \ # end +AM_CXXFLAGS = \ + $(AC_CXXFLAGS) \ + $(LIBYANG_CFLAGS) \ + $(WERROR) \ + # end # CPPFLAGS_BASE does not contain the include path for overriding assert.h, # therefore should be used in tools that do *not* link libfrr or do not want diff --git a/configure.ac b/configure.ac index f84a3d3c5..6ed438ace 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.69]) -AC_INIT([frr], [7.7-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [8.1-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst index a447f5830..3b33eb387 100644 --- a/doc/developer/building-libyang.rst +++ b/doc/developer/building-libyang.rst @@ -10,7 +10,7 @@ The FRR project builds some binary ``libyang`` packages. RPM packages are at our `RPM repository <https://rpm.frrouting.org>`_. DEB packages are available as CI artifacts `here -<https://ci1.netdef.org/browse/LIBYANG-LIBYANG-V2/latestSuccessful/artifact>`_. +<https://ci1.netdef.org/browse/LIBYANG-LIBYANGV2/latestSuccessful/artifact>`_. .. warning:: diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile index d2ec9f974..748b5345a 100644 --- a/docker/centos-7/Dockerfile +++ b/docker/centos-7/Dockerfile @@ -5,8 +5,8 @@ RUN yum install -y rpm-build autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig \ json-c-devel pam-devel bison flex pytest c-ares-devel \ python3-devel python3-sphinx systemd-devel libcap-devel \ - https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ - https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang-devel-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm @@ -32,7 +32,7 @@ RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> # This stage installs frr from the rpm FROM centos:centos7 RUN mkdir -p /pkgs/rpm \ - && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/ diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile index 104501aab..e273be055 100644 --- a/docker/centos-8/Dockerfile +++ b/docker/centos-8/Dockerfile @@ -5,8 +5,8 @@ RUN dnf install --enablerepo=powertools -y rpm-build git autoconf pcre-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 systemd-devel libcap-devel platform-python-devel \ - https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ - https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang-devel-1.0.184-0.x86_64.rpm \ + 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-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm @@ -33,7 +33,7 @@ RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> # This stage installs frr from the rpm FROM centos:centos8 RUN mkdir -p /pkgs/rpm \ - && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + && yum install -y 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-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index c4024772f..f7cef43d0 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -361,9 +361,9 @@ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, pcfg->last_hop_behavior = yang_get_default_enum( "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR); - /* Set the N-flag when appropriate. */ + /* Mark as node Sid if the prefix is host and configured in loopback */ ifp = if_lookup_prefix(prefix, VRF_DEFAULT); - if (ifp && sr_prefix_is_node_sid(ifp, prefix) && !pcfg->n_flag_clear) + if (ifp && sr_prefix_is_node_sid(ifp, prefix)) pcfg->node_sid = true; /* Save prefix-sid configuration. */ @@ -438,7 +438,7 @@ void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, } if (external) SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED); - if (pcfg->node_sid) + if (pcfg->node_sid && !pcfg->n_flag_clear) SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE); /* Set SID value. */ @@ -948,8 +948,7 @@ static int sr_if_new_hook(struct interface *ifp) if (!pcfg) continue; - if (sr_prefix_is_node_sid(ifp, &pcfg->prefix) - && !pcfg->n_flag_clear) { + if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) { pcfg->node_sid = true; lsp_regenerate_schedule(area, area->is_type, 0); } diff --git a/lib/northbound_db.c b/lib/northbound_db.c index 244e760b2..dce9b2ec2 100644 --- a/lib/northbound_db.c +++ b/lib/northbound_db.c @@ -87,9 +87,12 @@ int nb_db_transaction_save(const struct nb_transaction *transaction, goto exit; client_name = nb_client_name(transaction->context->client); - /* Always record configurations in the XML format. */ + /* + * Always record configurations in the XML format, save the default + * values too, as this covers the case where defaults may change. + */ if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML, - LYP_FORMAT | LYP_WITHSIBLINGS) + LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL) != 0) goto exit; @@ -149,6 +152,7 @@ struct nb_config *nb_db_transaction_load(uint32_t transaction_id) struct lyd_node *dnode; const char *config_str; struct sqlite3_stmt *ss; + LY_ERR err; ss = db_prepare( "SELECT\n" @@ -169,10 +173,11 @@ struct nb_config *nb_db_transaction_load(uint32_t transaction_id) if (db_loadf(ss, "%s", &config_str) != 0) goto exit; - dnode = lyd_parse_mem(ly_native_ctx, config_str, LYD_XML, - LYD_OPT_CONFIG); - if (!dnode) - flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_mem() failed", + err = lyd_parse_data_mem(ly_native_ctx, config_str, LYD_XML, + LYD_PARSE_STRICT | LYD_PARSE_NO_STATE, + LYD_VALIDATE_NO_STATE, &dnode); + if (err || !dnode) + flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_data_mem() failed", __func__); else config = nb_config_new(dnode); diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index dc2d29c11..c61effdda 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -199,9 +199,8 @@ class NorthboundImpl auto m = tag->response.add_supported_modules(); m->set_name(module->name); - if (module->info->rev_size) - m->set_revision( - module->info->rev[0].date); + if (module->info->revision) + m->set_revision(module->info->revision); m->set_organization(module->info->org); } @@ -1068,14 +1067,13 @@ class NorthboundImpl const std::string &path, const std::string &value) { - ly_errno = LY_SUCCESS; - dnode = lyd_new_path(dnode, ly_native_ctx, path.c_str(), - (void *)value.c_str(), - (LYD_ANYDATA_VALUETYPE)0, - LYD_PATH_OPT_UPDATE); - if (!dnode && ly_errno != LY_SUCCESS) { - flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", - __func__); + LY_ERR err = lyd_new_path(dnode, ly_native_ctx, path.c_str(), + value.c_str(), LYD_NEW_PATH_UPDATE, + &dnode); + if (err != LY_SUCCESS) { + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_new_path() failed: %s", __func__, + ly_errmsg(ly_native_ctx)); return -1; } @@ -1089,7 +1087,7 @@ class NorthboundImpl if (!dnode) return -1; - lyd_free(dnode); + lyd_free_tree(dnode); return 0; } @@ -1132,46 +1130,53 @@ class NorthboundImpl std::string(date), std::string(comment))); } - static int data_tree_from_dnode(frr::DataTree *dt, - const struct lyd_node *dnode, - LYD_FORMAT lyd_format, - bool with_defaults) + static LY_ERR data_tree_from_dnode(frr::DataTree *dt, + const struct lyd_node *dnode, + LYD_FORMAT lyd_format, + bool with_defaults) { char *strp; int options = 0; - SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS); + SET_FLAG(options, LYD_PRINT_WITHSIBLINGS); if (with_defaults) - SET_FLAG(options, LYP_WD_ALL); + SET_FLAG(options, LYD_PRINT_WD_ALL); else - SET_FLAG(options, LYP_WD_TRIM); + SET_FLAG(options, LYD_PRINT_WD_TRIM); - if (lyd_print_mem(&strp, dnode, lyd_format, options) == 0) { + LY_ERR err = lyd_print_mem(&strp, dnode, lyd_format, options); + if (err == LY_SUCCESS) { if (strp) { dt->set_data(strp); free(strp); } - return 0; } - - return -1; + return err; } static struct lyd_node *dnode_from_data_tree(const frr::DataTree *dt, bool config_only) { struct lyd_node *dnode; - int options; - - if (config_only) - options = LYD_OPT_CONFIG; - else - options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; - - dnode = lyd_parse_mem(ly_native_ctx, dt->data().c_str(), - encoding2lyd_format(dt->encoding()), - options); + int options, opt2; + LY_ERR err; + + if (config_only) { + options = LYD_PARSE_STRICT | LYD_PARSE_NO_STATE; + opt2 = LYD_VALIDATE_NO_STATE; + } else { + options = LYD_PARSE_STRICT; + opt2 = 0; + } + err = lyd_parse_data_mem(ly_native_ctx, dt->data().c_str(), + encoding2lyd_format(dt->encoding()), + options, opt2, &dnode); + if (err != LY_SUCCESS) { + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_parse_mem() failed: %s", __func__, + ly_errmsg(ly_native_ctx)); + } return dnode; } @@ -1239,14 +1244,15 @@ class NorthboundImpl // Combine configuration and state data into a single // dnode. // - if (lyd_merge(dnode_state, dnode_config, - LYD_OPT_EXPLICIT) - != 0) { + if (lyd_merge_tree(&dnode_state, dnode_config, + LYD_MERGE_DESTRUCT) + != LY_SUCCESS) { yang_dnode_free(dnode_state); yang_dnode_free(dnode_config); return grpc::Status( grpc::StatusCode::INTERNAL, - "Failed to merge configuration and state data"); + "Failed to merge configuration and state data", + ly_errmsg(ly_native_ctx)); } dnode_final = dnode_state; @@ -1262,19 +1268,25 @@ class NorthboundImpl // Validate data to create implicit default nodes if necessary. int validate_opts = 0; if (type == frr::GetRequest_DataType_CONFIG) - validate_opts = LYD_OPT_CONFIG; + validate_opts = LYD_VALIDATE_NO_STATE; else - validate_opts = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; - lyd_validate(&dnode_final, validate_opts, ly_native_ctx); + validate_opts = 0; + LY_ERR err = lyd_validate_all(&dnode_final, ly_native_ctx, + validate_opts, NULL); + + if (err) + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_validate_all() failed: %s", __func__, + ly_errmsg(ly_native_ctx)); // Dump data using the requested format. - int ret = data_tree_from_dnode(dt, dnode_final, lyd_format, - with_defaults); + if (!err) + err = data_tree_from_dnode(dt, dnode_final, lyd_format, + with_defaults); yang_dnode_free(dnode_final); - if (ret != 0) + if (err) return grpc::Status(grpc::StatusCode::INTERNAL, "Failed to dump data"); - return grpc::Status::OK; } diff --git a/lib/xref.h b/lib/xref.h index 949458b31..6cff1a376 100644 --- a/lib/xref.h +++ b/lib/xref.h @@ -169,6 +169,7 @@ extern const struct xref * const __stop_xref_array[1] DSO_LOCAL; static void __attribute__((used, _CONSTRUCTOR(1100))) \ _xref_init(void) { \ static struct xref_block _xref_block = { \ + .next = NULL, \ .start = __start_xref_array, \ .stop = __stop_xref_array, \ }; \ diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index ba6004861..5a904423c 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -1788,6 +1788,7 @@ def create_interfaces_cfg(tgen, topo, build=False): "network", "priority", "cost", + "mtu_ignore" ] if "ospf" in data: interface_data += _create_interfaces_ospf_cfg( diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 04a12d0ee..7ad64de4a 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -352,6 +352,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= data_ospf_auth = ospf_data.setdefault("authentication", None) data_ospf_dr_priority = ospf_data.setdefault("priority", None) data_ospf_cost = ospf_data.setdefault("cost", None) + data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None) try: intf = topo["routers"][router]["links"][lnk]["interface"] @@ -400,19 +401,26 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= config_data.append(cmd) # interface ospf dr priority - if data_ospf_dr_priority in ospf_data: + if data_ospf_dr_priority: cmd = "ip ospf priority {}".format(ospf_data["priority"]) if "del_action" in ospf_data: cmd = "no {}".format(cmd) config_data.append(cmd) # interface ospf cost - if data_ospf_cost in ospf_data: + if data_ospf_cost: cmd = "ip ospf cost {}".format(ospf_data["cost"]) if "del_action" in ospf_data: cmd = "no {}".format(cmd) config_data.append(cmd) + # interface ospf mtu + if data_ospf_mtu: + cmd = "ip ospf mtu-ignore" + if 'del_action' in ospf_data: + cmd = "no {}".format(cmd) + config_data.append(cmd) + if build: return config_data else: diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py index 1432a82b1..9dfde325f 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -62,6 +62,8 @@ from lib.ospf import ( verify_ospf_rib, create_router_ospf, redistribute_ospf, + config_ospf_interface, + verify_ospf_interface, ) # Global variables @@ -576,6 +578,89 @@ def test_ospf_redistribution_tc8_p1(request): write_test_footer(tc_name) +def test_ospf_cost_tc52_p0(request): + """OSPF Cost - verifying ospf interface cost functionality""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + global topo + step("Bring up the base config.") + reset_config_on_routers(tgen) + + step( + "Configure ospf cost as 20 on interface between R0 and R1. " + "Configure ospf cost as 30 between interface between R0 and R2." + ) + + r0_ospf_cost = { + "r0": {"links": {"r1": {"ospf": {"cost": 20}}, "r2": {"ospf": {"cost": 30}}}} + } + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between" + " r0 and r1 as 30 and r0 and r2 as 20" + ) + dut = "r0" + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Swap the costs between interfaces on r0, between r0 and r1 to 30" + ", r0 and r2 to 20" + ) + + r0_ospf_cost = { + "r0": {"links": {"r1": {"ospf": {"cost": 30}}, "r2": {"ospf": {"cost": 20}}}} + } + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between r0 " + "and r1 as 30 and r0 and r2 as 20." + ) + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure cost from the interface r0 - r1.") + + r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 30, "del_action": True}}}}} + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict = { + "r0": {"links": {"r1": {"ospf": {"cost": 10}}, "r2": {"ospf": {"cost": 20}}}} + } + step( + "Verify that cost is updated in the ospf interface between r0" + " and r1 as 10 and r0 and r2 as 20." + ) + + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step(" Un configure cost from the interface r0 - r2.") + + r0_ospf_cost = {"r0": {"links": {"r2": {"ospf": {"cost": 20, "del_action": True}}}}} + result = config_ospf_interface(tgen, topo, r0_ospf_cost) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that cost is updated in the ospf interface between r0" + "and r1 as 10 and r0 and r2 as 10" + ) + + input_dict = { + "r0": {"links": {"r1": {"ospf": {"cost": 10}}, "r2": {"ospf": {"cost": 10}}}} + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py index 6f6b119ab..e94680d97 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -101,6 +101,7 @@ TESTCASES = 2. OSPF Timers - Verify OSPF interface timer hello interval functionality 3. OSPF Timers - Verify OSPF interface timer dead interval functionality 4. Verify ospf show commands with json output. +5. Verify NFSM events when ospf nbr changes with different MTU values. """ @@ -976,6 +977,153 @@ def test_ospf_dead_tc11_p0(request): write_test_footer(tc_name) +def test_ospf_tc4_mtu_ignore_p0(request): + """ + OSPF NFSM - MTU change + + Verify NFSM events when ospf nbr changes with different MTU values + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + step(" Bring up the base config as per the topology") + step("Configure OSPF on all the routers of the topology.") + step("Verify that OSPF neighbors are FULL.") + reset_config_on_routers(tgen) + result = verify_ospf_neighbor(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Modify the MTU to non default Value on R0 to R1 interface. " + "Reset ospf neighbors on R0." + ) + + rtr0 = tgen.routers()["r0"] + rtr1 = tgen.routers()["r1"] + + r0_r1_intf = topo["routers"]["r0"]["links"]["r1"]["interface"] + r1_r0_intf = topo["routers"]["r1"]["links"]["r0"]["interface"] + + rtr0.run("ifconfig {} mtu 1200".format(r0_r1_intf)) + + clear_ospf(tgen, "r0") + + step( + "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." + ) + result = verify_ospf_neighbor(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n OSPF nbrs are Full " + "instead of Exstart. Error: {}".format(tc_name, result) + ) + + step( + "Verify that configured MTU value is updated in the show ip " "ospf interface." + ) + + dut = "r0" + input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}} + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Modify the MTU to non default Value on R0 to R1 interface. " + "Reset ospf neighbors on R0." + ) + rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf)) + + clear_ospf(tgen, "r0") + + step("Verify that OSPF neighborship between R0 and R1 becomes full.") + result = verify_ospf_neighbor(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure mtu ignore and change the value of the mtu to non default" + " on R0 to R1 interface. Reset ospf neighbors on R0." + ) + r0_ospf_mtu = {"r0": {"links": {"r1": {"ospf": {"mtu_ignore": True}}}}} + result = config_ospf_interface(tgen, topo, r0_ospf_mtu) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + dut = "r0" + input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuMismatchDetect": True}}}}} + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + r1_ospf_mtu = {"r1": {"links": {"r0": {"ospf": {"mtu_ignore": True}}}}} + result = config_ospf_interface(tgen, topo, r1_ospf_mtu) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + rtr0.run("ifconfig {} mtu 1200".format(r0_r1_intf)) + + clear_ospf(tgen, "r0") + + step("Verify that OSPF neighborship between R0 and R1 becomes full.") + result = verify_ospf_neighbor(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Unconfigure mtu-ignore command from the interface. " + "Reset ospf neighbors on R0." + ) + + r1_ospf_mtu = { + "r1": {"links": {"r0": {"ospf": {"mtu_ignore": True, "delete": True}}}} + } + result = config_ospf_interface(tgen, topo, r1_ospf_mtu) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + clear_ospf(tgen, "r0") + + step( + "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." + ) + result = verify_ospf_neighbor(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n OSPF nbrs are Full " + "instead of Exstart. Error: {}".format(tc_name, result) + ) + + step("Modify the MTU to again default valaue on R0 to R1 interface.") + + rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf)) + + clear_ospf(tgen, "r0") + + step("Verify that OSPF neighborship between R0 and R1 becomes full.") + result = verify_ospf_neighbor(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." + ) + + rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf)) + rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf)) + + clear_ospf(tgen, "r0") + clear_ospf(tgen, "r1") + + step("Verify that OSPF neighborship between R0 and R1 becomes full.") + result = verify_ospf_neighbor(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that jumbo MTU is updated in the show ip ospf interface.") + dut = "r0" + input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 9216}}}}} + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) |