summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lib_errors.c6
-rw-r--r--lib/lib_errors.h1
-rw-r--r--lib/northbound.c432
-rw-r--r--lib/northbound.h85
-rw-r--r--lib/northbound_cli.c120
-rw-r--r--lib/northbound_confd.c322
-rw-r--r--lib/northbound_sysrepo.c186
-rw-r--r--lib/yang.c110
-rw-r--r--lib/yang.h118
-rw-r--r--lib/yang_translator.c54
-rw-r--r--lib/yang_wrappers.c4
-rw-r--r--lib/yang_wrappers.h4
-rw-r--r--ripd/rip_northbound.c59
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/lib/northbound/test_oper_data.c466
-rw-r--r--tests/lib/northbound/test_oper_data.in1
-rw-r--r--tests/lib/northbound/test_oper_data.py4
-rw-r--r--tests/lib/northbound/test_oper_data.refout119
-rw-r--r--tests/lib/test_srcdest_table.c2
-rw-r--r--tests/subdir.am9
-rw-r--r--tools/gen_northbound_callbacks.c47
-rw-r--r--tools/gen_yang_deviations.c9
-rw-r--r--yang/frr-test-module.yang59
-rw-r--r--yang/subdir.am1
24 files changed, 1760 insertions, 459 deletions
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
@@ -135,6 +135,12 @@ static struct log_ref ferr_lib_warn[] = {
.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",
.description = "The northbound subsystem failed 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 int nb_node_check_config_only(const struct lys_node *snode, void *arg)
+{
+ bool *config_only = arg;
+
+ if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
+ *config_only = false;
+ return YANG_ITER_STOP;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
-static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2)
+static int nb_node_new_cb(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node;
struct lys_node *sparent, *sparent_list;
@@ -67,21 +85,46 @@ static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2)
if (sparent_list)
nb_node->parent_list = sparent_list->priv;
+ /* Set flags. */
+ if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
+ bool config_only = true;
+
+ yang_snodes_iterate_subtree(snode, nb_node_check_config_only,
+ YANG_ITER_ALLOW_AUGMENTATIONS,
+ &config_only);
+ if (config_only)
+ SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
+ }
+
/*
* Link the northbound node and the libyang schema node with one
* another.
*/
nb_node->snode = snode;
lys_set_private(snode, nb_node);
+
+ return YANG_ITER_CONTINUE;
}
-static void nb_node_del_cb(const struct lys_node *snode, void *arg1, void *arg2)
+static int nb_node_del_cb(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node;
nb_node = snode->priv;
lys_set_private(snode, NULL);
XFREE(MTYPE_NB_NODE, nb_node);
+
+ return YANG_ITER_CONTINUE;
+}
+
+void nb_nodes_create(void)
+{
+ yang_snodes_iterate_all(nb_node_new_cb, 0, NULL);
+}
+
+void nb_nodes_delete(void)
+{
+ yang_snodes_iterate_all(nb_node_del_cb, 0, NULL);
}
struct nb_node *nb_node_find(const char *xpath)
@@ -170,15 +213,16 @@ static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
return 0;
}
-static void nb_node_validate(const struct lys_node *snode, void *arg1,
- void *arg2)
+static int nb_node_validate(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node = snode->priv;
- unsigned int *errors = arg1;
+ unsigned int *errors = arg;
/* Validate callbacks and priority. */
*errors += nb_node_validate_cbs(nb_node);
*errors += nb_node_validate_priority(nb_node);
+
+ return YANG_ITER_CONTINUE;
}
struct nb_config *nb_config_new(struct lyd_node *dnode)
@@ -189,7 +233,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
if (dnode)
config->dnode = dnode;
else
- config->dnode = yang_dnode_new(ly_native_ctx);
+ config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
return config;
@@ -236,7 +280,8 @@ void nb_config_replace(struct nb_config *config_dst,
config_dst->version = config_src->version;
/* Update dnode. */
- yang_dnode_free(config_dst->dnode);
+ if (config_dst->dnode)
+ yang_dnode_free(config_dst->dnode);
if (preserve_source) {
config_dst->dnode = yang_dnode_dup(config_src->dnode);
} else {
@@ -380,7 +425,8 @@ static void nb_config_diff(const struct nb_config *config1,
nb_config_diff_add_change(changes, operation, dnode);
if (type == LYD_DIFF_CREATED
- && (dnode->schema->nodetype & (LYS_CONTAINER | LYS_LIST)))
+ && CHECK_FLAG(dnode->schema->nodetype,
+ LYS_CONTAINER | LYS_LIST))
nb_config_diff_new_subtree(dnode, changes);
}
@@ -890,15 +936,341 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
}
}
+static int nb_oper_data_iter_children(const struct lys_node *snode,
+ const char *xpath, const void *list_entry,
+ const struct yang_list_keys *list_keys,
+ struct yang_translator *translator,
+ bool first, uint32_t flags,
+ nb_oper_data_cb cb, void *arg)
+{
+ struct lys_node *child;
+
+ LY_TREE_FOR (snode->child, child) {
+ int ret;
+
+ ret = nb_oper_data_iter_node(child, xpath, list_entry,
+ list_keys, translator, false,
+ flags, cb, arg);
+ if (ret != NB_OK)
+ return ret;
+ }
+
+ return NB_OK;
+}
+
+static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
+ const char *xpath, const void *list_entry,
+ const struct yang_list_keys *list_keys,
+ struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+ struct yang_data *data;
+
+ if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
+ return NB_OK;
+
+ /* Ignore list keys. */
+ if (lys_is_key((struct lys_node_leaf *)nb_node->snode, NULL))
+ return NB_OK;
+
+ data = nb_node->cbs.get_elem(xpath, list_entry);
+ if (data == NULL)
+ /* Leaf of type "empty" is not present. */
+ return NB_OK;
+
+ return (*cb)(nb_node->snode, translator, data, arg);
+}
+
+static int nb_oper_data_iter_container(const struct nb_node *nb_node,
+ const char *xpath,
+ const void *list_entry,
+ const struct yang_list_keys *list_keys,
+ struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb,
+ void *arg)
+{
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+ return NB_OK;
+
+ /* Presence containers. */
+ if (nb_node->cbs.get_elem) {
+ struct yang_data *data;
+ int ret;
+
+ data = nb_node->cbs.get_elem(xpath, list_entry);
+ if (data == NULL)
+ /* Presence container is not present. */
+ return NB_OK;
+
+ ret = (*cb)(nb_node->snode, translator, data, arg);
+ if (ret != NB_OK)
+ return ret;
+ }
+
+ /* Iterate over the child nodes. */
+ return nb_oper_data_iter_children(nb_node->snode, xpath, list_entry,
+ list_keys, translator, false, flags,
+ cb, arg);
+}
+
+static int
+nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
+ const void *parent_list_entry,
+ const struct yang_list_keys *parent_list_keys,
+ struct yang_translator *translator, uint32_t flags,
+ nb_oper_data_cb cb, void *arg)
+{
+ const void *list_entry = NULL;
+
+ if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
+ return NB_OK;
+
+ do {
+ struct yang_data *data;
+ int ret;
+
+ list_entry =
+ nb_node->cbs.get_next(parent_list_entry, list_entry);
+ if (!list_entry)
+ /* End of the list. */
+ break;
+
+ data = nb_node->cbs.get_elem(xpath, list_entry);
+ if (data == NULL)
+ continue;
+
+ ret = (*cb)(nb_node->snode, translator, data, arg);
+ if (ret != NB_OK)
+ return ret;
+ } while (list_entry);
+
+ return NB_OK;
+}
+
+static int nb_oper_data_iter_list(const struct nb_node *nb_node,
+ const char *xpath_list,
+ const void *parent_list_entry,
+ const struct yang_list_keys *parent_list_keys,
+ struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+ struct lys_node_list *slist = (struct lys_node_list *)nb_node->snode;
+ const void *list_entry = NULL;
+
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+ return NB_OK;
+
+ /* Iterate over all list entries. */
+ do {
+ struct yang_list_keys list_keys;
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ /* Obtain list entry. */
+ list_entry =
+ nb_node->cbs.get_next(parent_list_entry, list_entry);
+ if (!list_entry)
+ /* End of the list. */
+ break;
+
+ /* Obtain the list entry keys. */
+ if (nb_node->cbs.get_keys(list_entry, &list_keys) != NB_OK) {
+ flog_warn(EC_LIB_NB_CB_STATE,
+ "%s: failed to get list keys", __func__);
+ return NB_ERR;
+ }
+
+ /* Build XPath of the list entry. */
+ strlcpy(xpath, xpath_list, sizeof(xpath));
+ for (unsigned int i = 0; i < list_keys.num; i++) {
+ snprintf(xpath + strlen(xpath),
+ sizeof(xpath) - strlen(xpath), "[%s='%s']",
+ slist->keys[i]->name, list_keys.key[i]);
+ }
+
+ /* Iterate over the child nodes. */
+ ret = nb_oper_data_iter_children(
+ nb_node->snode, xpath, list_entry, &list_keys,
+ translator, false, flags, cb, arg);
+ if (ret != NB_OK)
+ return ret;
+ } while (list_entry);
+
+ return NB_OK;
+}
+
+static int nb_oper_data_iter_node(const struct lys_node *snode,
+ const char *xpath_parent,
+ const void *list_entry,
+ const struct yang_list_keys *list_keys,
+ struct yang_translator *translator,
+ bool first, uint32_t flags,
+ nb_oper_data_cb cb, void *arg)
+{
+ struct nb_node *nb_node;
+ char xpath[XPATH_MAXLEN];
+ int ret = NB_OK;
+
+ if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
+ && CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
+ return NB_OK;
+
+ /* Update XPath. */
+ strlcpy(xpath, xpath_parent, sizeof(xpath));
+ if (!first && snode->nodetype != LYS_USES)
+ snprintf(xpath + strlen(xpath), sizeof(xpath) - strlen(xpath),
+ "/%s", snode->name);
+
+ nb_node = snode->priv;
+ switch (snode->nodetype) {
+ case LYS_CONTAINER:
+ ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
+ list_keys, translator, flags,
+ cb, arg);
+ break;
+ case LYS_LEAF:
+ ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
+ list_keys, translator, flags, cb,
+ arg);
+ break;
+ case LYS_LEAFLIST:
+ ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
+ list_keys, translator, flags,
+ cb, arg);
+ break;
+ case LYS_LIST:
+ ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
+ list_keys, translator, flags, cb,
+ arg);
+ break;
+ case LYS_USES:
+ ret = nb_oper_data_iter_children(snode, xpath, list_entry,
+ list_keys, translator, false,
+ flags, cb, arg);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
+ uint32_t flags, nb_oper_data_cb cb, void *arg)
+{
+ struct nb_node *nb_node;
+ const void *list_entry = NULL;
+ struct yang_list_keys list_keys;
+ struct list *list_dnodes;
+ struct lyd_node *dnode, *dn;
+ struct listnode *ln;
+ int ret;
+
+ nb_node = nb_node_find(xpath);
+ if (!nb_node) {
+ flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
+ "%s: unknown data path: %s", __func__, xpath);
+ return NB_ERR;
+ }
+
+ /* For now this function works only with containers and lists. */
+ if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
+ flog_warn(
+ EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
+ __func__, xpath);
+ return NB_ERR;
+ }
+
+ /*
+ * Create a data tree from the XPath so that we can parse the keys of
+ * all YANG lists (if any).
+ */
+ ly_errno = 0;
+ dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0,
+ LYD_PATH_OPT_UPDATE);
+ if (!dnode && ly_errno) {
+ flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
+ __func__);
+ return NB_ERR;
+ }
+ /*
+ * We can remove the following two lines once we depend on
+ * libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for
+ * lyd_new_path().
+ */
+ dnode = yang_dnode_get(dnode, xpath);
+ assert(dnode);
+
+ /*
+ * Create a linked list to sort the data nodes starting from the root.
+ */
+ list_dnodes = list_new();
+ for (dn = dnode; dn; dn = dn->parent) {
+ if (dn->schema->nodetype != LYS_LIST || !dn->child)
+ continue;
+ listnode_add_head(list_dnodes, dn);
+ }
+ /*
+ * Use the northbound callbacks to find list entry pointer corresponding
+ * to the given XPath.
+ */
+ for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
+ struct lyd_node *child;
+ struct nb_node *nn;
+ unsigned int n = 0;
+
+ /* Obtain the list entry keys. */
+ memset(&list_keys, 0, sizeof(list_keys));
+ LY_TREE_FOR (dn->child, child) {
+ if (!lys_is_key((struct lys_node_leaf *)child->schema,
+ NULL))
+ continue;
+ strlcpy(list_keys.key[n],
+ yang_dnode_get_string(child, NULL),
+ sizeof(list_keys.key[n]));
+ n++;
+ }
+ list_keys.num = n;
+ assert(list_keys.num
+ == ((struct lys_node_list *)dn->schema)->keys_size);
+
+ /* Find the list entry pointer. */
+ nn = dn->schema->priv;
+ list_entry = nn->cbs.lookup_entry(list_entry, &list_keys);
+ if (list_entry == NULL) {
+ list_delete(&list_dnodes);
+ yang_dnode_free(dnode);
+ return NB_ERR_NOT_FOUND;
+ }
+ }
+
+ /* If a list entry was given, iterate over that list entry only. */
+ if (dnode->schema->nodetype == LYS_LIST && dnode->child)
+ ret = nb_oper_data_iter_children(
+ nb_node->snode, xpath, list_entry, &list_keys,
+ translator, true, flags, cb, arg);
+ else
+ ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
+ &list_keys, translator, true,
+ flags, cb, arg);
+
+ list_delete(&list_dnodes);
+ yang_dnode_free(dnode);
+
+ return ret;
+}
+
bool nb_operation_is_valid(enum nb_operation operation,
const struct lys_node *snode)
{
+ struct nb_node *nb_node = snode->priv;
struct lys_node_container *scontainer;
struct lys_node_leaf *sleaf;
switch (operation) {
case NB_OP_CREATE:
- if (!(snode->flags & LYS_CONFIG_W))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@@ -920,7 +1292,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_MODIFY:
- if (!(snode->flags & LYS_CONFIG_W))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@@ -938,7 +1310,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_DELETE:
- if (!(snode->flags & LYS_CONFIG_W))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@@ -957,7 +1329,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return true;
if (sleaf->when)
return true;
- if ((sleaf->flags & LYS_MAND_TRUE) || sleaf->dflt)
+ if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
+ || sleaf->dflt)
return false;
break;
case LYS_CONTAINER:
@@ -973,13 +1346,13 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_MOVE:
- if (!(snode->flags & LYS_CONFIG_W))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
case LYS_LIST:
case LYS_LEAFLIST:
- if (!(snode->flags & LYS_USERORDERED))
+ if (!CHECK_FLAG(snode->flags, LYS_USERORDERED))
return false;
break;
default:
@@ -987,15 +1360,16 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_APPLY_FINISH:
- if (!(snode->flags & LYS_CONFIG_W))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
return true;
case NB_OP_GET_ELEM:
- if (!(snode->flags & LYS_CONFIG_R))
+ if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return false;
switch (snode->nodetype) {
case LYS_LEAF:
+ case LYS_LEAFLIST:
break;
case LYS_CONTAINER:
scontainer = (struct lys_node_container *)snode;
@@ -1007,20 +1381,32 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_GET_NEXT:
+ switch (snode->nodetype) {
+ case LYS_LIST:
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+ return false;
+ break;
+ case LYS_LEAFLIST:
+ if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ return true;
case NB_OP_GET_KEYS:
case NB_OP_LOOKUP_ENTRY:
- if (!(snode->flags & LYS_CONFIG_R))
- return false;
-
switch (snode->nodetype) {
case LYS_LIST:
+ if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
+ return false;
break;
default:
return false;
}
return true;
case NB_OP_RPC:
- if (snode->flags & (LYS_CONFIG_W | LYS_CONFIG_R))
+ if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
return false;
switch (snode->nodetype) {
@@ -1162,14 +1548,14 @@ void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules)
yang_module_load(modules[i]->name);
/* Create a nb_node for all YANG schema nodes. */
- yang_all_snodes_iterate(nb_node_new_cb, 0, NULL, NULL);
+ nb_nodes_create();
/* Load northbound callbacks. */
for (size_t i = 0; i < nmodules; i++)
nb_load_callbacks(modules[i]);
/* Validate northbound callbacks. */
- yang_all_snodes_iterate(nb_node_validate, 0, &errors, NULL);
+ yang_snodes_iterate_all(nb_node_validate, 0, &errors);
if (errors > 0) {
flog_err(
EC_LIB_NB_CBS_VALIDATION,
@@ -1197,7 +1583,7 @@ void nb_terminate(void)
nb_cli_terminate();
/* Delete all nb_node's from all YANG modules. */
- yang_all_snodes_iterate(nb_node_del_cb, 0, NULL, NULL);
+ nb_nodes_delete();
/* Delete the running configuration. */
nb_config_free(running_config);
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,6 +441,14 @@ 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))
@@ -436,6 +456,16 @@ 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.
*
* xpath
@@ -686,6 +716,31 @@ extern int nb_candidate_commit(struct nb_config *candidate,
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.
*
* operation
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 <json$json|xml$xml>\
+ |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. */
+ 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;
}
- 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__);
+
+ 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;
}
- /* 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);
+ 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;
+
+ if (nvalues > CONFD_MAX_CHILD_NODES)
+ break;
+
+ v = &values[nvalues++];
- child = set->set.s[i];
- nb_node_child = child->priv;
+ /* 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;
+
+ if (nvalues > CONFD_MAX_CHILD_NODES)
+ break;
- child = set->set.s[i];
- nb_node_child = child->priv;
+ 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;
+
+ 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;
+ }
- option_arg = data->snode->priv;
- CONFD_SET_TAG_STR(&values[i++], option_arg->confd_hash,
+ 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;
-
- data = nb_node->cbs.get_elem(xpath, list_entry);
- if (data)
- listnode_add(elements, data);
-}
+ struct list *elements = arg;
-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);
-}
+ listnode_add(elements, data);
-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;
@@ -129,6 +130,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.
*
* module_name
@@ -150,46 +156,66 @@ 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], &address);
- yang_str2ipv4(keys->key[0].value, &address);
+ for (ALL_LIST_ELEMENTS_RO(peer_list, node, peer)) {
+ if (IPV4_ADDR_SAME(&peer->addr, &address))
+ return node;
+ }
- return rip_peer_lookup(&address);
+ 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 <zebra.h>
+
+#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