summaryrefslogtreecommitdiff
path: root/lib/mgmt_be_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mgmt_be_client.c')
-rw-r--r--lib/mgmt_be_client.c391
1 files changed, 337 insertions, 54 deletions
diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c
index f03006ad0e..806242ed53 100644
--- a/lib/mgmt_be_client.c
+++ b/lib/mgmt_be_client.c
@@ -99,12 +99,12 @@ struct mgmt_be_client {
struct nb_config *candidate_config;
struct nb_config *running_config;
- unsigned long num_edit_nb_cfg;
- unsigned long avg_edit_nb_cfg_tm;
- unsigned long num_prep_nb_cfg;
- unsigned long avg_prep_nb_cfg_tm;
- unsigned long num_apply_nb_cfg;
- unsigned long avg_apply_nb_cfg_tm;
+ uint64_t num_edit_nb_cfg;
+ uint64_t avg_edit_nb_cfg_tm;
+ uint64_t num_prep_nb_cfg;
+ uint64_t avg_prep_nb_cfg_tm;
+ uint64_t num_apply_nb_cfg;
+ uint64_t avg_apply_nb_cfg_tm;
struct mgmt_be_txns_head txn_head;
@@ -117,7 +117,7 @@ struct mgmt_be_client {
struct debug mgmt_dbg_be_client = {
.conf = "debug mgmt client backend",
- .desc = "Management backend client operations"
+ .desc = "Management backend client operations",
};
/* NOTE: only one client per proc for now. */
@@ -312,49 +312,102 @@ static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
return ret;
}
-static int mgmt_be_send_notification(void *__be_client, const char *xpath,
- const struct lyd_node *tree)
+static int __send_notification(struct mgmt_be_client *client, const char *xpath,
+ const struct lyd_node *tree, uint8_t op)
{
- struct mgmt_be_client *client = __be_client;
struct mgmt_msg_notify_data *msg = NULL;
+ // LYD_FORMAT format = LYD_LYB;
LYD_FORMAT format = LYD_JSON;
uint8_t **darrp;
LY_ERR err;
int ret = 0;
- assert(tree);
-
- debug_be_client("%s: sending YANG notification: %s", __func__,
- tree->schema->name);
+ assert(op != NOTIFY_OP_NOTIFICATION || xpath || tree);
+ debug_be_client("%s: sending %sYANG %snotification: %s", __func__,
+ op == NOTIFY_OP_DS_DELETE ? "delete "
+ : op == NOTIFY_OP_DS_REPLACE ? "replace "
+ : op == NOTIFY_OP_DS_PATCH ? "patch "
+ : "",
+ op == NOTIFY_OP_NOTIFICATION ? "" : "DS ", xpath ?: 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 = 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;
+ msg->op = op;
mgmt_msg_native_xpath_encode(msg, xpath);
- 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));
- ret = 1;
- goto done;
+ if (tree) {
+ 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));
+ ret = 1;
+ goto done;
+ }
}
- (void)be_client_send_native_msg(client, msg,
- mgmt_msg_native_get_msg_len(msg), false);
+ ret = be_client_send_native_msg(client, msg, mgmt_msg_native_get_msg_len(msg), false);
done:
mgmt_msg_native_free_msg(msg);
return ret;
}
+/**
+ * mgmt_be_send_ds_delete_notification() - Send DS notification to mgmtd
+ */
+int mgmt_be_send_ds_delete_notification(const char *path)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, NULL, NOTIFY_OP_DS_DELETE);
+}
+
+/**
+ * mgmt_be_send_ds_patch_notification() - Send a YANG patch DS notification to mgmtd
+ */
+int mgmt_be_send_ds_patch_notification(const char *path, const struct lyd_node *patch)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, patch, NOTIFY_OP_DS_PATCH);
+}
+
+/**
+ * mgmt_be_send_ds_replace_notification() - Send a replace DS notification to mgmtd
+ */
+int mgmt_be_send_ds_replace_notification(const char *path, const struct lyd_node *tree)
+{
+ if (!__be_client) {
+ debug_be_client("%s: No mgmtd connection for DS delete notification: %s", __func__,
+ path);
+ return 1;
+ }
+ return __send_notification(__be_client, path, tree, NOTIFY_OP_DS_REPLACE);
+}
+
+/**
+ * mgmt_be_send_notification() - Send notification to mgmtd
+ *
+ * This function is attached to the northbound notification hook.
+ */
+static int mgmt_be_send_notification(void *__client, const char *path, const struct lyd_node *tree)
+{
+ __send_notification(__client, path, tree, NOTIFY_OP_NOTIFICATION);
+ return 0;
+}
+
static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
uint64_t txn_id, bool create)
{
@@ -568,7 +621,7 @@ static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn)
mgmt_be_send_cfgdata_create_reply(client_ctx, txn->txn_id,
error ? false : true, error ? err_buf : NULL);
- debug_be_client("Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u",
+ debug_be_client("Avg-nb-edit-duration %Lu uSec, nb-prep-duration %lu (avg: %Lu) uSec, batch size %u",
client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm,
client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed);
@@ -717,10 +770,9 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
gettimeofday(&apply_nb_cfg_end, NULL);
apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start);
- client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm *
- client_ctx->num_apply_nb_cfg) +
- apply_nb_cfg_tm) /
- (client_ctx->num_apply_nb_cfg + 1);
+ client_ctx->avg_apply_nb_cfg_tm =
+ ((client_ctx->avg_apply_nb_cfg_tm * client_ctx->num_apply_nb_cfg) + apply_nb_cfg_tm) /
+ (client_ctx->num_apply_nb_cfg + 1);
client_ctx->num_apply_nb_cfg++;
txn->nb_txn = NULL;
@@ -736,8 +788,8 @@ static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn)
mgmt_be_send_apply_reply(client_ctx, txn->txn_id, true, NULL);
- debug_be_client("Nb-apply-duration %lu (avg: %lu) uSec",
- apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm);
+ debug_be_client("Nb-apply-duration %lu (avg: %Lu) uSec", apply_nb_cfg_tm,
+ client_ctx->avg_apply_nb_cfg_tm);
return 0;
}
@@ -854,8 +906,15 @@ static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree,
more = true;
ret = NB_OK;
}
- if (ret != NB_OK)
+ if (ret != NB_OK) {
+ if (be_client_send_error(client, args->txn_id, args->req_id, false, -EINVAL,
+ "BE client %s txn-id %Lu error fetching oper state %d",
+ client->name, args->txn_id, ret))
+ ret = NB_ERR;
+ else
+ ret = NB_OK;
goto done;
+ }
tree_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0,
MTYPE_MSG_NATIVE_TREE_DATA);
@@ -870,20 +929,15 @@ static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree,
(LYD_PRINT_SHRINK | LYD_PRINT_WD_EXPLICIT |
LYD_PRINT_WITHSIBLINGS));
if (err) {
- ret = NB_ERR;
- goto done;
+ mgmt_msg_native_free_msg(tree_msg);
+ /* We will be called again to send the error */
+ return NB_ERR;
}
(void)be_client_send_native_msg(client, tree_msg,
mgmt_msg_native_get_msg_len(tree_msg),
false);
-done:
mgmt_msg_native_free_msg(tree_msg);
- if (ret)
- be_client_send_error(client, args->txn_id, args->req_id, false,
- -EINVAL,
- "BE client %s txn-id %" PRIu64
- " error fetching oper state %d",
- client->name, args->txn_id, ret);
+done:
if (ret != NB_OK || !more)
XFREE(MTYPE_MGMTD_BE_GT_CB_ARGS, args);
return ret;
@@ -1060,19 +1114,24 @@ 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 nb_node *nb_node;
- struct lyd_node *dnode;
+ struct nb_node *nb_node, *nb_parent;
+ struct lyd_node *dnode = NULL;
const char *data = NULL;
const char *notif;
- LY_ERR err;
+ bool is_yang_notify;
+ LY_ERR err = LY_SUCCESS;
debug_be_client("Received notification for client %s", client->name);
notif = mgmt_msg_native_xpath_data_decode(notif_msg, msg_len, data);
- if (!notif || !data) {
+ if (!notif) {
log_err_be_client("Corrupt notify msg");
return;
}
+ if (!data && (notif_msg->op == NOTIFY_OP_DS_REPLACE || notif_msg->op == NOTIFY_OP_DS_PATCH)) {
+ log_err_be_client("Corrupt replace/patch notify msg: missing data");
+ return;
+ }
nb_node = nb_node_find(notif);
if (!nb_node) {
@@ -1080,25 +1139,62 @@ static void be_client_handle_notify(struct mgmt_be_client *client, void *msgbuf,
return;
}
- if (!nb_node->cbs.notify) {
+ is_yang_notify = !!CHECK_FLAG(nb_node->snode->nodetype, LYS_NOTIF);
+
+ if (is_yang_notify && !nb_node->cbs.notify) {
debug_be_client("No notification callback for: %s", notif);
return;
}
- err = yang_parse_notification(notif, notif_msg->result_type, data,
+ if (!nb_node->cbs.notify) {
+ /*
+ * See if a parent has a callback, this is so backend's can
+ * listen for changes on an entire datastore sub-tree.
+ */
+ for (nb_parent = nb_node->parent; nb_parent; nb_parent = nb_node->parent)
+ if (nb_parent->cbs.notify)
+ break;
+ if (!nb_parent) {
+ debug_be_client("Including parents, no DS notification callback for: %s",
+ notif);
+ return;
+ }
+ nb_node = nb_parent;
+ }
+
+ if (data && is_yang_notify) {
+ err = yang_parse_notification(notif, notif_msg->result_type, data, &dnode);
+ } else if (data) {
+ err = yang_parse_data(notif, notif_msg->result_type, false, true, false, data,
&dnode);
+ }
if (err) {
- log_err_be_client("Can't parse notification data for: %s",
- notif);
+ log_err_be_client("Can't parse notification data for: %s", notif);
return;
}
- nb_callback_notify(nb_node, notif, dnode);
+ nb_callback_notify(nb_node, notif_msg->op, notif, dnode);
lyd_free_all(dnode);
}
/*
+ * Process a notify select msg
+ */
+static void be_client_handle_notify_select(struct mgmt_be_client *client, void *msgbuf,
+ size_t msg_len)
+{
+ struct mgmt_msg_notify_select *msg = msgbuf;
+ const char **selectors = NULL;
+
+ debug_be_client("Received notify-select for client %s", client->name);
+
+ if (msg_len >= sizeof(*msg))
+ selectors = mgmt_msg_native_strings_decode(msg, msg_len, msg->selectors);
+ nb_notif_set_filters(selectors, msg->replace);
+}
+
+/*
* Handle a native encoded message
*
* We don't create transactions with native messaging.
@@ -1119,6 +1215,9 @@ static void be_client_handle_native_msg(struct mgmt_be_client *client,
case MGMT_MSG_CODE_NOTIFY:
be_client_handle_notify(client, msg, msg_len);
break;
+ case MGMT_MSG_CODE_NOTIFY_SELECT:
+ be_client_handle_notify_select(client, msg, msg_len);
+ break;
default:
log_err_be_client("unknown native message txn-id %" PRIu64
" req-id %" PRIu64 " code %u to client %s",
@@ -1259,6 +1358,190 @@ DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd,
return CMD_SUCCESS;
}
+/*
+ * XPath: /frr-backend:clients/client
+ *
+ * We only implement a list of one entry (for the this backend client) the
+ * results will be merged inside mgmtd.
+ */
+static const void *clients_client_get_next(struct nb_cb_get_next_args *args)
+{
+ if (args->list_entry == NULL)
+ return __be_client;
+ return NULL;
+}
+
+static int clients_client_get_keys(struct nb_cb_get_keys_args *args)
+{
+ args->keys->num = 1;
+ strlcpy(args->keys->key[0], __be_client->name, sizeof(args->keys->key[0]));
+
+ return NB_OK;
+}
+
+static const void *clients_client_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ const char *name = args->keys->key[0];
+
+ if (!strcmp(name, __be_client->name))
+ return __be_client;
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-backend:clients/client/name
+ */
+static enum nb_error clients_client_name_get(const struct nb_node *nb_node,
+ const void *parent_list_entry, struct lyd_node *parent)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ LY_ERR err;
+
+ err = lyd_new_term(parent, snode->module, snode->name, __be_client->name, false, NULL);
+ if (err != LY_SUCCESS)
+ return NB_ERR_RESOURCE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-backend:clients/client/state/candidate-config-version
+ */
+static enum nb_error clients_client_state_candidate_config_version_get(
+ const struct nb_node *nb_node, const void *parent_list_entry, struct lyd_node *parent)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ uint64_t value = __be_client->candidate_config->version;
+
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-backend:clients/client/state/running-config-version
+ */
+static enum nb_error clients_client_state_running_config_version_get(const struct nb_node *nb_node,
+ const void *parent_list_entry,
+ struct lyd_node *parent)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ uint64_t value = __be_client->running_config->version;
+
+ if (lyd_new_term_bin(parent, snode->module, snode->name, &value, sizeof(value),
+ LYD_NEW_PATH_UPDATE, NULL))
+ return NB_ERR_RESOURCE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-backend:clients/client/state/notify-selectors
+ *
+ * Is this better in northbound_notif.c? Let's decide when we add more to this module.
+ */
+
+static enum nb_error clients_client_state_notify_selectors_get(const struct nb_node *nb_node,
+ const void *parent_list_entry,
+ struct lyd_node *parent)
+{
+ const struct lysc_node *snode = nb_node->snode;
+ const char **p;
+ LY_ERR err;
+
+ darr_foreach_p (nb_notif_filters, p) {
+ err = lyd_new_term(parent, snode->module, snode->name, *p, false, NULL);
+ if (err != LY_SUCCESS)
+ return NB_ERR_RESOURCE;
+ }
+
+ return NB_OK;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_backend_info = {
+ .name = "frr-backend",
+ .nodes = {
+ {
+ .xpath = "/frr-backend:clients/client",
+ .cbs = {
+ .get_next = clients_client_get_next,
+ .get_keys = clients_client_get_keys,
+ .lookup_entry = clients_client_lookup_entry,
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/name",
+ .cbs.get = clients_client_name_get,
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/candidate-config-version",
+ .cbs = {
+ .get = clients_client_state_candidate_config_version_get,
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/running-config-version",
+ .cbs = {
+ .get = clients_client_state_running_config_version_get,
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/edit-count",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_edit_nb_cfg),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/avg-edit-time",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_edit_nb_cfg_tm),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/prep-count",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_prep_nb_cfg),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/avg-prep-time",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_prep_nb_cfg_tm),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/apply-count",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, num_apply_nb_cfg),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/avg-apply-time",
+ .cbs = {
+ .get = nb_oper_uint64_get,
+ .get_elem = (void *)(intptr_t)offsetof(struct mgmt_be_client, avg_apply_nb_cfg_tm),
+ }
+ },
+ {
+ .xpath = "/frr-backend:clients/client/state/notify-selectors",
+ .cbs.get = clients_client_state_notify_selectors_get,
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
+/* clang-format on */
+
struct mgmt_be_client *mgmt_be_client_create(const char *client_name,
struct mgmt_be_client_cbs *cbs,
uintptr_t user_data,