lib, mgmtd: rework processing of yang notifications

Currently, YANG notification processing is done using a special type of
callbacks registered in backend clients. In this commit, we start using
regular northbound infrastructure instead, because it already has a
convenient way of registering xpath-specific callbacks without the need
for creating additional structures for each necessary notification. We
also now pass a notification data to the callback, instead of a plain
JSON. This allows to use regular YANG library functions for inspecting
notification fields, instead of manually parsing the JSON.

Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
This commit is contained in:
Igor Ryzhov 2024-02-10 02:11:13 +02:00
parent d94f80fbc4
commit 3ac3a6605d
7 changed files with 87 additions and 56 deletions

View file

@ -963,11 +963,10 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
size_t msg_len) size_t msg_len)
{ {
struct mgmt_msg_notify_data *notif_msg = msgbuf; struct mgmt_msg_notify_data *notif_msg = msgbuf;
struct mgmt_be_client_notification_cb *cb; struct nb_node *nb_node;
char notif[XPATH_MAXLEN]; char notif[XPATH_MAXLEN];
struct lyd_node *dnode; struct lyd_node *dnode;
LY_ERR err; LY_ERR err;
uint i;
debug_be_client("Received notification for client %s", client->name); debug_be_client("Received notification for client %s", client->name);
@ -978,15 +977,15 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
lysc_path(dnode->schema, LYSC_PATH_DATA, notif, sizeof(notif)); lysc_path(dnode->schema, LYSC_PATH_DATA, notif, sizeof(notif));
lyd_free_all(dnode); nb_node = nb_node_find(notif);
if (!nb_node || !nb_node->cbs.notify) {
for (i = 0; i < client->cbs.nnotify_cbs; i++) { debug_be_client("No notification callback for %s", notif);
cb = &client->cbs.notify_cbs[i]; goto cleanup;
if (strncmp(cb->xpath, notif, strlen(cb->xpath)))
continue;
cb->callback(client, client->user_data, cb,
(const char *)notif_msg->result);
} }
nb_callback_notify(nb_node, notif, dnode);
cleanup:
lyd_free_all(dnode);
} }
/* /*
@ -1057,8 +1056,6 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
{ {
Mgmtd__BeMessage be_msg; Mgmtd__BeMessage be_msg;
Mgmtd__BeSubscribeReq subscr_req; Mgmtd__BeSubscribeReq subscr_req;
const char **notif_xpaths = NULL;
int ret;
mgmtd__be_subscribe_req__init(&subscr_req); mgmtd__be_subscribe_req__init(&subscr_req);
subscr_req.client_name = client_ctx->name; subscr_req.client_name = client_ctx->name;
@ -1068,16 +1065,8 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
subscr_req.oper_xpaths = oper_xpaths; subscr_req.oper_xpaths = oper_xpaths;
/* See if we should register for notifications */ /* See if we should register for notifications */
subscr_req.n_notif_xpaths = client_ctx->cbs.nnotify_cbs; subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths;
if (client_ctx->cbs.nnotify_cbs) { subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths;
struct mgmt_be_client_notification_cb *cb, *ecb;
cb = client_ctx->cbs.notify_cbs;
ecb = cb + client_ctx->cbs.nnotify_cbs;
for (; cb < ecb; cb++)
*darr_append(notif_xpaths) = cb->xpath;
}
subscr_req.notif_xpaths = (char **)notif_xpaths;
mgmtd__be_message__init(&be_msg); mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
@ -1087,9 +1076,7 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
subscr_req.client_name, subscr_req.n_config_xpaths, subscr_req.client_name, subscr_req.n_config_xpaths,
subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths); subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths);
ret = mgmt_be_client_send_msg(client_ctx, &be_msg); return mgmt_be_client_send_msg(client_ctx, &be_msg);
darr_free(notif_xpaths);
return ret;
} }
static int _notify_conenct_disconnect(struct msg_client *msg_client, static int _notify_conenct_disconnect(struct msg_client *msg_client,

View file

@ -73,16 +73,8 @@ struct mgmt_be_client_cbs {
struct mgmt_be_client_txn_ctx *txn_ctx, struct mgmt_be_client_txn_ctx *txn_ctx,
bool destroyed); bool destroyed);
struct mgmt_be_client_notification_cb *notify_cbs; const char **notif_xpaths;
uint nnotify_cbs; uint nnotif_xpaths;
};
struct mgmt_be_client_notification_cb {
const char *xpath; /* the notification */
uint8_t format; /* currently only LYD_JSON supported */
void (*callback)(struct mgmt_be_client *client, uintptr_t usr_data,
struct mgmt_be_client_notification_cb *this,
const char *notif_data);
}; };
/*************************************************************** /***************************************************************

View file

@ -284,6 +284,8 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
!!nb_node->cbs.lookup_entry, false); !!nb_node->cbs.lookup_entry, false);
error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc, error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc,
false); false);
error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY,
!!nb_node->cbs.notify, true);
return error; return error;
} }
@ -1605,6 +1607,18 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
return nb_node->cbs.rpc(&args); return nb_node->cbs.rpc(&args);
} }
void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
struct lyd_node *dnode)
{
struct nb_cb_notify_args args = {};
DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath);
args.xpath = xpath;
args.dnode = dnode;
nb_node->cbs.notify(&args);
}
/* /*
* Call the northbound configuration callback associated to a given * Call the northbound configuration callback associated to a given
* configuration change. * configuration change.
@ -1653,6 +1667,7 @@ static int nb_callback_configuration(struct nb_context *context,
case NB_CB_GET_KEYS: case NB_CB_GET_KEYS:
case NB_CB_LOOKUP_ENTRY: case NB_CB_LOOKUP_ENTRY:
case NB_CB_RPC: case NB_CB_RPC:
case NB_CB_NOTIFY:
yang_dnode_get_path(dnode, xpath, sizeof(xpath)); yang_dnode_get_path(dnode, xpath, sizeof(xpath));
flog_err(EC_LIB_DEVELOPMENT, flog_err(EC_LIB_DEVELOPMENT,
"%s: unknown operation (%u) [xpath %s]", __func__, "%s: unknown operation (%u) [xpath %s]", __func__,
@ -2047,6 +2062,10 @@ bool nb_cb_operation_is_valid(enum nb_cb_operation operation,
return false; return false;
} }
return true; return true;
case NB_CB_NOTIFY:
if (snode->nodetype != LYS_NOTIF)
return false;
return true;
default: default:
return false; return false;
} }
@ -2279,6 +2298,8 @@ const char *nb_cb_operation_name(enum nb_cb_operation operation)
return "lookup_entry"; return "lookup_entry";
case NB_CB_RPC: case NB_CB_RPC:
return "rpc"; return "rpc";
case NB_CB_NOTIFY:
return "notify";
} }
assert(!"Reached end of function we should never hit"); assert(!"Reached end of function we should never hit");

View file

@ -99,6 +99,7 @@ enum nb_cb_operation {
NB_CB_GET_KEYS, NB_CB_GET_KEYS,
NB_CB_LOOKUP_ENTRY, NB_CB_LOOKUP_ENTRY,
NB_CB_RPC, NB_CB_RPC,
NB_CB_NOTIFY,
}; };
union nb_resource { union nb_resource {
@ -286,6 +287,18 @@ struct nb_cb_rpc_args {
size_t errmsg_len; size_t errmsg_len;
}; };
struct nb_cb_notify_args {
/* XPath of the notification. */
const char *xpath;
/*
* libyang data node representing the notification. If the notification
* is not top-level, it still points to the notification node, but it's
* part of the full data tree with all its parents.
*/
struct lyd_node *dnode;
};
/* /*
* Set of configuration callbacks that can be associated to a northbound node. * Set of configuration callbacks that can be associated to a northbound node.
*/ */
@ -509,6 +522,17 @@ struct nb_callbacks {
*/ */
int (*rpc)(struct nb_cb_rpc_args *args); int (*rpc)(struct nb_cb_rpc_args *args);
/*
* Notification callback.
*
* The callback is called when a YANG notification is received.
*
* args
* Refer to the documentation comments of nb_cb_notify_args for
* details.
*/
void (*notify)(struct nb_cb_notify_args *args);
/* /*
* Optional callback to compare the data nodes when printing * Optional callback to compare the data nodes when printing
* the CLI commands associated with them. * the CLI commands associated with them.
@ -786,6 +810,7 @@ DECLARE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set));
extern struct debug nb_dbg_cbs_config; extern struct debug nb_dbg_cbs_config;
extern struct debug nb_dbg_cbs_state; extern struct debug nb_dbg_cbs_state;
extern struct debug nb_dbg_cbs_rpc; extern struct debug nb_dbg_cbs_rpc;
extern struct debug nb_dbg_cbs_notify;
extern struct debug nb_dbg_notif; extern struct debug nb_dbg_notif;
extern struct debug nb_dbg_events; extern struct debug nb_dbg_events;
extern struct debug nb_dbg_libyang; extern struct debug nb_dbg_libyang;
@ -814,6 +839,8 @@ extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output, const struct list *input, struct list *output,
char *errmsg, size_t errmsg_len); char *errmsg, size_t errmsg_len);
extern void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
struct lyd_node *dnode);
/* /*
* Create a northbound node for all YANG schema nodes. * Create a northbound node for all YANG schema nodes.

View file

@ -25,6 +25,7 @@
struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"}; struct debug nb_dbg_cbs_config = {0, "Northbound callbacks: configuration"};
struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"}; struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"};
struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"}; struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"};
struct debug nb_dbg_cbs_notify = {0, "Northbound callbacks: notifications"};
struct debug nb_dbg_notif = {0, "Northbound notifications"}; struct debug nb_dbg_notif = {0, "Northbound notifications"};
struct debug nb_dbg_events = {0, "Northbound events"}; struct debug nb_dbg_events = {0, "Northbound events"};
struct debug nb_dbg_libyang = {0, "libyang debugging"}; struct debug nb_dbg_libyang = {0, "libyang debugging"};
@ -1772,13 +1773,15 @@ DEFPY (rollback_config,
/* Debug CLI commands. */ /* Debug CLI commands. */
static struct debug *nb_debugs[] = { static struct debug *nb_debugs[] = {
&nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc, &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc,
&nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang, &nb_dbg_cbs_notify, &nb_dbg_notif, &nb_dbg_events,
&nb_dbg_libyang,
}; };
static const char *const nb_debugs_conflines[] = { static const char *const nb_debugs_conflines[] = {
"debug northbound callbacks configuration", "debug northbound callbacks configuration",
"debug northbound callbacks state", "debug northbound callbacks state",
"debug northbound callbacks rpc", "debug northbound callbacks rpc",
"debug northbound callbacks notify",
"debug northbound notifications", "debug northbound notifications",
"debug northbound events", "debug northbound events",
"debug northbound libyang", "debug northbound libyang",
@ -1803,7 +1806,7 @@ DEFPY (debug_nb,
debug_nb_cmd, debug_nb_cmd,
"[no] debug northbound\ "[no] debug northbound\
[<\ [<\
callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\ callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc|notify$cbs_notify}]\
|notifications$notifications\ |notifications$notifications\
|events$events\ |events$events\
|libyang$libyang\ |libyang$libyang\
@ -1816,13 +1819,14 @@ DEFPY (debug_nb,
"State\n" "State\n"
"RPC\n" "RPC\n"
"Notifications\n" "Notifications\n"
"Notifications\n"
"Events\n" "Events\n"
"libyang debugging\n") "libyang debugging\n")
{ {
uint32_t mode = DEBUG_NODE2MODE(vty->node); uint32_t mode = DEBUG_NODE2MODE(vty->node);
if (cbs) { if (cbs) {
bool none = (!cbs_cfg && !cbs_state && !cbs_rpc); bool none = (!cbs_cfg && !cbs_state && !cbs_rpc && !cbs_notify);
if (none || cbs_cfg) if (none || cbs_cfg)
DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no); DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no);
@ -1830,6 +1834,8 @@ DEFPY (debug_nb,
DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no); DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no);
if (none || cbs_rpc) if (none || cbs_rpc)
DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no); DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no);
if (none || cbs_notify)
DEBUG_MODE_SET(&nb_dbg_cbs_notify, mode, !no);
} }
if (notifications) if (notifications)
DEBUG_MODE_SET(&nb_dbg_notif, mode, !no); DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);

