Skip to content

Commit

Permalink
Merge pull request #15103 from LabNConsulting/chopps/oper-filter
Browse files Browse the repository at this point in the history
Implement full XPath 1.0 functionality
  • Loading branch information
donaldsharp authored Jan 9, 2024
2 parents 153ab6e + e85ff7a commit 22b4390
Show file tree
Hide file tree
Showing 14 changed files with 425 additions and 139 deletions.
7 changes: 7 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1964,6 +1964,13 @@ AC_CHECK_MEMBER([struct lyd_node.priv], [], [
Instructions for this are included in the build documentation for your platform at http://docs.frrouting.org/projects/dev-guide/en/latest/building.html])
])
], [[#include <libyang/libyang.h>]])

AC_CHECK_LIB([yang],[lyd_find_xpath3],[],[AC_MSG_ERROR([m4_normalize([
libyang missing lyd_find_xpath3])])])
dnl -- don't add lyd_new_list3 to this list unless bug is fixed upstream
dnl -- https://github.com/CESNET/libyang/issues/2149
AC_CHECK_FUNCS([ly_strerrcode ly_strvecode lyd_trim_xpath])

CFLAGS="$ac_cflags_save"

dnl ---------------
Expand Down
23 changes: 16 additions & 7 deletions lib/darr.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
*/

#include <zebra.h>
#include <limits.h>
#include "memory.h"

DECLARE_MTYPE(DARR);
Expand Down Expand Up @@ -249,6 +250,10 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
* pointers into the previous memory block are no longer valid. The `A` value
* is guaranteed not to change if there is sufficient capacity in the array.
*
* The exception to the no-change rule is if @C is passed as 0, it will be
* considered 1 so that an array is always allocated if currently NULL,
* i.e., @A will never be NULL after a call to darr_ensure_cap_mt()
*
* Args:
* A: (IN/OUT) the dynamic array, can be NULL.
* C: Total capacity to guarantee.
Expand All @@ -259,8 +264,9 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
#define darr_ensure_cap_mt(A, C, MT) \
({ \
/* Cast to avoid warning when C == 0 */ \
if ((ssize_t)darr_cap(A) < (ssize_t)(C)) \
_darr_resize_mt((A), (C), MT); \
uint _c = (C) > 0 ? (C) : 1; \
if ((size_t)darr_cap(A) < _c) \
_darr_resize_mt((A), _c, MT); \
(A); \
})
#define darr_ensure_cap(A, C) darr_ensure_cap_mt(A, C, MTYPE_DARR)
Expand All @@ -285,11 +291,14 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
*/
#define darr_ensure_i_mt(A, I, MT) \
({ \
if ((int)(I) > darr_maxi(A)) \
_darr_resize_mt((A), (I) + 1, MT); \
if ((I) + 1 > _darr_len(A)) \
_darr_len(A) = (I) + 1; \
&(A)[I]; \
assert((int)(I) >= 0 && (int)(I) <= INT_MAX); \
int _i = (int)(I); \
if (_i > darr_maxi(A)) \
_darr_resize_mt((A), _i + 1, MT); \
assert((A) != NULL); \
if ((uint)_i + 1 > _darr_len(A)) \
_darr_len(A) = _i + 1; \
&(A)[_i]; \
})
#define darr_ensure_i(A, I) darr_ensure_i_mt(A, I, MTYPE_DARR)

Expand Down
2 changes: 1 addition & 1 deletion lib/mgmt_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
21 changes: 21 additions & 0 deletions lib/northbound.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <zebra.h>

#include "darr.h"
#include "libfrr.h"
#include "log.h"
#include "lib_errors.h"
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions lib/northbound.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
42 changes: 33 additions & 9 deletions lib/northbound_oper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -806,6 +809,13 @@ static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys,
const struct lysc_node *sib = lysc_node_child(parent);
const struct lysc_node *first_sib;

/*
* NOTE: when we want to handle root level walks we will need to use
* lys_getnext() to walk root level of each module and
* ly_ctx_get_module_iter() to walk the modules.
*/
assert(darr_len(ys->node_infos) > 0);

/*
* The top of the node stack points at @parent.
*
Expand All @@ -814,7 +824,7 @@ static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys,
* base of the user query, return the next schema node from the query
* string (schema_path).
*/
assert(darr_last(ys->node_infos)->schema == parent);
assert(darr_last(ys->node_infos) != NULL && darr_last(ys->node_infos)->schema == parent);
if (darr_lasti(ys->node_infos) < ys->query_base_level)
return ys->schema_path[darr_lasti(ys->node_infos) + 1];

Expand Down Expand Up @@ -1010,10 +1020,14 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
* should be kept.
*/
ret = nb_op_iter_leaf(ys, nn, xpath_child);
if (ret != NB_OK)
goto done;
sib = nb_op_sib_next(ys, sib);
continue;
case LYS_LEAFLIST:
ret = nb_op_iter_leaflist(ys, nn, xpath_child);
if (ret != NB_OK)
goto done;
sib = nb_op_sib_next(ys, sib);
continue;
case LYS_CONTAINER:
Expand Down Expand Up @@ -1131,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;
}
}

