diff --git a/lib/lib_errors.c b/lib/lib_errors.c index 71d1ec6e58..b1ed7d2f6b 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -134,6 +134,12 @@ static struct log_ref ferr_lib_warn[] = { .description = "The northbound subsystem failed to edit a candidate configuration", .suggestion = "This is a bug; please report it" }, + { + .code = EC_LIB_NB_OPERATIONAL_DATA, + .title = "Failure to obtain operational data", + .description = "The northbound subsystem failed to obtain YANG-modeled operational data", + .suggestion = "This is a bug; please report it" + }, { .code = EC_LIB_NB_TRANSACTION_CREATION_FAILED, .title = "Failure to create a configuration transaction", diff --git a/lib/lib_errors.h b/lib/lib_errors.h index 38c75f913e..5534edbd8d 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -62,6 +62,7 @@ enum lib_log_refs { EC_LIB_NB_CB_RPC, EC_LIB_NB_CANDIDATE_INVALID, EC_LIB_NB_CANDIDATE_EDIT_ERROR, + EC_LIB_NB_OPERATIONAL_DATA, EC_LIB_NB_TRANSACTION_CREATION_FAILED, EC_LIB_NB_TRANSACTION_RECORD_FAILED, EC_LIB_LIBYANG, 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 void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2) +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 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: - 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; + 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: + 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); diff --git a/lib/northbound.h b/lib/northbound.h index 8ab6662ecc..e26a2f8617 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -21,9 +21,10 @@ #define _FRR_NORTHBOUND_H_ #include "hook.h" -#include "yang.h" #include "linklist.h" #include "openbsd-tree.h" +#include "yang.h" +#include "yang_translator.h" /* Forward declaration(s). */ struct vty; @@ -211,15 +212,15 @@ struct nb_callbacks { /* * Operational data callback. * - * The callback function should return the value of a specific leaf or - * inform if a typeless value (presence containers or leafs of type - * empty) exists or not. + * The callback function should return the value of a specific leaf, + * leaf-list entry or inform if a typeless value (presence containers or + * leafs of type empty) exists or not. * * xpath * YANG data path of the data we want to get. * * list_entry - * Pointer to list entry. + * Pointer to list entry (might be NULL). * * Returns: * Pointer to newly created yang_data structure, or NULL to indicate @@ -229,22 +230,24 @@ struct nb_callbacks { const void *list_entry); /* - * Operational data callback for YANG lists. + * Operational data callback for YANG lists and leaf-lists. * - * The callback function should return the next entry in the list. The - * 'list_entry' parameter will be NULL on the first invocation. + * The callback function should return the next entry in the list or + * leaf-list. The 'list_entry' parameter will be NULL on the first + * invocation. * - * xpath - * Data path of the YANG list. + * parent_list_entry + * Pointer to parent list entry. * * list_entry - * Pointer to list entry. + * Pointer to (leaf-)list entry. * * Returns: - * Pointer to the next entry in the list, or NULL to signal that the - * end of the list was reached. + * Pointer to the next entry in the (leaf-)list, or NULL to signal + * that the end of the (leaf-)list was reached. */ - const void *(*get_next)(const char *xpath, const void *list_entry); + const void *(*get_next)(const void *parent_list_entry, + const void *list_entry); /* * Operational data callback for YANG lists. @@ -270,13 +273,17 @@ struct nb_callbacks { * The callback function should return a list entry based on the list * keys given as a parameter. * + * parent_list_entry + * Pointer to parent list entry. + * * keys * Structure containing the keys of the list entry. * * Returns: * Pointer to the list entry if found, or NULL if not found. */ - const void *(*lookup_entry)(const struct yang_list_keys *keys); + const void *(*lookup_entry)(const void *parent_list_entry, + const struct yang_list_keys *keys); /* * RPC and action callback. @@ -349,11 +356,16 @@ struct nb_node { /* Pointer to the nearest parent list, if any. */ struct nb_node *parent_list; + /* Flags. */ + uint8_t flags; + #ifdef HAVE_CONFD /* ConfD hash value corresponding to this YANG path. */ int confd_hash; #endif }; +/* The YANG container or list contains only config data. */ +#define F_NB_NODE_CONFIG_ONLY 0x01 struct frr_yang_module_info { /* YANG module name. */ @@ -429,12 +441,30 @@ struct nb_transaction { struct nb_config_cbs changes; }; +/* Callback function used by nb_oper_data_iterate(). */ +typedef int (*nb_oper_data_cb)(const struct lys_node *snode, + struct yang_translator *translator, + struct yang_data *data, void *arg); + +/* Iterate over direct child nodes only. */ +#define NB_OPER_DATA_ITER_NORECURSE 0x0001 + DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments), (xpath, arguments)) extern int debug_northbound; extern struct nb_config *running_config; +/* + * Create a northbound node for all YANG schema nodes. + */ +void nb_nodes_create(void); + +/* + * Delete all northbound nodes from all YANG schema nodes. + */ +void nb_nodes_delete(void); + /* * Find the northbound node corresponding to a YANG data path. * @@ -685,6 +715,31 @@ extern int nb_candidate_commit(struct nb_config *candidate, enum nb_client client, bool save_transaction, const char *comment, uint32_t *transaction_id); +/* + * Iterate over operetional data. + * + * xpath + * Data path of the YANG data we want to iterate over. + * + * translator + * YANG module translator (might be NULL). + * + * flags + * NB_OPER_DATA_ITER_ flags to control how the iteration is performed. + * + * cb + * Function to call with each data node. + * + * arg + * Arbitrary argument passed as the fourth parameter in each call to 'cb'. + * + * Returns: + * NB_OK on success, NB_ERR otherwise. + */ +extern int nb_oper_data_iterate(const char *xpath, + struct yang_translator *translator, + uint32_t flags, nb_oper_data_cb cb, void *arg); + /* * Validate if the northbound operation is valid for the given node. * diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 94f7bb7c1e..a3006264f1 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -386,7 +386,7 @@ static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format, { struct lyd_node *dnode; char *strp; - int options; + int options = 0; dnode = yang_dnode_dup(config->dnode); if (translator @@ -398,11 +398,11 @@ static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format, return CMD_WARNING; } - options = LYP_FORMAT | LYP_WITHSIBLINGS; + SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS); if (with_defaults) - options |= LYP_WD_ALL; + SET_FLAG(options, LYP_WD_ALL); else - options |= LYP_WD_TRIM; + SET_FLAG(options, LYP_WD_TRIM); if (lyd_print_mem(&strp, dnode, format, options) == 0 && strp) { vty_out(vty, "%s", strp); @@ -1124,6 +1124,117 @@ DEFPY (show_config_transaction, #endif /* HAVE_CONFIG_ROLLBACKS */ } +static int nb_cli_oper_data_cb(const struct lys_node *snode, + struct yang_translator *translator, + struct yang_data *data, void *arg) +{ + struct lyd_node *dnode = arg; + struct ly_ctx *ly_ctx; + + if (translator) { + int ret; + + ret = yang_translate_xpath(translator, + YANG_TRANSLATE_FROM_NATIVE, + data->xpath, sizeof(data->xpath)); + switch (ret) { + case YANG_TRANSLATE_SUCCESS: + break; + case YANG_TRANSLATE_NOTFOUND: + goto exit; + case YANG_TRANSLATE_FAILURE: + goto error; + } + + ly_ctx = translator->ly_ctx; + } else + ly_ctx = ly_native_ctx; + + ly_errno = 0; + dnode = lyd_new_path(dnode, ly_ctx, data->xpath, (void *)data->value, 0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + goto error; + } + +exit: + yang_data_free(data); + return NB_OK; + +error: + yang_data_free(data); + return NB_ERR; +} + +DEFPY (show_yang_operational_data, + show_yang_operational_data_cmd, + "show yang operational-data XPATH$xpath\ + [{\ + format \ + |translate WORD$translator_family\ + }]", + SHOW_STR + "YANG information\n" + "Show YANG operational data\n" + "XPath expression specifying the YANG data path\n" + "Set the output format\n" + "JavaScript Object Notation\n" + "Extensible Markup Language\n" + "Translate operational data\n" + "YANG module translator\n") +{ + LYD_FORMAT format; + struct yang_translator *translator = NULL; + struct ly_ctx *ly_ctx; + struct lyd_node *dnode; + char *strp; + + if (xml) + format = LYD_XML; + else + format = LYD_JSON; + + if (translator_family) { + translator = yang_translator_find(translator_family); + if (!translator) { + vty_out(vty, "%% Module translator \"%s\" not found\n", + translator_family); + return CMD_WARNING; + } + + ly_ctx = translator->ly_ctx; + } else + ly_ctx = ly_native_ctx; + + /* Obtain data. */ + dnode = yang_dnode_new(ly_ctx, false); + if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, + dnode) + != NB_OK) { + vty_out(vty, "%% Failed to fetch operational data.\n"); + yang_dnode_free(dnode); + return CMD_WARNING; + } + lyd_validate(&dnode, LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB, ly_ctx); + + /* Display the data. */ + if (lyd_print_mem(&strp, dnode, format, + LYP_FORMAT | LYP_WITHSIBLINGS | LYP_WD_ALL) + != 0 + || !strp) { + vty_out(vty, "%% Failed to display operational data.\n"); + yang_dnode_free(dnode); + return CMD_WARNING; + } + vty_out(vty, "%s", strp); + free(strp); + yang_dnode_free(dnode); + + return CMD_SUCCESS; +} + DEFPY (show_yang_module, show_yang_module_cmd, "show yang module [module-translator WORD$translator_family]", @@ -1463,6 +1574,7 @@ void nb_cli_init(void) /* Other commands. */ install_element(CONFIG_NODE, &yang_module_translator_load_cmd); install_element(CONFIG_NODE, &yang_module_translator_unload_cmd); + install_element(ENABLE_NODE, &show_yang_operational_data_cmd); install_element(ENABLE_NODE, &show_yang_module_cmd); install_element(ENABLE_NODE, &show_yang_module_detail_cmd); install_element(ENABLE_NODE, &show_yang_module_translator_cmd); diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index ec5e0c32c3..3579d1da00 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -91,22 +91,61 @@ static int frr_confd_val2str(const char *xpath, const confd_value_t *value, return 0; } -/* Obtain list keys from ConfD hashed keypath. */ -static void frr_confd_hkeypath_get_keys(const confd_hkeypath_t *kp, - struct yang_list_keys *keys) +/* Obtain list entry from ConfD hashed keypath. */ +static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp, + struct nb_node *nb_node, + const void **list_entry) { - memset(keys, 0, sizeof(*keys)); - for (int i = 0; i < kp->len; i++) { + struct nb_node *nb_node_list; + int parent_lists = 0; + int curr_list = 0; + + *list_entry = NULL; + + /* + * Count the number of YANG lists in the path, disconsidering the + * last element. + */ + nb_node_list = nb_node; + while (nb_node_list->parent_list) { + nb_node_list = nb_node_list->parent_list; + parent_lists++; + } + if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0) + return 0; + + /* Start from the beginning and move down the tree. */ + for (int i = kp->len; i >= 0; i--) { + struct yang_list_keys keys; + + /* Not a YANG list. */ if (kp->v[i][0].type != C_BUF) continue; + /* Obtain list keys. */ + memset(&keys, 0, sizeof(keys)); for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) { - strlcpy(keys->key[keys->num].value, + strlcpy(keys.key[keys.num], (char *)kp->v[i][j].val.buf.ptr, - sizeof(keys->key[keys->num].value)); - keys->num++; + sizeof(keys.key[keys.num])); + keys.num++; } + + /* Obtain northbound node associated to the YANG list. */ + nb_node_list = nb_node; + for (int j = curr_list; j < parent_lists; j++) + nb_node_list = nb_node_list->parent_list; + + /* Obtain list entry. */ + *list_entry = + nb_node_list->cbs.lookup_entry(*list_entry, &keys); + if (*list_entry == NULL) + return -1; + + curr_list++; } + + return 0; } /* Fill the current date and time into a confd_datetime structure. */ @@ -430,6 +469,9 @@ static int frr_confd_init_cdb(void) continue; } + if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) + continue; + nb_node = snode->priv; if (debug_northbound) zlog_debug("%s: subscribing to '%s'", __func__, @@ -490,12 +532,13 @@ static int frr_confd_transaction_init(struct confd_trans_ctx *tctx) return CONFD_OK; } +#define CONFD_MAX_CHILD_NODES 32 + static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp) { - struct nb_node *nb_node, *parent_list; + struct nb_node *nb_node; char xpath[BUFSIZ]; - struct yang_list_keys keys; struct yang_data *data; confd_value_t v; const void *list_entry = NULL; @@ -510,17 +553,9 @@ static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, return CONFD_OK; } - parent_list = nb_node->parent_list; - if (parent_list) { - frr_confd_hkeypath_get_keys(kp, &keys); - list_entry = parent_list->cbs.lookup_entry(&keys); - if (!list_entry) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: list entry not found: %s", __func__, - xpath); - confd_data_reply_not_found(tctx); - return CONFD_OK; - } + if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { + confd_data_reply_not_found(tctx); + return CONFD_OK; } data = nb_node->cbs.get_elem(xpath, list_entry); @@ -543,7 +578,8 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, struct nb_node *nb_node; char xpath[BUFSIZ]; struct yang_list_keys keys; - const void *nb_next; + struct yang_data *data; + const void *parent_list_entry, *nb_next; confd_value_t v[LIST_MAXKEYS]; frr_confd_get_xpath(kp, xpath, sizeof(xpath)); @@ -556,24 +592,51 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, return CONFD_OK; } - nb_next = nb_node->cbs.get_next(xpath, - (next == -1) ? NULL : (void *)next); - if (!nb_next) { - /* End of the list. */ - confd_data_reply_next_key(tctx, NULL, -1, -1); - return CONFD_OK; - } - if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, "%s: failed to get list keys", - __func__); + if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) + != 0) { + /* List entry doesn't exist anymore. */ confd_data_reply_next_key(tctx, NULL, -1, -1); return CONFD_OK; } - /* Feed keys to ConfD. */ - for (size_t i = 0; i < keys.num; i++) - CONFD_SET_STR(&v[i], keys.key[i].value); - confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next); + nb_next = nb_node->cbs.get_next(parent_list_entry, + (next == -1) ? NULL : (void *)next); + if (!nb_next) { + /* End of the list or leaf-list. */ + confd_data_reply_next_key(tctx, NULL, -1, -1); + return CONFD_OK; + } + + switch (nb_node->snode->nodetype) { + case LYS_LIST: + memset(&keys, 0, sizeof(keys)); + if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) { + flog_warn(EC_LIB_NB_CB_STATE, + "%s: failed to get list keys", __func__); + confd_data_reply_next_key(tctx, NULL, -1, -1); + return CONFD_OK; + } + + /* Feed keys to ConfD. */ + for (size_t i = 0; i < keys.num; i++) + CONFD_SET_STR(&v[i], keys.key[i]); + confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next); + break; + case LYS_LEAFLIST: + data = nb_node->cbs.get_elem(xpath, nb_next); + if (data) { + if (data->value) { + CONFD_SET_STR(&v[0], data->value); + confd_data_reply_next_key(tctx, v, 1, + (long)nb_next); + } + yang_data_free(data); + } else + confd_data_reply_next_key(tctx, NULL, -1, -1); + break; + default: + break; + } return CONFD_OK; } @@ -585,15 +648,14 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp) { struct nb_node *nb_node; + const struct lys_node *child; char xpath[BUFSIZ]; - char xpath_children[XPATH_MAXLEN]; char xpath_child[XPATH_MAXLEN]; - struct yang_list_keys keys; struct list *elements; struct yang_data *data; const void *list_entry; - struct ly_set *set; - confd_value_t *values; + confd_value_t values[CONFD_MAX_CHILD_NODES]; + size_t nvalues = 0; frr_confd_get_xpath(kp, xpath, sizeof(xpath)); @@ -602,57 +664,53 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); confd_data_reply_not_found(tctx); - return CONFD_OK; + return CONFD_ERR; } - frr_confd_hkeypath_get_keys(kp, &keys); - list_entry = nb_node->cbs.lookup_entry(&keys); - if (!list_entry) { - flog_warn(EC_LIB_NB_CB_STATE, "%s: list entry not found: %s", - __func__, xpath); + if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) { confd_data_reply_not_found(tctx); return CONFD_OK; } - /* Find list child nodes. */ - snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath); - set = lys_find_path(nb_node->snode->module, NULL, xpath_children); - if (!set) { - flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed", - __func__); - return CONFD_ERR; - } - elements = yang_data_list_new(); - values = XMALLOC(MTYPE_CONFD, set->number * sizeof(*values)); /* Loop through list child nodes. */ - for (size_t i = 0; i < set->number; i++) { - struct lys_node *child; - struct nb_node *nb_node_child; + LY_TREE_FOR (nb_node->snode->child, child) { + struct nb_node *nb_node_child = child->priv; + confd_value_t *v; - child = set->set.s[i]; - nb_node_child = child->priv; + if (nvalues > CONFD_MAX_CHILD_NODES) + break; + + v = &values[nvalues++]; + + /* Non-presence containers, lists and leaf-lists. */ + if (!nb_node_child->cbs.get_elem) { + CONFD_SET_NOEXISTS(v); + continue; + } snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, child->name); - data = nb_node_child->cbs.get_elem(xpath_child, list_entry); if (data) { if (data->value) - CONFD_SET_STR(&values[i], data->value); - else - CONFD_SET_NOEXISTS(&values[i]); + CONFD_SET_STR(v, data->value); + else { + /* Presence containers and empty leafs. */ + CONFD_SET_XMLTAG( + v, nb_node_child->confd_hash, + confd_str2hash(nb_node_child->snode + ->module->ns)); + } listnode_add(elements, data); } else - CONFD_SET_NOEXISTS(&values[i]); + CONFD_SET_NOEXISTS(v); } - confd_data_reply_value_array(tctx, values, set->number); + confd_data_reply_value_array(tctx, values, nvalues); /* Release memory. */ - ly_set_free(set); - XFREE(MTYPE_CONFD, values); list_delete(&elements); return CONFD_OK; @@ -665,10 +723,9 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next) { char xpath[BUFSIZ]; - char xpath_children[XPATH_MAXLEN]; struct nb_node *nb_node; - struct ly_set *set; struct list *elements; + const void *parent_list_entry; const void *nb_next; #define CONFD_OBJECTS_PER_TIME 100 struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1]; @@ -684,13 +741,10 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, return CONFD_OK; } - /* Find list child nodes. */ - snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath); - set = lys_find_path(nb_node->snode->module, NULL, xpath_children); - if (!set) { - flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed", - __func__); - return CONFD_ERR; + if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry) + != 0) { + confd_data_reply_next_object_array(tctx, NULL, 0, 0); + return CONFD_OK; } elements = yang_data_list_new(); @@ -699,62 +753,76 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, memset(objects, 0, sizeof(objects)); for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) { struct confd_next_object *object; - struct yang_list_keys keys; + struct lys_node *child; struct yang_data *data; - const void *list_entry; + size_t nvalues = 0; object = &objects[j]; - nb_next = nb_node->cbs.get_next(xpath, nb_next); + nb_next = nb_node->cbs.get_next(parent_list_entry, nb_next); if (!nb_next) /* End of the list. */ break; - if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to get list keys", __func__); - continue; - } object->next = (long)nb_next; - list_entry = nb_node->cbs.lookup_entry(&keys); - if (!list_entry) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to lookup list entry", __func__); - continue; + /* Leaf-lists require special handling. */ + if (nb_node->snode->nodetype == LYS_LEAFLIST) { + object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t)); + data = nb_node->cbs.get_elem(xpath, nb_next); + assert(data && data->value); + CONFD_SET_STR(object->v, data->value); + nvalues++; + listnode_add(elements, data); + goto next; } - object->v = XMALLOC(MTYPE_CONFD, - set->number * sizeof(confd_value_t)); + object->v = + XMALLOC(MTYPE_CONFD, + CONFD_MAX_CHILD_NODES * sizeof(confd_value_t)); /* Loop through list child nodes. */ - for (unsigned int i = 0; i < set->number; i++) { - struct lys_node *child; - struct nb_node *nb_node_child; + LY_TREE_FOR (nb_node->snode->child, child) { + struct nb_node *nb_node_child = child->priv; char xpath_child[XPATH_MAXLEN]; - confd_value_t *v = &object->v[i]; + confd_value_t *v; - child = set->set.s[i]; - nb_node_child = child->priv; + if (nvalues > CONFD_MAX_CHILD_NODES) + break; + + v = &object->v[nvalues++]; + + /* Non-presence containers, lists and leaf-lists. */ + if (!nb_node_child->cbs.get_elem) { + CONFD_SET_NOEXISTS(v); + continue; + } snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, child->name); - data = nb_node_child->cbs.get_elem(xpath_child, - list_entry); + nb_next); if (data) { if (data->value) CONFD_SET_STR(v, data->value); - else - CONFD_SET_NOEXISTS(v); + else { + /* + * Presence containers and empty leafs. + */ + CONFD_SET_XMLTAG( + v, nb_node_child->confd_hash, + confd_str2hash( + nb_node_child->snode + ->module->ns)); + } listnode_add(elements, data); } else CONFD_SET_NOEXISTS(v); } - object->n = set->number; + next: + object->n = nvalues; nobjects++; } - ly_set_free(set); if (nobjects == 0) { confd_data_reply_next_object_array(tctx, NULL, 0, 0); @@ -816,10 +884,18 @@ static int frr_confd_notification_send(const char *xpath, CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash, module->confd_hash); for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) { - struct nb_node *option_arg; + struct nb_node *nb_node_arg; - option_arg = data->snode->priv; - CONFD_SET_TAG_STR(&values[i++], option_arg->confd_hash, + nb_node_arg = nb_node_find(data->xpath); + if (!nb_node_arg) { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, + data->xpath); + XFREE(MTYPE_CONFD, values); + return NB_ERR; + } + + CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash, data->value); } CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash, @@ -921,9 +997,18 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo, listcount(output) * sizeof(*reply)); for (ALL_LIST_ELEMENTS_RO(output, node, data)) { + struct nb_node *nb_node_output; int hash; - hash = confd_str2hash(data->snode->name); + nb_node_output = nb_node_find(data->xpath); + if (!nb_node_output) { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, + data->xpath); + goto exit; + } + + hash = confd_str2hash(nb_node_output->snode->name); CONFD_SET_TAG_STR(&reply[i++], hash, data->value); } confd_action_reply_values(uinfo, reply, listcount(output)); @@ -962,17 +1047,16 @@ static int frr_confd_dp_read(struct thread *thread) return 0; } -static void frr_confd_subscribe_state(const struct lys_node *snode, void *arg1, - void *arg2) +static int frr_confd_subscribe_state(const struct lys_node *snode, void *arg) { struct nb_node *nb_node = snode->priv; - struct confd_data_cbs *data_cbs = arg1; + struct confd_data_cbs *data_cbs = arg; - if (!(snode->flags & LYS_CONFIG_R)) - return; + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) + return YANG_ITER_CONTINUE; /* We only need to subscribe to the root of the state subtrees. */ - if (snode->parent && (snode->parent->flags & LYS_CONFIG_R)) - return; + if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) + return YANG_ITER_CONTINUE; if (debug_northbound) zlog_debug("%s: providing data to '%s' (callpoint %s)", @@ -981,6 +1065,8 @@ static void frr_confd_subscribe_state(const struct lys_node *snode, void *arg1, strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint)); if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK) flog_err_confd("confd_register_data_cb"); + + return YANG_ITER_CONTINUE; } static int frr_confd_init_dp(const char *program_name) @@ -1053,7 +1139,7 @@ static int frr_confd_init_dp(const char *program_name) * Iterate over all loaded YANG modules and subscribe to the paths * referent to state data. */ - yang_all_snodes_iterate(frr_confd_subscribe_state, 0, &data_cbs, NULL); + yang_snodes_iterate_all(frr_confd_subscribe_state, 0, &data_cbs); /* Register notification stream. */ memset(&ncbs, 0, sizeof(ncbs)); @@ -1117,12 +1203,14 @@ static void frr_confd_finish_dp(void) /* ------------ Main ------------ */ -static void frr_confd_calculate_snode_hash(const struct lys_node *snode, - void *arg1, void *arg2) +static int frr_confd_calculate_snode_hash(const struct lys_node *snode, + void *arg) { struct nb_node *nb_node = snode->priv; nb_node->confd_hash = confd_str2hash(snode->name); + + return YANG_ITER_CONTINUE; } static int frr_confd_init(const char *program_name) @@ -1153,7 +1241,7 @@ static int frr_confd_init(const char *program_name) goto error; } - yang_all_snodes_iterate(frr_confd_calculate_snode_hash, 0, NULL, NULL); + yang_snodes_iterate_all(frr_confd_calculate_snode_hash, 0, NULL); hook_register(nb_notification_send, frr_confd_notification_send); diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index f645adede3..ffda4c65d0 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -45,6 +45,7 @@ static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data) { + struct nb_node *nb_node; const struct lys_node *snode; struct lys_node_container *scontainer; struct lys_node_leaf *sleaf; @@ -53,7 +54,15 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data) sr_val_set_xpath(sr_data, frr_data->xpath); - snode = frr_data->snode; + nb_node = nb_node_find(frr_data->xpath); + if (!nb_node) { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, + frr_data->xpath); + return -1; + } + + snode = nb_node->snode; switch (snode->nodetype) { case LYS_CONTAINER: scontainer = (struct lys_node_container *)snode; @@ -290,111 +299,15 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, } } -static void frr_sr_state_get_elem(struct list *elements, - struct nb_node *nb_node, - const void *list_entry, const char *xpath) +static int frr_sr_state_data_iter_cb(const struct lys_node *snode, + struct yang_translator *translator, + struct yang_data *data, void *arg) { - struct yang_data *data; + struct list *elements = arg; - data = nb_node->cbs.get_elem(xpath, list_entry); - if (data) - listnode_add(elements, data); -} + listnode_add(elements, data); -static void frr_sr_state_cb_container(struct list *elements, const char *xpath, - const struct lys_node *snode) -{ - struct lys_node *child; - - LY_TREE_FOR (snode->child, child) { - struct nb_node *nb_node = child->priv; - char xpath_child[XPATH_MAXLEN]; - - if (!nb_operation_is_valid(NB_OP_GET_ELEM, child)) - continue; - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath, - child->name); - - frr_sr_state_get_elem(elements, nb_node, NULL, xpath_child); - } -} - -static void frr_sr_state_cb_list_entry(struct list *elements, - const char *xpath_list, - const void *list_entry, - struct lys_node *child) -{ - struct nb_node *nb_node = child->priv; - struct lys_node_leaf *sleaf; - char xpath_child[XPATH_MAXLEN]; - - /* Sysrepo doesn't want to know about list keys. */ - switch (child->nodetype) { - case LYS_LEAF: - sleaf = (struct lys_node_leaf *)child; - if (lys_is_key(sleaf, NULL)) - return; - break; - case LYS_LEAFLIST: - break; - default: - return; - } - - if (!nb_operation_is_valid(NB_OP_GET_ELEM, child)) - return; - - snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath_list, - child->name); - - frr_sr_state_get_elem(elements, nb_node, list_entry, xpath_child); -} - -static void frr_sr_state_cb_list(struct list *elements, const char *xpath, - const struct lys_node *snode) -{ - struct nb_node *nb_node = snode->priv; - struct lys_node_list *slist = (struct lys_node_list *)snode; - const void *next; - - for (next = nb_node->cbs.get_next(xpath, NULL); next; - next = nb_node->cbs.get_next(xpath, next)) { - struct yang_list_keys keys; - const void *list_entry; - char xpath_list[XPATH_MAXLEN]; - struct lys_node *child; - - /* Get the list keys. */ - if (nb_node->cbs.get_keys(next, &keys) != NB_OK) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to get list keys", __func__); - continue; - } - - /* Get list item. */ - list_entry = nb_node->cbs.lookup_entry(&keys); - if (!list_entry) { - flog_warn(EC_LIB_NB_CB_STATE, - "%s: failed to lookup list entry", __func__); - continue; - } - - /* Append list keys to the XPath. */ - strlcpy(xpath_list, xpath, sizeof(xpath_list)); - for (unsigned int i = 0; i < keys.num; i++) { - snprintf(xpath_list + strlen(xpath_list), - sizeof(xpath_list) - strlen(xpath_list), - "[%s='%s']", slist->keys[i]->name, - keys.key[i].value); - } - - /* Loop through list entries. */ - LY_TREE_FOR (snode->child, child) { - frr_sr_state_cb_list_entry(elements, xpath_list, - list_entry, child); - } - } + return NB_OK; } /* Callback for state retrieval. */ @@ -404,26 +317,20 @@ static int frr_sr_state_cb(const char *xpath, sr_val_t **values, { struct list *elements; struct yang_data *data; - const struct lys_node *snode; struct listnode *node; sr_val_t *v; int ret, count, i = 0; - /* Find schema node. */ - snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0); - elements = yang_data_list_new(); - - switch (snode->nodetype) { - case LYS_CONTAINER: - frr_sr_state_cb_container(elements, xpath, snode); - break; - case LYS_LIST: - frr_sr_state_cb_list(elements, xpath, snode); - break; - default: - break; + if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE, + frr_sr_state_data_iter_cb, elements) + != NB_OK) { + flog_warn(EC_LIB_NB_OPERATIONAL_DATA, + "%s: failed to obtain operational data [xpath %s]", + __func__, xpath); + goto exit; } + if (list_isempty(elements)) goto exit; @@ -723,18 +630,17 @@ static void frr_sr_subscribe_config(struct yang_module *module) sr_strerror(ret)); } -static void frr_sr_subscribe_state(const struct lys_node *snode, void *arg1, - void *arg2) +static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) { - struct yang_module *module = arg1; + struct yang_module *module = arg; struct nb_node *nb_node; int ret; - if (!(snode->flags & LYS_CONFIG_R)) - return; + if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R)) + return YANG_ITER_CONTINUE; /* We only need to subscribe to the root of the state subtrees. */ - if (snode->parent && (snode->parent->flags & LYS_CONFIG_R)) - return; + if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R)) + return YANG_ITER_CONTINUE; nb_node = snode->priv; if (debug_northbound) @@ -747,17 +653,18 @@ static void frr_sr_subscribe_state(const struct lys_node *snode, void *arg1, if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s", sr_strerror(ret)); + + return YANG_ITER_CONTINUE; } -static void frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg1, - void *arg2) +static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) { - struct yang_module *module = arg1; + struct yang_module *module = arg; struct nb_node *nb_node; int ret; if (snode->nodetype != LYS_RPC) - return; + return YANG_ITER_CONTINUE; nb_node = snode->priv; if (debug_northbound) @@ -770,17 +677,18 @@ static void frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg1, if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", sr_strerror(ret)); + + return YANG_ITER_CONTINUE; } -static void frr_sr_subscribe_action(const struct lys_node *snode, void *arg1, - void *arg2) +static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg) { - struct yang_module *module = arg1; + struct yang_module *module = arg; struct nb_node *nb_node; int ret; if (snode->nodetype != LYS_ACTION) - return; + return YANG_ITER_CONTINUE; nb_node = snode->priv; if (debug_northbound) @@ -793,6 +701,8 @@ static void frr_sr_subscribe_action(const struct lys_node *snode, void *arg1, if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s", sr_strerror(ret)); + + return YANG_ITER_CONTINUE; } /* FRR's Sysrepo initialization. */ @@ -830,12 +740,12 @@ static int frr_sr_init(const char *program_name) /* Perform subscriptions. */ RB_FOREACH (module, yang_modules, &yang_modules) { frr_sr_subscribe_config(module); - yang_module_snodes_iterate(module->info, frr_sr_subscribe_state, - 0, module, NULL); - yang_module_snodes_iterate(module->info, frr_sr_subscribe_rpc, - 0, module, NULL); - yang_module_snodes_iterate( - module->info, frr_sr_subscribe_action, 0, module, NULL); + yang_snodes_iterate_module(module->info, frr_sr_subscribe_state, + 0, module); + yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc, + 0, module); + yang_snodes_iterate_module(module->info, + frr_sr_subscribe_action, 0, module); } hook_register(nb_notification_send, frr_sr_notification_send); diff --git a/lib/yang.c b/lib/yang.c index 757982d367..462e693549 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -71,6 +71,11 @@ static const char *yang_module_imp_clb(const char *mod_name, return NULL; } +static const char * const frr_native_modules[] = { + "frr-interface", + "frr-ripd", +}; + /* Generate the yang_modules tree. */ static inline int yang_module_compare(const struct yang_module *a, const struct yang_module *b) @@ -108,6 +113,12 @@ struct yang_module *yang_module_load(const char *module_name) return module; } +void yang_module_load_all(void) +{ + for (size_t i = 0; i < array_size(frr_native_modules); i++) + yang_module_load(frr_native_modules[i]); +} + struct yang_module *yang_module_find(const char *module_name) { struct yang_module s; @@ -116,23 +127,18 @@ struct yang_module *yang_module_find(const char *module_name) return RB_FIND(yang_modules, &yang_modules, &s); } -/* - * Helper function for yang_module_snodes_iterate() and - * yang_all_snodes_iterate(). This is a recursive function. - */ -static void yang_snodes_iterate(const struct lys_node *snode, - void (*func)(const struct lys_node *, void *, - void *), - uint16_t flags, void *arg1, void *arg2) +int yang_snodes_iterate_subtree(const struct lys_node *snode, + yang_iterate_cb cb, uint16_t flags, void *arg) { struct lys_node *child; + int ret = YANG_ITER_CONTINUE; if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) { switch (snode->nodetype) { case LYS_CASE: case LYS_INPUT: case LYS_OUTPUT: - if (snode->flags & LYS_IMPLICIT) + if (CHECK_FLAG(snode->flags, LYS_IMPLICIT)) goto next; break; default: @@ -162,7 +168,7 @@ static void yang_snodes_iterate(const struct lys_node *snode, break; case LYS_GROUPING: /* Return since we're not interested in the grouping subtree. */ - return; + return YANG_ITER_CONTINUE; case LYS_USES: case LYS_AUGMENT: /* Always ignore nodes of these types. */ @@ -176,50 +182,66 @@ static void yang_snodes_iterate(const struct lys_node *snode, break; } - (*func)(snode, arg1, arg2); + ret = (*cb)(snode, arg); + if (ret == YANG_ITER_STOP) + return ret; next: /* * YANG leafs and leaf-lists can't have child nodes, and trying to * access snode->child is undefined behavior. */ - if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) - return; + if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST)) + return YANG_ITER_CONTINUE; LY_TREE_FOR (snode->child, child) { - if (child->parent != snode) + if (!CHECK_FLAG(flags, YANG_ITER_ALLOW_AUGMENTATIONS) + && child->parent != snode) continue; - yang_snodes_iterate(child, func, flags, arg1, arg2); + + ret = yang_snodes_iterate_subtree(child, cb, flags, arg); + if (ret == YANG_ITER_STOP) + return ret; } + + return ret; } -void yang_module_snodes_iterate(const struct lys_module *module, - void (*func)(const struct lys_node *, void *, - void *), - uint16_t flags, void *arg1, void *arg2) +int yang_snodes_iterate_module(const struct lys_module *module, + yang_iterate_cb cb, uint16_t flags, void *arg) { struct lys_node *snode; + int ret = YANG_ITER_CONTINUE; LY_TREE_FOR (module->data, snode) { - yang_snodes_iterate(snode, func, flags, arg1, arg2); + ret = yang_snodes_iterate_subtree(snode, cb, flags, arg); + if (ret == YANG_ITER_STOP) + return ret; } for (uint8_t i = 0; i < module->augment_size; i++) { - yang_snodes_iterate( - (const struct lys_node *)&module->augment[i], func, - flags, arg1, arg2); + ret = yang_snodes_iterate_subtree( + (const struct lys_node *)&module->augment[i], cb, flags, + arg); + if (ret == YANG_ITER_STOP) + return ret; } + + return ret; } -void yang_all_snodes_iterate(void (*func)(const struct lys_node *, void *, - void *), - uint16_t flags, void *arg1, void *arg2) +int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg) { struct yang_module *module; + int ret = YANG_ITER_CONTINUE; - RB_FOREACH (module, yang_modules, &yang_modules) - yang_module_snodes_iterate(module->info, func, flags, arg1, - arg2); + RB_FOREACH (module, yang_modules, &yang_modules) { + ret = yang_snodes_iterate_module(module->info, cb, flags, arg); + if (ret == YANG_ITER_STOP) + return ret; + } + + return ret; } void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type, @@ -324,7 +346,7 @@ const struct lys_type *yang_snode_get_type(const struct lys_node *snode) struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode; struct lys_type *type; - if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) + if (!CHECK_FLAG(sleaf->nodetype, LYS_LEAF | LYS_LEAFLIST)) return NULL; type = &sleaf->type; @@ -463,7 +485,7 @@ bool yang_dnode_is_default_recursive(const struct lyd_node *dnode) struct lyd_node *root, *next, *dnode_iter; snode = dnode->schema; - if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) + if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST)) return yang_dnode_is_default(dnode, NULL); if (!yang_dnode_is_default(dnode, NULL)) @@ -489,7 +511,7 @@ void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value) void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry) { - assert(dnode->schema->nodetype & (LYS_LIST | LYS_CONTAINER)); + assert(CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)); lyd_set_private(dnode, entry); } @@ -523,12 +545,18 @@ void *yang_dnode_get_entry(const struct lyd_node *dnode, abort(); } -struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx) +struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only) { struct lyd_node *dnode; + int options; + + if (config_only) + options = LYD_OPT_CONFIG; + else + options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; dnode = NULL; - if (lyd_validate(&dnode, LYD_OPT_CONFIG, ly_ctx) != 0) { + if (lyd_validate(&dnode, options, ly_ctx) != 0) { /* Should never happen. */ flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__); exit(1); @@ -544,27 +572,17 @@ struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode) void yang_dnode_free(struct lyd_node *dnode) { + while (dnode->parent) + dnode = dnode->parent; lyd_free_withsiblings(dnode); } struct yang_data *yang_data_new(const char *xpath, const char *value) { - const struct lys_node *snode; struct yang_data *data; - snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0); - if (!snode) - snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 1); - if (!snode) { - flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - zlog_backtrace(LOG_ERR); - abort(); - } - data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data)); strlcpy(data->xpath, xpath, sizeof(data->xpath)); - data->snode = snode; if (value) data->value = strdup(value); diff --git a/lib/yang.h b/lib/yang.h index c920060071..3259189e98 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -69,12 +69,6 @@ struct yang_data { /* XPath identifier of the data element. */ char xpath[XPATH_MAXLEN]; - /* - * Schema information (necessary to interpret certain values like - * enums). - */ - const struct lys_node *snode; - /* Value encoded as a raw string. */ char *value; }; @@ -83,16 +77,8 @@ struct yang_list_keys { /* Number os keys (max: LIST_MAXKEYS). */ uint8_t num; - struct { - /* - * Schema information (necessary to interpret certain values - * like enums). - */ - struct lys_node *snode; - - /* Value encoded as a raw string. */ - char value[LIST_MAXKEYLEN]; - } key[LIST_MAXKEYS]; + /* Value encoded as a raw string. */ + char key[LIST_MAXKEYS][LIST_MAXKEYLEN]; }; enum yang_path_type { @@ -100,14 +86,29 @@ enum yang_path_type { YANG_PATH_DATA, }; -/* Filter non-presence containers. */ -#define YANG_ITER_FILTER_NPCONTAINERS 0x0001 -/* Filter list keys (leafs). */ -#define YANG_ITER_FILTER_LIST_KEYS 0x0002 -/* Filter RPC input/output nodes. */ -#define YANG_ITER_FILTER_INPUT_OUTPUT 0x0004 -/* Filter implicitely created nodes. */ -#define YANG_ITER_FILTER_IMPLICIT 0x0008 +enum yang_iter_flags { + /* Filter non-presence containers. */ + YANG_ITER_FILTER_NPCONTAINERS = (1<<0), + + /* Filter list keys (leafs). */ + YANG_ITER_FILTER_LIST_KEYS = (1<<1), + + /* Filter RPC input/output nodes. */ + YANG_ITER_FILTER_INPUT_OUTPUT = (1<<2), + + /* Filter implicitely created nodes. */ + YANG_ITER_FILTER_IMPLICIT = (1<<3), + + /* Allow iteration over augmentations. */ + YANG_ITER_ALLOW_AUGMENTATIONS = (1<<4), +}; + +/* Callback used by the yang_snodes_iterate_*() family of functions. */ +typedef int (*yang_iterate_cb)(const struct lys_node *snode, void *arg); + +/* Return values of the 'yang_iterate_cb' callback. */ +#define YANG_ITER_CONTINUE 0 +#define YANG_ITER_STOP -1 /* Global libyang context for native FRR models. */ extern struct ly_ctx *ly_native_ctx; @@ -128,6 +129,11 @@ extern struct yang_modules yang_modules; */ extern struct yang_module *yang_module_load(const char *module_name); +/* + * Load all FRR native YANG models. + */ +extern void yang_module_load_all(void); + /* * Find a YANG module by its name. * @@ -149,47 +155,67 @@ extern struct yang_module *yang_module_find(const char *module_name); */ extern void yang_module_embed(struct yang_module_embed *embed); +/* + * Iterate recursively over all children of a schema node. + * + * snode + * YANG schema node to operate on. + * + * cb + * Function to call with each schema node. + * + * flags + * YANG_ITER_* flags to control how the iteration is performed. + * + * arg + * Arbitrary argument passed as the second parameter in each call to 'cb'. + * + * Returns: + * The return value of the last called callback. + */ +extern int yang_snodes_iterate_subtree(const struct lys_node *snode, + yang_iterate_cb cb, uint16_t flags, + void *arg); + /* * Iterate over all libyang schema nodes from the given YANG module. * * module * YANG module to operate on. * - * func + * cb * Function to call with each schema node. * * flags - * YANG_ITER_FILTER_* flags to specify node types that should be filtered. + * YANG_ITER_* flags to control how the iteration is performed. * - * arg1 - * Arbitrary argument passed as the second parameter in each call to 'func'. + * arg + * Arbitrary argument passed as the second parameter in each call to 'cb'. * - * arg2 - * Arbitrary argument passed as the third parameter in each call to 'func'. + * Returns: + * The return value of the last called callback. */ -extern void yang_module_snodes_iterate(const struct lys_module *module, - void (*func)(const struct lys_node *, - void *, void *), - uint16_t flags, void *arg1, void *arg2); +extern int yang_snodes_iterate_module(const struct lys_module *module, + yang_iterate_cb cb, uint16_t flags, + void *arg); /* * Iterate over all libyang schema nodes from all loaded YANG modules. * - * func + * cb * Function to call with each schema node. * * flags - * YANG_ITER_FILTER_* flags to specify node types that should be filtered. + * YANG_ITER_* flags to control how the iteration is performed. * - * arg1 - * Arbitrary argument passed as the second parameter in each call to 'func'. + * arg + * Arbitrary argument passed as the second parameter in each call to 'cb'. * - * arg2 - * Arbitrary argument passed as the third parameter in each call to 'func'. + * Returns: + * The return value of the last called callback. */ -extern void yang_all_snodes_iterate(void (*func)(const struct lys_node *, - void *, void *), - uint16_t flags, void *arg1, void *arg2); +extern int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, + void *arg); /* * Build schema path or data path of the schema node. @@ -423,10 +449,14 @@ extern void *yang_dnode_get_entry(const struct lyd_node *dnode, * ly_ctx * libyang context to operate on. * + * config + * Specify whether the data node will contain only configuration data (true) + * or both configuration data and state data (false). + * * Returns: * Pointer to newly created libyang data node. */ -extern struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx); +extern struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only); /* * Duplicate a libyang data node. diff --git a/lib/yang_translator.c b/lib/yang_translator.c index 02da3ebd6a..c3092e56e5 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -351,7 +351,7 @@ int yang_translate_dnode(const struct yang_translator *translator, int dir, ly_ctx = ly_native_ctx; else ly_ctx = translator->ly_ctx; - new = yang_dnode_new(ly_ctx); + new = yang_dnode_new(ly_ctx, false); /* Iterate over all nodes from the data tree. */ LY_TREE_FOR (*dnode, root) { @@ -400,24 +400,28 @@ error: return YANG_TRANSLATE_FAILURE; } -static void yang_translator_validate_cb(const struct lys_node *snode_custom, - void *arg1, void *arg2) +struct translator_validate_args { + struct yang_translator *translator; + unsigned int errors; +}; + +static int yang_translator_validate_cb(const struct lys_node *snode_custom, + void *arg) { - struct yang_translator *translator = arg1; - unsigned int *errors = arg2; + struct translator_validate_args *args = arg; struct yang_mapping_node *mapping; const struct lys_node *snode_native; const struct lys_type *stype_custom, *stype_native; char xpath[XPATH_MAXLEN]; yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath)); - mapping = yang_mapping_lookup(translator, YANG_TRANSLATE_TO_NATIVE, - xpath); + mapping = yang_mapping_lookup(args->translator, + YANG_TRANSLATE_TO_NATIVE, xpath); if (!mapping) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, "%s: missing mapping for \"%s\"", __func__, xpath); - *errors += 1; - return; + args->errors += 1; + return YANG_ITER_CONTINUE; } snode_native = @@ -433,12 +437,14 @@ static void yang_translator_validate_cb(const struct lys_node *snode_custom, EC_LIB_YANG_TRANSLATOR_LOAD, "%s: YANG types are incompatible (xpath: \"%s\")", __func__, xpath); - *errors += 1; - return; + args->errors += 1; + return YANG_ITER_CONTINUE; } /* TODO: check if the value spaces are identical. */ } + + return YANG_ITER_CONTINUE; } /* @@ -449,32 +455,36 @@ static unsigned int yang_translator_validate(struct yang_translator *translator) { struct yang_tmodule *tmodule; struct listnode *ln; - unsigned int errors = 0; + struct translator_validate_args args; + + args.translator = translator; + args.errors = 0; for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) { - yang_module_snodes_iterate( + yang_snodes_iterate_module( tmodule->module, yang_translator_validate_cb, YANG_ITER_FILTER_NPCONTAINERS | YANG_ITER_FILTER_LIST_KEYS | YANG_ITER_FILTER_INPUT_OUTPUT, - translator, &errors); + &args); } - if (errors) + if (args.errors) flog_warn( EC_LIB_YANG_TRANSLATOR_LOAD, "%s: failed to validate \"%s\" module translator: %u error(s)", - __func__, translator->family, errors); + __func__, translator->family, args.errors); - return errors; + return args.errors; } -static void yang_module_nodes_count_cb(const struct lys_node *snode, void *arg1, - void *arg2) +static int yang_module_nodes_count_cb(const struct lys_node *snode, void *arg) { - unsigned int *total = arg1; + unsigned int *total = arg; *total += 1; + + return YANG_ITER_CONTINUE; } /* Calculate the number of nodes for the given module. */ @@ -482,11 +492,11 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module) { unsigned int total = 0; - yang_module_snodes_iterate(module, yang_module_nodes_count_cb, + yang_snodes_iterate_module(module, yang_module_nodes_count_cb, YANG_ITER_FILTER_NPCONTAINERS | YANG_ITER_FILTER_LIST_KEYS | YANG_ITER_FILTER_INPUT_OUTPUT, - &total, NULL); + &total); return total; } diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index da9d37669b..96076d6468 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -840,7 +840,7 @@ void yang_str2ipv4p(const char *value, union prefixptr prefix) } struct yang_data *yang_data_new_ipv4p(const char *xpath, - const union prefixptr prefix) + union prefixconstptr prefix) { char value_str[PREFIX2STR_BUFFER]; @@ -950,7 +950,7 @@ void yang_str2ipv6p(const char *value, union prefixptr prefix) } struct yang_data *yang_data_new_ipv6p(const char *xpath, - const union prefixptr prefix) + union prefixconstptr prefix) { char value_str[PREFIX2STR_BUFFER]; diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 08a263bac4..5203a033ad 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -127,7 +127,7 @@ extern void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, /* ipv4p */ extern void yang_str2ipv4p(const char *value, union prefixptr prefix); extern struct yang_data *yang_data_new_ipv4p(const char *xpath, - const union prefixptr prefix); + union prefixconstptr prefix); extern void yang_dnode_get_ipv4p(union prefixptr prefix, const struct lyd_node *dnode, const char *xpath_fmt, ...); @@ -147,7 +147,7 @@ extern void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt, /* ipv6p */ extern void yang_str2ipv6p(const char *value, union prefixptr prefix); extern struct yang_data *yang_data_new_ipv6p(const char *xpath, - const union prefixptr prefix); + union prefixconstptr prefix); extern void yang_dnode_get_ipv6p(union prefixptr prefix, const struct lyd_node *dnode, const char *xpath_fmt, ...); diff --git a/ripd/rip_northbound.c b/ripd/rip_northbound.c index d1e298c25c..421b0afe38 100644 --- a/ripd/rip_northbound.c +++ b/ripd/rip_northbound.c @@ -1003,7 +1003,7 @@ lib_interface_rip_authentication_key_chain_delete(enum nb_event event, * XPath: /frr-ripd:ripd/state/neighbors/neighbor */ static const void * -ripd_state_neighbors_neighbor_get_next(const char *xpath, +ripd_state_neighbors_neighbor_get_next(const void *parent_list_entry, const void *list_entry) { struct listnode *node; @@ -1023,20 +1023,28 @@ static int ripd_state_neighbors_neighbor_get_keys(const void *list_entry, const struct rip_peer *peer = listgetdata(node); keys->num = 1; - (void)inet_ntop(AF_INET, &peer->addr, keys->key[0].value, - sizeof(keys->key[0].value)); + (void)inet_ntop(AF_INET, &peer->addr, keys->key[0], + sizeof(keys->key[0])); return NB_OK; } static const void * -ripd_state_neighbors_neighbor_lookup_entry(const struct yang_list_keys *keys) +ripd_state_neighbors_neighbor_lookup_entry(const void *parent_list_entry, + const struct yang_list_keys *keys) { struct in_addr address; + struct rip_peer *peer; + struct listnode *node; - yang_str2ipv4(keys->key[0].value, &address); + yang_str2ipv4(keys->key[0], &address); - return rip_peer_lookup(&address); + for (ALL_LIST_ELEMENTS_RO(peer_list, node, peer)) { + if (IPV4_ADDR_SAME(&peer->addr, &address)) + return node; + } + + return NULL; } /* @@ -1046,7 +1054,8 @@ static struct yang_data * ripd_state_neighbors_neighbor_address_get_elem(const char *xpath, const void *list_entry) { - const struct rip_peer *peer = list_entry; + const struct listnode *node = list_entry; + const struct rip_peer *peer = listgetdata(node); return yang_data_new_ipv4(xpath, &peer->addr); } @@ -1069,7 +1078,8 @@ static struct yang_data * ripd_state_neighbors_neighbor_bad_packets_rcvd_get_elem(const char *xpath, const void *list_entry) { - const struct rip_peer *peer = list_entry; + const struct listnode *node = list_entry; + const struct rip_peer *peer = listgetdata(node); return yang_data_new_uint32(xpath, peer->recv_badpackets); } @@ -1081,7 +1091,8 @@ static struct yang_data * ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem(const char *xpath, const void *list_entry) { - const struct rip_peer *peer = list_entry; + const struct listnode *node = list_entry; + const struct rip_peer *peer = listgetdata(node); return yang_data_new_uint32(xpath, peer->recv_badroutes); } @@ -1089,8 +1100,9 @@ ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem(const char *xpath, /* * XPath: /frr-ripd:ripd/state/routes/route */ -static const void *ripd_state_routes_route_get_next(const char *xpath, - const void *list_entry) +static const void * +ripd_state_routes_route_get_next(const void *parent_list_entry, + const void *list_entry) { struct route_node *rn; @@ -1113,19 +1125,19 @@ static int ripd_state_routes_route_get_keys(const void *list_entry, const struct route_node *rn = list_entry; keys->num = 1; - (void)prefix2str(&rn->p, keys->key[0].value, - sizeof(keys->key[0].value)); + (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0])); return NB_OK; } static const void * -ripd_state_routes_route_lookup_entry(const struct yang_list_keys *keys) +ripd_state_routes_route_lookup_entry(const void *parent_list_entry, + const struct yang_list_keys *keys) { struct prefix prefix; struct route_node *rn; - yang_str2ipv4p(keys->key[0].value, &prefix); + yang_str2ipv4p(keys->key[0], &prefix); rn = route_node_lookup(rip->table, &prefix); if (!rn || !rn->info) @@ -1133,10 +1145,7 @@ ripd_state_routes_route_lookup_entry(const struct yang_list_keys *keys) route_unlock_node(rn); - /* - * TODO: we need to handle ECMP properly. - */ - return listnode_head(rn->info); + return rn; } /* @@ -1146,7 +1155,8 @@ static struct yang_data * ripd_state_routes_route_prefix_get_elem(const char *xpath, const void *list_entry) { - const struct rip_info *rinfo = list_entry; + const struct route_node *rn = list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); return yang_data_new_ipv4p(xpath, &rinfo->rp->p); } @@ -1158,7 +1168,8 @@ static struct yang_data * ripd_state_routes_route_next_hop_get_elem(const char *xpath, const void *list_entry) { - const struct rip_info *rinfo = list_entry; + const struct route_node *rn = list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); switch (rinfo->nh.type) { case NEXTHOP_TYPE_IPV4: @@ -1176,7 +1187,8 @@ static struct yang_data * ripd_state_routes_route_interface_get_elem(const char *xpath, const void *list_entry) { - const struct rip_info *rinfo = list_entry; + const struct route_node *rn = list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); switch (rinfo->nh.type) { case NEXTHOP_TYPE_IFINDEX: @@ -1195,7 +1207,8 @@ static struct yang_data * ripd_state_routes_route_metric_get_elem(const char *xpath, const void *list_entry) { - const struct rip_info *rinfo = list_entry; + const struct route_node *rn = list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); return yang_data_new_uint8(xpath, rinfo->metric); } diff --git a/tests/.gitignore b/tests/.gitignore index a6202786be..5453c0d80a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -18,6 +18,7 @@ /lib/cli/test_cli_clippy.c /lib/cli/test_commands /lib/cli/test_commands_defun.c +/lib/northbound/test_oper_data /lib/test_buffer /lib/test_checksum /lib/test_graph diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c new file mode 100644 index 0000000000..a9a89ee491 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "memory_vty.h" +#include "log.h" +#include "northbound.h" + +static struct thread_master *master; + +struct troute { + struct prefix_ipv4 prefix; + struct in_addr nexthop; + char ifname[IFNAMSIZ]; + uint8_t metric; + bool active; +}; + +struct tvrf { + char name[32]; + struct list *interfaces; + struct list *routes; +}; + +static struct list *vrfs; + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf + */ +static const void * +frr_test_module_vrfs_vrf_get_next(const void *parent_list_entry, + const void *list_entry) +{ + struct listnode *node; + + if (list_entry == NULL) + node = listhead(vrfs); + else + node = listnextnode((struct listnode *)list_entry); + + return node; +} + +static int frr_test_module_vrfs_vrf_get_keys(const void *list_entry, + struct yang_list_keys *keys) +{ + const struct tvrf *vrf; + + vrf = listgetdata((struct listnode *)list_entry); + + keys->num = 1; + strlcpy(keys->key[0], vrf->name, sizeof(keys->key[0])); + + return NB_OK; +} + +static const void * +frr_test_module_vrfs_vrf_lookup_entry(const void *parent_list_entry, + const struct yang_list_keys *keys) +{ + struct listnode *node; + struct tvrf *vrf; + const char *vrfname; + + vrfname = keys->key[0]; + + for (ALL_LIST_ELEMENTS_RO(vrfs, node, vrf)) { + if (strmatch(vrf->name, vrfname)) + return node; + } + + return NULL; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/name + */ +static struct yang_data * +frr_test_module_vrfs_vrf_name_get_elem(const char *xpath, + const void *list_entry) +{ + const struct tvrf *vrf; + + vrf = listgetdata((struct listnode *)list_entry); + return yang_data_new_string(xpath, vrf->name); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface + */ +static struct yang_data * +frr_test_module_vrfs_vrf_interfaces_interface_get_elem(const char *xpath, + const void *list_entry) +{ + const char *interface; + + interface = listgetdata((struct listnode *)list_entry); + return yang_data_new_string(xpath, interface); +} + +static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next( + const void *parent_list_entry, const void *list_entry) +{ + const struct tvrf *vrf; + struct listnode *node; + + vrf = listgetdata((struct listnode *)parent_list_entry); + if (list_entry == NULL) + node = listhead(vrf->interfaces); + else + node = listnextnode((struct listnode *)list_entry); + + return node; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route + */ +static const void * +frr_test_module_vrfs_vrf_routes_route_get_next(const void *parent_list_entry, + const void *list_entry) +{ + const struct tvrf *vrf; + struct listnode *node; + + vrf = listgetdata((struct listnode *)parent_list_entry); + if (list_entry == NULL) + node = listhead(vrf->routes); + else + node = listnextnode((struct listnode *)list_entry); + + return node; +} + +static int +frr_test_module_vrfs_vrf_routes_route_get_keys(const void *list_entry, + struct yang_list_keys *keys) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + + keys->num = 1; + (void)prefix2str(&route->prefix, keys->key[0], sizeof(keys->key[0])); + + return NB_OK; +} + +static const void *frr_test_module_vrfs_vrf_routes_route_lookup_entry( + const void *parent_list_entry, const struct yang_list_keys *keys) +{ + const struct tvrf *vrf; + const struct troute *route; + struct listnode *node; + struct prefix prefix; + + yang_str2ipv4p(keys->key[0], &prefix); + + vrf = listgetdata((struct listnode *)parent_list_entry); + for (ALL_LIST_ELEMENTS_RO(vrf->routes, node, route)) { + if (prefix_same((struct prefix *)&route->prefix, &prefix)) + return node; + } + + return NULL; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_prefix_get_elem(const char *xpath, + const void *list_entry) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + return yang_data_new_ipv4p(xpath, &route->prefix); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem(const char *xpath, + const void *list_entry) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + return yang_data_new_ipv4(xpath, &route->nexthop); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_interface_get_elem(const char *xpath, + const void *list_entry) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + return yang_data_new_string(xpath, route->ifname); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_metric_get_elem(const char *xpath, + const void *list_entry) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + return yang_data_new_uint8(xpath, route->metric); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/active + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_active_get_elem(const char *xpath, + const void *list_entry) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)list_entry); + if (route->active) + return yang_data_new(xpath, NULL); + + return NULL; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_test_module_info = { + .name = "frr-test-module", + .nodes = { + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf", + .cbs.get_next = frr_test_module_vrfs_vrf_get_next, + .cbs.get_keys = frr_test_module_vrfs_vrf_get_keys, + .cbs.lookup_entry = frr_test_module_vrfs_vrf_lookup_entry, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/name", + .cbs.get_elem = frr_test_module_vrfs_vrf_name_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface", + .cbs.get_elem = frr_test_module_vrfs_vrf_interfaces_interface_get_elem, + .cbs.get_next = frr_test_module_vrfs_vrf_interfaces_interface_get_next, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route", + .cbs.get_next = frr_test_module_vrfs_vrf_routes_route_get_next, + .cbs.get_keys = frr_test_module_vrfs_vrf_routes_route_get_keys, + .cbs.lookup_entry = frr_test_module_vrfs_vrf_routes_route_lookup_entry, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_prefix_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_interface_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_metric_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem, + }, + { + .xpath = NULL, + }, + } +}; +/* clang-format on */ + +static const struct frr_yang_module_info *modules[] = { + &frr_test_module_info, +}; + +static void create_data(unsigned int num_vrfs, unsigned int num_interfaces, + unsigned int num_routes) +{ + struct prefix_ipv4 base_prefix; + struct in_addr base_nexthop; + + (void)str2prefix_ipv4("10.0.0.0/32", &base_prefix); + (void)inet_pton(AF_INET, "172.16.0.0", &base_nexthop); + + vrfs = list_new(); + + /* Create VRFs. */ + for (unsigned int i = 0; i < num_vrfs; i++) { + struct tvrf *vrf; + + vrf = XCALLOC(MTYPE_TMP, sizeof(*vrf)); + snprintf(vrf->name, sizeof(vrf->name), "vrf%u", i); + vrf->interfaces = list_new(); + vrf->routes = list_new(); + + /* Create interfaces. */ + for (unsigned int j = 0; j < num_interfaces; j++) { + char ifname[32]; + char *interface; + + snprintf(ifname, sizeof(ifname), "eth%u", j); + interface = XSTRDUP(MTYPE_TMP, ifname); + listnode_add(vrf->interfaces, interface); + } + + /* Create routes. */ + for (unsigned int j = 0; j < num_routes; j++) { + struct troute *route; + + route = XCALLOC(MTYPE_TMP, sizeof(*route)); + + memcpy(&route->prefix, &base_prefix, + sizeof(route->prefix)); + route->prefix.prefix.s_addr = + htonl(ntohl(route->prefix.prefix.s_addr) + j); + + memcpy(&route->nexthop, &base_nexthop, + sizeof(route->nexthop)); + route->nexthop.s_addr = + htonl(ntohl(route->nexthop.s_addr) + j); + + snprintf(route->ifname, sizeof(route->ifname), "eth%u", + j); + route->metric = j % 256; + route->active = (j % 2 == 0); + listnode_add(vrf->routes, route); + } + + listnode_add(vrfs, vrf); + } +} + +static void interface_delete(void *ptr) +{ + char *interface = ptr; + + XFREE(MTYPE_TMP, interface); +} + +static void route_delete(void *ptr) +{ + struct troute *route = ptr; + + XFREE(MTYPE_TMP, route); +} + +static void vrf_delete(void *ptr) +{ + struct tvrf *vrf = ptr; + + vrf->interfaces->del = interface_delete; + list_delete(&vrf->interfaces); + vrf->routes->del = route_delete; + list_delete(&vrf->routes); + XFREE(MTYPE_TMP, vrf); +} + +static void delete_data(void) +{ + vrfs->del = vrf_delete; + list_delete(&vrfs); +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + delete_data(); + + cmd_terminate(); + vty_terminate(); + nb_terminate(); + yang_terminate(); + thread_master_free(master); + closezlog(); + + log_memstats(stderr, "test-nb-oper-data"); + if (!isexit) + exit(0); +} + +/* main routine. */ +int main(int argc, char **argv) +{ + struct thread thread; + unsigned int num_vrfs = 2; + unsigned int num_interfaces = 4; + unsigned int num_routes = 6; + + if (argc > 1) + num_vrfs = atoi(argv[1]); + if (argc > 2) + num_interfaces = atoi(argv[2]); + if (argc > 3) + num_routes = atoi(argv[3]); + + /* Set umask before anything for security */ + umask(0027); + + /* master init. */ + master = thread_master_create(NULL); + + openzlog("test-nb-oper-data", "NONE", 0, + LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED); + zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master); + memory_init(); + yang_init(); + nb_init(modules, array_size(modules)); + + /* Create artificial data. */ + create_data(num_vrfs, num_interfaces, num_routes); + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/lib/northbound/test_oper_data.in b/tests/lib/northbound/test_oper_data.in new file mode 100644 index 0000000000..a6c4f874f5 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.in @@ -0,0 +1 @@ +show yang operational-data /frr-test-module:frr-test-module diff --git a/tests/lib/northbound/test_oper_data.py b/tests/lib/northbound/test_oper_data.py new file mode 100644 index 0000000000..8f5fdd6fd0 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.py @@ -0,0 +1,4 @@ +import frrtest + +class TestNbOperData(frrtest.TestRefOut): + program = './test_oper_data' diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout new file mode 100644 index 0000000000..57ecd2f0a0 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.refout @@ -0,0 +1,119 @@ +test# show yang operational-data /frr-test-module:frr-test-module +{ + "frr-test-module:frr-test-module": { + "vrfs": { + "vrf": [ + { + "name": "vrf0", + "interfaces": { + "interface": [ + "eth0", + "eth1", + "eth2", + "eth3" + ] + }, + "routes": { + "route": [ + { + "prefix": "10.0.0.0/32", + "next-hop": "172.16.0.0", + "interface": "eth0", + "metric": 0, + "active": [null] + }, + { + "prefix": "10.0.0.1/32", + "next-hop": "172.16.0.1", + "interface": "eth1", + "metric": 1 + }, + { + "prefix": "10.0.0.2/32", + "next-hop": "172.16.0.2", + "interface": "eth2", + "metric": 2, + "active": [null] + }, + { + "prefix": "10.0.0.3/32", + "next-hop": "172.16.0.3", + "interface": "eth3", + "metric": 3 + }, + { + "prefix": "10.0.0.4/32", + "next-hop": "172.16.0.4", + "interface": "eth4", + "metric": 4, + "active": [null] + }, + { + "prefix": "10.0.0.5/32", + "next-hop": "172.16.0.5", + "interface": "eth5", + "metric": 5 + } + ] + } + }, + { + "name": "vrf1", + "interfaces": { + "interface": [ + "eth0", + "eth1", + "eth2", + "eth3" + ] + }, + "routes": { + "route": [ + { + "prefix": "10.0.0.0/32", + "next-hop": "172.16.0.0", + "interface": "eth0", + "metric": 0, + "active": [null] + }, + { + "prefix": "10.0.0.1/32", + "next-hop": "172.16.0.1", + "interface": "eth1", + "metric": 1 + }, + { + "prefix": "10.0.0.2/32", + "next-hop": "172.16.0.2", + "interface": "eth2", + "metric": 2, + "active": [null] + }, + { + "prefix": "10.0.0.3/32", + "next-hop": "172.16.0.3", + "interface": "eth3", + "metric": 3 + }, + { + "prefix": "10.0.0.4/32", + "next-hop": "172.16.0.4", + "interface": "eth4", + "metric": 4, + "active": [null] + }, + { + "prefix": "10.0.0.5/32", + "next-hop": "172.16.0.5", + "interface": "eth5", + "metric": 5 + } + ] + } + } + ] + } + } +} +test# +end. diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index 70db69aadf..959358bbec 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -102,7 +102,7 @@ static unsigned int log_key(void *data) return hash; } -static int log_cmp(const void *a, const void *b) +static bool log_cmp(const void *a, const void *b) { if (a == NULL || b == NULL) return 0; diff --git a/tests/subdir.am b/tests/subdir.am index 6b52c90bc0..7d2800a3a2 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -68,6 +68,7 @@ check_PROGRAMS = \ tests/lib/test_graph \ tests/lib/cli/test_cli \ tests/lib/cli/test_commands \ + tests/lib/northbound/test_oper_data \ $(TESTS_BGPD) \ $(TESTS_ISISD) \ $(TESTS_OSPF6D) \ @@ -175,6 +176,11 @@ tests_lib_cli_test_commands_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) nodist_tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands_defun.c tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands.c tests/helpers/c/prng.c +tests_lib_northbound_test_oper_data_CFLAGS = $(TESTS_CFLAGS) +tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD) +tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c +nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) @@ -284,6 +290,9 @@ EXTRA_DIST += \ tests/lib/cli/test_cli.in \ tests/lib/cli/test_cli.py \ tests/lib/cli/test_cli.refout \ + tests/lib/northbound/test_oper_data.in \ + tests/lib/northbound/test_oper_data.py \ + tests/lib/northbound/test_oper_data.refout \ tests/lib/test_nexthop_iter.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index 8ef105484f..eded87c12e 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -84,19 +84,22 @@ static struct nb_callback_info { .operation = NB_OP_GET_NEXT, .return_type = "const void *", .return_value = "NULL", - .arguments = "const char *xpath, const void *list_entry", + .arguments = + "const void *parent_list_entry, const void *list_entry", }, { .operation = NB_OP_GET_KEYS, .return_type = "int ", .return_value = "NB_OK", - .arguments = "const void *list_entry, struct yang_list_keys *keys", + .arguments = + "const void *list_entry, struct yang_list_keys *keys", }, { .operation = NB_OP_LOOKUP_ENTRY, .return_type = "const void *", .return_value = "NULL", - .arguments = "const struct yang_list_keys *keys", + .arguments = + "const void *parent_list_entry, const struct yang_list_keys *keys", }, { .operation = NB_OP_RPC, @@ -130,9 +133,9 @@ static void generate_callback_name(struct lys_node *snode, snodes = list_new(); for (; snode; snode = lys_parent(snode)) { /* Skip schema-only snodes. */ - if (snode->nodetype - & (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_INPUT - | LYS_OUTPUT)) + if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE + | LYS_INPUT + | LYS_OUTPUT)) continue; listnode_add_head(snodes, snode); @@ -149,8 +152,7 @@ static void generate_callback_name(struct lys_node *snode, replace_hyphens_by_underscores(buffer); } -static void generate_callbacks(const struct lys_node *snode, void *arg1, - void *arg2) +static int generate_callbacks(const struct lys_node *snode, void *arg) { bool first = true; @@ -163,7 +165,7 @@ static void generate_callbacks(const struct lys_node *snode, void *arg1, case LYS_RPC: break; default: - return; + return YANG_ITER_CONTINUE; } for (struct nb_callback_info *cb = &nb_callbacks[0]; @@ -198,10 +200,11 @@ static void generate_callbacks(const struct lys_node *snode, void *arg1, nb_callbacks[cb->operation].arguments, nb_callbacks[cb->operation].return_value); } + + return YANG_ITER_CONTINUE; } -static void generate_nb_nodes(const struct lys_node *snode, void *arg1, - void *arg2) +static int generate_nb_nodes(const struct lys_node *snode, void *arg) { bool first = true; @@ -214,7 +217,7 @@ static void generate_nb_nodes(const struct lys_node *snode, void *arg1, case LYS_RPC: break; default: - return; + return YANG_ITER_CONTINUE; } for (struct nb_callback_info *cb = &nb_callbacks[0]; @@ -245,6 +248,8 @@ static void generate_nb_nodes(const struct lys_node *snode, void *arg1, if (!first) printf("\t\t},\n"); + + return YANG_ITER_CONTINUE; } int main(int argc, char *argv[]) @@ -270,12 +275,18 @@ int main(int argc, char *argv[]) yang_init(); - /* Load YANG module. */ - module = yang_module_load(argv[0]); + /* Load all FRR native models to ensure all augmentations are loaded. */ + yang_module_load_all(); + module = yang_module_find(argv[0]); + if (!module) + /* Non-native FRR module (e.g. modules from unit tests). */ + module = yang_module_load(argv[0]); + + /* Create a nb_node for all YANG schema nodes. */ + nb_nodes_create(); /* Generate callback functions. */ - yang_module_snodes_iterate(module->info, generate_callbacks, 0, NULL, - NULL); + yang_snodes_iterate_module(module->info, generate_callbacks, 0, NULL); strlcpy(module_name_underscores, module->name, sizeof(module_name_underscores)); @@ -287,8 +298,7 @@ int main(int argc, char *argv[]) "\t.name = \"%s\",\n" "\t.nodes = {\n", module_name_underscores, module->name); - yang_module_snodes_iterate(module->info, generate_nb_nodes, 0, NULL, - NULL); + yang_snodes_iterate_module(module->info, generate_nb_nodes, 0, NULL); printf("\t\t{\n" "\t\t\t.xpath = NULL,\n" "\t\t},\n"); @@ -296,6 +306,7 @@ int main(int argc, char *argv[]) "};\n"); /* Cleanup and exit. */ + nb_nodes_delete(); yang_terminate(); return 0; diff --git a/tools/gen_yang_deviations.c b/tools/gen_yang_deviations.c index 121969c6fe..f611f1c57e 100644 --- a/tools/gen_yang_deviations.c +++ b/tools/gen_yang_deviations.c @@ -32,8 +32,7 @@ static void __attribute__((noreturn)) usage(int status) exit(status); } -static void generate_yang_deviation(const struct lys_node *snode, void *arg1, - void *arg2) +static int generate_yang_deviation(const struct lys_node *snode, void *arg) { char xpath[XPATH_MAXLEN]; @@ -42,6 +41,8 @@ static void generate_yang_deviation(const struct lys_node *snode, void *arg1, printf(" deviation \"%s\" {\n", xpath); printf(" deviate not-supported;\n"); printf(" }\n\n"); + + return YANG_ITER_CONTINUE; } int main(int argc, char *argv[]) @@ -70,8 +71,8 @@ int main(int argc, char *argv[]) module = yang_module_load(argv[0]); /* Generate deviations. */ - yang_module_snodes_iterate(module->info, generate_yang_deviation, - YANG_ITER_FILTER_IMPLICIT, NULL, NULL); + yang_snodes_iterate_module(module->info, generate_yang_deviation, + YANG_ITER_FILTER_IMPLICIT, NULL); /* Cleanup and exit. */ yang_terminate(); diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang new file mode 100644 index 0000000000..c02c0a11d7 --- /dev/null +++ b/yang/frr-test-module.yang @@ -0,0 +1,59 @@ +module frr-test-module { + yang-version 1.1; + namespace "urn:frr-test-module"; + prefix frr-test-module; + + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + import frr-interface { + prefix frr-interface; + } + + revision 2018-11-26 { + description + "Initial revision."; + } + + container frr-test-module { + config false; + container vrfs { + list vrf { + key "name"; + + leaf name { + type string; + } + container interfaces { + leaf-list interface { + type string; + } + } + container routes { + list route { + key "prefix"; + + leaf prefix { + type inet:ipv4-prefix; + } + leaf next-hop { + type inet:ipv4-address; + } + leaf interface { + type string; + } + leaf metric { + type uint8; + } + leaf active { + type empty; + } + } + } + } + } + } +} diff --git a/yang/subdir.am b/yang/subdir.am index ee6fbc181d..07bd225780 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -20,6 +20,7 @@ EXTRA_DIST += yang/embedmodel.py # without problems, as seen in libfrr. dist_yangmodels_DATA += yang/frr-module-translator.yang +dist_yangmodels_DATA += yang/frr-test-module.yang dist_yangmodels_DATA += yang/frr-interface.yang dist_yangmodels_DATA += yang/frr-route-types.yang