Skip to content

Commit

Permalink
context UPDATE support for storing leafref references (#2155)
Browse files Browse the repository at this point in the history
* Adding leafref references and API to lyd_node_term

* Adjusted to require context flag

* Extended the utest for tree_data

* Fixed typo in docs

* Refactored based on PR discussion

* Refactored to use hash table for leafref nodes
  • Loading branch information
steweg authored Jan 18, 2024
1 parent 42c96e2 commit f9041a2
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ struct ly_ctx {
void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */
pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
struct ly_ht *leafref_links_ht; /**< hash table of leafref links between term data nodes */
};

/**
Expand Down
44 changes: 44 additions & 0 deletions src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,30 @@ ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UN
return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid);
}

/**
* @brief Hash table value-equal callback for comparing leafref links hash table record.
*/
static ly_bool
ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
{
struct lyd_leafref_links_rec *rec1 = val1_p, *rec2 = val2_p;

return rec1->node == rec2->node;
}

/**
* @brief Callback for freeing leafref links recorcd internal resources.
*
* @param[in] val_p Pointer to leafref links record
*/
static void
ly_ctx_ht_leafref_links_rec_free(void *val_p)
{
struct lyd_leafref_links_rec *rec = val_p;

lyd_free_leafref_links_rec(rec);
}

LIBYANG_API_DEF LY_ERR
ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
{
Expand All @@ -262,6 +286,11 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
/* plugins */
LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);

if (options & LY_CTX_LEAFREF_LINKING) {
ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1);
LY_CHECK_ERR_GOTO(!ctx->leafref_links_ht, rc = LY_EMEM, cleanup);
}

/* initialize thread-specific error hash table */
ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1);
LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup);
Expand Down Expand Up @@ -588,6 +617,11 @@ ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option)
LY_CHECK_ERR_RET((option & LY_CTX_NO_YANGLIBRARY) && !(ctx->flags & LY_CTX_NO_YANGLIBRARY),
LOGARG(ctx, option), LY_EINVAL);

if (!(ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) {
ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1);
LY_CHECK_ERR_RET(!ctx->leafref_links_ht, LOGARG(ctx, option), LY_EMEM);
}

if (!(ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
ctx->flags |= LY_CTX_SET_PRIV_PARSED;
/* recompile the whole context to set the priv pointers */
Expand Down Expand Up @@ -628,6 +662,11 @@ ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option)
LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);

if ((ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) {
lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free);
ctx->leafref_links_ht = NULL;
}

if ((ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
struct lys_module *mod;
uint32_t index;
Expand Down Expand Up @@ -1318,6 +1357,11 @@ ly_ctx_destroy(struct ly_ctx *ctx)
/* leftover unres */
lys_unres_glob_erase(&ctx->unres);

/* clean the leafref links hash table */
if (ctx->leafref_links_ht) {
lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free);
}

/* clean the error hash table */
lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free);

Expand Down
4 changes: 4 additions & 0 deletions src/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ struct ly_ctx;
enabled. */
#define LY_CTX_LEAFREF_EXTENDED 0x0200 /**< By default, path attribute of leafref accepts only path as defined in RFC 7950.
By using this option, the path attribute will also allow using XPath functions as deref() */
#define LY_CTX_LEAFREF_LINKING 0x0400 /**< Link valid leafref nodes with its target during validation if leafref node is not using
'require-instance false;'. It also enables usage of
[lyd_leafref_get_links](@ref lyd_leafref_get_links) and
[lyd_leafref_link_node_tree](@ref lyd_leafref_link_node_tree) APIs. */

/** @} contextoptions */

Expand Down
10 changes: 8 additions & 2 deletions src/plugins_types/leafref.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "common.h"
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
#include "tree_data_internal.h" /* lyd_link_leafref_node */

/**
* @page howtoDataLYB LYB Binary Format
Expand Down Expand Up @@ -63,12 +64,13 @@ lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type,
}

LIBYANG_API_DEF LY_ERR
lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node,
lyplg_type_validate_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
{
LY_ERR ret;
struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
char *errmsg = NULL, *path;
struct lyd_node *target = NULL;

*err = NULL;

Expand All @@ -78,13 +80,17 @@ lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_
}

/* check leafref target existence */
if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, &target, &errmsg)) {
path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg);
free(errmsg);
return ret;
}

if (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_LINKING) {
return lyd_link_leafref_node((struct lyd_node_term *)target, (struct lyd_node_term *)ctx_node);
}

return LY_SUCCESS;
}

Expand Down
150 changes: 150 additions & 0 deletions src/tree_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,11 @@ lyd_unlink(struct lyd_node *node)
/* update hashes while still linked into the tree */
lyd_unlink_hash(node);

/* unlink leafref nodes */
if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) {
lyd_free_leafref_nodes((struct lyd_node_term *)node);
}

/* unlink from siblings */
if (node->prev->next) {
node->prev->next = node->next;
Expand Down Expand Up @@ -3268,3 +3273,148 @@ lyd_get_value(const struct lyd_node *node)

return NULL;
}

LY_ERR
lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create)
{
struct ly_ht *ht;
uint32_t hash;
struct lyd_leafref_links_rec rec;

assert(node);
assert(record);

if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
*record = NULL;
return LY_EDENIED;
}

rec.node = node;
rec.leafref_nodes = NULL;
rec.target_node = NULL;

ht = LYD_CTX(node)->leafref_links_ht;
hash = lyht_hash((const char *)&node, sizeof & node);
if (lyht_find(ht, &rec, hash, (void **)record) == LY_ENOTFOUND) {
if (create) {
LY_CHECK_RET(lyht_insert_no_check(ht, &rec, hash, (void **)record));
} else {
*record = NULL;
return LY_ENOTFOUND;
}
}

