Skip to content

Commit

Permalink
lib: mgmtd: implement full XPath 1.0 functionality
Browse files Browse the repository at this point in the history
Allow user to specify full YANG compatible XPath 1.0 functionality. This allows
for trimming results of generic queries using functions and other non-key
predicates from XPath 1.0

Signed-off-by: Christian Hopps <[email protected]>
  • Loading branch information
choppsv1 committed Jan 6, 2024
1 parent 4e249eb commit 89a7f47
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 18 deletions.
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 @@ -174,6 +175,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
8 changes: 8 additions & 0 deletions lib/northbound_oper.c
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,8 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume)
/* predicate resolved to specific node */
is_specific_node = true;
else {
/* Should we should cache this result? */
/* XXX Change this to a debug, it's expected with complex xpaths */
flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
"%s: unable to create node for specific query string: %s",
__func__,
Expand Down Expand Up @@ -1559,6 +1561,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 +1579,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
105 changes: 105 additions & 0 deletions lib/yang.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
}
29 changes: 29 additions & 0 deletions lib/yang.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 36 additions & 7 deletions mgmtd/mgmt_fe_adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1132,29 +1132,54 @@ static int fe_adapter_send_tree_data(struct mgmt_fe_session_ctx *session,
}

/**
* Handle a get-tree message from the client.
* fe_adapter_handle_get_tree() - Handle a get-tree message from a FE client.
* @session: the client session.
* @msg_raw: the message data.
* @msg_len: the length of the message data.
*/
static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
void *data, size_t len)
void *__msg, size_t msg_len)
{
struct mgmt_msg_get_tree *msg = data;
struct mgmt_msg_get_tree *msg = __msg;
struct lysc_node **snodes = NULL;
char *xpath_resolved = NULL;
uint64_t req_id = msg->req_id;
uint64_t clients;
bool simple_xpath;
LY_ERR err;
int ret;

MGMTD_FE_ADAPTER_DBG("Received get-tree request from client %s for session-id %" PRIu64
" req-id %" PRIu64,
session->adapter->name, session->session_id,
msg->req_id);

if (!MGMT_MSG_VALIDATE_NUL_TERM(msg, msg_len)) {
fe_adapter_send_error(session, req_id, false, -EINVAL,
"Invalid message rcvd from session-id: %" PRIu64,
session->session_id);
goto done;
}

if (session->txn_id != MGMTD_TXN_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"Transaction in progress txn-id: %" PRIu64
" for session-id: %" PRIu64,
session->txn_id, session->session_id);
return;
goto done;
}


err = yang_resolve_snode_xpath(ly_native_ctx, msg->xpath, &snodes,
&simple_xpath);
if (err) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"XPath doesn't resolve for session-id: %" PRIu64,
session->session_id);
goto done;
}
darr_free(snodes);

clients = mgmt_be_interested_clients(msg->xpath, false);
if (!clients) {
MGMTD_FE_ADAPTER_DBG("No backends provide xpath: %s for txn-id: %" PRIu64
Expand All @@ -1164,7 +1189,7 @@ static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,

fe_adapter_send_tree_data(session, req_id, false,
msg->result_type, NULL, 0);
return;
goto done;
}

/* Start a SHOW Transaction */
Expand All @@ -1173,7 +1198,7 @@ static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
if (session->txn_id == MGMTD_SESSION_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"failed to create a 'show' txn");
return;
goto done;
}

MGMTD_FE_ADAPTER_DBG("Created new show txn-id: %" PRIu64
Expand All @@ -1182,13 +1207,17 @@ static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,

/* Create a GET-TREE request under the transaction */
ret = mgmt_txn_send_get_tree_oper(session->txn_id, req_id, clients,
msg->result_type, msg->xpath);
msg->result_type, simple_xpath,
msg->xpath);
if (ret) {
/* destroy the just created txn */
mgmt_destroy_txn(&session->txn_id);
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"failed to create a 'show' txn");
}
done:
darr_free(snodes);
darr_free(xpath_resolved);
}

/**
Expand Down
Loading

0 comments on commit 89a7f47

Please sign in to comment.