Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chopps/notifications #15256

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ refix
/test-suite.log
pceplib/test/*.log
pceplib/test/*.trs
/tests/topotests/lib/mgmt_pb2.py
7 changes: 7 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,8 @@ AC_ARG_ENABLE([mgmtd],
AS_HELP_STRING([--disable-mgmtd], [do not build mgmtd]))
AC_ARG_ENABLE([mgmtd_local_validations],
AS_HELP_STRING([--enable-mgmtd-local-validations], [dev: unimplemented local validation]))
AC_ARG_ENABLE([mgmtd_test_be_client],
AS_HELP_STRING([--enable-mgmtd-test-be-client], [build test backend client]))
AC_ARG_ENABLE([ripd],
AS_HELP_STRING([--disable-ripd], [do not build ripd]))
AC_ARG_ENABLE([ripngd],
Expand Down Expand Up @@ -1811,6 +1813,10 @@ AS_IF([test "$enable_mgmtd" != "no"], [
])
])

AS_IF([test "$enable_mgmtd_test_be_client" = "yes"], [
AC_DEFINE([HAVE_MGMTD_TESTC], [1], [mgmtd_testc])
])

AS_IF([test "$enable_ripd" != "no"], [
AC_DEFINE([HAVE_RIPD], [1], [ripd])
])
Expand Down Expand Up @@ -2772,6 +2778,7 @@ AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"])
AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"])
AM_CONDITIONAL([BGPD], [test "$enable_bgpd" != "no"])
AM_CONDITIONAL([MGMTD], [test "$enable_mgmtd" != "no"])
AM_CONDITIONAL([MGMTD_TESTC], [test "$enable_mgmtd_test_be_client" = "yes"])
AM_CONDITIONAL([RIPD], [test "$enable_ripd" != "no"])
AM_CONDITIONAL([OSPFD], [test "$enable_ospfd" != "no"])
AM_CONDITIONAL([LDPD], [test "$enable_ldpd" != "no"])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ future.
For libfrr commands, it’s not possible to centralize all commands in a
single file because the *extract.pl* script from *vtysh* treats commands
differently depending on the file in which they are defined (e.g. DEFUNs
from *lib/routemap.c* are installed using the ``VTYSH_RMAP`` constant,
from *lib/routemap.c* are installed using the ``VTYSH_RMAP_SHOW`` constant,
which identifies the daemons that support route-maps). In this case, the
CLI commands should be rewritten but maintained in the same file.

Expand Down
5 changes: 3 additions & 2 deletions lib/mgmt.proto
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ message YangGetDataReq {
//
message BeSubscribeReq {
required string client_name = 1;
required bool subscribe_xpaths = 2;
repeated string xpath_reg = 3;
repeated string config_xpaths = 2;
repeated string oper_xpaths = 3;
repeated string notif_xpaths = 4;
}

message BeSubscribeReply {
Expand Down
182 changes: 159 additions & 23 deletions lib/mgmt_be_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,90 @@ static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
return ret;
}

void mgmt_be_send_notification(struct lyd_node *tree)
{
struct mgmt_be_client *client = __be_client;
struct mgmt_msg_notify_data *msg = NULL;
LYD_FORMAT format = LYD_JSON;
uint8_t **darrp;
LY_ERR err;

assert(tree);

MGMTD_BE_CLIENT_DBG("%s: sending YANG notification: %s", __func__,
tree->schema->name);
/*
* Allocate a message and append the data to it using `format`
*/
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_notify_data, 0,
MTYPE_MSG_NATIVE_NOTIFY);
msg->code = MGMT_MSG_CODE_NOTIFY;
msg->result_type = format;

darrp = mgmt_msg_native_get_darrp(msg);
err = yang_print_tree_append(darrp, tree, format,
(LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
LYD_PRINT_WITHSIBLINGS));
if (err) {
flog_err(EC_LIB_LIBYANG,
"%s: error creating notification data: %s", __func__,
ly_strerrcode(err));
goto done;
}

(void)be_client_send_native_msg(client, msg,
mgmt_msg_native_get_msg_len(msg), false);
done:
mgmt_msg_native_free_msg(msg);
lyd_free_all(tree);
}

