diff options
author | Christian Hopps <chopps@labn.net> | 2024-01-06 10:45:29 +0100 |
---|---|---|
committer | Christian Hopps <chopps@labn.net> | 2024-01-07 16:17:30 +0100 |
commit | cf67a7e26577b0dda276324b40a602ae084e504e (patch) | |
tree | 7019c29239c15d507736b2330ead23774d636481 /lib | |
parent | lib: fix clang SA warnings (diff) | |
download | frr-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.c | 2 | ||||
-rw-r--r-- | lib/northbound.c | 21 | ||||
-rw-r--r-- | lib/northbound.h | 8 | ||||
-rw-r--r-- | lib/northbound_oper.c | 24 | ||||
-rw-r--r-- | lib/yang.c | 105 | ||||
-rw-r--r-- | lib/yang.h | 29 |
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 |