summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChristian Hopps <chopps@labn.net>2024-01-06 10:45:29 +0100
committerChristian Hopps <chopps@labn.net>2024-01-07 16:17:30 +0100
commitcf67a7e26577b0dda276324b40a602ae084e504e (patch)
tree7019c29239c15d507736b2330ead23774d636481 /lib
parentlib: fix clang SA warnings (diff)
downloadfrr-cf67a7e26577b0dda276324b40a602ae084e504e.tar.xz
frr-cf67a7e26577b0dda276324b40a602ae084e504e.zip
lib: mgmtd: implement full XPath 1.0 predicate functionality
Allow user to specify full YANG compatible XPath 1.0 predicates. This allows for trimming results of generic queries using functions and other non-key predicates from XPath 1.0 Signed-off-by: Christian Hopps <chopps@labn.net>
Diffstat (limited to 'lib')
-rw-r--r--lib/mgmt_msg.c2
-rw-r--r--lib/northbound.c21
-rw-r--r--lib/northbound.h8
-rw-r--r--lib/northbound_oper.c24
-rw-r--r--lib/yang.c105
-rw-r--r--lib/yang.h29
6 files changed, 184 insertions, 5 deletions
diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c
index 99d000537..782707b46 100644
--- a/lib/mgmt_msg.c
+++ b/lib/mgmt_msg.c
@@ -207,7 +207,7 @@ bool mgmt_msg_procbufs(struct mgmt_msg_state *ms,
}
/**
- * Write data from a onto the socket, using streams that have been queued for
+ * Write data onto the socket, using streams that have been queued for
* sending by mgmt_msg_send_msg. This function should be reschedulable.
*
* Args:
diff --git a/lib/northbound.c b/lib/northbound.c
index 18d65e47f..03d252ee5 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -6,6 +6,7 @@
#include <zebra.h>
+#include "darr.h"
#include "libfrr.h"
#include "log.h"
#include "lib_errors.h"
@@ -168,6 +169,26 @@ struct nb_node *nb_node_find(const char *path)
return snode->priv;
}
+struct nb_node **nb_nodes_find(const char *xpath)
+{
+ struct lysc_node **snodes = NULL;
+ struct nb_node **nb_nodes = NULL;
+ bool simple;
+ LY_ERR err;
+ uint i;
+
+ err = yang_resolve_snode_xpath(ly_native_ctx, xpath, &snodes, &simple);
+ if (err)
+ return NULL;
+
+ darr_ensure_i(nb_nodes, darr_lasti(snodes));
+ darr_foreach_i (snodes, i)
+ nb_nodes[i] = snodes[i]->priv;
+ darr_free(snodes);
+ return nb_nodes;
+}
+
+
void nb_node_set_dependency_cbs(const char *dependency_xpath,
const char *dependant_xpath,
struct nb_dependency_callbacks *cbs)
diff --git a/lib/northbound.h b/lib/northbound.h
index 018d09fac..37b7055c1 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -813,6 +813,14 @@ void nb_nodes_delete(void);
*/
extern struct nb_node *nb_node_find(const char *xpath);
+/**
+ * nb_nodes_find() - find the NB nodes corresponding to complex xpath.
+ * @xpath: XPath to search for (with or without predicates).
+ *
+ * Return: a dynamic array (darr) of `struct nb_node *`s.
+ */
+extern struct nb_node **nb_nodes_find(const char *xpath);
+
extern void nb_node_set_dependency_cbs(const char *dependency_xpath,
const char *dependant_xpath,
struct nb_dependency_callbacks *cbs);
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index bd6d870eb..4e131154e 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -72,6 +72,7 @@ struct nb_op_node_info {
* @schema_path: the schema nodes for each node in the query string.
# @query_tokstr: the query string tokenized with NUL bytes.
* @query_tokens: the string pointers to each query token (node).
+ * @non_specific_predicate: tracks if a query_token is non-specific predicate.
* @walk_root_level: The topmost specific node, +1 is where we start walking.
* @walk_start_level: @walk_root_level + 1.
* @query_base_level: the level the query string stops at and full walks
@@ -85,6 +86,7 @@ struct nb_op_yield_state {
const struct lysc_node **schema_path;
char *query_tokstr;
char **query_tokens;
+ uint8_t *non_specific_predicate;
int walk_root_level;
int walk_start_level;
int query_base_level;
@@ -158,6 +160,7 @@ static inline void nb_op_free_yield_state(struct nb_op_yield_state *ys,
if (!nofree_tree && ys_root_node(ys))
lyd_free_all(ys_root_node(ys));
darr_free(ys->query_tokens);
+ darr_free(ys->non_specific_predicate);
darr_free(ys->query_tokstr);
darr_free(ys->schema_path);
darr_free(ys->node_infos);
@@ -1142,18 +1145,23 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
is_specific_node = false;
if (list_start &&
at_clevel <= darr_lasti(ys->query_tokens) &&
+ !ys->non_specific_predicate[at_clevel] &&
nb_op_schema_path_has_predicate(ys, at_clevel)) {
err = lyd_new_path(&pni->inner->node, NULL,
ys->query_tokens[at_clevel],
NULL, 0, &node);
if (!err)
- /* predicate resolved to specific node */
is_specific_node = true;
+ else if (err == LY_EVALID)
+ ys->non_specific_predicate[at_clevel] = true;
else {
- flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
- "%s: unable to create node for specific query string: %s",
+ flog_err(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: unable to create node for specific query string: %s: %s",
__func__,
- ys->query_tokens[at_clevel]);
+ ys->query_tokens[at_clevel],
+ yang_ly_strerrcode(err));
+ ret = NB_ERR;
+ goto done;
}
}
@@ -1570,6 +1578,7 @@ static enum nb_error nb_op_yield(struct nb_op_yield_state *ys)
static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
struct nb_node **last)
{
+ struct nb_node **nb_nodes = NULL;
const struct lysc_node *sn;
struct nb_node *nblast;
char *s, *s2;
@@ -1588,6 +1597,11 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
*/
nblast = nb_node_find(ys->xpath);
if (!nblast) {
+ nb_nodes = nb_nodes_find(ys->xpath);
+ nblast = darr_len(nb_nodes) ? nb_nodes[0] : NULL;
+ darr_free(nb_nodes);
+ }
+ if (!nblast) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, ys->xpath);
return NB_ERR;
@@ -1614,6 +1628,7 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
/* create our arrays */
darr_append_n(ys->schema_path, count);
darr_append_n(ys->query_tokens, count);
+ darr_append_nz(ys->non_specific_predicate, count);
for (sn = nblast->snode; sn; sn = sn->parent)
ys->schema_path[--count] = sn;
@@ -1675,6 +1690,7 @@ error:
darr_free(ys->query_tokstr);
darr_free(ys->schema_path);
darr_free(ys->query_tokens);
+ darr_free(ys->non_specific_predicate);
return NB_ERR;
}
diff --git a/lib/yang.c b/lib/yang.c
index 18d2ac58d..b2cc71b30 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -251,6 +251,38 @@ void yang_snode_get_path(const struct lysc_node *snode,
}
}
+LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath,
+ struct lysc_node ***snodes, bool *simple)
+{
+ struct lysc_node *snode;
+ struct ly_set *set;
+ LY_ERR err;
+
+ /* lys_find_path will not resolve complex xpaths */
+ snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0);
+ if (snode) {
+ *darr_append(*snodes) = snode;
+ *simple = true;
+ return LY_SUCCESS;
+ }
+
+ /* Try again to catch complex query cases */
+ err = lys_find_xpath(ly_native_ctx, NULL, xpath, 0, &set);
+ if (err)
+ return err;
+ if (!set->count) {
+ ly_set_free(set, NULL);
+ return LY_ENOTFOUND;
+ }
+
+ *simple = false;
+ darr_ensure_i(*snodes, set->count - 1);
+ memcpy(*snodes, set->snodes, set->count * sizeof(set->snodes[0]));
+ ly_set_free(set, NULL);
+ return LY_SUCCESS;
+}
+
+
struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath,
uint32_t options)
{
@@ -1019,3 +1051,76 @@ LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
/*NOTREACHED*/
return LY_EINVAL;
}
+
+
+int yang_trim_tree(struct lyd_node *root, const char *xpath)
+{
+ enum nb_error ret = NB_OK;
+ LY_ERR err;
+#if 0
+ err = lyd_trim_xpath(&root, xpath, NULL);
+ if (err) {
+ flog_err_sys(EC_LIB_LIBYANG,
+ "cannot obtain specific result for xpath \"%s\"",
+ xpath);
+ return NB_ERR;
+ }
+ return NB_OK;
+#else
+ struct lyd_node *node;
+ struct lyd_node **remove = NULL;
+ struct ly_set *set = NULL;
+ uint32_t i;
+
+ err = lyd_find_xpath3(NULL, root, xpath, NULL, &set);
+ if (err) {
+ flog_err_sys(EC_LIB_LIBYANG,
+ "cannot obtain specific result for xpath \"%s\"",
+ xpath);
+ ret = NB_ERR;
+ goto done;
+ }
+ /*
+ * Mark keepers and sweep deleting non-keepers.
+ *
+ * NOTE: We assume the data-nodes have NULL priv pointers and use that
+ * for our mark.
+ */
+
+ /* Mark */
+ for (i = 0; i < set->count; i++) {
+ for (node = set->dnodes[i]; node; node = &node->parent->node) {
+ if (node->priv)
+ break;
+ if (node == set->dnodes[i])
+ node->priv = (void *)2;
+ else
+ node->priv = (void *)1;
+ }
+ }
+
+ darr_ensure_cap(remove, 128);
+ LYD_TREE_DFS_BEGIN (root, node) {
+ /*
+ * If this is a direct matching node then include it's subtree
+ * which won't be marked and would otherwise be removed.
+ */
+ if (node->priv == (void *)2)
+ LYD_TREE_DFS_continue = 1;
+ else if (!node->priv) {
+ LYD_TREE_DFS_continue = 1;
+ *darr_append(remove) = node;
+ }
+ LYD_TREE_DFS_END(root, node);
+ }
+ darr_foreach_i (remove, i)
+ lyd_free_tree(remove[i]);
+ darr_free(remove);
+
+done:
+ if (set)
+ ly_set_free(set, NULL);
+
+ return ret;
+#endif
+}
diff --git a/lib/yang.h b/lib/yang.h
index 3ce584b34..75dcab2d2 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -724,6 +724,35 @@ extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
const struct lysc_node *snode,
const struct yang_list_keys *keys,
struct lyd_node_inner **node);
+/**
+ * yang_resolve_snodes() - Resolve an XPath to matching schema nodes.
+ * @ly_ctx: libyang context to operate on.
+ * @xpath: the path or XPath to resolve.
+ * @snodes: [OUT] pointer for resulting dynamic array (darr) of schema node
+ * pointers.
+ * @simple: [OUT] indicates if @xpath was resolvable simply or not. Non-simple
+ * means that the @xpath is not a simple path and utilizes XPath 1.0
+ * functionality beyond simple key predicates.
+ *
+ * This function can be used to find the schema node (or nodes) that correspond
+ * to a given @xpath. If the @xpath includes non-key predicates (e.g., using
+ * functions) then @simple will be set to false, and @snodes may contain more
+ * than a single schema node.
+ *
+ * Return: a libyang error or LY_SUCCESS.
+ */
+extern LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath,
+ struct lysc_node ***snodes, bool *simple);
+
+/**
+ * yang_trim_tree() - trim the data tree to the given xpath
+ * @root: the data tree
+ * @xpath: the xpath to trim @root to.
+ *
+ * Return: enum nb_error..
+ */
+extern int yang_trim_tree(struct lyd_node *root, const char *xpath);
+
#ifdef __cplusplus
}
#endif