summaryrefslogtreecommitdiffstats
path: root/lib/northbound.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/northbound.c')
-rw-r--r--lib/northbound.c1067
1 files changed, 425 insertions, 642 deletions
diff --git a/lib/northbound.c b/lib/northbound.c
index 6ff5c24..487f225 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -6,6 +6,7 @@
#include <zebra.h>
+#include "darr.h"
#include "libfrr.h"
#include "log.h"
#include "lib_errors.h"
@@ -70,12 +71,6 @@ static int nb_transaction_process(enum nb_event event,
char *errmsg, size_t errmsg_len);
static void nb_transaction_apply_finish(struct nb_transaction *transaction,
char *errmsg, size_t errmsg_len);
-static int nb_oper_data_iter_node(const struct lysc_node *snode,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg);
static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
{
@@ -130,8 +125,8 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
assert(snode->priv == NULL);
((struct lysc_node *)snode)->priv = nb_node;
- if (module && module->ignore_cbs)
- SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS);
+ if (module && module->ignore_cfg_cbs)
+ SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS);
return YANG_ITER_CONTINUE;
}
@@ -162,18 +157,45 @@ void nb_nodes_delete(void)
struct nb_node *nb_node_find(const char *path)
{
const struct lysc_node *snode;
+ uint32_t llopts = 0;
/*
* Use libyang to find the schema node associated to the path and get
- * the northbound node from there (snode private pointer).
+ * the northbound node from there (snode private pointer). We need to
+ * disable logging temporarily to avoid libyang from logging an error
+ * message when the node is not found.
*/
+ ly_temp_log_options(&llopts);
+
snode = yang_find_snode(ly_native_ctx, path, 0);
+
+ ly_temp_log_options(NULL);
if (!snode)
return NULL;
return snode->priv;
}
+struct nb_node **nb_nodes_find(const char *xpath)
+{
+ struct lysc_node **snodes = NULL;
+ struct nb_node **nb_nodes = NULL;
+ bool simple;
+ LY_ERR err;
+ uint i;
+
+ err = yang_resolve_snode_xpath(ly_native_ctx, xpath, &snodes, &simple);
+ if (err)
+ return NULL;
+
+ darr_ensure_i(nb_nodes, darr_lasti(snodes));
+ darr_foreach_i (snodes, i)
+ nb_nodes[i] = snodes[i]->priv;
+ darr_free(snodes);
+ return nb_nodes;
+}
+
+
void nb_node_set_dependency_cbs(const char *dependency_xpath,
const char *dependant_xpath,
struct nb_dependency_callbacks *cbs)
@@ -194,12 +216,12 @@ bool nb_node_has_dependency(struct nb_node *node)
}
static int nb_node_validate_cb(const struct nb_node *nb_node,
- enum nb_operation operation,
+ enum nb_cb_operation operation,
int callback_implemented, bool optional)
{
bool valid;
- valid = nb_operation_is_valid(operation, nb_node->snode);
+ valid = nb_cb_operation_is_valid(operation, nb_node->snode);
/*
* Add an exception for operational data callbacks. A rw list usually
@@ -211,15 +233,15 @@ static int nb_node_validate_cb(const struct nb_node *nb_node,
* depends on context (e.g. some daemons might augment "frr-interface"
* while others don't).
*/
- if (!valid && callback_implemented && operation != NB_OP_GET_NEXT
- && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY)
+ if (!valid && callback_implemented && operation != NB_CB_GET_NEXT
+ && operation != NB_CB_GET_KEYS && operation != NB_CB_LOOKUP_ENTRY)
flog_warn(EC_LIB_NB_CB_UNNEEDED,
"unneeded '%s' callback for '%s'",
- nb_operation_name(operation), nb_node->xpath);
+ nb_cb_operation_name(operation), nb_node->xpath);
if (!optional && valid && !callback_implemented) {
flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
- nb_operation_name(operation), nb_node->xpath);
+ nb_cb_operation_name(operation), nb_node->xpath);
return 1;
}
@@ -235,31 +257,33 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
{
unsigned int error = 0;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return error;
- error += nb_node_validate_cb(nb_node, NB_OP_CREATE,
+ error += nb_node_validate_cb(nb_node, NB_CB_CREATE,
!!nb_node->cbs.create, false);
- error += nb_node_validate_cb(nb_node, NB_OP_MODIFY,
+ error += nb_node_validate_cb(nb_node, NB_CB_MODIFY,
!!nb_node->cbs.modify, false);
- error += nb_node_validate_cb(nb_node, NB_OP_DESTROY,
+ error += nb_node_validate_cb(nb_node, NB_CB_DESTROY,
!!nb_node->cbs.destroy, false);
- error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move,
+ error += nb_node_validate_cb(nb_node, NB_CB_MOVE, !!nb_node->cbs.move,
false);
- error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE,
+ error += nb_node_validate_cb(nb_node, NB_CB_PRE_VALIDATE,
!!nb_node->cbs.pre_validate, true);
- error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH,
+ error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH,
!!nb_node->cbs.apply_finish, true);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM,
!!nb_node->cbs.get_elem, false);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_NEXT,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT,
!!nb_node->cbs.get_next, false);
- error += nb_node_validate_cb(nb_node, NB_OP_GET_KEYS,
+ error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS,
!!nb_node->cbs.get_keys, false);
- error += nb_node_validate_cb(nb_node, NB_OP_LOOKUP_ENTRY,
+ error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY,
!!nb_node->cbs.lookup_entry, false);
- error += nb_node_validate_cb(nb_node, NB_OP_RPC, !!nb_node->cbs.rpc,
+ 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;
}
@@ -305,8 +329,6 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
- RB_INIT(nb_config_cbs, &config->cfg_chgs);
-
return config;
}
@@ -314,7 +336,7 @@ void nb_config_free(struct nb_config *config)
{
if (config->dnode)
yang_dnode_free(config->dnode);
- nb_config_diff_del_changes(&config->cfg_chgs);
+
XFREE(MTYPE_NB_CONFIG, config);
}
@@ -326,8 +348,6 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
dup->dnode = yang_dnode_dup(config->dnode);
dup->version = config->version;
- RB_INIT(nb_config_cbs, &dup->cfg_chgs);
-
return dup;
}
@@ -369,13 +389,29 @@ void nb_config_replace(struct nb_config *config_dst,
static inline int nb_config_cb_compare(const struct nb_config_cb *a,
const struct nb_config_cb *b)
{
- /* Sort by priority first. */
- if (a->nb_node->priority < b->nb_node->priority)
+ bool a_destroy = a->operation == NB_CB_DESTROY;
+ bool b_destroy = b->operation == NB_CB_DESTROY;
+
+ /*
+ * Sort by operation first. All "destroys" must come first, to correctly
+ * process the change of a "case" inside a "choice". The old "case" must
+ * be deleted before the new "case" is created.
+ */
+ if (a_destroy && !b_destroy)
return -1;
- if (a->nb_node->priority > b->nb_node->priority)
+ if (!a_destroy && b_destroy)
return 1;
/*
+ * Then sort by priority. If the operation is "destroy", reverse the
+ * order, so that the dependants are deleted before the dependencies.
+ */
+ if (a->nb_node->priority < b->nb_node->priority)
+ return !a_destroy ? -1 : 1;
+ if (a->nb_node->priority > b->nb_node->priority)
+ return !a_destroy ? 1 : -1;
+
+ /*
* Preserve the order of the configuration changes as told by libyang.
*/
if (a->seq < b->seq)
@@ -397,10 +433,9 @@ static inline int nb_config_cb_compare(const struct nb_config_cb *a,
}
RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
-static void nb_config_diff_add_change(struct nb_config_cbs *changes,
- enum nb_operation operation,
- uint32_t *seq,
- const struct lyd_node *dnode)
+void nb_config_diff_add_change(struct nb_config_cbs *changes,
+ enum nb_cb_operation operation, uint32_t *seq,
+ const struct lyd_node *dnode)
{
struct nb_config_change *change;
@@ -438,7 +473,7 @@ void nb_config_diff_del_changes(struct nb_config_cbs *changes)
void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes)
{
- enum nb_operation operation;
+ enum nb_cb_operation operation;
struct lyd_node *child;
/* Ignore unimplemented nodes. */
@@ -451,10 +486,10 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
if (lyd_is_default(dnode))
break;
- if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
- operation = NB_OP_CREATE;
- else if (nb_operation_is_valid(NB_OP_MODIFY, dnode->schema))
- operation = NB_OP_MODIFY;
+ if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
+ operation = NB_CB_CREATE;
+ else if (nb_cb_operation_is_valid(NB_CB_MODIFY, dnode->schema))
+ operation = NB_CB_MODIFY;
else
return;
@@ -462,8 +497,8 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
break;
case LYS_CONTAINER:
case LYS_LIST:
- if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema))
- nb_config_diff_add_change(changes, NB_OP_CREATE, seq,
+ if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
+ nb_config_diff_add_change(changes, NB_CB_CREATE, seq,
dnode);
/* Process child nodes recursively. */
@@ -483,8 +518,8 @@ static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq,
if (!dnode->schema->priv)
return;
- if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema))
- nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode);
+ if (nb_cb_operation_is_valid(NB_CB_DESTROY, dnode->schema))
+ nb_config_diff_add_change(changes, NB_CB_DESTROY, seq, dnode);
else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
struct lyd_node *child;
@@ -633,7 +668,7 @@ void nb_config_diff(const struct nb_config *config1,
/* either moving an entry or changing a value */
target = yang_dnode_get(config2->dnode, path);
assert(target);
- nb_config_diff_add_change(changes, NB_OP_MODIFY,
+ nb_config_diff_add_change(changes, NB_CB_MODIFY,
&seq, target);
break;
case 'n': /* none */
@@ -648,44 +683,54 @@ void nb_config_diff(const struct nb_config *config1,
lyd_free_all(diff);
}
-int nb_candidate_edit(struct nb_config *candidate,
- const struct nb_node *nb_node,
+static int dnode_create(struct nb_config *candidate, const char *xpath,
+ const char *value, uint32_t options,
+ struct lyd_node **new_dnode)
+{
+ struct lyd_node *dnode;
+ LY_ERR err;
+
+ err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value,
+ options, &dnode);
+ if (err) {
+ flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d",
+ __func__, xpath, err);
+ return NB_ERR;
+ } else if (dnode) {
+ err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL);
+ if (err) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: lyd_new_implicit_all failed: %d",
+ __func__, err);
+ }
+ }
+ if (new_dnode)
+ *new_dnode = dnode;
+ return NB_OK;
+}
+
+int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node,
enum nb_operation operation, const char *xpath,
const struct yang_data *previous,
const struct yang_data *data)
{
- struct lyd_node *dnode, *dep_dnode;
- char xpath_edit[XPATH_MAXLEN];
+ struct lyd_node *dnode, *dep_dnode, *old_dnode;
char dep_xpath[XPATH_MAXLEN];
+ struct lyd_node *parent = NULL;
+ uint32_t options = 0;
LY_ERR err;
- /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */
- if (nb_node->snode->nodetype == LYS_LEAFLIST)
- snprintf(xpath_edit, sizeof(xpath_edit), "%s[.='%s']", xpath,
- data->value);
- else
- strlcpy(xpath_edit, xpath, sizeof(xpath_edit));
-
switch (operation) {
case NB_OP_CREATE:
case NB_OP_MODIFY:
- err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit,
- (void *)data->value, LYD_NEW_PATH_UPDATE,
+ options = LYD_NEW_PATH_UPDATE;
+ fallthrough;
+ case NB_OP_CREATE_EXCL:
+ err = dnode_create(candidate, xpath, data->value, options,
&dnode);
if (err) {
- flog_warn(EC_LIB_LIBYANG,
- "%s: lyd_new_path(%s) failed: %d", __func__,
- xpath_edit, err);
- return NB_ERR;
+ return err;
} else if (dnode) {
- /* Create default nodes */
- LY_ERR err = lyd_new_implicit_tree(
- dnode, LYD_IMPLICIT_NO_STATE, NULL);
- if (err) {
- flog_warn(EC_LIB_LIBYANG,
- "%s: lyd_new_implicit_all failed: %d",
- __func__, err);
- }
/*
* create dependency
*
@@ -697,33 +742,24 @@ int nb_candidate_edit(struct nb_config *candidate,
nb_node->dep_cbs.get_dependency_xpath(
dnode, dep_xpath);
- err = lyd_new_path(candidate->dnode,
- ly_native_ctx, dep_xpath,
- NULL, LYD_NEW_PATH_UPDATE,
- &dep_dnode);
- /* Create default nodes */
- if (!err && dep_dnode)
- err = lyd_new_implicit_tree(
- dep_dnode,
- LYD_IMPLICIT_NO_STATE, NULL);
+ err = dnode_create(candidate, dep_xpath, NULL,
+ LYD_NEW_PATH_UPDATE, NULL);
if (err) {
- flog_warn(
- EC_LIB_LIBYANG,
- "%s: dependency: lyd_new_path(%s) failed: %d",
- __func__, dep_xpath, err);
- return NB_ERR;
+ lyd_free_tree(dnode);
+ return err;
}
}
}
break;
case NB_OP_DESTROY:
- dnode = yang_dnode_get(candidate->dnode, xpath_edit);
- if (!dnode)
- /*
- * Return a special error code so the caller can choose
- * whether to ignore it or not.
- */
- return NB_ERR_NOT_FOUND;
+ case NB_OP_DELETE:
+ dnode = yang_dnode_get(candidate->dnode, xpath);
+ if (!dnode) {
+ if (operation == NB_OP_DELETE)
+ return NB_ERR;
+ else
+ return NB_OK;
+ }
/* destroy dependant */
if (nb_node->dep_cbs.get_dependant_xpath) {
nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
@@ -734,103 +770,88 @@ int nb_candidate_edit(struct nb_config *candidate,
}
lyd_free_tree(dnode);
break;
+ case NB_OP_REPLACE:
+ old_dnode = yang_dnode_get(candidate->dnode, xpath);
+ if (old_dnode) {
+ parent = lyd_parent(old_dnode);
+ lyd_unlink_tree(old_dnode);
+ }
+ err = dnode_create(candidate, xpath, data->value, options,
+ &dnode);
+ if (!err && dnode && !old_dnode) {
+ /* create dependency if the node didn't exist */
+ nb_node = dnode->schema->priv;
+ if (nb_node->dep_cbs.get_dependency_xpath) {
+ nb_node->dep_cbs.get_dependency_xpath(
+ dnode, dep_xpath);
+
+ err = dnode_create(candidate, dep_xpath, NULL,
+ LYD_NEW_PATH_UPDATE, NULL);
+ if (err)
+ lyd_free_tree(dnode);
+ }
+ }
+ if (old_dnode) {
+ /* restore original node on error, free it otherwise */
+ if (err) {
+ if (parent)
+ lyd_insert_child(parent, old_dnode);
+ else
+ lyd_insert_sibling(candidate->dnode,
+ old_dnode, NULL);
+ return err;
+ }
+
+ lyd_free_tree(old_dnode);
+ }
+ break;
case NB_OP_MOVE:
/* TODO: update configuration. */
break;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
- flog_warn(EC_LIB_DEVELOPMENT,
- "%s: unknown operation (%u) [xpath %s]", __func__,
- operation, xpath_edit);
- return NB_ERR;
}
return NB_OK;
}
-static void nb_update_candidate_changes(struct nb_config *candidate,
- struct nb_cfg_change *change,
- uint32_t *seq)
+const char *nb_operation_name(enum nb_operation operation)
{
- enum nb_operation oper = change->operation;
- char *xpath = change->xpath;
- struct lyd_node *root = NULL;
- struct lyd_node *dnode;
- struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs;
- int op;
-
- switch (oper) {
+ switch (operation) {
+ case NB_OP_CREATE_EXCL:
+ return "create exclusive";
case NB_OP_CREATE:
+ return "create";
case NB_OP_MODIFY:
- root = yang_dnode_get(candidate->dnode, xpath);
- break;
+ return "modify";
case NB_OP_DESTROY:
- root = yang_dnode_get(running_config->dnode, xpath);
- /* code */
- break;
+ return "destroy";
+ case NB_OP_DELETE:
+ return "delete";
+ case NB_OP_REPLACE:
+ return "replace";
case NB_OP_MOVE:
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
- break;
- default:
- assert(!"non-enum value, invalid");
+ return "move";
}
- if (!root)
- return;
-
- LYD_TREE_DFS_BEGIN (root, dnode) {
- op = nb_lyd_diff_get_op(dnode);
- switch (op) {
- case 'c': /* create */
- nb_config_diff_created(dnode, seq, cfg_chgs);
- LYD_TREE_DFS_continue = 1;
- break;
- case 'd': /* delete */
- nb_config_diff_deleted(dnode, seq, cfg_chgs);
- LYD_TREE_DFS_continue = 1;
- break;
- case 'r': /* replace */
- nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq,
- dnode);
- break;
- case 'n': /* none */
- default:
- break;
- }
- LYD_TREE_DFS_END(root, dnode);
- }
+ assert(!"Reached end of function we should never hit");
}
-static bool nb_is_operation_allowed(struct nb_node *nb_node,
- struct nb_cfg_change *change)
+bool nb_is_operation_allowed(struct nb_node *nb_node, enum nb_operation oper)
{
- enum nb_operation oper = change->operation;
-
if (lysc_is_key(nb_node->snode)) {
- if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY)
+ if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY
+ || oper == NB_OP_DELETE || oper == NB_OP_REPLACE)
return false;
}
return true;
}
-void nb_candidate_edit_config_changes(
- struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[],
- size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath,
- int xpath_index, char *err_buf, int err_bufsize, bool *error)
+void nb_candidate_edit_config_changes(struct nb_config *candidate_config,
+ struct nb_cfg_change cfg_changes[],
+ size_t num_cfg_changes,
+ const char *xpath_base, bool in_backend,
+ char *err_buf, int err_bufsize,
+ bool *error)
{
- uint32_t seq = 0;
-
if (error)
*error = false;
@@ -841,38 +862,38 @@ void nb_candidate_edit_config_changes(
for (size_t i = 0; i < num_cfg_changes; i++) {
struct nb_cfg_change *change = &cfg_changes[i];
struct nb_node *nb_node;
+ char *change_xpath = change->xpath;
char xpath[XPATH_MAXLEN];
const char *value;
struct yang_data *data;
int ret;
- /* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
- if (xpath_index > 0 &&
- (xpath_base[0] == '.' || change->xpath[0] == '.'))
- strlcpy(xpath, curr_xpath, sizeof(xpath));
- if (xpath_base[0]) {
- if (xpath_base[0] == '.')
- strlcat(xpath, xpath_base + 1, sizeof(xpath));
- else
- strlcat(xpath, xpath_base, sizeof(xpath));
+ /* If change xpath is relative, prepend base xpath. */
+ if (change_xpath[0] == '.') {
+ strlcpy(xpath, xpath_base, sizeof(xpath));
+ change_xpath++; /* skip '.' */
}
- if (change->xpath[0] == '.')
- strlcat(xpath, change->xpath + 1, sizeof(xpath));
- else
- strlcpy(xpath, change->xpath, sizeof(xpath));
+ strlcat(xpath, change_xpath, sizeof(xpath));
/* Find the northbound node associated to the data path. */
nb_node = nb_node_find(xpath);
if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- if (error)
- *error = true;
+ if (in_backend)
+ DEBUGD(&nb_dbg_cbs_config,
+ "%s: ignoring non-handled path: %s",
+ __func__, xpath);
+ else {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__,
+ xpath);
+ if (error)
+ *error = true;
+ }
continue;
}
/* Find if the node to be edited is not a key node */
- if (!nb_is_operation_allowed(nb_node, change)) {
+ if (!nb_is_operation_allowed(nb_node, change->operation)) {
zlog_err(" Xpath %s points to key node", xpath);
if (error)
*error = true;
@@ -892,7 +913,7 @@ void nb_candidate_edit_config_changes(
ret = nb_candidate_edit(candidate_config, nb_node,
change->operation, xpath, NULL, data);
yang_data_free(data);
- if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) {
+ if (ret != NB_OK) {
flog_warn(
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
@@ -902,17 +923,11 @@ void nb_candidate_edit_config_changes(
*error = true;
continue;
}
- nb_update_candidate_changes(candidate_config, change, &seq);
}
if (error && *error) {
char buf[BUFSIZ];
- /*
- * Failure to edit the candidate configuration should never
- * happen in practice, unless there's a bug in the code. When
- * that happens, log the error but otherwise ignore it.
- */
snprintf(err_buf, err_bufsize,
"%% Failed to edit configuration.\n\n%s",
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
@@ -949,9 +964,19 @@ int nb_candidate_update(struct nb_config *candidate)
int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
char *errmsg, size_t errmsg_len)
{
- if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
- no_state ? LYD_VALIDATE_NO_STATE
- : LYD_VALIDATE_PRESENT,
+ uint32_t options = 0;
+
+#ifdef LYD_VALIDATE_MULTI_ERROR
+ /* libyang 2.1.36+ */
+ options |= LYD_VALIDATE_MULTI_ERROR;
+#endif
+
+ if (no_state)
+ SET_FLAG(options, LYD_VALIDATE_NO_STATE);
+ else
+ SET_FLAG(options, LYD_VALIDATE_PRESENT);
+
+ if (lyd_validate_all(&candidate->dnode, ly_native_ctx, options,
NULL) != 0) {
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
return NB_ERR_VALIDATION;
@@ -1193,7 +1218,7 @@ int nb_running_lock_check(enum nb_client client, const void *user)
}
static void nb_log_config_callback(const enum nb_event event,
- enum nb_operation operation,
+ enum nb_cb_operation operation,
const struct lyd_node *dnode)
{
const char *value;
@@ -1210,7 +1235,7 @@ static void nb_log_config_callback(const enum nb_event event,
zlog_debug(
"northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
- nb_event_name(event), nb_operation_name(operation), xpath,
+ nb_event_name(event), nb_cb_operation_name(operation), xpath,
value);
}
@@ -1224,9 +1249,9 @@ static int nb_callback_create(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_CREATE, dnode);
+ nb_log_config_callback(event, NB_CB_CREATE, dnode);
args.context = context;
args.event = event;
@@ -1275,9 +1300,9 @@ static int nb_callback_modify(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_MODIFY, dnode);
+ nb_log_config_callback(event, NB_CB_MODIFY, dnode);
args.context = context;
args.event = event;
@@ -1326,9 +1351,9 @@ static int nb_callback_destroy(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_DESTROY, dnode);
+ nb_log_config_callback(event, NB_CB_DESTROY, dnode);
args.context = context;
args.event = event;
@@ -1371,9 +1396,9 @@ static int nb_callback_move(struct nb_context *context,
bool unexpected_error = false;
int ret;
- assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS));
+ assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
- nb_log_config_callback(event, NB_OP_MOVE, dnode);
+ nb_log_config_callback(event, NB_CB_MOVE, dnode);
args.context = context;
args.event = event;
@@ -1416,10 +1441,10 @@ static int nb_callback_pre_validate(struct nb_context *context,
bool unexpected_error = false;
int ret;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
- nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
+ nb_log_config_callback(NB_EV_VALIDATE, NB_CB_PRE_VALIDATE, dnode);
args.dnode = dnode;
args.errmsg = errmsg;
@@ -1450,10 +1475,10 @@ static void nb_callback_apply_finish(struct nb_context *context,
{
struct nb_cb_apply_finish_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return;
- nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
+ nb_log_config_callback(NB_EV_APPLY, NB_CB_APPLY_FINISH, dnode);
args.context = context;
args.dnode = dnode;
@@ -1468,7 +1493,7 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
{
struct nb_cb_get_elem_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1486,7 +1511,7 @@ const void *nb_callback_get_next(const struct nb_node *nb_node,
{
struct nb_cb_get_next_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1503,7 +1528,7 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
{
struct nb_cb_get_keys_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_state,
@@ -1521,7 +1546,7 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
{
struct nb_cb_lookup_entry_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
@@ -1533,13 +1558,57 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
return nb_node->cbs.lookup_entry(&args);
}
+const void *nb_callback_lookup_node_entry(struct lyd_node *node,
+ const void *parent_list_entry)
+{
+ struct yang_list_keys keys;
+ struct nb_cb_lookup_entry_args args = {};
+ const struct nb_node *nb_node = node->schema->priv;
+
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
+ return NULL;
+
+ if (yang_get_node_keys(node, &keys)) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: can't get keys for lookup from existing data node %s",
+ __func__, node->schema->name);
+ return NULL;
+ }
+
+ DEBUGD(&nb_dbg_cbs_state,
+ "northbound callback (lookup_node_entry): node [%s] parent_list_entry [%p]",
+ nb_node->xpath, parent_list_entry);
+
+ args.parent_list_entry = parent_list_entry;
+ args.keys = &keys;
+ return nb_node->cbs.lookup_entry(&args);
+}
+
+const void *nb_callback_lookup_next(const struct nb_node *nb_node,
+ const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ struct nb_cb_lookup_entry_args args = {};
+
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
+ return NULL;
+
+ DEBUGD(&nb_dbg_cbs_state,
+ "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
+ nb_node->xpath, parent_list_entry);
+
+ args.parent_list_entry = parent_list_entry;
+ args.keys = keys;
+ return nb_node->cbs.lookup_next(&args);
+}
+
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)
{
struct nb_cb_rpc_args args = {};
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return 0;
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
@@ -1552,6 +1621,18 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
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.
@@ -1561,14 +1642,14 @@ static int nb_callback_configuration(struct nb_context *context,
struct nb_config_change *change,
char *errmsg, size_t errmsg_len)
{
- enum nb_operation operation = change->cb.operation;
+ enum nb_cb_operation operation = change->cb.operation;
char xpath[XPATH_MAXLEN];
const struct nb_node *nb_node = change->cb.nb_node;
const struct lyd_node *dnode = change->cb.dnode;
union nb_resource *resource;
int ret = NB_ERR;
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
return NB_OK;
if (event == NB_EV_VALIDATE)
@@ -1577,29 +1658,30 @@ static int nb_callback_configuration(struct nb_context *context,
resource = &change->resource;
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
ret = nb_callback_create(context, nb_node, event, dnode,
resource, errmsg, errmsg_len);
break;
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
ret = nb_callback_modify(context, nb_node, event, dnode,
resource, errmsg, errmsg_len);
break;
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
ret = nb_callback_destroy(context, nb_node, event, dnode,
errmsg, errmsg_len);
break;
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
errmsg_len);
break;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
- case NB_OP_GET_ELEM:
- case NB_OP_GET_NEXT:
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
- case NB_OP_RPC:
+ case NB_CB_PRE_VALIDATE:
+ case NB_CB_APPLY_FINISH:
+ case NB_CB_GET_ELEM:
+ case NB_CB_GET_NEXT:
+ 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__,
@@ -1615,28 +1697,28 @@ static int nb_callback_configuration(struct nb_context *context,
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_PREPARE:
flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_ABORT:
flog_warn(EC_LIB_NB_CB_CONFIG_ABORT,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
case NB_EV_APPLY:
flog_err(EC_LIB_NB_CB_CONFIG_APPLY,
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
nb_err_name(ret), nb_event_name(event),
- nb_operation_name(operation), xpath,
+ nb_cb_operation_name(operation), xpath,
errmsg[0] ? " message: " : "", errmsg);
break;
default:
@@ -1743,6 +1825,7 @@ nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node,
struct nb_config_cb *cb;
cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
+ cb->operation = NB_CB_APPLY_FINISH;
cb->nb_node = nb_node;
cb->dnode = dnode;
RB_INSERT(nb_config_cbs, cbs, cb);
@@ -1757,6 +1840,7 @@ nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
{
struct nb_config_cb s;
+ s.operation = NB_CB_APPLY_FINISH;
s.seq = 0;
s.nb_node = nb_node;
s.dnode = dnode;
@@ -1784,12 +1868,12 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction,
* (the 'apply_finish' callbacks from the node ancestors should
* be called though).
*/
- if (change->cb.operation == NB_OP_DESTROY) {
+ if (change->cb.operation == NB_CB_DESTROY) {
char xpath[XPATH_MAXLEN];
dnode = lyd_parent(dnode);
if (!dnode)
- break;
+ continue;
/*
* The dnode from 'delete' callbacks point to elements
@@ -1835,394 +1919,15 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction,
}
}
-static int nb_oper_data_iter_children(const struct lysc_node *snode,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- const struct lysc_node *child;
-
- LY_LIST_FOR (lysc_node_child(snode), child) {
- int ret;
-
- ret = nb_oper_data_iter_node(child, xpath, list_entry,
- list_keys, translator, false,
- flags, cb, arg);
- if (ret != NB_OK)
- return ret;
- }
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
- const char *xpath, const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- struct yang_data *data;
-
- if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
- return NB_OK;
-
- /* Ignore list keys. */
- if (lysc_is_key(nb_node->snode))
- return NB_OK;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- /* Leaf of type "empty" is not present. */
- return NB_OK;
-
- return (*cb)(nb_node->snode, translator, data, arg);
-}
-
-static int nb_oper_data_iter_container(const struct nb_node *nb_node,
- const char *xpath,
- const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb,
- void *arg)
-{
- const struct lysc_node *snode = nb_node->snode;
-
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
- return NB_OK;
-
- /* Read-only presence containers. */
- if (nb_node->cbs.get_elem) {
- struct yang_data *data;
- int ret;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- /* Presence container is not present. */
- return NB_OK;
-
- ret = (*cb)(snode, translator, data, arg);
- if (ret != NB_OK)
- return ret;
- }
-
- /* Read-write presence containers. */
- if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) {
- struct lysc_node_container *scontainer;
-
- scontainer = (struct lysc_node_container *)snode;
- if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE)
- && !yang_dnode_get(running_config->dnode, xpath))
- return NB_OK;
- }
-
- /* Iterate over the child nodes. */
- return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
- translator, false, flags, cb, arg);
-}
-
-static int
-nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
- const void *parent_list_entry,
- const struct yang_list_keys *parent_list_keys,
- struct yang_translator *translator, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- const void *list_entry = NULL;
-
- if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
- return NB_OK;
-
- do {
- struct yang_data *data;
- int ret;
-
- list_entry = nb_callback_get_next(nb_node, parent_list_entry,
- list_entry);
- if (!list_entry)
- /* End of the list. */
- break;
-
- data = nb_callback_get_elem(nb_node, xpath, list_entry);
- if (data == NULL)
- continue;
-
- ret = (*cb)(nb_node->snode, translator, data, arg);
- if (ret != NB_OK)
- return ret;
- } while (list_entry);
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_list(const struct nb_node *nb_node,
- const char *xpath_list,
- const void *parent_list_entry,
- const struct yang_list_keys *parent_list_keys,
- struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- const struct lysc_node *snode = nb_node->snode;
- const void *list_entry = NULL;
- uint32_t position = 1;
-
- if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
- return NB_OK;
-
- /* Iterate over all list entries. */
- do {
- const struct lysc_node_leaf *skey;
- struct yang_list_keys list_keys = {};
- char xpath[XPATH_MAXLEN * 2];
- int ret;
-
- /* Obtain list entry. */
- list_entry = nb_callback_get_next(nb_node, parent_list_entry,
- list_entry);
- if (!list_entry)
- /* End of the list. */
- break;
-
- if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
- /* Obtain the list entry keys. */
- if (nb_callback_get_keys(nb_node, list_entry,
- &list_keys)
- != NB_OK) {
- flog_warn(EC_LIB_NB_CB_STATE,
- "%s: failed to get list keys",
- __func__);
- return NB_ERR;
- }
-
- /* Build XPath of the list entry. */
- strlcpy(xpath, xpath_list, sizeof(xpath));
- unsigned int i = 0;
- LY_FOR_KEYS (snode, skey) {
- assert(i < list_keys.num);
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath),
- "[%s='%s']", skey->name,
- list_keys.key[i]);
- i++;
- }
- assert(i == list_keys.num);
- } else {
- /*
- * Keyless list - build XPath using a positional index.
- */
- snprintf(xpath, sizeof(xpath), "%s[%u]", xpath_list,
- position);
- position++;
- }
-
- /* Iterate over the child nodes. */
- ret = nb_oper_data_iter_children(
- nb_node->snode, xpath, list_entry, &list_keys,
- translator, false, flags, cb, arg);
- if (ret != NB_OK)
- return ret;
- } while (list_entry);
-
- return NB_OK;
-}
-
-static int nb_oper_data_iter_node(const struct lysc_node *snode,
- const char *xpath_parent,
- const void *list_entry,
- const struct yang_list_keys *list_keys,
- struct yang_translator *translator,
- bool first, uint32_t flags,
- nb_oper_data_cb cb, void *arg)
-{
- struct nb_node *nb_node;
- char xpath[XPATH_MAXLEN];
- int ret = NB_OK;
-
- if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
- && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
- return NB_OK;
-
- /* Update XPath. */
- strlcpy(xpath, xpath_parent, sizeof(xpath));
- if (!first && snode->nodetype != LYS_USES) {
- struct lysc_node *parent;
-
- /* Get the real parent. */
- parent = snode->parent;
-
- /*
- * When necessary, include the namespace of the augmenting
- * module.
- */
- if (parent && parent->module != snode->module)
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath), "/%s:%s",
- snode->module->name, snode->name);
- else
- snprintf(xpath + strlen(xpath),
- sizeof(xpath) - strlen(xpath), "/%s",
- snode->name);
- }
-
- nb_node = snode->priv;
- switch (snode->nodetype) {
- case LYS_CONTAINER:
- ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
- list_keys, translator, flags,
- cb, arg);
- break;
- case LYS_LEAF:
- ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
- list_keys, translator, flags, cb,
- arg);
- break;
- case LYS_LEAFLIST:
- ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
- list_keys, translator, flags,
- cb, arg);
- break;
- case LYS_LIST:
- ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
- list_keys, translator, flags, cb,
- arg);
- break;
- case LYS_USES:
- ret = nb_oper_data_iter_children(snode, xpath, list_entry,
- list_keys, translator, false,
- flags, cb, arg);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
- uint32_t flags, nb_oper_data_cb cb, void *arg)
-{
- struct nb_node *nb_node;
- const void *list_entry = NULL;
- struct yang_list_keys list_keys;
- struct list *list_dnodes;
- struct lyd_node *dnode, *dn;
- struct listnode *ln;
- int ret;
-
- nb_node = nb_node_find(xpath);
- if (!nb_node) {
- flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
- "%s: unknown data path: %s", __func__, xpath);
- return NB_ERR;
- }
-
- /* For now this function works only with containers and lists. */
- if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
- flog_warn(
- EC_LIB_NB_OPERATIONAL_DATA,
- "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
- __func__, xpath);
- return NB_ERR;
- }
-
- /*
- * Create a data tree from the XPath so that we can parse the keys of
- * all YANG lists (if any).
- */
-
- LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
- LYD_NEW_PATH_UPDATE, NULL, &dnode);
- if (err || !dnode) {
- const char *errmsg =
- err ? ly_errmsg(ly_native_ctx) : "node not found";
- flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
- __func__, errmsg);
- return NB_ERR;
- }
-
- /*
- * Create a linked list to sort the data nodes starting from the root.
- */
- list_dnodes = list_new();
- for (dn = dnode; dn; dn = lyd_parent(dn)) {
- if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
- continue;
- listnode_add_head(list_dnodes, dn);
- }
- /*
- * Use the northbound callbacks to find list entry pointer corresponding
- * to the given XPath.
- */
- for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
- struct lyd_node *child;
- struct nb_node *nn;
- unsigned int n = 0;
-
- /* Obtain the list entry keys. */
- memset(&list_keys, 0, sizeof(list_keys));
- LY_LIST_FOR (lyd_child(dn), child) {
- if (!lysc_is_key(child->schema))
- break;
- strlcpy(list_keys.key[n],
- yang_dnode_get_string(child, NULL),
- sizeof(list_keys.key[n]));
- n++;
- }
- list_keys.num = n;
- if (list_keys.num != yang_snode_num_keys(dn->schema)) {
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR_NOT_FOUND;
- }
-
- /* Find the list entry pointer. */
- nn = dn->schema->priv;
- if (!nn->cbs.lookup_entry) {
- flog_warn(
- EC_LIB_NB_OPERATIONAL_DATA,
- "%s: data path doesn't support iteration over operational data: %s",
- __func__, xpath);
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR;
- }
-
- list_entry =
- nb_callback_lookup_entry(nn, list_entry, &list_keys);
- if (list_entry == NULL) {
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
- return NB_ERR_NOT_FOUND;
- }
- }
-
- /* If a list entry was given, iterate over that list entry only. */
- if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
- ret = nb_oper_data_iter_children(
- nb_node->snode, xpath, list_entry, &list_keys,
- translator, true, flags, cb, arg);
- else
- ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
- &list_keys, translator, true,
- flags, cb, arg);
-
- list_delete(&list_dnodes);
- yang_dnode_free(dnode);
-
- return ret;
-}
-
-bool nb_operation_is_valid(enum nb_operation operation,
- const struct lysc_node *snode)
+bool nb_cb_operation_is_valid(enum nb_cb_operation operation,
+ const struct lysc_node *snode)
{
struct nb_node *nb_node = snode->priv;
struct lysc_node_container *scontainer;
struct lysc_node_leaf *sleaf;
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2233,6 +1938,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
break;
case LYS_CONTAINER:
+ if (snode->parent && snode->parent->nodetype == LYS_CASE)
+ return true;
scontainer = (struct lysc_node_container *)snode;
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
return false;
@@ -2244,7 +1951,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2262,7 +1969,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2287,6 +1994,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
break;
case LYS_CONTAINER:
+ if (snode->parent && snode->parent->nodetype == LYS_CASE)
+ return true;
scontainer = (struct lysc_node_container *)snode;
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
return false;
@@ -2298,7 +2007,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
@@ -2312,12 +2021,12 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_PRE_VALIDATE:
- case NB_OP_APPLY_FINISH:
+ case NB_CB_PRE_VALIDATE:
+ case NB_CB_APPLY_FINISH:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
return true;
- case NB_OP_GET_ELEM:
+ case NB_CB_GET_ELEM:
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return false;
@@ -2334,7 +2043,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_GET_NEXT:
+ case NB_CB_GET_NEXT:
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
@@ -2348,8 +2057,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_GET_KEYS:
- case NB_OP_LOOKUP_ENTRY:
+ case NB_CB_GET_KEYS:
+ case NB_CB_LOOKUP_ENTRY:
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
@@ -2361,7 +2070,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
- case NB_OP_RPC:
+ case NB_CB_RPC:
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
return false;
@@ -2373,6 +2082,10 @@ bool nb_operation_is_valid(enum nb_operation operation,
return false;
}
return true;
+ case NB_CB_NOTIFY:
+ if (snode->nodetype != LYS_NOTIF)
+ return false;
+ return true;
default:
return false;
}
@@ -2383,17 +2096,81 @@ DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
int nb_notification_send(const char *xpath, struct list *arguments)
{
+ struct lyd_node *root = NULL;
+ struct lyd_node *dnode;
+ struct yang_data *data;
+ struct listnode *ln;
+ LY_ERR err;
int ret;
DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
+ /*
+ * Call old hook functions
+ */
ret = hook_call(nb_notification_send, xpath, arguments);
+
+ if (!hook_have_hooks(nb_notification_tree_send))
+ goto done;
+ /*
+ * Convert yang data arguments list to a libyang data tree for new hook
+ * functions.
+ */
+ for (ALL_LIST_ELEMENTS_RO(arguments, ln, data)) {
+ err = lyd_new_path(root, ly_native_ctx, data->xpath,
+ data->value, LYD_NEW_PATH_UPDATE, &dnode);
+ if (err != LY_SUCCESS)
+ goto lyerr;
+ 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) {
+lyerr:
+ flog_err(EC_LIB_LIBYANG,
+ "%s: error creating notification data: %s",
+ __func__, ly_strerrcode(err));
+ ret += 1;
+ goto done;
+ }
+ }
+
+ /*
+ * Call new hook functions
+ */
+ ret += nb_notification_tree_send(xpath, root);
+
+done:
+ if (root)
+ lyd_free_all(root);
if (arguments)
list_delete(&arguments);
return ret;
}
+DEFINE_HOOK(nb_notification_tree_send,
+ (const char *xpath, const struct lyd_node *tree), (xpath, tree));
+
+int nb_notification_tree_send(const char *xpath, const struct lyd_node *tree)
+{
+ int ret;
+
+ assert(tree);
+
+ DEBUGD(&nb_dbg_notif, "northbound tree notification: %s",
+ tree->schema->name);
+
+ ret = hook_call(nb_notification_tree_send, xpath, tree);
+
+ return ret;
+}
+
/* Running configuration user pointers management. */
struct nb_config_entry {
char xpath[XPATH_MAXLEN];
@@ -2563,31 +2340,33 @@ const char *nb_event_name(enum nb_event event)
assert(!"Reached end of function we should never hit");
}
-const char *nb_operation_name(enum nb_operation operation)
+const char *nb_cb_operation_name(enum nb_cb_operation operation)
{
switch (operation) {
- case NB_OP_CREATE:
+ case NB_CB_CREATE:
return "create";
- case NB_OP_MODIFY:
+ case NB_CB_MODIFY:
return "modify";
- case NB_OP_DESTROY:
+ case NB_CB_DESTROY:
return "destroy";
- case NB_OP_MOVE:
+ case NB_CB_MOVE:
return "move";
- case NB_OP_PRE_VALIDATE:
+ case NB_CB_PRE_VALIDATE:
return "pre_validate";
- case NB_OP_APPLY_FINISH:
+ case NB_CB_APPLY_FINISH:
return "apply_finish";
- case NB_OP_GET_ELEM:
+ case NB_CB_GET_ELEM:
return "get_elem";
- case NB_OP_GET_NEXT:
+ case NB_CB_GET_NEXT:
return "get_next";
- case NB_OP_GET_KEYS:
+ case NB_CB_GET_KEYS:
return "get_keys";
- case NB_OP_LOOKUP_ENTRY:
+ case NB_CB_LOOKUP_ENTRY:
return "lookup_entry";
- case NB_OP_RPC:
+ case NB_CB_RPC:
return "rpc";
+ case NB_CB_NOTIFY:
+ return "notify";
}
assert(!"Reached end of function we should never hit");
@@ -2612,6 +2391,8 @@ const char *nb_err_name(enum nb_error error)
return "failed to allocate resource";
case NB_ERR_INCONSISTENCY:
return "internal inconsistency";
+ case NB_YIELD:
+ return "should yield";
}
assert(!"Reached end of function we should never hit");
@@ -2643,10 +2424,6 @@ const char *nb_client_name(enum nb_client client)
static void nb_load_callbacks(const struct frr_yang_module_info *module)
{
-
- if (module->ignore_cbs)
- return;
-
for (size_t i = 0; module->nodes[i].xpath; i++) {
struct nb_node *nb_node;
uint32_t priority;
@@ -2712,7 +2489,8 @@ void nb_init(struct event_loop *tm,
for (size_t i = 0; i < nmodules; i++) {
DEBUGD(&nb_dbg_events, "northbound: loading %s.yang",
modules[i]->name);
- *loadedp++ = yang_module_load(modules[i]->name);
+ *loadedp++ = yang_module_load(modules[i]->name,
+ modules[i]->features);
}
if (explicit_compile)
@@ -2737,10 +2515,15 @@ void nb_init(struct event_loop *tm,
/* Initialize the northbound CLI. */
nb_cli_init(tm);
+
+ /* Initialize oper-state */
+ nb_oper_init(tm);
}
void nb_terminate(void)
{
+ nb_oper_terminate();
+
/* Terminate the northbound CLI. */
nb_cli_terminate();