diff options
author | Christian Hopps <chopps@labn.net> | 2024-01-11 14:25:54 +0100 |
---|---|---|
committer | Christian Hopps <chopps@labn.net> | 2024-01-11 14:38:57 +0100 |
commit | 32a4c4019ec6b2f813d4992a878f192c8ea422bb (patch) | |
tree | aaa6aeea8bdfdee7d871c52a41f08e12af625296 /lib/northbound_oper.c | |
parent | Merge pull request #15098 from donaldsharp/lib_zebra_h_cleanup_2 (diff) | |
download | frr-32a4c4019ec6b2f813d4992a878f192c8ea422bb.tar.xz frr-32a4c4019ec6b2f813d4992a878f192c8ea422bb.zip |
lib: implement missing YANG choice/case statements.
Signed-off-by: Christian Hopps <chopps@labn.net>
Diffstat (limited to 'lib/northbound_oper.c')
-rw-r--r-- | lib/northbound_oper.c | 109 |
1 files changed, 68 insertions, 41 deletions
diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index b5201c630..b796c9d04 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -69,8 +69,10 @@ struct nb_op_node_info { * @xpath: current xpath representing the node_info stack. * @xpath_orig: the original query string from the user * @node_infos: the container stack for the walk from root to current - * @schema_path: the schema nodes for each node in the query string. - # @query_tokstr: the query string tokenized with NUL bytes. + * @schema_path: the schema nodes along the path indicated by the query string. + * this will include the choice and case nodes which are not + * present 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. @@ -204,6 +206,14 @@ static void ys_pop_inner(struct nb_op_yield_state *ys) ys_trim_xpath(ys); } +static void ys_free_inner(struct nb_op_yield_state *ys, + struct nb_op_node_info *ni) +{ + if (!CHECK_FLAG(ni->schema->nodetype, LYS_CASE | LYS_CHOICE)) + lyd_free_tree(&ni->inner->node); + ni->inner = NULL; +} + static void nb_op_get_keys(struct lyd_node_inner *list_node, struct yang_list_keys *keys) { @@ -254,8 +264,7 @@ static bool __move_back_to_next(struct nb_op_yield_state *ys, int i) * The i'th node has been lost after a yield so trim it from the tree * now. */ - lyd_free_tree(&ni->inner->node); - ni->inner = NULL; + ys_free_inner(ys, ni); ni->list_entry = NULL; /* @@ -296,7 +305,7 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) ni = &ys->node_infos[i]; nn = ni->schema->priv; - if (CHECK_FLAG(ni->schema->nodetype, LYS_CONTAINER)) + if (!CHECK_FLAG(ni->schema->nodetype, LYS_LIST)) continue; assert(ni->list_entry != NULL || @@ -418,7 +427,8 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, /* Assert that we are walking the rightmost branch */ assert(!inner->parent || &inner->node == inner->parent->child->prev); - if (CHECK_FLAG(inner->schema->nodetype, LYS_CONTAINER)) { + if (CHECK_FLAG(inner->schema->nodetype, + LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) { /* containers have only zero or one child on a branch of a tree */ inner = (struct lyd_node_inner *)inner->child; assert(!inner || inner->prev == &inner->node); @@ -502,9 +512,12 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) ret = NB_ERR_NOT_FOUND; return ret; } - while (node && - !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) + + /* Move up to the container if on a leaf currently. */ + if (node && + !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) node = &node->parent->node; + assert(CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)); if (!node) return NB_ERR_NOT_FOUND; @@ -512,7 +525,6 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) for (len = 1; inner->parent; len++) inner = inner->parent; - darr_append_nz_mt(ys->node_infos, len, MTYPE_NB_NODE_INFOS); /* @@ -761,7 +773,7 @@ static const struct lysc_node *nb_op_sib_next(struct nb_op_yield_state *ys, /* * If the node info stack is shorter than the schema path then we are * doign specific query still on the node from the schema path (should - * match) so just return NULL. + * match) so just return NULL (i.e., don't process siblings) */ if (darr_len(ys->schema_path) > darr_len(ys->node_infos)) return NULL; @@ -826,7 +838,10 @@ 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) != NULL && darr_last(ys->node_infos)->schema == parent); + if (darr_last(ys->node_infos) != NULL && + !CHECK_FLAG(darr_last(ys->node_infos)->schema->nodetype, + LYS_CASE | LYS_CHOICE)) + assert(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]; @@ -937,28 +952,30 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) if (!sib) { /* * We've reached the end of the siblings inside a - * containing node; either a container or a specific - * list node entry. + * containing node; either a container, case, choice, or + * a specific list node entry. * - * We handle container node inline; however, for lists - * we are only done with a specific entry and need to - * move to the next element on the list so we drop down - * into the switch for that case. + * We handle case/choice/container node inline; however, + * for lists we are only done with a specific entry and + * need to move to the next element on the list so we + * drop down into the switch for that case. */ /* Grab the containing node. */ sib = ni->schema; - if (sib->nodetype == LYS_CONTAINER) { + if (CHECK_FLAG(sib->nodetype, + LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) { /* If we added an empty container node (no * children) and it's not a presence container * or it's not backed by the get_elem callback, * remove the node from the tree. */ - if (!lyd_child(&ni->inner->node) && + if (sib->nodetype == LYS_CONTAINER && + !lyd_child(&ni->inner->node) && !nb_op_empty_container_ok(sib, ys->xpath, ni->list_entry)) - lyd_free_tree(&ni->inner->node); + ys_free_inner(ys, ni); /* If we have returned to our original walk base, * then we are done with the walk. @@ -995,11 +1012,13 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) */ } - /* TODO: old code checked for "first" here and skipped if set */ if (CHECK_FLAG(sib->nodetype, LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER)) xpath_child = nb_op_get_child_path(ys->xpath, sib, xpath_child); + else if (CHECK_FLAG(sib->nodetype, LYS_CASE | LYS_CHOICE)) + darr_in_strdup(xpath_child, ys->xpath); + nn = sib->priv; switch (sib->nodetype) { @@ -1032,27 +1051,31 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) goto done; sib = nb_op_sib_next(ys, sib); continue; + case LYS_CASE: + case LYS_CHOICE: case LYS_CONTAINER: if (CHECK_FLAG(nn->flags, F_NB_NODE_CONFIG_ONLY)) { sib = nb_op_sib_next(ys, sib); continue; } - node = NULL; - err = lyd_new_inner(&ni->inner->node, sib->module, - sib->name, false, &node); - if (err) { - ret = NB_ERR_RESOURCE; - goto done; + if (sib->nodetype != LYS_CONTAINER) { + /* Case/choice use parent inner. */ + node = &ni->inner->node; + } else { + err = lyd_new_inner(&ni->inner->node, + sib->module, sib->name, + false, &node); + if (err) { + ret = NB_ERR_RESOURCE; + goto done; + } } - /* push this container node on top of the stack */ + /* push this choice/container node on top of the stack */ ni = darr_appendz(ys->node_infos); ni->inner = (struct lyd_node_inner *)node; - ni->schema = node->schema; - ni->niters = 0; - ni->nents = 0; - ni->has_lookup_next = false; + ni->schema = sib; ni->lookup_next_ok = ni[-1].lookup_next_ok; ni->list_entry = ni[-1].list_entry; @@ -1279,7 +1302,7 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) lysc_is_key((*darr_last(ys->schema_path)))) && /* is this at or below the base? */ darr_ilen(ys->node_infos) <= ys->query_base_level) - lyd_free_tree(&ni->inner->node); + ys_free_inner(ys, ni); if (!list_entry) { @@ -1433,12 +1456,6 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) sib = nb_op_sib_first(ys, sib); continue; - case LYS_CHOICE: - /* Container type with no data */ - /*FALLTHROUGH*/ - case LYS_CASE: - /* Container type with no data */ - /*FALLTHROUGH*/ default: /*FALLTHROUGH*/ case LYS_ANYXML: @@ -1534,8 +1551,7 @@ static void nb_op_trim_yield_state(struct nb_op_yield_state *ys) DEBUGD(&nb_dbg_events, "NB oper-state: deleting tree at level %d", i); __free_siblings(&ni->inner->node); - lyd_free_tree(&ni->inner->node); - ni->inner = NULL; + ys_free_inner(ys, ni); while (--i > 0) { DEBUGD(&nb_dbg_events, @@ -1648,6 +1664,17 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys, int nlen = strlen(name); int mnlen = 0; + /* + * Technically the query_token for choice/case should probably be pointing at + * the child (leaf) rather than the parent (container), however, + * we only use these for processing list nodes so KISS. + */ + if (CHECK_FLAG(ys->schema_path[i]->nodetype, + LYS_CASE | LYS_CHOICE)) { + ys->query_tokens[i] = ys->query_tokens[i - 1]; + continue; + } + while (true) { s2 = strstr(s, name); if (!s2) |