diff options
| -rw-r--r-- | configure.ac | 1 | ||||
| -rw-r--r-- | doc/user/isisd.rst | 4 | ||||
| -rw-r--r-- | isisd/isis_cli.c | 41 | ||||
| -rw-r--r-- | isisd/isis_lsp.c | 31 | ||||
| -rw-r--r-- | isisd/isis_lsp.h | 1 | ||||
| -rw-r--r-- | isisd/isis_nb.c | 13 | ||||
| -rw-r--r-- | isisd/isis_nb.h | 6 | ||||
| -rw-r--r-- | isisd/isis_nb_config.c | 24 | ||||
| -rw-r--r-- | isisd/isisd.c | 113 | ||||
| -rw-r--r-- | isisd/isisd.h | 10 | ||||
| -rw-r--r-- | tests/topotests/isis_topo1/test_isis_topo1.py | 313 | ||||
| -rw-r--r-- | yang/frr-isisd.yang | 22 |
12 files changed, 538 insertions, 41 deletions
diff --git a/configure.ac b/configure.ac index 4e1080045e..4cbdfe0fcc 100644 --- a/configure.ac +++ b/configure.ac @@ -2631,6 +2631,7 @@ AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra ap AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) +AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information]) AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information]) AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 9ccb5ba4b5..2b114ad127 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -83,6 +83,10 @@ writing, *isisd* does not support multiple ISIS processes. Set overload bit to avoid any transit traffic. +.. clicmd:: set-overload-bit on-startup (0-86400) + + Set overload bit on startup for the specified duration, in seconds. Reference: :rfc:`3277` + .. clicmd:: purge-originator Enable or disable :rfc:`6232` purge originator identification. diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index a673cb8c1e..9db867e2c0 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -404,7 +404,7 @@ DEFPY_YANG(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit", "Reset overload bit to accept transit traffic\n" "Set overload bit to avoid any transit traffic\n") { - nb_cli_enqueue_change(vty, "./overload", NB_OP_MODIFY, + nb_cli_enqueue_change(vty, "./overload/enabled", NB_OP_MODIFY, no ? "false" : "true"); return nb_cli_apply_changes(vty, NULL); @@ -419,6 +419,42 @@ void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode, } /* + * XPath: /frr-isisd:isis/instance/overload/on-startup + */ +DEFPY_YANG(set_overload_bit_on_startup, set_overload_bit_on_startup_cmd, + "set-overload-bit on-startup (0-86400)$val", + "Set overload bit to avoid any transit traffic\n" + "Set overload bit on startup\n" + "Set overload time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_MODIFY, + val_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_overload_bit_on_startup, no_set_overload_bit_on_startup_cmd, + "no set-overload-bit on-startup [(0-86400)$val]", + NO_STR + "Reset overload bit to accept transit traffic\n" + "Set overload bit on startup\n" + "Set overload time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_overload_on_startup(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " set-overload-bit on-startup %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +/* * XPath: /frr-isisd:isis/instance/attach-send */ DEFPY_YANG(attached_bit_send, attached_bit_send_cmd, "[no] attached-bit send", @@ -3107,6 +3143,9 @@ void isis_cli_init(void) install_element(ISIS_NODE, &dynamic_hostname_cmd); install_element(ISIS_NODE, &set_overload_bit_cmd); + install_element(ISIS_NODE, &set_overload_bit_on_startup_cmd); + install_element(ISIS_NODE, &no_set_overload_bit_on_startup_cmd); + install_element(ISIS_NODE, &attached_bit_send_cmd); install_element(ISIS_NODE, &attached_bit_receive_ignore_cmd); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 5387f37039..63b4edb1e1 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -68,6 +68,8 @@ static void lsp_l2_refresh_pseudo(struct thread *thread); static void lsp_destroy(struct isis_lsp *lsp); +static bool device_startup; + int lsp_id_cmp(uint8_t *id1, uint8_t *id2) { return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2); @@ -437,6 +439,21 @@ bool isis_level2_adj_up(struct isis_area *area) return false; } +/* + * Unset the overload bit after the timer expires + */ +void set_overload_on_start_timer(struct thread *thread) +{ + struct isis_area *area = THREAD_ARG(thread); + assert(area); + + area->t_overload_on_startup_timer = NULL; + + /* Check if set-overload-bit is not currently configured */ + if (!area->overload_configured) + isis_area_overload_bit_set(area, false); +} + static void isis_reset_attach_bit(struct isis_adjacency *adj) { struct isis_area *area = adj->circuit->area; @@ -1355,6 +1372,7 @@ int lsp_generate(struct isis_area *area, int level) uint32_t seq_num = 0; uint8_t lspid[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; + uint32_t overload_time; if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; @@ -1363,6 +1381,18 @@ int lsp_generate(struct isis_area *area, int level) memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN); + /* Check if device should be overloaded on startup */ + if (device_startup) { + overload_time = isis_restart_read_overload_time(area); + if (overload_time > 0) { + isis_area_overload_bit_set(area, true); + thread_add_timer(master, set_overload_on_start_timer, + area, overload_time, + &area->t_overload_on_startup_timer); + } + device_startup = false; + } + /* only builds the lsp if the area shares the level */ oldlsp = lsp_search(&area->lspdb[level - 1], lspid); if (oldlsp) { @@ -2373,6 +2403,7 @@ int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, void lsp_init(void) { + device_startup = true; hook_register(isis_adj_state_change_hook, lsp_handle_adj_state_change); } diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index b13b2a35e6..d7762324d9 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -66,6 +66,7 @@ DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare); void lsp_db_init(struct lspdb_head *head); void lsp_db_fini(struct lspdb_head *head); void lsp_tick(struct thread *thread); +void set_overload_on_start_timer(struct thread *thread); int lsp_generate(struct isis_area *area, int level); #define lsp_regenerate_schedule(area, level, all_pseudo) \ diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index a2ba33d078..4f4e6dc730 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -81,11 +81,18 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { - .xpath = "/frr-isisd:isis/instance/overload", + .xpath = "/frr-isisd:isis/instance/overload/enabled", .cbs = { .cli_show = cli_show_isis_overload, - .modify = isis_instance_overload_modify, - }, + .modify = isis_instance_overload_enabled_modify, + } + }, + { + .xpath = "/frr-isisd:isis/instance/overload/on-startup", + .cbs = { + .cli_show = cli_show_isis_overload_on_startup, + .modify = isis_instance_overload_on_startup_modify, + } }, { .xpath = "/frr-isisd:isis/instance/metric-style", diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index 00ca8be3b0..a9f2eaea95 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -37,7 +37,8 @@ int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args); int isis_instance_attached_send_modify(struct nb_cb_modify_args *args); int isis_instance_attached_receive_modify(struct nb_cb_modify_args *args); int isis_instance_attached_modify(struct nb_cb_modify_args *args); -int isis_instance_overload_modify(struct nb_cb_modify_args *args); +int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args); +int isis_instance_overload_on_startup_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_lsp_mtu_modify(struct nb_cb_modify_args *args); @@ -442,6 +443,9 @@ void cli_show_isis_attached_receive(struct vty *vty, bool show_defaults); void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_overload_on_startup(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_metric_style(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_area_pwd(struct vty *vty, const struct lyd_node *dnode, diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index e0decf48f2..1b7663fcfd 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -336,9 +336,9 @@ int isis_instance_attached_modify(struct nb_cb_modify_args *args) } /* - * XPath: /frr-isisd:isis/instance/overload + * XPath: /frr-isisd:isis/instance/overload/enabled */ -int isis_instance_overload_modify(struct nb_cb_modify_args *args) +int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args) { struct isis_area *area; bool overload; @@ -348,12 +348,32 @@ int isis_instance_overload_modify(struct nb_cb_modify_args *args) area = nb_running_get_entry(args->dnode, NULL, true); overload = yang_dnode_get_bool(args->dnode, NULL); + area->overload_configured = overload; + isis_area_overload_bit_set(area, overload); return NB_OK; } /* + * XPath: /frr-isisd:isis/instance/overload/on-startup + */ +int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint32_t overload_time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + overload_time = yang_dnode_get_uint32(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_overload_on_startup_set(area, overload_time); + + return NB_OK; +} + +/* * XPath: /frr-isisd:isis/instance/metric-style */ int isis_instance_metric_style_modify(struct nb_cb_modify_args *args) diff --git a/isisd/isisd.c b/isisd/isisd.c index 0ff31df0f8..efea1e5d5e 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -3198,9 +3198,15 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) if (new_overload_bit != area->overload_bit) { area->overload_bit = new_overload_bit; - - if (new_overload_bit) + if (new_overload_bit) { area->overload_counter++; + } else { + /* Cancel overload on startup timer if it's running */ + if (area->t_overload_on_startup_timer) { + THREAD_OFF(area->t_overload_on_startup_timer); + area->t_overload_on_startup_timer = NULL; + } + } #ifndef FABRICD hook_call(isis_hook_db_overload, area); @@ -3213,6 +3219,109 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) #endif /* ifndef FABRICD */ } +void isis_area_overload_on_startup_set(struct isis_area *area, + uint32_t startup_time) +{ + if (area->overload_on_startup_time != startup_time) { + area->overload_on_startup_time = startup_time; + isis_restart_write_overload_time(area, startup_time); + } +} + +/* + * Returns the path of the file (non-volatile memory) that contains restart + * information. + */ +char *isis_restart_filepath() +{ + static char filepath[MAXPATHLEN]; + snprintf(filepath, sizeof(filepath), ISISD_RESTART, ""); + return filepath; +} + +/* + * Record in non-volatile memory the overload on startup time. + */ +void isis_restart_write_overload_time(struct isis_area *isis_area, + uint32_t overload_time) +{ + char *filepath; + const char *area_name; + json_object *json; + json_object *json_areas; + json_object *json_area; + + filepath = isis_restart_filepath(); + area_name = isis_area->area_tag; + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "areas", &json_areas); + if (!json_areas) { + json_areas = json_object_new_object(); + json_object_object_add(json, "areas", json_areas); + } + + json_object_object_get_ex(json_areas, area_name, &json_area); + if (!json_area) { + json_area = json_object_new_object(); + json_object_object_add(json_areas, area_name, json_area); + } + + json_object_int_add(json_area, "overload_time", + isis_area->overload_on_startup_time); + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory the overload on startup time. + */ +uint32_t isis_restart_read_overload_time(struct isis_area *isis_area) +{ + char *filepath; + const char *area_name; + json_object *json; + json_object *json_areas; + json_object *json_area; + json_object *json_overload_time; + uint32_t overload_time = 0; + + filepath = isis_restart_filepath(); + area_name = isis_area->area_tag; + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "areas", &json_areas); + if (!json_areas) { + json_areas = json_object_new_object(); + json_object_object_add(json, "areas", json_areas); + } + + json_object_object_get_ex(json_areas, area_name, &json_area); + if (!json_area) { + json_area = json_object_new_object(); + json_object_object_add(json_areas, area_name, json_area); + } + + json_object_object_get_ex(json_area, "overload_time", + &json_overload_time); + if (json_overload_time) { + overload_time = json_object_get_int(json_overload_time); + } + + json_object_object_del(json_areas, area_name); + + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); + + return overload_time; +} + void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit) { diff --git a/isisd/isisd.h b/isisd/isisd.h index 4951e5809b..a9c1d60439 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -142,6 +142,7 @@ struct isis_area { struct flags flags; struct thread *t_tick; /* LSP walker */ struct thread *t_lsp_refresh[ISIS_LEVELS]; + struct thread *t_overload_on_startup_timer; struct timeval last_lsp_refresh_event[ISIS_LEVELS]; struct thread *t_rlfa_rib_update; /* t_lsp_refresh is used in two ways: @@ -180,7 +181,9 @@ struct isis_area { char is_type; /* level-1 level-1-2 or level-2-only */ /* are we overloaded? */ char overload_bit; + bool overload_configured; uint32_t overload_counter; + uint32_t overload_on_startup_time; /* L1/L2 router identifier for inter-area traffic */ char attached_bit_send; char attached_bit_rcv_ignore; @@ -290,6 +293,8 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit); +void isis_area_overload_on_startup_set(struct isis_area *area, + uint32_t startup_time); void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit); void isis_area_attached_bit_receive_set(struct isis_area *area, bool attached_bit); @@ -315,7 +320,10 @@ void show_isis_database_lspdb_json(struct json_object *json, void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, int level, struct lspdb_head *lspdb, const char *argv, int ui_level); - +char *isis_restart_filepath(void); +void isis_restart_write_overload_time(struct isis_area *isis_area, + uint32_t overload_time); +uint32_t isis_restart_read_overload_time(struct isis_area *isis_area); /* YANG paths */ #define ISIS_INSTANCE "/frr-isisd:isis/instance" #define ISIS_SR "/frr-isisd:isis/instance/segment-routing" diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 014722387f..8b6c3772b8 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -25,7 +25,7 @@ """ test_isis_topo1.py: Test ISIS topology. """ - +import datetime import functools import json import os @@ -38,6 +38,11 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest +from lib.common_config import ( + retry, + stop_router, + start_router, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger @@ -248,10 +253,12 @@ def test_isis_summary_json(): for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True) - assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['vrf'] == "default", assertmsg - assert json_output['areas'][0]['area'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + assertmsg = "Test isis summary json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["vrf"] == "default", assertmsg + assert json_output["areas"][0]["area"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg def test_isis_interface_json(): @@ -265,15 +272,29 @@ def test_isis_interface_json(): logger.info("Checking 'show isis interface json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True) - assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis interface json", isjson=True + ) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True) - assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis interface detail json", isjson=True + ) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg def test_isis_neighbor_json(): @@ -284,19 +305,32 @@ def test_isis_neighbor_json(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - #tgen.mininet_cli() + # tgen.mininet_cli() logger.info("Checking 'show isis neighbor json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True) - assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis neighbor json", isjson=True + ) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"] == rname + "-eth0" + ), assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True) - assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis neighbor detail json", isjson=True + ) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg def test_isis_database_json(): @@ -307,21 +341,246 @@ def test_isis_database_json(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - #tgen.mininet_cli() + # tgen.mininet_cli() logger.info("Checking 'show isis database json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True) - assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['area']['name'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis database json", isjson=True + ) + assertmsg = "Test isis database json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["areas"][0]["area"]["name"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True) - assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['area']['name'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis database detail json", isjson=True + ) + assertmsg = "Test isis database json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["areas"][0]["area"]["name"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg + + +def test_isis_overload_on_startup(): + "Check that overload on startup behaves as expected" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 120 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Testing overload on startup behavior") + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + + tstamp_before_start_router = datetime.datetime.now() + start_router(tgen, "r3") + tstamp_after_start_router = datetime.datetime.now() + startup_router_time = ( + tstamp_after_start_router - tstamp_before_start_router + ).total_seconds() + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + check_lsp_overload_bit("r1", "r3.00-00", "0/0/1") + + # Attempt to unset overload bit while timer is still running + r3.vtysh_cmd( + """ + configure + router isis 1 + no set-overload-bit on-startup + no set-overload-bit + """ + ) + + # Check overload bit is still set + check_lsp_overload_bit("r1", "r3.00-00", "0/0/1") + + # Check that overload bit is unset after timer completes + check_lsp_overload_bit("r3", "r3.00-00", "0/0/0") + tstamp_after_bit_unset = datetime.datetime.now() + check_lsp_overload_bit("r1", "r3.00-00", "0/0/0") + + # Collect time overloaded + time_overloaded = ( + tstamp_after_bit_unset - tstamp_after_start_router + ).total_seconds() + logger.info(f"Time Overloaded: {time_overloaded}") + + # Use time it took to startup router as lower bound + logger.info( + f"Assert that overload time falls in range: {overload_time - startup_router_time} < {time_overloaded} <= {overload_time}" + ) + result = overload_time - startup_router_time < time_overloaded <= overload_time + assert result + + +def test_isis_overload_on_startup_cancel_timer(): + "Check that overload on startup timer is cancelled when overload bit is set/unset" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 90 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Testing overload on startup behavior with set overload bit: cancel timer" + ) + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + set-overload-bit + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + start_router(tgen, "r3") + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + # Check that overload timer is running + check_overload_timer("r3", True) + + # Unset overload bit while timer is running + r3.vtysh_cmd( + """ + configure + router isis 1 + no set-overload-bit + """ + ) + + # Check that overload timer is cancelled + check_overload_timer("r3", False) + + # Check overload bit is unset + check_lsp_overload_bit("r3", "r3.00-00", "0/0/0") + + +def test_isis_overload_on_startup_override_timer(): + "Check that overload bit remains set after overload timer expires if overload bit is configured" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 60 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Testing overload on startup behavior with set overload bit: override timer" + ) + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + set-overload-bit + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + start_router(tgen, "r3") + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + # Check that overload timer is running + check_overload_timer("r3", True) + + # Check that overload timer expired + check_overload_timer("r3", False) + + # Check overload bit is still set + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + +@retry(retry_timeout=200) +def _check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected): + "Verfiy overload bit in router's LSP" + + tgen = get_topogen() + router = tgen.gears[router] + logger.info(f"check_overload_bit {router}") + isis_database_output = router.vtysh_cmd( + "show isis database {} json".format(overloaded_router_lsp) + ) + + database_json = json.loads(isis_database_output) + att_p_ol = database_json["areas"][0]["levels"][1]["att-p-ol"] + if att_p_ol == att_p_ol_expected: + return True + return "{} peer with expected att_p_ol {} got {} ".format( + router.name, att_p_ol_expected, att_p_ol + ) + + +def check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected): + "Verfiy overload bit in router's LSP" + + assertmsg = _check_lsp_overload_bit( + router, overloaded_router_lsp, att_p_ol_expected + ) + assert assertmsg is True, assertmsg + + +@retry(retry_timeout=200) +def _check_overload_timer(router, timer_expected): + "Verfiy overload bit in router's LSP" + + tgen = get_topogen() + router = tgen.gears[router] + thread_output = router.vtysh_cmd("show thread timers") + + timer_running = "set_overload_on_start_timer" in thread_output + if timer_running == timer_expected: + return True + return "Expected timer running status: {}".format(timer_expected) + + +def check_overload_timer(router, timer_expected): + "Verfiy overload bit in router's LSP" + + assertmsg = _check_overload_timer(router, timer_expected) + assert assertmsg is True, assertmsg def test_memory_leak(): diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 0812c86fac..380fce3859 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -1066,11 +1066,25 @@ module frr-isisd { "If true, identify as L1/L2 router for inter-area traffic."; } - leaf overload { - type boolean; - default "false"; + container overload { description - "If true, avoid any transit traffic."; + "Overload bit configuration."; + leaf enabled { + type boolean; + default "false"; + description + "If true, avoid any transit traffic."; + } + + leaf on-startup { + type uint32 { + range "0..86400"; + } + units "seconds"; + default "0"; + description + "The duration the overload bit should be set on startup."; + } } leaf metric-style { |
