plugins exts UPDATE ext callback for data node validation
Fixes #1908
diff --git a/src/parser_common.c b/src/parser_common.c
index 3660b96..9a86ac1 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -58,6 +58,7 @@
ly_set_erase(&lydctx->node_types, NULL);
ly_set_erase(&lydctx->meta_types, NULL);
ly_set_erase(&lydctx->node_when, NULL);
+ ly_set_erase(&lydctx->ext_node, free);
ly_set_erase(&lydctx->ext_val, free);
}
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 8d1f24a..627836a 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -59,7 +59,8 @@
struct ly_set node_when; /**< set of nodes with "when" conditions */
struct ly_set node_types; /**< set of nodes validated with LY_EINCOMPLETE result */
struct ly_set meta_types; /**< set of metadata validated with LY_EINCOMPLETE result */
- struct ly_set ext_val; /**< set of first siblings parsed by extensions to validate */
+ struct ly_set ext_node; /**< set of nodes with extension instances to validate */
+ struct ly_set ext_val; /**< set of nested subtrees parsed by extensions to validate */
struct lyd_node *op_node; /**< if an RPC/action/notification is being parsed, store the pointer to it */
/* callbacks */
@@ -85,6 +86,7 @@
struct ly_set node_when;
struct ly_set node_types;
struct ly_set meta_types;
+ struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
@@ -107,6 +109,7 @@
struct ly_set node_when;
struct ly_set node_types;
struct ly_set meta_types;
+ struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
@@ -135,6 +138,7 @@
struct ly_set node_when;
struct ly_set node_types;
struct ly_set meta_types;
+ struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
@@ -153,6 +157,14 @@
};
/**
+ * @brief Parsed data node with extension instance to validate.
+ */
+struct lyd_ctx_ext_node {
+ struct lysc_ext_instance *ext;
+ struct lyd_node *node;
+};
+
+/**
* @brief Common part to supplement the specific ::lyd_ctx_free_clb callbacks.
*/
void lyd_ctx_free(struct lyd_ctx *ctx);
diff --git a/src/parser_json.c b/src/parser_json.c
index 4babe34..82275b6 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1413,7 +1413,10 @@
}
/* add/correct flags */
- lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_RET(lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext));
+
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_RET(lyd_validate_node_ext(*node, &lydctx->ext_node));
} else if (ret == LY_ENOT) {
/* parse it again as an opaq node */
ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node);
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index db68205..fb7d3a1 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -989,7 +989,12 @@
(*node)->meta = *meta;
*meta = NULL;
+ /* insert into parent */
lyb_insert_node(lybctx, parent, *node, first_p, parsed);
+
+ /* store for ext instance node validation, if needed */
+ (void)lyd_validate_node_ext(*node, &lybctx->ext_node);
+
*node = NULL;
}
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 4e19fd8..da43b40 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -841,9 +841,12 @@
}
assert(node);
- /* add/correct flags */
if (snode) {
+ /* add/correct flags */
LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext), error);
+
+ /* store for ext instance node validation, if needed */
+ LY_CHECK_GOTO(ret = lyd_validate_node_ext(node, &lydctx->ext_node), error);
}
/* parser next */
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index 75dbf85..0e521ec 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -102,7 +102,7 @@
/**
* @brief Extensions API version
*/
-#define LYPLG_EXT_API_VERSION 4
+#define LYPLG_EXT_API_VERSION 5
/**
* @brief Macro to define plugin information in external plugins
@@ -161,6 +161,19 @@
typedef void (*lyplg_ext_free_clb)(struct ly_ctx *ctx, struct lysc_ext_instance *ext);
/**
+ * @brief Callback called for all data nodes connected to the extension instance.
+ *
+ * Can be used for additional data node validation. Is called only after the whole data tree is created and standard
+ * validation succeeds.
+ *
+ * @param[in] ext Compiled extension instance.
+ * @param[in] node Data node to process.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+typedef LY_ERR (*lyplg_ext_data_node_clb)(struct lysc_ext_instance *ext, struct lyd_node *node);
+
+/**
* @brief Callback for getting a schema node for a new YANG instance data described by an extension instance.
* Needed only if the extension instance supports some nested standard YANG data.
*
@@ -211,6 +224,8 @@
instance */
lyplg_ext_free_clb free; /**< free the extension-specific data created by its compilation */
+ lyplg_ext_data_node_clb node; /**< callback to validate most relevant data instance for the extension
+ instance */
lyplg_ext_data_snode_clb snode; /**< callback to get schema node for nested YANG data */
lyplg_ext_data_validate_clb validate; /**< callback to validate parsed data instances according to the extension
definition */
diff --git a/src/plugins_exts/metadata.c b/src/plugins_exts/metadata.c
index eebb434..8326f6b 100644
--- a/src/plugins_exts/metadata.c
+++ b/src/plugins_exts/metadata.c
@@ -163,6 +163,7 @@
.plugin.compile = &annotation_compile,
.plugin.sprinter = &annotation_schema_printer,
.plugin.free = annotation_free,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
},
diff --git a/src/plugins_exts/nacm.c b/src/plugins_exts/nacm.c
index 1a50ea3..a83e32b 100644
--- a/src/plugins_exts/nacm.c
+++ b/src/plugins_exts/nacm.c
@@ -161,6 +161,7 @@
.plugin.compile = &nacm_compile,
.plugin.sprinter = NULL,
.plugin.free = NULL,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
}, {
@@ -172,6 +173,7 @@
.plugin.compile = &nacm_compile,
.plugin.sprinter = NULL,
.plugin.free = NULL,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
}, {
@@ -183,6 +185,7 @@
.plugin.compile = &nacm_compile,
.plugin.sprinter = NULL,
.plugin.free = NULL,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
}, {
@@ -194,6 +197,7 @@
.plugin.compile = &nacm_compile,
.plugin.sprinter = NULL,
.plugin.free = NULL,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
},
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index 1cfea52..dce83dd 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -850,6 +850,7 @@
.plugin.compile = &schema_mount_compile,
.plugin.sprinter = NULL,
.plugin.free = &schema_mount_free,
+ .plugin.node = NULL,
.plugin.snode = &schema_mount_snode,
.plugin.validate = &schema_mount_validate
},
diff --git a/src/plugins_exts/yangdata.c b/src/plugins_exts/yangdata.c
index b2e6f62..38e4c16 100644
--- a/src/plugins_exts/yangdata.c
+++ b/src/plugins_exts/yangdata.c
@@ -171,9 +171,10 @@
.name = "yang-data",
.plugin.id = "libyang 2 - yang-data, version 1",
- .plugin.compile = &yangdata_compile,
- .plugin.sprinter = &yangdata_schema_printer,
+ .plugin.compile = yangdata_compile,
+ .plugin.sprinter = yangdata_schema_printer,
.plugin.free = yangdata_free,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
},
diff --git a/src/tree_data.c b/src/tree_data.c
index 0e8e4dc..a216317 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -143,7 +143,7 @@
if (!(parse_opts & LYD_PARSE_ONLY)) {
/* validate data */
rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
- &lydctx->ext_val, NULL);
+ &lydctx->ext_node, &lydctx->ext_val, NULL);
LY_CHECK_GOTO(rc, cleanup);
}
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index ef99f3b..04fc32c 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -1711,7 +1711,7 @@
}
/* resolve when and remove any invalid defaults */
- ret = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff);
+ ret = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL, 0, diff);
LY_CHECK_GOTO(ret, cleanup);
cleanup:
@@ -1781,7 +1781,7 @@
LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, implicit_options, diff), cleanup);
/* resolve when and remove any invalid defaults */
- LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff),
+ LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL, 0, diff),
cleanup);
/* process nested nodes */
diff --git a/src/validation.c b/src/validation.c
index a0e7dfa..b95d0b2 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -264,8 +264,8 @@
LY_ERR
lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type, struct ly_set *node_when,
- uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_val,
- uint32_t val_opts, struct lyd_node **diff)
+ uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_node,
+ struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
uint32_t i;
@@ -287,6 +287,23 @@
} while (i);
}
+ if (ext_node && ext_node->count) {
+ /* validate data nodes with extension instances */
+ i = ext_node->count;
+ do {
+ --i;
+
+ struct lyd_ctx_ext_node *ext_n = ext_node->objs[i];
+
+ /* validate the node */
+ ret = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node);
+ LY_CHECK_RET(ret);
+
+ /* remove this item from the set */
+ ly_set_rm_index(ext_node, i, free);
+ } while (i);
+ }
+
if (node_when) {
/* evaluate all when conditions */
uint32_t prev_count;
@@ -1466,6 +1483,29 @@
return LY_SUCCESS;
}
+LY_ERR
+lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node)
+{
+ struct lyd_ctx_ext_node *ext_n;
+ struct lysc_ext_instance *exts;
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* try to find a relevant extension instance with node callback */
+ exts = node->schema->exts;
+ LY_ARRAY_FOR(exts, u) {
+ if (exts[u].def->plugin && exts[u].def->plugin->node) {
+ /* store for validation */
+ ext_n = malloc(sizeof *ext_n);
+ LY_CHECK_ERR_RET(!ext_n, LOGMEM(LYD_CTX(node)), LY_EMEM);
+ ext_n->ext = &exts[u];
+ ext_n->node = node;
+ LY_CHECK_RET(ly_set_add(ext_node, ext_n, 1, NULL));
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
/**
* @brief Validate the whole data subtree.
*
@@ -1529,22 +1569,23 @@
LY_ERR
lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
- struct ly_set *ext_val_p, struct lyd_node **diff)
+ struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *first, *next, **first2, *iter;
const struct lys_module *mod;
- struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_val = {0};
+ struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
uint32_t i = 0;
assert(tree && ctx);
- assert((node_when_p && node_types_p && meta_types_p && ext_val_p) ||
- (!node_when_p && !node_types_p && !meta_types_p && !ext_val_p));
+ assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
+ (!node_when_p && !node_types_p && !meta_types_p && !ext_node_p && !ext_val_p));
if (!node_when_p) {
node_when_p = &node_when;
node_types_p = &node_types;
meta_types_p = &meta_types;
+ ext_node_p = &ext_node;
ext_val_p = &ext_val;
}
@@ -1598,8 +1639,8 @@
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p, ext_val_p,
- val_opts, diff);
+ ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+ ext_node_p, ext_val_p, val_opts, diff);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
@@ -1611,6 +1652,7 @@
ly_set_erase(&node_when, NULL);
ly_set_erase(&node_types, NULL);
ly_set_erase(&meta_types, NULL);
+ ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
return ret;
}
@@ -1627,7 +1669,7 @@
*diff = NULL;
}
- return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, NULL, diff);
+ return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
}
LIBYANG_API_DEF LY_ERR
@@ -1639,7 +1681,8 @@
*diff = NULL;
}
- return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, diff);
+ return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL,
+ diff);
}
/**
@@ -1717,6 +1760,7 @@
* @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
* @param[in] node_types_p Set of unres node types, if NULL a local set is used.
* @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[in] ext_node_p Set of unres nodes with extensions to validate, if NULL a local set is used.
* @param[in] ext_val_p Set of parsed extension data to validate, if NULL a local set is used.
* @param[out] diff Optional diff with any changes made by the validation.
* @return LY_SUCCESS on success.
@@ -1725,20 +1769,21 @@
static LY_ERR
_lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree, enum lyd_type data_type,
uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p,
- struct ly_set *meta_types_p, struct ly_set *ext_val_p, struct lyd_node **diff)
+ struct ly_set *meta_types_p, struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *op_sibling_before, *op_sibling_after, *child;
- struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_val = {0};
+ struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
assert(op_tree && op_node);
- assert((node_when_p && node_types_p && meta_types_p && ext_val_p) ||
- (!node_when_p && !node_types_p && !meta_types_p && !ext_val_p));
+ assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
+ (!node_when_p && !node_types_p && !meta_types_p && !ext_node_p && !ext_val_p));
if (!node_when_p) {
node_when_p = &node_when;
node_types_p = &node_types;
meta_types_p = &meta_types;
+ ext_node_p = &ext_node;
ext_val_p = &ext_val;
}
@@ -1780,7 +1825,7 @@
/* finish incompletely validated terminal values/attributes and when conditions on the full tree,
* account for unresolved 'when' that may appear in the non-validated dependency data tree */
LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, node_when_p, LYXP_IGNORE_WHEN,
- node_types_p, meta_types_p, ext_val_p, 0, diff), cleanup);
+ node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff), cleanup);
/* perform final validation of the operation/notification */
lyd_validate_obsolete(op_node);
@@ -1806,6 +1851,7 @@
ly_set_erase(&node_when, NULL);
ly_set_erase(&node_types, NULL);
ly_set_erase(&meta_types, NULL);
+ ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
return rc;
}
@@ -1848,8 +1894,8 @@
} else if (op_node->flags & LYD_EXT) {
/* fully validate the rest using the extension instance callback */
LY_CHECK_RET(lyd_validate_nested_ext(op_node, &ext_val));
- rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, NULL, 0, NULL, NULL, &ext_val,
- 0, diff);
+ rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, NULL, 0, NULL, NULL, NULL,
+ &ext_val, 0, diff);
ly_set_erase(&ext_val, free);
return rc;
}
@@ -1877,5 +1923,5 @@
}
/* validate */
- return _lyd_validate_op(op_tree, op_node, dep_tree, data_type, int_opts, 1, NULL, NULL, NULL, NULL, diff);
+ return _lyd_validate_op(op_tree, op_node, dep_tree, data_type, int_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
}
diff --git a/src/validation.h b/src/validation.h
index 0fdd027..c9f5da0 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Validation routines.
*
- * Copyright (c) 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -51,6 +51,7 @@
* @param[in] when_xp_opts Additional XPath options to use for evaluating "when".
* @param[in] node_types Set with nodes with unresolved types, can be NULL
* @param[in] meta_types Set with metadata with unresolved types, can be NULL.
+ * @param[in] ext_node Set with nodes with extensions to validate, can be NULL.
* @param[in] ext_val Set with extension data to validate, can be NULL.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
* @param[in,out] diff Validation diff.
@@ -58,7 +59,7 @@
*/
LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type,
struct ly_set *node_when, uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types,
- struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff);
+ struct ly_set *ext_node, struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff);
/**
* @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
@@ -75,6 +76,15 @@
struct lyd_node **diff);
/**
+ * @brief Validate data node with an extension instance, if any, by storing it in its unres set.
+ *
+ * @param[in] node Node to check for an extension instance with a node callback.
+ * @param[in,out] ext_node Set with data nodes to validate.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node);
+
+/**
* @brief Validate a data tree.
*
* @param[in,out] tree Data tree to validate, nodes may be autodeleted.
@@ -85,12 +95,13 @@
* @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
* @param[in] node_types_p Set of unres node types, if NULL a local set is used.
* @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[in] ext_node_p Set of unres nodes with extensions to validate, if NULL a local set is used.
* @param[in] ext_val_p Set of unres extension data to validate, if NULL a local set is used.
* @param[out] diff Generated validation diff, not generated if NULL.
* @return LY_ERR value.
*/
LY_ERR lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
- struct ly_set *ext_val_p, struct lyd_node **diff);
+ struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff);
#endif /* LY_VALIDATION_H_ */
diff --git a/tests/plugins/simple.c b/tests/plugins/simple.c
index fdd3fb3..b910f9e 100644
--- a/tests/plugins/simple.c
+++ b/tests/plugins/simple.c
@@ -54,6 +54,7 @@
.plugin.compile = &hint_compile,
.plugin.sprinter = NULL,
.plugin.free = NULL,
+ .plugin.node = NULL,
.plugin.snode = NULL,
.plugin.validate = NULL
},
@@ -79,9 +80,11 @@
.plugin.store = lyplg_type_store_string,
.plugin.validate = NULL,
.plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
.plugin.print = lyplg_type_print_simple,
.plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 0
},
{0}
};