Expand Down Expand Up @@ -1390,11 +1409,8 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
*/

if (!node) {
/* NOTE: can also use lyd_new_list2 here when available */
err = yang_lyd_new_list(ni[-1].inner, sib,
&ni->keys,
(struct lyd_node_inner *
*)&node);
&ni->keys, &node);
if (err) {
darr_pop(ys->node_infos);
ret = NB_ERR_RESOURCE;
Expand Down Expand Up @@ -1559,6 +1575,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;
Expand All @@ -1576,6 +1593,11 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
* string over each schema trunk in the set.
*/
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);
Expand Down Expand Up @@ -1603,6 +1625,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;

Expand Down Expand Up @@ -1664,6 +1687,7 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys,
darr_free(ys->query_tokstr);
darr_free(ys->schema_path);
darr_free(ys->query_tokens);
darr_free(ys->non_specific_predicate);
return NB_ERR;
}

Expand Down
85 changes: 3 additions & 82 deletions lib/vty.c
Original file line number Diff line number Diff line change
Expand Up @@ -3688,88 +3688,9 @@ static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
else if (ei->level == LY_LLWRN)
severity = "warning";

switch (ei->no) {
case LY_SUCCESS:
ecode = "ok";
break;
case LY_EMEM:
ecode = "out of memory";
break;
case LY_ESYS:
ecode = "system error";
break;
case LY_EINVAL:
ecode = "invalid value given";
break;
case LY_EEXIST:
ecode = "item exists";
break;
case LY_ENOTFOUND:
ecode = "item not found";
break;
case LY_EINT:
ecode = "operation interrupted";
break;
case LY_EVALID:
ecode = "validation failed";
break;
case LY_EDENIED:
ecode = "access denied";
break;
case LY_EINCOMPLETE:
ecode = "incomplete";
break;
case LY_ERECOMPILE:
ecode = "compile error";
break;
case LY_ENOT:
ecode = "not";
break;
default:
case LY_EPLUGIN:
case LY_EOTHER:
ecode = "other";
break;
}

if (err == LY_EVALID) {
switch (ei->vecode) {
case LYVE_SUCCESS:
evalid = NULL;
break;
case LYVE_SYNTAX:
evalid = "syntax";
break;
case LYVE_SYNTAX_YANG:
evalid = "yang-syntax";
break;
case LYVE_SYNTAX_YIN:
evalid = "yin-syntax";
break;
case LYVE_REFERENCE:
evalid = "reference";
break;
case LYVE_XPATH:
evalid = "xpath";
break;
case LYVE_SEMANTICS:
evalid = "semantics";
break;
case LYVE_SYNTAX_XML:
evalid = "xml-syntax";
break;
case LYVE_SYNTAX_JSON:
evalid = "json-syntax";
break;
case LYVE_DATA:
evalid = "data";
break;
default:
case LYVE_OTHER:
evalid = "other";
break;
}
}
ecode = yang_ly_strerrcode(err);
if (err == LY_EVALID && ei->vecode != LYVE_SUCCESS)
evalid = yang_ly_strvecode(ei->vecode);

switch (format) {
case LYD_XML:
Expand Down
Loading

0 comments on commit 22b4390

Please sign in to comment.