View file

@ -11,14 +11,13 @@
#include "darr.h" #include "darr.h"
#include "libfrr.h" #include "libfrr.h"
#include "mgmt_be_client.h" #include "mgmt_be_client.h"
#include "northbound.h"
/* ---------------- */ /* ---------------- */
/* Local Prototypes */ /* Local Prototypes */
/* ---------------- */ /* ---------------- */
static void async_notification(struct mgmt_be_client *client, uintptr_t usr_data, static void async_notification(struct nb_cb_notify_args *args);
struct mgmt_be_client_notification_cb *this,
const char *notif_data);
static void sigusr1(void); static void sigusr1(void);
static void sigint(void); static void sigint(void);
@ -83,6 +82,10 @@ static const struct frr_yang_module_info frr_ripd_info = {
.name = "frr-ripd", .name = "frr-ripd",
.ignore_cfg_cbs = true, .ignore_cfg_cbs = true,
.nodes = { .nodes = {
{
.xpath = "/frr-ripd:authentication-failure",
.cbs.notify = async_notification,
},
{ {
.xpath = NULL, .xpath = NULL,
} }
@ -109,7 +112,7 @@ FRR_DAEMON_INFO(mgmtd_testc, MGMTD_TESTC,
); );
/* clang-format on */ /* clang-format on */
struct mgmt_be_client_notification_cb *__notify_cbs; const char **__notif_xpaths;
struct mgmt_be_client_cbs __client_cbs = {}; struct mgmt_be_client_cbs __client_cbs = {};
struct event *event_timeout; struct event *event_timeout;
@ -131,7 +134,7 @@ static void quit(int exit_code)
{ {
EVENT_OFF(event_timeout); EVENT_OFF(event_timeout);
frr_fini(); frr_fini();
darr_free(__client_cbs.notify_cbs); darr_free(__client_cbs.notif_xpaths);
exit(exit_code); exit(exit_code);
} }
@ -147,13 +150,12 @@ static void timeout(struct event *event)
quit(1); quit(1);
} }
static void async_notification(struct mgmt_be_client *client, uintptr_t usr_data, static void async_notification(struct nb_cb_notify_args *args)
struct mgmt_be_client_notification_cb *this,
const char *notif_data)
{ {
zlog_notice("Received YANG notification"); zlog_notice("Received YANG notification");
printf("%s\n", notif_data); printf("{\"frr-ripd:authentication-failure\": {\"interface-name\": \"%s\"}}\n",
yang_dnode_get_string(args->dnode, "interface-name"));
if (o_notif_count && !--o_notif_count) if (o_notif_count && !--o_notif_count)
quit(0); quit(0);
@ -205,17 +207,12 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
if (argc && f_listen) { if (argc && f_listen) {
struct mgmt_be_client_notification_cb *cb;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
zlog_notice("Listen on xpath: %s", argv[i]); zlog_notice("Listen on xpath: %s", argv[i]);
cb = darr_append(__notify_cbs); darr_push(__notif_xpaths, argv[i]);
cb->xpath = argv[i];
cb->format = LYD_JSON;
cb->callback = async_notification;
} }
__client_cbs.notify_cbs = __notify_cbs; __client_cbs.notif_xpaths = __notif_xpaths;
__client_cbs.nnotify_cbs = darr_len(__notify_cbs); __client_cbs.nnotif_xpaths = darr_len(__notif_xpaths);
} }
mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0, mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0,

View file

@ -3169,7 +3169,7 @@ DEFUNSH(VTYSH_ALL, debug_nb,
debug_nb_cmd, debug_nb_cmd,
"[no] debug northbound\ "[no] debug northbound\
[<\ [<\
callbacks [{configuration|state|rpc}]\ callbacks [{configuration|state|rpc|notify}]\
|notifications\ |notifications\
|events\ |events\
|libyang\ |libyang\
@ -3182,6 +3182,7 @@ DEFUNSH(VTYSH_ALL, debug_nb,
"State\n" "State\n"
"RPC\n" "RPC\n"
"Notifications\n" "Notifications\n"
"Notifications\n"
"Events\n" "Events\n"
"libyang debugging\n") "libyang debugging\n")
{ {