/*
* Convert old style NB notification data into new MGMTD YANG tree and send.
*/
static int mgmt_be_notification_send(void *arg, const char *xpath,
struct list *args)
{
struct lyd_node *root = NULL;
struct lyd_node *dnode;
struct yang_data *data;
struct listnode *ln;
LY_ERR err;

MGMTD_BE_CLIENT_DBG("%s: sending notification: %s", __func__, xpath);

/*
* Convert yang data args list to a libyang data tree
*/
for (ALL_LIST_ELEMENTS_RO(args, ln, data)) {
err = lyd_new_path(root, ly_native_ctx, data->xpath,
data->value, LYD_NEW_PATH_UPDATE, &dnode);
if (err != LY_SUCCESS) {
lyerr:
flog_err(EC_LIB_LIBYANG,
"%s: error creating notification data: %s",
__func__, ly_strerrcode(err));
if (root)
lyd_free_all(root);
return 1;
}
if (!root) {
root = dnode;
while (root->parent)
root = lyd_parent(root);
}
}

if (!root) {
err = lyd_new_path(NULL, ly_native_ctx, xpath, "", 0, &root);
if (err)
goto lyerr;
}

mgmt_be_send_notification(root);
return 0;
}

static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
uint64_t txn_id, bool create)
{
Expand Down Expand Up @@ -738,6 +822,12 @@ static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx,
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u",
be_msg->subscr_reply->success);

if (client_ctx->cbs.subscr_done)
(*client_ctx->cbs.subscr_done)(client_ctx,
client_ctx->user_data,
be_msg->subscr_reply
->success);
break;
case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64,
Expand Down Expand Up @@ -824,7 +914,7 @@ static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree,

darrp = mgmt_msg_native_get_darrp(tree_msg);
err = yang_print_tree_append(darrp, tree, args->result_type,
(LYD_PRINT_WD_EXPLICIT |
(LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
LYD_PRINT_WITHSIBLINGS));
if (err) {
ret = NB_ERR;
Expand Down Expand Up @@ -873,6 +963,31 @@ static void be_client_handle_get_tree(struct mgmt_be_client *client,
be_client_send_tree_data_batch, args);
}

/*
* Process the notification.
*/
static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
size_t msg_len)
{
struct mgmt_msg_notify_data *notif_msg = msgbuf;
struct mgmt_be_client_notification_cb *cb;
const char *notif;
uint i;

MGMTD_BE_CLIENT_DBG("Received notification for client %s", client->name);

/* "{\"modname:notification-name\": ...}" */
notif = (const char *)notif_msg->result + 2;

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);
}
}

/*
* Handle a native encoded message
*
Expand All @@ -888,12 +1003,16 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
case MGMT_MSG_CODE_GET_TREE:
be_client_handle_get_tree(client, txn_id, msg, msg_len);
break;
case MGMT_MSG_CODE_NOTIFY:
be_client_handle_notify(client, msg, msg_len);
break;
default:
MGMTD_BE_CLIENT_ERR("unknown native message txn-id %" PRIu64
" req-id %" PRIu64 " code %u to client %s",
txn_id, msg->req_id, msg->code,
client->name);
be_client_send_error(client, msg->refer_id, msg->req_id, false, -1,
be_client_send_error(client, msg->refer_id, msg->req_id, false,
-1,
"BE cilent %s recv msg unknown txn-id %" PRIu64,
client->name, txn_id);
break;
Expand Down Expand Up @@ -927,38 +1046,51 @@ static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data,
len);
return;
}
MGMTD_BE_CLIENT_DBG(
"Decoded %zu bytes of message(msg: %u/%u) from server", len,
be_msg->message_case, be_msg->message_case);
MGMTD_BE_CLIENT_DBG("Decoded %zu bytes of message(msg: %u/%u) from server",
len, be_msg->message_case, be_msg->message_case);
(void)mgmt_be_client_handle_msg(client_ctx, be_msg);
mgmtd__be_message__free_unpacked(be_msg, NULL);
}

int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
bool subscr_xpaths, int num_xpaths,
char **reg_xpaths)
int n_config_xpaths, char **config_xpaths,
int n_oper_xpaths, char **oper_xpaths)
{
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.n_xpath_reg = num_xpaths;
if (num_xpaths)
subscr_req.xpath_reg = reg_xpaths;
else
subscr_req.xpath_reg = NULL;
subscr_req.subscribe_xpaths = subscr_xpaths;
subscr_req.n_config_xpaths = n_config_xpaths;
subscr_req.config_xpaths = config_xpaths;
subscr_req.n_oper_xpaths = n_oper_xpaths;
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;

mgmtd__be_message__init(&be_msg);
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req;

MGMTD_BE_CLIENT_DBG("Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu",
subscr_req.client_name, subscr_req.subscribe_xpaths,
subscr_req.n_xpath_reg);
MGMTD_BE_CLIENT_DBG("Sending SUBSCR_REQ name: %s xpaths: config %zu oper: %zu notif: %zu",
subscr_req.client_name, subscr_req.n_config_xpaths,
subscr_req.n_oper_xpaths, subscr_req.n_notif_xpaths);

return mgmt_be_client_send_msg(client_ctx, &be_msg);
ret = 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,
Expand All @@ -970,15 +1102,16 @@ static int _notify_conenct_disconnect(struct msg_client *msg_client,

if (connected) {
assert(msg_client->conn.fd != -1);
ret = mgmt_be_send_subscr_req(client, false, 0, NULL);
ret = mgmt_be_send_subscr_req(client, 0, NULL, 0, NULL);
if (ret)
return ret;
}

/* Notify BE client through registered callback (if any) */
if (client->cbs.client_connect_notify)
(void)(*client->cbs.client_connect_notify)(
client, client->user_data, connected);
(void)(*client->cbs.client_connect_notify)(client,
client->user_data,
connected);

