diff --git a/mgmtd/mgmt_testc.c b/mgmtd/mgmt_testc.c index 70cd2bb0cd5a..02a308f32849 100644 --- a/mgmtd/mgmt_testc.c +++ b/mgmtd/mgmt_testc.c @@ -8,6 +8,7 @@ #include #include +#include "darr.h" #include "libfrr.h" #include "mgmt_be_client.h" @@ -15,9 +16,9 @@ /* Local Prototypes */ /* ---------------- */ -static void ripd_notification(struct mgmt_be_client *client, uintptr_t usr_data, - struct mgmt_be_client_notification_cb *this, - const char *notif_data); +static void async_notification(struct mgmt_be_client *client, uintptr_t usr_data, + struct mgmt_be_client_notification_cb *this, + const char *notif_data); static void sigusr1(void); static void sigint(void); @@ -42,7 +43,16 @@ struct zebra_privs_t __privs = { .cap_num_i = 0, }; -struct option longopts[] = {{0}}; +#define OPTION_LISTEN 2000 +#define OPTION_NOTIF_COUNT 2001 +#define OPTION_TIMEOUT 2002 +const struct option longopts[] = { + { "listen", no_argument, NULL, OPTION_LISTEN }, + { "notif-count", required_argument, NULL, OPTION_NOTIF_COUNT }, + { "timeout", required_argument, NULL, OPTION_TIMEOUT }, + { 0 } +}; + /* Master of threads. */ struct event_loop *master; @@ -85,17 +95,13 @@ FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC, ); /* clang-format on */ -struct mgmt_be_client_notification_cb __notify_cbs[] = { { - .xpath = "frr-ripd", - .format = LYD_JSON, - .callback = ripd_notification, -} }; +struct mgmt_be_client_notification_cb *__notify_cbs; -struct mgmt_be_client_cbs __client_cbs = { - .notify_cbs = __notify_cbs, - .nnotify_cbs = array_size(__notify_cbs), -}; +struct mgmt_be_client_cbs __client_cbs = {}; +struct event *event_timeout; +int o_notif_count = 1; +int o_timeout; /* --------- */ /* Functions */ @@ -107,22 +113,43 @@ static void sigusr1(void) zlog_rotate(); } +static void quit(int exit_code) +{ + EVENT_OFF(event_timeout); + frr_fini(); + darr_free(__client_cbs.notify_cbs); + exit(exit_code); +} + static void sigint(void) { zlog_notice("Terminating on signal"); - frr_fini(); - exit(0); + quit(0); } -static void ripd_notification(struct mgmt_be_client *client, uintptr_t usr_data, - struct mgmt_be_client_notification_cb *this, - const char *notif_data) +static void timeout(struct event *event) { - zlog_notice("Received RIPd notification"); + zlog_notice("Timeout, exiting"); + quit(1); +} + +static void async_notification(struct mgmt_be_client *client, uintptr_t usr_data, + struct mgmt_be_client_notification_cb *this, + const char *notif_data) +{ + zlog_notice("Received YANG notification"); + + printf("%s\n", notif_data); + + if (o_notif_count && !--o_notif_count) + quit(0); } int main(int argc, char **argv) { + int f_listen = 0; + int i; + frr_preinit(&mgmtd_testc_di, argc, argv); frr_opt_add("", longopts, ""); @@ -135,6 +162,15 @@ int main(int argc, char **argv) break; switch (opt) { + case OPTION_LISTEN: + f_listen = 1; + break; + case OPTION_NOTIF_COUNT: + o_notif_count = atoi(optarg); + break; + case OPTION_TIMEOUT: + o_timeout = atoi(optarg); + break; case 0: break; default: @@ -144,10 +180,38 @@ int main(int argc, char **argv) master = frr_init(); + /* + * Setup notification listen + */ + argv += optind; + argc -= optind; + if (!argc && f_listen) { + fprintf(stderr, + "Must specify at least one notification xpath to listen to\n"); + exit(1); + } + if (argc && f_listen) { + struct mgmt_be_client_notification_cb *cb; + + for (i = 0; i < argc; i++) { + zlog_notice("Listen on xpath: %s", argv[i]); + cb = darr_append(__notify_cbs); + cb->xpath = argv[i]; + cb->format = LYD_JSON; + cb->callback = async_notification; + } + __client_cbs.notify_cbs = __notify_cbs; + __client_cbs.nnotify_cbs = darr_len(__notify_cbs); + } + mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0, master); frr_config_fork(); + + if (o_timeout) + event_add_timer(master, timeout, NULL, o_timeout, &event_timeout); + frr_run(master); /* Reached. */ diff --git a/tests/topotests/mgmt_notif/test_notif.py b/tests/topotests/mgmt_notif/test_notif.py index 873b82d99978..2f923e398c15 100644 --- a/tests/topotests/mgmt_notif/test_notif.py +++ b/tests/topotests/mgmt_notif/test_notif.py @@ -43,7 +43,7 @@ def tgen(request): tgen.stop_topology() -def test_oper_simple(tgen): +def test_frontend_notification(tgen): if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -57,9 +57,46 @@ def test_oper_simple(tgen): if rc: pytest.skip("No protoc or present cannot run test") + # The first notifications is a frr-ripd:authentication-type-failure + # So we filter to avoid that, all the rest are frr-ripd:authentication-failure + # making our test deterministic + output = r1.cmd_raises( + fe_client_path + " --listen frr-ripd:authentication-failure" + ) + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + output = r1.cmd_raises(fe_client_path + " --listen") jsout = json.loads(output) - expected = {"frr-ripd:authentication-type-failure": {"interface-name": "r1-eth0"}} + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} + result = json_cmp(jsout, expected) + assert result is None + + +def test_backend_notification(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"].net + + check_kernel_32(r1, "11.11.11.11", 1, "") + + be_client_path = "/usr/lib/frr/mgmtd_testc" + rc, _, _ = r1.cmd_status(be_client_path + " --help") + + if rc: + pytest.skip("No mgmtd_testc") + + output = r1.cmd_raises( + be_client_path + " --timeout 20 --log file:mgmt_testc.log --listen frr-ripd" + ) + + jsout = json.loads(output) + + expected = {"frr-ripd:authentication-failure": {"interface-name": "r1-eth0"}} result = json_cmp(jsout, expected) assert result is None