return LY_SUCCESS;
}

LIBYANG_API_DEF LY_ERR
lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record)
{
LY_CHECK_ARG_RET(NULL, node, record, LY_EINVAL);

return lyd_get_or_create_leafref_links_record(node, (struct lyd_leafref_links_rec **)record, 0);
}

LY_ERR
lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node)
{
LY_ARRAY_COUNT_TYPE u;
const struct lyd_node_term **item = NULL;
struct lyd_leafref_links_rec *rec;

assert(node);
assert(leafref_node);

if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
return LY_EDENIED;
}

LY_CHECK_RET(lyd_get_or_create_leafref_links_record(node, &rec, 1));
LY_ARRAY_FOR(rec->leafref_nodes, u) {
if (rec->leafref_nodes[u] == leafref_node) {
return LY_SUCCESS;
}
}

LY_ARRAY_NEW_RET(LYD_CTX(node), rec->leafref_nodes, item, LY_EMEM);
*item = leafref_node;
LY_CHECK_RET(lyd_get_or_create_leafref_links_record(leafref_node, &rec, 1));
rec->target_node = node;
return LY_SUCCESS;
}

LIBYANG_API_DEF LY_ERR
lyd_leafref_link_node_tree(const struct lyd_node *tree)
{
const struct lyd_node *sibling, *elem;
struct lyd_node *target;
char *errmsg;
struct lyd_node_term *leafref_node;
struct lysc_node_leaf *leaf_schema;
struct lysc_type_leafref *lref;
LY_ERR ret;

LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);

if (!(ly_ctx_get_options(LYD_CTX(tree)) & LY_CTX_LEAFREF_LINKING)) {
return LY_EDENIED;
}

LY_LIST_FOR(tree, sibling) {
LYD_TREE_DFS_BEGIN(sibling, elem) {
if (elem->schema->nodetype & LYD_NODE_TERM) {
leafref_node = (struct lyd_node_term *)elem;
leaf_schema = (struct lysc_node_leaf *)elem->schema;
if (leaf_schema->type->basetype == LY_TYPE_LEAFREF) {
lref = (struct lysc_type_leafref *)leaf_schema->type;
if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, &target, &errmsg)) {
free(errmsg);
} else if (target->schema->nodetype & LYD_NODE_TERM) {
ret = lyd_link_leafref_node((struct lyd_node_term *)target, leafref_node);
if (ret != LY_SUCCESS) {
return ret;
}
}
}
}
LYD_TREE_DFS_END(sibling, elem);
}
}
return LY_SUCCESS;
}

LY_ERR
lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node)
{
LY_ERR ret;
struct lyd_leafref_links_rec *rec;

assert(node);
assert(leafref_node);

if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
return LY_EDENIED;
}

ret = lyd_get_or_create_leafref_links_record(node, &rec, 0);
if (ret == LY_SUCCESS) {
LY_ARRAY_REMOVE_VALUE(rec->leafref_nodes, leafref_node);
if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
lyd_free_leafref_nodes(node);
}
} else if (ret != LY_ENOTFOUND) {
return ret;
}

ret = lyd_get_or_create_leafref_links_record(leafref_node, &rec, 0);
if (ret == LY_SUCCESS) {
rec->target_node = NULL;
if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
lyd_free_leafref_nodes(leafref_node);
}
} else if (ret != LY_ENOTFOUND) {
return ret;
}

return LY_SUCCESS;
}
39 changes: 39 additions & 0 deletions src/tree_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,22 @@ struct lyd_node_opaq {
const struct ly_ctx *ctx; /**< libyang context */
};

/**
* @brief Structure of leafref links record.
*/
struct lyd_leafref_links_rec {
const struct lyd_node_term *node; /** pointer to the data node itself */
const struct lyd_node_term **leafref_nodes; /** list of the leafref pointing to this data node [sized array](@ref sizedarrays)),
By default it is empty. It is filled automatically by validation function of
leafref nodes, which are valid and are not using 'require-instance false;'.
It can also be populated based on manual request using
[link api](@ref lyd_leafref_link_node_tree). Freeing of the resources is
automatic. */
const struct lyd_node_term *target_node; /** pointer to leafref target data node, by default is NULL. The logic
is the same as for [leafref_nodes](@ref leafref_nodes) and is filled only
for leafrefs */
};

/**
* @brief Get the generic parent pointer of a data node.
*
Expand Down Expand Up @@ -2702,6 +2718,29 @@ LIBYANG_API_DECL LY_ERR ly_time_str2ts(const char *value, struct timespec *ts);
*/
LIBYANG_API_DECL LY_ERR ly_time_ts2str(const struct timespec *ts, char **str);

/**
* @brief Gets the leafref links record for given node
*
* This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
*
* @param[in] node The term data node.
* @param[out] record The leafref links record
* @return LY_SUCCESS on success.
* @return LY_ERR value on error.
*/
LIBYANG_API_DECL LY_ERR lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record);

/**
* @brief Traverse through data tree including root node siblings and adds leafrefs links to the given nodes
*
* This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
*
* @param[in] tree The data tree root node.
* @return LY_SUCCESS on success.
* @return LY_ERR value on error.
*/
LIBYANG_API_DECL LY_ERR lyd_leafref_link_node_tree(const struct lyd_node *tree);

#ifdef __cplusplus
}
#endif
Expand Down
Loading

0 comments on commit f9041a2

Please sign in to comment.