mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-25 22:00:41 +00:00
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>
This commit is contained in:
parent
00138ffb47
commit
cf67a7e265
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
@ -1587,6 +1596,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);
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
105
lib/yang.c
105
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
|
||||
}
|
||||
|
29
lib/yang.h
29
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
|
||||
|
@ -1132,14 +1132,21 @@ done:
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -1147,14 +1154,32 @@ static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
|
||||
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
|
||||
@ -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 */
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,10 +173,11 @@ struct mgmt_get_data_req {
|
||||
|
||||
struct txn_req_get_tree {
|
||||
char *xpath; /* xpath of tree to get */
|
||||
uint8_t result_type; /* LYD_FORMAT for results */
|
||||
uint64_t sent_clients; /* Bitmask of clients sent req to */
|
||||
uint64_t recv_clients; /* Bitmask of clients recv reply from */
|
||||
int32_t partial_error; /* an error while gather results */
|
||||
uint8_t result_type; /* LYD_FORMAT for results */
|
||||
uint8_t simple_xpath; /* if xpath is simple */
|
||||
struct lyd_node *client_results; /* result tree from clients */
|
||||
};
|
||||
|
||||
@ -1268,22 +1269,33 @@ static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn,
|
||||
{
|
||||
struct txn_req_get_tree *get_tree = txn_req->req.get_tree;
|
||||
uint64_t req_id = txn_req->req_id;
|
||||
int ret = 0;
|
||||
int ret = NB_OK;
|
||||
|
||||
/* cancel timer and send reply onward */
|
||||
EVENT_OFF(txn->get_tree_timeout);
|
||||
|
||||
ret = mgmt_fe_adapter_send_tree_data(txn->session_id, txn->txn_id,
|
||||
txn_req->req_id,
|
||||
get_tree->result_type,
|
||||
get_tree->client_results,
|
||||
get_tree->partial_error, false);
|
||||
if (!get_tree->simple_xpath && get_tree->client_results) {
|
||||
/*
|
||||
* We have a complex query so Filter results by the xpath query.
|
||||
*/
|
||||
ret = yang_trim_tree(get_tree->client_results,
|
||||
txn_req->req.get_tree->xpath);
|
||||
}
|
||||
|
||||
if (ret == NB_OK)
|
||||
ret = mgmt_fe_adapter_send_tree_data(txn->session_id,
|
||||
txn->txn_id,
|
||||
txn_req->req_id,
|
||||
get_tree->result_type,
|
||||
get_tree->client_results,
|
||||
get_tree->partial_error,
|
||||
false);
|
||||
|
||||
/* we're done with the request */
|
||||
mgmt_txn_req_free(&txn_req);
|
||||
|
||||
if (ret) {
|
||||
MGMTD_TXN_ERR("Error saving the results of GETTREE for txn-id %" PRIu64
|
||||
MGMTD_TXN_ERR("Error sending the results of GETTREE for txn-id %" PRIu64
|
||||
" req_id %" PRIu64 " to requested type %u",
|
||||
txn->txn_id, req_id, get_tree->result_type);
|
||||
|
||||
@ -2352,7 +2364,7 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id,
|
||||
*/
|
||||
int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
|
||||
uint64_t clients, LYD_FORMAT result_type,
|
||||
const char *xpath)
|
||||
bool simple_xpath, const char *xpath)
|
||||
{
|
||||
struct mgmt_msg_get_tree *msg;
|
||||
struct mgmt_txn_ctx *txn;
|
||||
@ -2370,6 +2382,7 @@ int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
|
||||
txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETTREE);
|
||||
get_tree = txn_req->req.get_tree;
|
||||
get_tree->result_type = result_type;
|
||||
get_tree->simple_xpath = simple_xpath;
|
||||
get_tree->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath);
|
||||
|
||||
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_tree, slen + 1,
|
||||
|
@ -203,13 +203,15 @@ extern int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id,
|
||||
* req_id: FE client request identifier.
|
||||
* clients: Bitmask of clients to send get-tree to.
|
||||
* result_type: LYD_FORMAT result format.
|
||||
* simple_xpath: true if xpath is simple (only key predicates).
|
||||
* xpath: The xpath to get the tree from.
|
||||
*
|
||||
* Return:
|
||||
* 0 on success.
|
||||
*/
|
||||
extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
|
||||
uint64_t clients, LYD_FORMAT result_type,
|
||||
const char *xpath);
|
||||
bool simple_xpath, const char *xpath);
|
||||
|
||||
/*
|
||||
* Notifiy backend adapter on connection.
|
||||
|
Loading…
Reference in New Issue
Block a user