summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorRenato Westphal <renato@opensourcerouting.org>2018-11-03 01:16:55 +0100
committerRenato Westphal <renato@opensourcerouting.org>2018-11-26 21:28:53 +0100
commit1a4bc045deac52e5c0b66d151dbbce8cc3675497 (patch)
treefa244f4c2f65da6f20bc8b44dd78315483e47bce /lib
parentlib: do not subscribe to config changes on a state data tree (confd plugin) (diff)
downloadfrr-1a4bc045deac52e5c0b66d151dbbce8cc3675497.tar.xz
frr-1a4bc045deac52e5c0b66d151dbbce8cc3675497.zip
lib, tests: major rework in the operational-data callbacks
The northbound infrastructure for operational data was subpar compared to the infrastructure for configuration data. This commit addresses most of the existing problems, making it possible to write operational-data callbacks for more complex YANG models. Summary of the changes: * Add support for nested YANG lists. * Add support for leaf-lists. * Add support for leafs of type "empty". * Introduce the "show yang operational-data XPATH" command, and write an unit test for it. The main purpose of this command is to make it easier to test the operational-data northbound callbacks. * Introduce the nb_oper_data_iterate() function, that can be used to iterate over operational data. Make the CLI and sysrepo use this function. * Since ConfD has a very peculiar API, it can't reuse the nb_oper_data_iterate() like the other northbound clients. In this case, adapt the existing ConfD callbacks to support the new features (and make some performance improvements in the process). Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/lib_errors.c6
-rw-r--r--lib/lib_errors.h1
-rw-r--r--lib/northbound.c345
-rw-r--r--lib/northbound.h70
-rw-r--r--lib/northbound_cli.c112
-rw-r--r--lib/northbound_confd.c269
-rw-r--r--lib/northbound_sysrepo.c130
7 files changed, 700 insertions, 233 deletions
diff --git a/lib/lib_errors.c b/lib/lib_errors.c
index 71d1ec6e5..b1ed7d2f6 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 38c75f913..5534edbd8 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 f8045b89a..490b3abe5 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -50,6 +50,12 @@ 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)
{
@@ -930,6 +936,331 @@ 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)
{
@@ -1038,6 +1369,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
switch (snode->nodetype) {
case LYS_LEAF:
+ case LYS_LEAFLIST:
break;
case LYS_CONTAINER:
scontainer = (struct lys_node_container *)snode;
@@ -1049,6 +1381,19 @@ 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:
switch (snode->nodetype) {
diff --git a/lib/northbound.h b/lib/northbound.h
index 68bce5b39..e26a2f861 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.
@@ -434,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))
@@ -701,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 c7378d244..1ffd65af4 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -1097,6 +1097,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]",
@@ -1436,6 +1547,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 886f17f81..9d0154120 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],
+ strlcpy(keys.key[keys.num],
(char *)kp->v[i][j].val.buf.ptr,
- sizeof(keys->key[keys->num]));
- 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. */
@@ -493,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;
@@ -513,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);
@@ -546,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));
@@ -559,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]);
- 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;
}
@@ -588,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));
@@ -605,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;
@@ -668,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];
@@ -687,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();
@@ -702,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);
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index 9c07db856..ffda4c65d 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -299,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);
-}
-
-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);
+ struct list *elements = arg;
- 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]);
- }
-
- /* 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. */
@@ -413,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;