diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 5c875204f7..24084f6fe8 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -21,7 +21,7 @@ #include "lib/mgmt_be_client_clippy.c" #define MGMTD_BE_CLIENT_DBG(fmt, ...) \ - DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s:" fmt, __func__, \ + DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s: " fmt, __func__, \ ##__VA_ARGS__) #define MGMTD_BE_CLIENT_ERR(fmt, ...) \ zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index 35a6d7d909..7af421405b 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -124,18 +124,15 @@ static int mgmt_fe_send_session_req(struct mgmt_fe_client *client, { Mgmtd__FeMessage fe_msg; Mgmtd__FeSessionReq sess_req; - bool scok; mgmtd__fe_session_req__init(&sess_req); sess_req.create = create; if (create) { sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID; sess_req.client_conn_id = session->client_id; - scok = true; } else { sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_SESSION_ID; sess_req.session_id = session->session_id; - scok = false; } mgmtd__fe_message__init(&fe_msg); @@ -146,7 +143,7 @@ static int mgmt_fe_send_session_req(struct mgmt_fe_client *client, "Sending SESSION_REQ %s message for client-id %" PRIu64, create ? "create" : "destroy", session->client_id); - return mgmt_fe_client_send_msg(client, &fe_msg, scok); + return mgmt_fe_client_send_msg(client, &fe_msg, true); } int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client, uint64_t session_id, diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h index edf861746c..845d0bd94a 100644 --- a/lib/mgmt_fe_client.h +++ b/lib/mgmt_fe_client.h @@ -120,7 +120,7 @@ struct mgmt_fe_client_cbs { extern struct debug mgmt_dbg_fe_client; #define MGMTD_FE_CLIENT_DBG(fmt, ...) \ - DEBUGD(&mgmt_dbg_fe_client, "FE-CLIENT: %s:" fmt, __func__, \ + DEBUGD(&mgmt_dbg_fe_client, "FE-CLIENT: %s: " fmt, __func__, \ ##__VA_ARGS__) #define MGMTD_FE_CLIENT_ERR(fmt, ...) \ zlog_err("FE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) diff --git a/lib/vty.c b/lib/vty.c index fedbdbb813..cbf42de473 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -2423,6 +2423,14 @@ void vty_close(struct vty *vty) vty->status = VTY_CLOSE; + /* + * If we reach here with pending config to commit we will be losing it + * so warn the user. + */ + if (vty->mgmt_num_pending_setcfg) + MGMTD_FE_CLIENT_ERR( + "vty closed, uncommitted config will be lost."); + if (mgmt_fe_client && vty->mgmt_session_id) { MGMTD_FE_CLIENT_DBG("closing vty session"); mgmt_fe_destroy_client_session(mgmt_fe_client, @@ -3445,7 +3453,9 @@ static void vty_mgmt_session_notify(struct mgmt_fe_client *client, vty->mgmt_session_id = session_id; } else { vty->mgmt_session_id = 0; - vty_close(vty); + /* We may come here by way of vty_close() and short-circuits */ + if (vty->status != VTY_CLOSE) + vty_close(vty); } } diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c index e4a62951d2..49a307e9c2 100644 --- a/mgmtd/mgmt_be_adapter.c +++ b/mgmtd/mgmt_be_adapter.c @@ -20,7 +20,7 @@ #include "mgmtd/mgmt_be_adapter.h" #define MGMTD_BE_ADAPTER_DBG(fmt, ...) \ - DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s:" fmt, __func__, ##__VA_ARGS__) + DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_BE_ADAPTER_ERR(fmt, ...) \ zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c index 3fd47862b2..2926c9dd4f 100644 --- a/mgmtd/mgmt_ds.c +++ b/mgmtd/mgmt_ds.c @@ -16,7 +16,7 @@ #include "libyang/libyang.h" #define MGMTD_DS_DBG(fmt, ...) \ - DEBUGD(&mgmt_debug_ds, "%s:" fmt, __func__, ##__VA_ARGS__) + DEBUGD(&mgmt_debug_ds, "DS: %s: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_DS_ERR(fmt, ...) \ zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c index 7509d24a6a..eb23fc3d60 100644 --- a/mgmtd/mgmt_fe_adapter.c +++ b/mgmtd/mgmt_fe_adapter.c @@ -21,7 +21,7 @@ #include "mgmtd/mgmt_fe_adapter.h" #define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ - DEBUGD(&mgmt_debug_fe, "FE-ADAPTER: %s:" fmt, __func__, ##__VA_ARGS__) + DEBUGD(&mgmt_debug_fe, "FE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ zlog_err("FE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c index 588693b7e3..18aeab711f 100644 --- a/mgmtd/mgmt_txn.c +++ b/mgmtd/mgmt_txn.c @@ -15,7 +15,7 @@ #include "mgmtd/mgmt_txn.h" #define MGMTD_TXN_DBG(fmt, ...) \ - DEBUGD(&mgmt_debug_txn, "%s:" fmt, __func__, ##__VA_ARGS__) + DEBUGD(&mgmt_debug_txn, "TXN: %s: " fmt, __func__, ##__VA_ARGS__) #define MGMTD_TXN_ERR(fmt, ...) \ zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) @@ -2618,26 +2618,6 @@ int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, return 0; } -int mgmt_txn_send_commit_config_reply(uint64_t txn_id, - enum mgmt_result result, - const char *error_if_any) -{ - struct mgmt_txn_ctx *txn; - - txn = mgmt_txn_id2ctx(txn_id); - if (!txn) - return -1; - - if (!txn->commit_cfg_req) { - MGMTD_TXN_ERR("NO commit in-progress txn-id: %" PRIu64 - " session-id: %" PRIu64, - txn->txn_id, txn->session_id); - return -1; - } - - return mgmt_txn_send_commit_cfg_reply(txn, result, error_if_any); -} - int mgmt_txn_send_get_config_req(uint64_t txn_id, uint64_t req_id, Mgmtd__DatastoreId ds_id, struct mgmt_ds_ctx *ds_ctx, diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h index 0718397138..1a9f6d8502 100644 --- a/mgmtd/mgmt_txn.h +++ b/mgmtd/mgmt_txn.h @@ -176,10 +176,6 @@ extern int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id, bool validate_only, bool abort, bool implicit); -extern int mgmt_txn_send_commit_config_reply(uint64_t txn_id, - enum mgmt_result result, - const char *error_if_any); - /* * Send get-config request to be processed later in transaction. * diff --git a/staticd/static_main.c b/staticd/static_main.c index f6b7847602..9101a95b17 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -56,11 +56,11 @@ struct event_loop *master; struct mgmt_be_client *mgmt_be_client; static struct frr_daemon_info staticd_di; + /* SIGHUP handler. */ static void sighup(void) { - zlog_info("SIGHUP received"); - vty_read_config(NULL, staticd_di.config_file, config_default); + zlog_info("SIGHUP received and ignored"); } /* SIGINT / SIGTERM handler. */ diff --git a/tests/topotests/mgmt_config/r1/early-end-zebra.conf b/tests/topotests/mgmt_config/r1/early-end-zebra.conf new file mode 100644 index 0000000000..44a2f96825 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end-zebra.conf @@ -0,0 +1,6 @@ +allow-external-route-update +end +ip multicast rpf-lookup-mode urib-only +end +ip table range 2 3 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end.conf b/tests/topotests/mgmt_config/r1/early-end.conf new file mode 100644 index 0000000000..3aacad6471 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end.conf @@ -0,0 +1,8 @@ +ip route 15.1.0.0/24 101.0.0.2 +end +ip route 15.2.0.0/24 101.0.0.2 +end +ip route 15.3.0.0/24 101.0.0.2 +end +ip route 15.4.0.0/24 101.0.0.2 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end2-zebra.conf b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf new file mode 100644 index 0000000000..37619d52ac --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf @@ -0,0 +1,7 @@ +conf t +allow-external-route-update +end +ip multicast rpf-lookup-mode urib-only +end +ip table range 2 3 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end2.conf b/tests/topotests/mgmt_config/r1/early-end2.conf new file mode 100644 index 0000000000..229ccc7410 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end2.conf @@ -0,0 +1,9 @@ +conf t +ip route 16.1.0.0/24 101.0.0.2 +end +ip route 16.2.0.0/24 101.0.0.2 +end +ip route 16.3.0.0/24 101.0.0.2 +end +ip route 16.4.0.0/24 101.0.0.2 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf new file mode 100644 index 0000000000..44f202dbcb --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf @@ -0,0 +1,6 @@ +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only +exit +ip table range 2 3 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit.conf b/tests/topotests/mgmt_config/r1/early-exit.conf new file mode 100644 index 0000000000..c6a52df5d3 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit.conf @@ -0,0 +1,8 @@ +ip route 13.1.0.0/24 101.0.0.2 +exit +ip route 13.2.0.0/24 101.0.0.2 +exit +ip route 13.3.0.0/24 101.0.0.2 +exit +ip route 13.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf new file mode 100644 index 0000000000..c7109bfd39 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf @@ -0,0 +1,7 @@ +conf t +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only +exit +ip table range 2 3 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit2.conf b/tests/topotests/mgmt_config/r1/early-exit2.conf new file mode 100644 index 0000000000..79510c0aec --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit2.conf @@ -0,0 +1,9 @@ +conf t +ip route 14.1.0.0/24 101.0.0.2 +exit +ip route 14.2.0.0/24 101.0.0.2 +exit +ip route 14.3.0.0/24 101.0.0.2 +exit +ip route 14.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/mgmtd.conf b/tests/topotests/mgmt_config/r1/mgmtd.conf new file mode 100644 index 0000000000..318de765c8 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/mgmtd.conf @@ -0,0 +1,11 @@ +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +ip route 12.0.0.0/24 101.0.0.2 + +ipv6 route 2012::/48 2101::2 \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/normal-exit.conf b/tests/topotests/mgmt_config/r1/normal-exit.conf new file mode 100644 index 0000000000..c6a52df5d3 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/normal-exit.conf @@ -0,0 +1,8 @@ +ip route 13.1.0.0/24 101.0.0.2 +exit +ip route 13.2.0.0/24 101.0.0.2 +exit +ip route 13.3.0.0/24 101.0.0.2 +exit +ip route 13.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/one-exit-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf new file mode 100644 index 0000000000..0c38459702 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf @@ -0,0 +1,3 @@ +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only diff --git a/tests/topotests/mgmt_config/r1/one-exit.conf b/tests/topotests/mgmt_config/r1/one-exit.conf new file mode 100644 index 0000000000..47147d44eb --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit.conf @@ -0,0 +1,3 @@ +ip route 20.1.0.0/24 101.0.0.2 +exit +ip route 20.2.0.0/24 101.0.0.2 diff --git a/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf new file mode 100644 index 0000000000..34acb76d92 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf @@ -0,0 +1,4 @@ +conf t +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/one-exit2.conf b/tests/topotests/mgmt_config/r1/one-exit2.conf new file mode 100644 index 0000000000..262339a854 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit2.conf @@ -0,0 +1,4 @@ +conf t +ip route 21.1.0.0/24 101.0.0.2 +exit +ip route 21.2.0.0/24 101.0.0.2 diff --git a/tests/topotests/mgmt_config/r1/zebra.conf b/tests/topotests/mgmt_config/r1/zebra.conf new file mode 100644 index 0000000000..f3264efb00 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/zebra.conf @@ -0,0 +1,7 @@ +log timestamp precision 6 +log file frr-r1.log debug + +interface r1-eth0 + ip address 101.0.0.1/24 + ipv6 address 2101::1/64 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/test_config.py b/tests/topotests/mgmt_config/test_config.py new file mode 100644 index 0000000000..b07ed8f7fd --- /dev/null +++ b/tests/topotests/mgmt_config/test_config.py @@ -0,0 +1,385 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# June 10 2023, Christian Hopps +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +""" +Test mgmtd parsing of configs. + +So: + +MGMTD matches zebra: + +one exit file: ONE: vty -f file +one exit redir: ONE: vty < file +early exit file: ONE: vty -f file +early exit redir: ONE: vty < file +early end file: ALL: vty -f file +early end redir: ONE: vty < file + +Raw tests: + +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work after 1 exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits +FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end + +FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing + +Before fixed: + +one exit file: NONE: vty -f file +early exit file: NONE: vty -f file + +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh -f didn't work before exit +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work before exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits +FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end + +FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing + +""" +import ipaddress +import logging +import os +import re +from pathlib import Path + +import pytest +from lib.common_config import retry, step +from lib.topogen import Topogen, TopoRouter + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +@retry(retry_timeout=1, initial_wait=0.1) +def check_kernel(r1, prefix, expected=True): + net = ipaddress.ip_network(prefix) + if net.version == 6: + kernel = r1.cmd_nostatus("ip -6 route show", warn=not expected) + else: + kernel = r1.cmd_nostatus("ip -4 route show", warn=not expected) + + logging.debug("checking kernel routing table:\n%0.1920s", kernel) + route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)" + m = re.search(route, kernel) + if expected and not m: + return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'" + elif not expected and m: + return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'" + return None + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD) + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def save_log_snippet(logfile, content, savepath=None): + os.sync() + os.sync() + os.sync() + + with open(logfile, encoding="utf-8") as f: + buf = f.read() + assert content == buf[: len(content)] + newcontent = buf[len(content) :] + + if savepath: + with open(savepath, "w", encoding="utf-8") as f: + f.write(newcontent) + + return buf + + +def mapname(lname): + return lname.replace(".conf", "") + "-log.txt" + + +logbuf = "" + + +@pytest.fixture(scope="module") +def r1(tgen): + return tgen.gears["r1"].net + + +@pytest.fixture(scope="module") +def confdir(): + return Path(os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]) / "r1" + + +@pytest.fixture(scope="module") +def tempdir(r1): + return Path(r1.rundir) + + +@pytest.fixture(scope="module") +def logpath(tempdir): + return tempdir / "mgmtd.log" + + +@pytest.fixture(autouse=True, scope="function") +def cleanup_config(r1, tempdir, logpath): + global logbuf + + logbuf = save_log_snippet(logpath, logbuf, "/dev/null") + + yield + + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no allow-external-route-update'") + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip multicast rpf-lookup-mode urib-only'") + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip table range 2 3'") + + logbuf = save_log_snippet(logpath, logbuf, "/dev/null") + + +def test_staticd_startup(r1): + r1.cmd_nostatus( + "vtysh -c 'debug mgmt client frontend' " + "-c 'debug mgmt client backend' " + "-c 'debug mgmt backend frontend datastore transaction'" + ) + step("Verifying routes are present on r1") + result = check_kernel(r1, "12.0.0.0/24", retry_timeout=3.0) + assert result is None + + +def test_mgmtd_one_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "20.1.0.0/24") + result2 = check_kernel(r1, "20.2.0.0/24") + + assert result1 is None, "vtysh -f didn't work before exit" + assert result2 is not None, "vtysh < worked after exit, unexpected" + + +def test_mgmtd_one_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "21.1.0.0/24") + result2 = check_kernel(r1, "21.2.0.0/24") + + assert result1 is None, "vtysh < didn't work before exit" + assert result2 is not None, "vtysh < worked after exit, unexpected" + + +def test_mgmtd_early_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "13.1.0.0/24") + result2 = check_kernel(r1, "13.2.0.0/24") + result3 = check_kernel(r1, "13.3.0.0/24") + + assert result1 is None, "vtysh -f didn't work before exit" + assert result2 is not None, "vtysh -f worked after 1 exit, unexpected" + assert result3 is not None, "vtysh -f worked after 2 exit, unexpected" + + +def test_mgmtd_early_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "14.1.0.0/24") + result2 = check_kernel(r1, "14.2.0.0/24") + result3 = check_kernel(r1, "14.3.0.0/24") + + assert result1 is None, "vtysh < didn't work before exit" + assert result2 is not None, "vtysh < worked after 1 exits, unexpected" + assert result3 is not None, "vtysh < worked after 2 exits, unexpected" + + +def test_mgmtd_early_end_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "15.1.0.0/24") + result2 = check_kernel(r1, "15.2.0.0/24") + result3 = check_kernel(r1, "15.3.0.0/24") + + assert result1 is None, "vtysh -f didn't work before end" + assert result2 is None, "vtysh -f didn't work after 1 end" + assert result3 is None, "vtysh -f didn't work after 2 ends" + + +def test_mgmtd_early_end_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "16.1.0.0/24") + result2 = check_kernel(r1, "16.2.0.0/24") + result3 = check_kernel(r1, "16.3.0.0/24") + + assert result1 is None, "vtysh < didn't work before end" + assert result2 is not None, "vtysh < worked after 1 end, unexpected" + assert result3 is not None, "vtysh < worked after 2 end, unexpected" + + +# +# Zebra +# + + +def test_zebra_one_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + + +def test_zebra_one_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + + +def test_zebra_early_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" + + +def test_zebra_early_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" + + +def test_zebra_early_end_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" in showrun + ), "zebra second conf missing" + assert "ip table range 2 3" in showrun, "zebra third missing" + + +def test_zebra_early_end_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" diff --git a/tests/topotests/mgmt_startup/test_config.py b/tests/topotests/mgmt_startup/test_cfgfile_var.py similarity index 100% rename from tests/topotests/mgmt_startup/test_config.py rename to tests/topotests/mgmt_startup/test_cfgfile_var.py diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 04f7ff65e9..c94b47fef5 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -880,6 +880,13 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) if (strmatch(vty_buf_trimmed, "end")) continue; + if (strmatch(vty_buf_trimmed, "exit") && + vty->node == CONFIG_NODE) { + fprintf(stderr, "line %d: Warning[%d]...: %s\n", lineno, + vty->node, "early exit from config file"); + break; + } + ret = command_config_read_one_line(vty, &cmd, lineno, 1); switch (ret) {