size_t msg_len)
{
struct mgmt_msg_notify_data *notif_msg = msgbuf;
- struct mgmt_be_client_notification_cb *cb;
+ struct nb_node *nb_node;
char notif[XPATH_MAXLEN];
struct lyd_node *dnode;
LY_ERR err;
- uint i;
debug_be_client("Received notification for client %s", client->name);
lysc_path(dnode->schema, LYSC_PATH_DATA, notif, sizeof(notif));
- lyd_free_all(dnode);
-
- for (i = 0; i < client->cbs.nnotify_cbs; i++) {
- cb = &client->cbs.notify_cbs[i];
- if (strncmp(cb->xpath, notif, strlen(cb->xpath)))
- continue;
- cb->callback(client, client->user_data, cb,
- (const char *)notif_msg->result);
+ nb_node = nb_node_find(notif);
+ if (!nb_node || !nb_node->cbs.notify) {
+ debug_be_client("No notification callback for %s", notif);
+ goto cleanup;
}
+
+ nb_callback_notify(nb_node, notif, dnode);
+cleanup:
+ lyd_free_all(dnode);
}
/*
{
Mgmtd__BeMessage be_msg;
Mgmtd__BeSubscribeReq subscr_req;
- const char **notif_xpaths = NULL;
- int ret;
mgmtd__be_subscribe_req__init(&subscr_req);
subscr_req.client_name = client_ctx->name;
subscr_req.oper_xpaths = oper_xpaths;
/* See if we should register for notifications */
- subscr_req.n_notif_xpaths = client_ctx->cbs.nnotify_cbs;
- if (client_ctx->cbs.nnotify_cbs) {
- 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;
+ subscr_req.n_notif_xpaths = client_ctx->cbs.nnotif_xpaths;
+ subscr_req.notif_xpaths = (char **)client_ctx->cbs.notif_xpaths;
mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
subscr_req.client_name, subscr_req.n_config_xpaths,
subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths);
- ret = mgmt_be_client_send_msg(client_ctx, &be_msg);
- darr_free(notif_xpaths);
- return ret;
+ return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
static int _notify_conenct_disconnect(struct msg_client *msg_client,
struct mgmt_be_client_txn_ctx *txn_ctx,
bool destroyed);
- struct mgmt_be_client_notification_cb *notify_cbs;
- uint nnotify_cbs;
-};
-
-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);
+ const char **notif_xpaths;
+ uint nnotif_xpaths;
};
/***************************************************************
!!nb_node->cbs.lookup_entry, false);
error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc,
false);
+ error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY,
+ !!nb_node->cbs.notify, true);
return error;
}
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
* configuration change.
case NB_CB_GET_KEYS:
case NB_CB_LOOKUP_ENTRY:
case NB_CB_RPC:
+ case NB_CB_NOTIFY:
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
flog_err(EC_LIB_DEVELOPMENT,
"%s: unknown operation (%u) [xpath %s]", __func__,
return false;
}
return true;
+ case NB_CB_NOTIFY:
+ if (snode->nodetype != LYS_NOTIF)
+ return false;
+ return true;
default:
return false;
}
return "lookup_entry";
case NB_CB_RPC:
return "rpc";
+ case NB_CB_NOTIFY:
+ return "notify";
}
assert(!"Reached end of function we should never hit");
NB_CB_GET_KEYS,
NB_CB_LOOKUP_ENTRY,
NB_CB_RPC,
+ NB_CB_NOTIFY,
};
union nb_resource {
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.
*/
*/
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
* the CLI commands associated with them.
extern struct debug nb_dbg_cbs_config;
extern struct debug nb_dbg_cbs_state;
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_events;
extern struct debug nb_dbg_libyang;
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output,
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.
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_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_events = {0, "Northbound events"};
struct debug nb_dbg_libyang = {0, "libyang debugging"};
/* Debug CLI commands. */
static struct debug *nb_debugs[] = {
&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[] = {
"debug northbound callbacks configuration",
"debug northbound callbacks state",
"debug northbound callbacks rpc",
+ "debug northbound callbacks notify",
"debug northbound notifications",
"debug northbound events",
"debug northbound libyang",
debug_nb_cmd,
"[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\
|events$events\
|libyang$libyang\
"State\n"
"RPC\n"
"Notifications\n"
+ "Notifications\n"
"Events\n"
"libyang debugging\n")
{
uint32_t mode = DEBUG_NODE2MODE(vty->node);
if (cbs) {
- bool none = (!cbs_cfg && !cbs_state && !cbs_rpc);
+ bool none = (!cbs_cfg && !cbs_state && !cbs_rpc && !cbs_notify);
if (none || cbs_cfg)
DEBUG_MODE_SET(&nb_dbg_cbs_config, mode, !no);
DEBUG_MODE_SET(&nb_dbg_cbs_state, mode, !no);
if (none || cbs_rpc)
DEBUG_MODE_SET(&nb_dbg_cbs_rpc, mode, !no);
+ if (none || cbs_notify)
+ DEBUG_MODE_SET(&nb_dbg_cbs_notify, mode, !no);
}
if (notifications)
DEBUG_MODE_SET(&nb_dbg_notif, mode, !no);
#include "darr.h"
#include "libfrr.h"
#include "mgmt_be_client.h"
+#include "northbound.h"
/* ---------------- */
/* Local Prototypes */
/* ---------------- */
-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 async_notification(struct nb_cb_notify_args *args);
static void sigusr1(void);
static void sigint(void);
.name = "frr-ripd",
.ignore_cfg_cbs = true,
.nodes = {
+ {
+ .xpath = "/frr-ripd:authentication-failure",
+ .cbs.notify = async_notification,
+ },
{
.xpath = NULL,
}
);
/* clang-format on */
-struct mgmt_be_client_notification_cb *__notify_cbs;
+const char **__notif_xpaths;
struct mgmt_be_client_cbs __client_cbs = {};
struct event *event_timeout;
{
EVENT_OFF(event_timeout);
frr_fini();
- darr_free(__client_cbs.notify_cbs);
+ darr_free(__client_cbs.notif_xpaths);
exit(exit_code);
}
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)
+static void async_notification(struct nb_cb_notify_args *args)
{
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)
quit(0);
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;
+ darr_push(__notif_xpaths, argv[i]);
}
- __client_cbs.notify_cbs = __notify_cbs;
- __client_cbs.nnotify_cbs = darr_len(__notify_cbs);
+ __client_cbs.notif_xpaths = __notif_xpaths;
+ __client_cbs.nnotif_xpaths = darr_len(__notif_xpaths);
}
mgmt_be_client = mgmt_be_client_create("mgmtd-testc", &__client_cbs, 0,
debug_nb_cmd,
"[no] debug northbound\
[<\
- callbacks [{configuration|state|rpc}]\
+ callbacks [{configuration|state|rpc|notify}]\
|notifications\
|events\
|libyang\
"State\n"
"RPC\n"
"Notifications\n"
+ "Notifications\n"
"Events\n"
"libyang debugging\n")
{