summaryrefslogtreecommitdiff
path: root/lib/northbound_oper.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/northbound_oper.c')
-rw-r--r--lib/northbound_oper.c293
1 files changed, 233 insertions, 60 deletions
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c
index a3ff360780..6336db502a 100644
--- a/lib/northbound_oper.c
+++ b/lib/northbound_oper.c
@@ -35,6 +35,7 @@
* We must also process containers with lookup-next descendants last.
*/
+DEFINE_MTYPE_STATIC(LIB, NB_STATE, "Northbound State");
DEFINE_MTYPE_STATIC(LIB, NB_YIELD_STATE, "NB Yield State");
DEFINE_MTYPE_STATIC(LIB, NB_NODE_INFOS, "NB Node Infos");
@@ -54,6 +55,7 @@ struct nb_op_node_info {
struct lyd_node *inner;
const struct lysc_node *schema; /* inner schema in case we rm inner */
struct yang_list_keys keys; /* if list, keys to locate element */
+ uint position; /* if keyless list, list position */
const void *list_entry; /* opaque entry from user or NULL */
uint xpath_len; /* length of the xpath string for this node */
uint niters; /* # list elems create this iteration */
@@ -232,6 +234,22 @@ static void nb_op_get_keys(struct lyd_node_inner *list_node,
keys->num = n;
}
+static uint nb_op_get_position_predicate(struct nb_op_yield_state *ys, struct nb_op_node_info *ni)
+{
+ const char *cursor = ys->xpath + ni->xpath_len - 1;
+
+ if (cursor[0] != ']')
+ return 0;
+
+ while (--cursor > ys->xpath && isdigit(cursor[0]))
+ ;
+
+ if (cursor[0] != '[')
+ return 0;
+
+ return atoi(&cursor[1]);
+}
+
/**
* __move_back_to_next() - move back to the next lookup-next schema
*/
@@ -344,7 +362,8 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys)
/**
* nb_op_xpath_to_trunk() - generate a lyd_node tree (trunk) using an xpath.
* @xpath_in: xpath query string to build trunk from.
- * @dnode: resulting tree (trunk)
+ * @xpath_out: resulting xpath for the trunk.
+ * @trunk: resulting tree (trunk)
*
* Use the longest prefix of @xpath_in as possible to resolve to a tree (trunk).
* This is logically as if we walked along the xpath string resolving each
@@ -352,7 +371,7 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys)
*
* Return: error if any, if no error then @dnode contains the tree (trunk).
*/
-static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in,
+static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, char **xpath_out,
struct lyd_node **trunk)
{
char *xpath = NULL;
@@ -370,7 +389,10 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in,
if (ret != NB_OK)
break;
}
- darr_free(xpath);
+ if (ret == NB_OK)
+ *xpath_out = xpath;
+ else
+ darr_free(xpath);
return ret;
}
@@ -410,28 +432,57 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys,
ni->lookup_next_ok = yield_ok && ni->has_lookup_next &&
(index == 0 || ni[-1].lookup_next_ok);
- nb_op_get_keys((struct lyd_node_inner *)inner, &ni->keys);
+ if (CHECK_FLAG(nn->flags, F_NB_NODE_KEYLESS_LIST)) {
+ uint i;
- /* A list entry cannot be present in a tree w/o it's keys */
- assert(ni->keys.num == yang_snode_num_keys(inner->schema));
+ ni->position = nb_op_get_position_predicate(ys, ni);
+ if (!ni->position) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: can't decode keyless list positional predicate in %s",
+ __func__, ys->xpath);
+ return NB_ERR_NOT_FOUND;
+ }
- /*
- * Get this nodes opaque list_entry object
- */
+ /*
+ * Get the entry at the position given by the predicate
+ */
- if (!nn->cbs.lookup_entry) {
- flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
- "%s: data path doesn't support iteration over operational data: %s",
- __func__, ys->xpath);
- return NB_ERR_NOT_FOUND;
- }
+ /* ni->list_entry starts as the parent entry of this node */
+ ni->list_entry = nb_callback_get_next(nn, ni->list_entry, NULL);
+ for (i = 1; i < ni->position && ni->list_entry; i++)
+ ni->list_entry = nb_callback_get_next(nn, ni->list_entry, ni->list_entry);
- /* ni->list_entry starts as the parent entry of this node */
- ni->list_entry = nb_callback_lookup_entry(nn, ni->list_entry, &ni->keys);
- if (ni->list_entry == NULL) {
- flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
- "%s: list entry lookup failed", __func__);
- return NB_ERR_NOT_FOUND;
+ if (i != ni->position || !ni->list_entry) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: entry at position %d doesn't exist in: %s", __func__,
+ ni->position, ys->xpath);
+ return NB_ERR_NOT_FOUND;
+ }
+
+ } else {
+ nb_op_get_keys((struct lyd_node_inner *)inner, &ni->keys);
+ /* A list entry cannot be present in a tree w/o it's keys */
+ assert(ni->keys.num == yang_snode_num_keys(inner->schema));
+
+ /*
+ * Get this nodes opaque list_entry object
+ */
+
+ /* We need a lookup entry unless this is a keyless list */
+ if (!nn->cbs.lookup_entry && ni->keys.num) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: data path doesn't support iteration over operational data: %s",
+ __func__, ys->xpath);
+ return NB_ERR_NOT_FOUND;
+ }
+
+ /* ni->list_entry starts as the parent entry of this node */
+ ni->list_entry = nb_callback_lookup_entry(nn, ni->list_entry, &ni->keys);
+ if (ni->list_entry == NULL) {
+ flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: list entry lookup failed",
+ __func__);
+ return NB_ERR_NOT_FOUND;
+ }
}
/*
@@ -460,8 +511,9 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
struct lyd_node *inner;
struct lyd_node *node = NULL;
enum nb_error ret;
- uint i, len;
- char *tmp;
+ const char *cur;
+ char *xpath = NULL;
+ uint i, len, prevlen, xplen;
/*
* Obtain the trunk of the data node tree of the query.
@@ -471,8 +523,8 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
* node could be identified (e.g., a list-node name with no keys).
*/
- ret = nb_op_xpath_to_trunk(ys->xpath, &node);
- if (ret || !node) {
+ ret = nb_op_xpath_to_trunk(ys->xpath, &xpath, &node);
+ if (ret != NB_OK || !node) {
flog_warn(EC_LIB_LIBYANG,
"%s: can't instantiate concrete path using xpath: %s",
__func__, ys->xpath);
@@ -482,12 +534,18 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
}
/* Move up to the container if on a leaf currently. */
- if (node &&
- !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) {
+ if (!CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) {
struct lyd_node *leaf = node;
node = &node->parent->node;
+ /* Have to trim the leaf from the xpath now */
+ ret = yang_xpath_pop_node(xpath);
+ if (ret != NB_OK) {
+ darr_free(xpath);
+ return ret;
+ }
+
/*
* If the leaf is not a key, delete it, because it has a wrong
* empty value.
@@ -495,10 +553,7 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
if (!lysc_is_key(leaf->schema))
lyd_free_tree(leaf);
}
- assert(!node ||
- CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST));
- if (!node)
- return NB_ERR_NOT_FOUND;
+ assert(CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST));
inner = node;
for (len = 1; inner->parent; len++)
@@ -511,26 +566,42 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys)
* -- save the prefix length.
*/
inner = node;
+ prevlen = 0;
+ xplen = strlen(xpath);
+ darr_free(xpath);
for (i = len; i > 0; i--, inner = &inner->parent->node) {
ni = &ys->node_infos[i - 1];
ni->inner = inner;
ni->schema = inner->schema;
+
+ if (i == len) {
+ prevlen = xplen;
+ ni->xpath_len = prevlen;
+ continue;
+ }
+
/*
- * NOTE: we could build this by hand with a litte more effort,
- * but this simple implementation works and won't be expensive
- * since the number of nodes is small and only done once per
- * query.
+ * The only predicates we should have are concrete ones at this
+ * point b/c of nb_op_xpath_to_trunk() above, so we aren't in
+ * danger of finding a division symbol in the path, only '/'s
+ * inside strings which frrstr_back_to_char skips over.
*/
- tmp = yang_dnode_get_path(inner, NULL, 0);
- ni->xpath_len = strlen(tmp);
- /* Replace users supplied xpath with the libyang returned value */
- if (i == len)
- darr_in_strdup(ys->xpath, tmp);
+ assert(prevlen == xplen || ys->xpath[prevlen] == '/');
+ if (prevlen != xplen)
+ ys->xpath[prevlen] = 0;
+ cur = frrstr_back_to_char(ys->xpath, '/');
+ if (prevlen != xplen)
+ ys->xpath[prevlen] = '/';
+
+ if (!cur || cur == ys->xpath) {
+ flog_warn(EC_LIB_LIBYANG, "%s: error tokenizing query xpath: %s", __func__,
+ ys->xpath);
+ return NB_ERR_VALIDATION;
+ }
- /* The prefix must match the prefix of the stored xpath */
- assert(!strncmp(tmp, ys->xpath, ni->xpath_len));
- free(tmp);
+ prevlen = cur - ys->xpath;
+ ni->xpath_len = prevlen;
}
/*
@@ -584,6 +655,10 @@ static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys,
if (lysc_is_key(snode))
return NB_OK;
+ /* Check for new simple get */
+ if (nb_node->cbs.get)
+ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner);
+
data = nb_callback_get_elem(nb_node, xpath, ni->list_entry);
if (data == NULL)
return NB_OK;
@@ -617,6 +692,10 @@ static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys,
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return NB_OK;
+ /* Check for new simple get */
+ if (nb_node->cbs.get)
+ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner);
+
do {
struct yang_data *data;
@@ -1483,17 +1562,13 @@ static void nb_op_walk_continue(struct event *thread)
ret = __walk(ys, true);
if (ret == NB_YIELD) {
- if (nb_op_yield(ys) != NB_OK) {
- if (ys->should_batch)
- goto stopped;
- else
- goto finish;
- }
- return;
+ ret = nb_op_yield(ys);
+ if (ret == NB_OK)
+ return;
}
finish:
+ assert(ret != NB_YIELD);
(*ys->finish)(ys_root_node(ys), ys->finish_arg, ret);
-stopped:
nb_op_free_yield_state(ys, false);
}
@@ -1552,6 +1627,13 @@ static void nb_op_trim_yield_state(struct nb_op_yield_state *ys)
(int)darr_lasti(ys->node_infos));
}
+/**
+ * nb_op_yield() - Yield during the walk.
+ * @ys: the yield state tracking the walk.
+ *
+ * Return: Any error from the `ys->finish` callback which should terminate the
+ * walk. Otherwise if `ys->should_batch` == false always returns NB_OK.
+ */
static enum nb_error nb_op_yield(struct nb_op_yield_state *ys)
{
enum nb_error ret;
@@ -1752,6 +1834,20 @@ bool nb_oper_is_yang_lib_query(const char *xpath)
return strlen(xpath) > liblen;
}
+void *nb_oper_walk_finish_arg(void *walk)
+{
+ struct nb_op_yield_state *ys = walk;
+
+ return ys->finish_arg;
+}
+
+void *nb_oper_walk_cb_arg(void *walk)
+{
+ struct nb_op_yield_state *ys = walk;
+
+ return ys->cb_arg;
+}
+
void *nb_oper_walk(const char *xpath, struct yang_translator *translator,
uint32_t flags, bool should_batch, nb_oper_data_cb cb,
void *cb_arg, nb_oper_data_finish_cb finish, void *finish_arg)
@@ -1764,17 +1860,13 @@ void *nb_oper_walk(const char *xpath, struct yang_translator *translator,
ret = nb_op_walk_start(ys);
if (ret == NB_YIELD) {
- if (nb_op_yield(ys) != NB_OK) {
- if (ys->should_batch)
- goto stopped;
- else
- goto finish;
- }
- return ys;
+ ret = nb_op_yield(ys);
+ if (ret == NB_OK)
+ return ys;
}
-finish:
+
+ assert(ret != NB_YIELD);
(void)(*ys->finish)(ys_root_node(ys), ys->finish_arg, ret);
-stopped:
nb_op_free_yield_state(ys, false);
return NULL;
}
@@ -1826,6 +1918,87 @@ enum nb_error nb_oper_iterate_legacy(const char *xpath,
return ret;
}
+static const char *__adjust_ptr(struct lysc_node_leaf *lsnode, const char *valuep, size_t *size)
+{
+ switch (lsnode->type->basetype) {
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+#ifdef BIG_ENDIAN
+ valuep += 7;
+#endif
+ *size = 1;
+ break;
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+#ifdef BIG_ENDIAN
+ valuep += 6;
+#endif
+ *size = 2;
+ break;
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+#ifdef BIG_ENDIAN
+ valuep += 4;
+#endif
+ *size = 4;
+ break;
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ *size = 8;
+ break;
+ case LY_TYPE_UNKNOWN:
+ case LY_TYPE_BINARY:
+ case LY_TYPE_STRING:
+ case LY_TYPE_BITS:
+ case LY_TYPE_BOOL:
+ case LY_TYPE_DEC64:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_ENUM:
+ case LY_TYPE_IDENT:
+ case LY_TYPE_INST:
+ case LY_TYPE_LEAFREF:
+ case LY_TYPE_UNION:
+ default:
+ assert(0);
+ }
+ return valuep;
+}
+
+enum nb_error nb_oper_uint64_get(const struct nb_node *nb_node, const void *parent_list_entry,
+ struct lyd_node *parent)
+{
+ struct lysc_node_leaf *lsnode = (struct lysc_node_leaf *)nb_node->snode;
+ struct lysc_node *snode = &lsnode->node;
+ ssize_t offset = (ssize_t)nb_node->cbs.get_elem;
+ uint64_t ubigval = *(uint64_t *)((char *)parent_list_entry + offset);
+ const char *valuep;
+ size_t size;
+
+ valuep = __adjust_ptr(lsnode, (const char *)&ubigval, &size);
+ if (lyd_new_term_bin(parent, snode->module, snode->name, valuep, size, LYD_NEW_PATH_UPDATE,
+ NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
+}
+
+
+enum nb_error nb_oper_uint32_get(const struct nb_node *nb_node, const void *parent_list_entry,
+ struct lyd_node *parent)
+{
+ struct lysc_node_leaf *lsnode = (struct lysc_node_leaf *)nb_node->snode;
+ struct lysc_node *snode = &lsnode->node;
+ ssize_t offset = (ssize_t)nb_node->cbs.get_elem;
+ uint64_t ubigval = *(uint64_t *)((char *)parent_list_entry + offset);
+ const char *valuep;
+ size_t size;
+
+ valuep = __adjust_ptr(lsnode, (const char *)&ubigval, &size);
+ if (lyd_new_term_bin(parent, snode->module, snode->name, valuep, size, LYD_NEW_PATH_UPDATE,
+ NULL))
+ return NB_ERR_RESOURCE;
+ return NB_OK;
+}
+
void nb_oper_init(struct event_loop *loop)
{
event_loop = loop;