/* Cleanup any in-progress TXN on disconnect */
if (!connected)
Expand Down Expand Up @@ -1016,9 +1149,8 @@ static void mgmt_debug_client_be_set(uint32_t flags, bool set)

DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
"[no] debug mgmt client backend",
NO_STR DEBUG_STR MGMTD_STR
"client\n"
"backend\n")
NO_STR DEBUG_STR MGMTD_STR "client\n"
"backend\n")
{
mgmt_debug_client_be_set(DEBUG_NODE2MODE(vty->node), !no);

Expand Down Expand Up @@ -1083,6 +1215,10 @@ struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MAX_MSG_LEN, false,
"BE-client", MGMTD_DBG_BE_CLIENT_CHECK());

/* Hook to receive notifications */
hook_register_arg(nb_notification_send, mgmt_be_notification_send,
client);

MGMTD_BE_CLIENT_DBG("Initialized client '%s'", client_name);

return client;
Expand Down
34 changes: 29 additions & 5 deletions lib/mgmt_be_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,29 @@ struct mgmt_be_client_txn_ctx {
* Callbacks:
* client_connect_notify: called when connection is made/lost to mgmtd.
* txn_notify: called when a txn has been created
* notify_cbs: callbacks for notifications.
* nnotify_cbs: number of notification callbacks.
*
*/
struct mgmt_be_client_cbs {
void (*client_connect_notify)(struct mgmt_be_client *client,
uintptr_t usr_data, bool connected);

void (*subscr_done)(struct mgmt_be_client *client, uintptr_t usr_data,
bool success);
void (*txn_notify)(struct mgmt_be_client *client, uintptr_t usr_data,
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);
};

/***************************************************************
Expand Down Expand Up @@ -124,17 +139,26 @@ extern void mgmt_debug_be_client_show_debug(struct vty *vty);
* The client object.
*
* reg_yang_xpaths
* Yang xpath(s) that needs to be [un]-subscribed from/to
* Yang xpath(s) that needs to be subscribed to
*
* num_xpaths
* Number of xpaths
*
* Returns:
* MGMTD_SUCCESS on success, MGMTD_* otherwise.
*/
extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client,
bool subscr_xpaths, int num_xpaths,
char **reg_xpaths);
extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
int n_config_xpaths, char **config_xpaths,
int n_oper_xpaths, char **oper_xpaths);

/**
* mgmt_be_notification_send() - send a YANG notification to FE clients.
* @tree: libyang tree for the notification. The tree will be freed by
* this function.
*
*/
extern void mgmt_be_send_notification(struct lyd_node *tree);


/*
* Destroy backend client and cleanup everything.
Expand Down
Loading
Loading