diff options
Diffstat (limited to 'lib/northbound.c')
| -rw-r--r-- | lib/northbound.c | 432 |
1 files changed, 409 insertions, 23 deletions
diff --git a/lib/northbound.c b/lib/northbound.c index 12d08310c6..490b3abe57 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -50,8 +50,26 @@ static void nb_transaction_free(struct nb_transaction *transaction); static int nb_transaction_process(enum nb_event event, struct nb_transaction *transaction); static void nb_transaction_apply_finish(struct nb_transaction *transaction); +static int nb_oper_data_iter_node(const struct lys_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 lys_node *snode, void *arg) +{ + bool *config_only = arg; + + if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) { + *config_only = false; + return YANG_ITER_STOP; + } + + return YANG_ITER_CONTINUE; +} -static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2) +static int nb_node_new_cb(const struct lys_node *snode, void *arg) { struct nb_node *nb_node; struct lys_node *sparent, *sparent_list; @@ -67,21 +85,46 @@ static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2) if (sparent_list) nb_node->parent_list = sparent_list->priv; + /* Set flags. */ + if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) { + bool config_only = true; + + yang_snodes_iterate_subtree(snode, nb_node_check_config_only, + YANG_ITER_ALLOW_AUGMENTATIONS, + &config_only); + if (config_only) + SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY); + } + /* * Link the northbound node and the libyang schema node with one * another. */ nb_node->snode = snode; lys_set_private(snode, nb_node); + + return YANG_ITER_CONTINUE; } -static void nb_node_del_cb(const struct lys_node *snode, void *arg1, void *arg2) +static int nb_node_del_cb(const struct lys_node *snode, void *arg) { struct nb_node *nb_node; nb_node = snode->priv; lys_set_private(snode, NULL); XFREE(MTYPE_NB_NODE, nb_node); + + return YANG_ITER_CONTINUE; +} + +void nb_nodes_create(void) +{ + yang_snodes_iterate_all(nb_node_new_cb, 0, NULL); +} + +void nb_nodes_delete(void) +{ + yang_snodes_iterate_all(nb_node_del_cb, 0, NULL); } struct nb_node *nb_node_find(const char *xpath) @@ -170,15 +213,16 @@ static unsigned int nb_node_validate_priority(const struct nb_node *nb_node) return 0; } -static void nb_node_validate(const struct lys_node *snode, void *arg1, - void *arg2) +static int nb_node_validate(const struct lys_node *snode, void *arg) { struct nb_node *nb_node = snode->priv; - unsigned int *errors = arg1; + unsigned int *errors = arg; /* Validate callbacks and priority. */ *errors += nb_node_validate_cbs(nb_node); *errors += nb_node_validate_priority(nb_node); + + return YANG_ITER_CONTINUE; } struct nb_config *nb_config_new(struct lyd_node *dnode) @@ -189,7 +233,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode) if (dnode) config->dnode = dnode; else - config->dnode = yang_dnode_new(ly_native_ctx); + config->dnode = yang_dnode_new(ly_native_ctx, true); config->version = 0; return config; @@ -236,7 +280,8 @@ void nb_config_replace(struct nb_config *config_dst, config_dst->version = config_src->version; /* Update dnode. */ - yang_dnode_free(config_dst->dnode); + if (config_dst->dnode) + yang_dnode_free(config_dst->dnode); if (preserve_source) { config_dst->dnode = yang_dnode_dup(config_src->dnode); } else { @@ -380,7 +425,8 @@ static void nb_config_diff(const struct nb_config *config1, nb_config_diff_add_change(changes, operation, dnode); if (type == LYD_DIFF_CREATED - && (dnode->schema->nodetype & (LYS_CONTAINER | LYS_LIST))) + && CHECK_FLAG(dnode->schema->nodetype, + LYS_CONTAINER | LYS_LIST)) nb_config_diff_new_subtree(dnode, changes); } @@ -890,15 +936,341 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) } } +static int nb_oper_data_iter_children(const struct lys_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) +{ + struct lys_node *child; + + LY_TREE_FOR (snode->child, 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 (lys_is_key((struct lys_node_leaf *)nb_node->snode, NULL)) + return NB_OK; + + data = nb_node->cbs.get_elem(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) +{ + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) + return NB_OK; + + /* Presence containers. */ + if (nb_node->cbs.get_elem) { + struct yang_data *data; + int ret; + + data = nb_node->cbs.get_elem(xpath, list_entry); + if (data == NULL) + /* Presence container is not present. */ + return NB_OK; + + ret = (*cb)(nb_node->snode, translator, data, arg); + if (ret != NB_OK) + return ret; + } + + /* Iterate over the child nodes. */ + return nb_oper_data_iter_children(nb_node->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_node->cbs.get_next(parent_list_entry, list_entry); + if (!list_entry) + /* End of the list. */ + break; + + data = nb_node->cbs.get_elem(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) +{ + struct lys_node_list *slist = (struct lys_node_list *)nb_node->snode; + const void *list_entry = NULL; + + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) + return NB_OK; + + /* Iterate over all list entries. */ + do { + struct yang_list_keys list_keys; + char xpath[XPATH_MAXLEN]; + int ret; + + /* Obtain list entry. */ + list_entry = + nb_node->cbs.get_next(parent_list_entry, list_entry); + if (!list_entry) + /* End of the list. */ + break; + + /* Obtain the list entry keys. */ + if (nb_node->cbs.get_keys(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)); + for (unsigned int i = 0; i < list_keys.num; i++) { + snprintf(xpath + strlen(xpath), + sizeof(xpath) - strlen(xpath), "[%s='%s']", + slist->keys[i]->name, list_keys.key[i]); + } + + /* 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 lys_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) + 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_errno = 0; + dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + return NB_ERR; + } + /* + * We can remove the following two lines once we depend on + * libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for + * lyd_new_path(). + */ + dnode = yang_dnode_get(dnode, xpath); + assert(dnode); + + /* + * Create a linked list to sort the data nodes starting from the root. + */ + list_dnodes = list_new(); + for (dn = dnode; dn; dn = dn->parent) { + if (dn->schema->nodetype != LYS_LIST || !dn->child) + 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_TREE_FOR (dn->child, child) { + if (!lys_is_key((struct lys_node_leaf *)child->schema, + NULL)) + continue; + strlcpy(list_keys.key[n], + yang_dnode_get_string(child, NULL), + sizeof(list_keys.key[n])); + n++; + } + list_keys.num = n; + assert(list_keys.num + == ((struct lys_node_list *)dn->schema)->keys_size); + + /* Find the list entry pointer. */ + nn = dn->schema->priv; + list_entry = nn->cbs.lookup_entry(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 && dnode->child) + 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 lys_node *snode) { + struct nb_node *nb_node = snode->priv; struct lys_node_container *scontainer; struct lys_node_leaf *sleaf; switch (operation) { case NB_OP_CREATE: - if (!(snode->flags & LYS_CONFIG_W)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; switch (snode->nodetype) { @@ -920,7 +1292,7 @@ bool nb_operation_is_valid(enum nb_operation operation, } return true; case NB_OP_MODIFY: - if (!(snode->flags & LYS_CONFIG_W)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; switch (snode->nodetype) { @@ -938,7 +1310,7 @@ bool nb_operation_is_valid(enum nb_operation operation, } return true; case NB_OP_DELETE: - if (!(snode->flags & LYS_CONFIG_W)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; switch (snode->nodetype) { @@ -957,7 +1329,8 @@ bool nb_operation_is_valid(enum nb_operation operation, return true; if (sleaf->when) return true; - if ((sleaf->flags & LYS_MAND_TRUE) || sleaf->dflt) + if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE) + || sleaf->dflt) return false; break; case LYS_CONTAINER: @@ -973,13 +1346,13 @@ bool nb_operation_is_valid(enum nb_operation operation, } return true; case NB_OP_MOVE: - if (!(snode->flags & LYS_CONFIG_W)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; switch (snode->nodetype) { case LYS_LIST: case LYS_LEAFLIST: - if (!(snode->flags & LYS_USERORDERED)) + if (!CHECK_FLAG(snode->flags, LYS_USERORDERED)) return false; break; default: @@ -987,15 +1360,16 @@ bool nb_operation_is_valid(enum nb_operation operation, } return true; case NB_OP_APPLY_FINISH: - if (!(snode->flags & LYS_CONFIG_W)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; return true; case NB_OP_GET_ELEM: - if (!(snode->flags & LYS_CONFIG_R)) + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) return false; switch (snode->nodetype) { case LYS_LEAF: + case LYS_LEAFLIST: break; case LYS_CONTAINER: scontainer = (struct lys_node_container *)snode; @@ -1007,20 +1381,32 @@ bool nb_operation_is_valid(enum nb_operation operation, } return true; case NB_OP_GET_NEXT: + switch (snode->nodetype) { + case LYS_LIST: + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) + return false; + break; + case LYS_LEAFLIST: + if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) + return false; + break; + default: + return false; + } + return true; case NB_OP_GET_KEYS: case NB_OP_LOOKUP_ENTRY: - if (!(snode->flags & LYS_CONFIG_R)) - return false; - switch (snode->nodetype) { case LYS_LIST: + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY)) + return false; break; default: return false; } return true; case NB_OP_RPC: - if (snode->flags & (LYS_CONFIG_W | LYS_CONFIG_R)) + if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R)) return false; switch (snode->nodetype) { @@ -1162,14 +1548,14 @@ void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules) yang_module_load(modules[i]->name); /* Create a nb_node for all YANG schema nodes. */ - yang_all_snodes_iterate(nb_node_new_cb, 0, NULL, NULL); + nb_nodes_create(); /* Load northbound callbacks. */ for (size_t i = 0; i < nmodules; i++) nb_load_callbacks(modules[i]); /* Validate northbound callbacks. */ - yang_all_snodes_iterate(nb_node_validate, 0, &errors, NULL); + yang_snodes_iterate_all(nb_node_validate, 0, &errors); if (errors > 0) { flog_err( EC_LIB_NB_CBS_VALIDATION, @@ -1197,7 +1583,7 @@ void nb_terminate(void) nb_cli_terminate(); /* Delete all nb_node's from all YANG modules. */ - yang_all_snodes_iterate(nb_node_del_cb, 0, NULL, NULL); + nb_nodes_delete(); /* Delete the running configuration. */ nb_config_free(running_config); |
