validation FEATURE optional diff creation
diff --git a/src/context.c b/src/context.c
index f4301c2..302638e 100644
--- a/src/context.c
+++ b/src/context.c
@@ -781,7 +781,7 @@
root_bis = 0;
}
- if (lyd_validate(&root, NULL, LYD_VALIDATE_PRESENT)) {
+ if (lyd_validate(&root, NULL, LYD_VALIDATE_PRESENT, NULL)) {
goto error;
}
diff --git a/src/parser_data.h b/src/parser_data.h
index e12b65e..7bf68b2 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -100,8 +100,6 @@
*/
#define LYD_VALIDATE_NO_STATE 0x0001 /**< Consider state data not allowed and raise an error if they are found. */
#define LYD_VALIDATE_PRESENT 0x0002 /**< Validate only modules whose data actually exist. */
-//#define LYD_VALIDATE_DIFF 0x0004 /**< Flag only for validation, store all the data node changes performed by the validation
-// in a diff structure. */
/** @} datavalidationoptions */
@@ -248,10 +246,11 @@
* @param[in,out] tree Data tree to recursively validate. May be changed by validation.
* @param[in] ctx libyang context. Can be NULL if @p tree is set.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[out] diff Optional diff with any changes made by the validation.
* @return LY_SUCCESS on success.
* @return LY_ERR error on error.
*/
-LY_ERR lyd_validate(struct lyd_node **tree, const struct ly_ctx *ctx, int val_opts);
+LY_ERR lyd_validate(struct lyd_node **tree, const struct ly_ctx *ctx, int val_opts, struct lyd_node **diff);
/**
* @brief Fully validate a data tree of a module.
@@ -259,10 +258,11 @@
* @param[in,out] tree Data tree to recursively validate. May be changed by validation.
* @param[in] module Module whose data (and schema restrictions) to validate.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[out] diff Optional diff with any changes made by the validation.
* @return LY_SUCCESS on success.
* @return LY_ERR error on error.
*/
-LY_ERR lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, int val_opts);
+LY_ERR lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, int val_opts, struct lyd_node **diff);
/**
* @brief Validate an RPC/action, notification, or RPC/action reply.
@@ -272,12 +272,13 @@
* @param[in] tree Tree to be used for validating references from the operation subtree.
* @param[in] op Operation to validate (@ref datavalidateop), the given @p op_tree must correspond to this value. Note that
* it isn't possible to detect the operation simply from the @p op_tree since RPC/action and their reply share the same
- * RPC/action data node and in case one of the input and output do not define any data node children, it is not passible
+ * RPC/action data node and in case one of the input and output do not define any data node children, it is not possible
* to get know what is here given for validation and if it is really valid.
+ * @param[out] diff Optional diff with any changes made by the validation.
* @return LY_SUCCESS on success.
* @return LY_ERR error on error.
*/
-LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op);
+LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op, struct lyd_node **diff);
/** @} datatree */
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 91d9b2c..0523bce 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -819,12 +819,12 @@
if (!(lybctx->parse_options & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL);
+ ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* add any missing default children */
ret = lyd_validate_defaults_r(node, lyd_node_children_p(node), NULL, NULL, &lybctx->unres_node_type,
- &lybctx->when_check, lybctx->validate_options);
+ &lybctx->when_check, lybctx->validate_options, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 8c1e36d..3a3dee3 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -588,12 +588,12 @@
if (!(lydctx->options & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(cur), snode, NULL);
+ ret = lyd_validate_new(lyd_node_children_p(cur), snode, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* add any missing default children */
ret = lyd_validate_defaults_r(cur, lyd_node_children_p(cur), NULL, NULL, &lydctx->unres_node_type,
- &lydctx->when_check, lydctx->options);
+ &lydctx->when_check, lydctx->options, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -714,15 +714,16 @@
}
/* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
- LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod), cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
/* add all top-level defaults for this module */
- ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check, validate_options);
+ ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &lydctx.unres_node_type, &lydctx.when_check,
+ validate_options, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* finish incompletely validated terminal values/attributes and when conditions */
ret = lyd_validate_unres(tree, &lydctx.when_check, &lydctx.unres_node_type, &lydctx.unres_meta_type, LYD_XML,
- lydxml_resolve_prefix, lydctx.xmlctx);
+ lydxml_resolve_prefix, lydctx.xmlctx, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
diff --git a/src/validation.c b/src/validation.c
index c550dc6..631fde6 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -21,6 +21,7 @@
#include "common.h"
#include "config.h"
+#include "diff.h"
#include "hash_table.h"
#include "log.h"
#include "parser_data.h"
@@ -97,17 +98,44 @@
}
/**
+ * @brief Add new changes into validation diff. They are always merged.
+ *
+ * @param[in] node Node/subtree to add.
+ * @param[in] op Operation of the change.
+ * @param[in,out] diff Validation diff.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *new_diff = NULL;
+
+ assert((op == LYD_DIFF_OP_DELETE) || (op == LYD_DIFF_OP_CREATE));
+
+ /* create new diff tree */
+ LY_CHECK_RET(lyd_diff_add(node, op, NULL, NULL, NULL, NULL, NULL, &new_diff));
+
+ /* merge into existing diff */
+ ret = lyd_diff_merge(new_diff, diff);
+
+ lyd_free_tree(new_diff);
+ return ret;
+}
+
+/**
* @brief Evaluate a single "when" condition.
*
* @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
* @param[in] node Node whose existence depends on this when.
* @param[in] when When to evaluate.
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
*/
static LY_ERR
-lyd_validate_when(struct lyd_node **tree, struct lyd_node *node, struct lysc_when *when)
+lyd_validate_when(struct lyd_node **tree, struct lyd_node *node, struct lysc_when *when, struct lyd_node **diff)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR ret;
const struct lyd_node *ctx_node;
struct lyxp_set xp_set;
@@ -135,11 +163,15 @@
if (LYD_DEL_IS_ROOT(*tree, node)) {
*tree = (*tree)->next;
}
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff));
+ }
lyd_free_tree(node);
} else {
/* invalid data */
LOGVAL(LYD_NODE_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOWHEN, when->cond->expr);
- ret = LY_EVALID;
+ return LY_EVALID;
}
} else {
/* remember that when evaluated to true */
@@ -151,7 +183,7 @@
LY_ERR
lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *meta_types,
- LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data)
+ LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
uint32_t i;
@@ -171,7 +203,7 @@
do {
LY_ARRAY_COUNT_TYPE u;
LY_ARRAY_FOR(schema->when, u) {
- ret = lyd_validate_when(tree, node, schema->when[u]);
+ ret = lyd_validate_when(tree, node, schema->when[u], diff);
if (ret) {
break;
}
@@ -295,10 +327,11 @@
*
* @param[in,out] first First sibling to search in, is updated if needed.
* @param[in] choic Choice node whose cases to check.
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_cases(struct lyd_node **first, const struct lysc_node_choice *choic)
+lyd_validate_cases(struct lyd_node **first, const struct lysc_node_choice *choic, struct lyd_node **diff)
{
const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
struct lyd_node *match, *to_del;
@@ -352,7 +385,12 @@
if (LYD_DEL_IS_ROOT(*first, to_del)) {
*first = (*first)->next;
}
+ /* free previous node */
lyd_free_tree(to_del);
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(match, LYD_DIFF_OP_DELETE, diff));
+ }
to_del = match;
}
if (LYD_DEL_IS_ROOT(*first, to_del)) {
@@ -403,11 +441,12 @@
* @param[in,out] first First sibling to search in, is updated if needed.
* @param[in] node Data node instance to check.
* @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
+ * @param[in,out] diff Validation diff.
*/
static void
-lyd_validate_autodel_dup(struct lyd_node **first, struct lyd_node *node, struct lyd_node **next_p)
+lyd_validate_autodel_dup(struct lyd_node **first, struct lyd_node *node, struct lyd_node **next_p, struct lyd_node **diff)
{
- struct lyd_node *match, *next;
+ struct lyd_node *match, *next, *iter;
if (lyd_val_has_default(node->schema)) {
assert(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER));
@@ -427,6 +466,17 @@
if (match == *next_p) {
*next_p = (*next_p)->next;
}
+ if (diff) {
+ /* add into diff */
+ if ((match->schema->nodetype == LYS_CONTAINER) && !(match->schema->flags & LYS_PRESENCE)) {
+ /* we do not want to track NP container changes, but remember any removed children */
+ LY_LIST_FOR(LYD_CHILD(match), iter) {
+ lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
+ }
+ } else {
+ lyd_val_diff_add(match, LYD_DIFF_OP_DELETE, diff);
+ }
+ }
lyd_free_tree(match);
/* remove only a single container/leaf default instance, if there are more, it is an error */
@@ -441,7 +491,8 @@
}
LY_ERR
-lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod)
+lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
+ struct lyd_node **diff)
{
struct lyd_node *next, *node;
const struct lysc_node *snode = NULL;
@@ -451,7 +502,7 @@
while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(first, (struct lysc_node_choice *)snode));
+ LY_CHECK_RET(lyd_validate_cases(first, (struct lysc_node_choice *)snode, diff));
}
}
@@ -467,7 +518,7 @@
}
/* remove old default(s) if it exists */
- lyd_validate_autodel_dup(first, node, &next);
+ lyd_validate_autodel_dup(first, node, &next, diff);
/* then check new node instance duplicities */
LY_CHECK_RET(lyd_validate_duplicates(*first, node));
@@ -968,7 +1019,8 @@
}
LY_ERR
-lyd_validate_final_r(struct lyd_node *first, const struct lysc_node *sparent, const struct lys_module *mod, int val_opts, LYD_VALIDATE_OP op)
+lyd_validate_final_r(struct lyd_node *first, const struct lysc_node *sparent, const struct lys_module *mod, int val_opts,
+ LYD_VALIDATE_OP op)
{
struct lyd_node *next = NULL, *node;
const struct lysc_node *snode;
@@ -1039,7 +1091,8 @@
LY_ERR
lyd_validate_defaults_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
- const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when, int val_opts)
+ const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when, int val_opts,
+ struct lyd_node **diff)
{
LY_ERR ret;
const struct lysc_node *iter = NULL;
@@ -1063,7 +1116,7 @@
if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
/* create default case data */
LY_CHECK_RET(lyd_validate_defaults_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
- NULL, node_types, node_when, val_opts));
+ NULL, node_types, node_when, val_opts, diff));
}
break;
case LYS_CONTAINER:
@@ -1079,7 +1132,8 @@
}
/* create any default children */
- LY_CHECK_RET(lyd_validate_defaults_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when, val_opts));
+ LY_CHECK_RET(lyd_validate_defaults_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when,
+ val_opts, diff));
}
break;
case LYS_LEAF:
@@ -1099,6 +1153,10 @@
/* remember to resolve when */
ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
}
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+ }
}
break;
case LYS_LEAFLIST:
@@ -1120,6 +1178,10 @@
/* remember to resolve when */
ly_set_add(node_when, node, LY_SET_OPT_USEASLIST);
}
+ if (diff) {
+ /* add into diff */
+ LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
+ }
}
}
break;
@@ -1140,11 +1202,12 @@
* @param[in,out] type_meta_check Set for unres metadata types.
* @param[in,out] when_check Set for nodes with when conditions.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_subtree(struct lyd_node *root, struct ly_set *type_check, struct ly_set *type_meta_check,
- struct ly_set *when_check, int val_opts)
+ struct ly_set *when_check, int val_opts, struct lyd_node **diff)
{
const struct lyd_meta *meta;
struct lyd_node *next, *node;
@@ -1162,11 +1225,11 @@
ly_set_add(type_check, (void *)node, LY_SET_OPT_USEASLIST);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* new node validation, autodelete */
- LY_CHECK_RET(lyd_validate_new(lyd_node_children_p((struct lyd_node *)node), node->schema, NULL));
+ LY_CHECK_RET(lyd_validate_new(lyd_node_children_p((struct lyd_node *)node), node->schema, NULL, diff));
/* add nested defaults */
LY_CHECK_RET(lyd_validate_defaults_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, type_check,
- when_check, val_opts));
+ when_check, val_opts, diff));
}
if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
@@ -1189,10 +1252,12 @@
* @param[in] mod_count Count of @p modules.
* @param[in] ly_ctx libyang context.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[out] diff Generated validation diff, not generated if NULL.
* @return LY_ERR value.
*/
static LY_ERR
-_lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, int val_opts)
+_lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, int val_opts,
+ struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *first, *next, **first2;
@@ -1201,6 +1266,9 @@
uint32_t i = 0;
LY_CHECK_ARG_RET(NULL, tree, *tree || ctx || module, LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
next = *tree;
while (1) {
@@ -1220,22 +1288,27 @@
}
/* validate new top-level nodes of this module, autodelete */
- LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod), cleanup);
+ ret = lyd_validate_new(first2, NULL, mod, diff);
+ LY_CHECK_GOTO(ret, cleanup);
/* add all top-level defaults for this module */
- LY_CHECK_GOTO(ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &type_check, &when_check, val_opts), cleanup);
+ ret = lyd_validate_defaults_r(NULL, first2, NULL, mod, &type_check, &when_check, val_opts, diff);
+ LY_CHECK_GOTO(ret, cleanup);
/* process nested nodes */
LY_LIST_FOR(*first2, first) {
- LY_CHECK_GOTO(ret = lyd_validate_subtree(first, &type_check, &type_meta_check, &when_check, val_opts), cleanup);
+ ret = lyd_validate_subtree(first, &type_check, &type_meta_check, &when_check, val_opts, diff);
+ LY_CHECK_GOTO(ret, cleanup);
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(tree, &when_check, &type_check, &type_meta_check, LYD_JSON, lydjson_resolve_prefix, NULL);
+ ret = lyd_validate_unres(tree, &when_check, &type_check, &type_meta_check, LYD_JSON, lydjson_resolve_prefix,
+ NULL, diff);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
- LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, mod, val_opts, 0), cleanup);
+ ret = lyd_validate_final_r(*first2, NULL, mod, val_opts, 0);
+ LY_CHECK_GOTO(ret, cleanup);
}
cleanup:
@@ -1246,15 +1319,15 @@
}
API LY_ERR
-lyd_validate(struct lyd_node **tree, const struct ly_ctx *ctx, int val_opts)
+lyd_validate(struct lyd_node **tree, const struct ly_ctx *ctx, int val_opts, struct lyd_node **diff)
{
- return _lyd_validate(tree, NULL, ctx, val_opts);
+ return _lyd_validate(tree, NULL, ctx, val_opts, diff);
}
API LY_ERR
-lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, int val_opts)
+lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, int val_opts, struct lyd_node **diff)
{
- return _lyd_validate(tree, module, NULL, val_opts);
+ return _lyd_validate(tree, module, NULL, val_opts, diff);
}
/**
@@ -1309,7 +1382,7 @@
}
API LY_ERR
-lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op)
+lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op, struct lyd_node **diff)
{
LY_ERR ret;
struct lyd_node *tree_sibling, *op_subtree, *op_next, *op_node, *op_parent;
@@ -1317,6 +1390,9 @@
LY_CHECK_ARG_RET(NULL, op_tree, !op_tree->parent, !tree || !tree->parent,
(op == LYD_VALIDATE_OP_NOTIF) || (op == LYD_VALIDATE_OP_RPC) || (op == LYD_VALIDATE_OP_REPLY), LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
/* find the operation/notification */
LYD_TREE_DFS_BEGIN(op_tree, op_next, op_node) {
@@ -1354,11 +1430,11 @@
}
/* prevalidate whole operation subtree */
- LY_CHECK_GOTO(ret = lyd_validate_subtree(op_node, &type_check, &type_meta_check, &when_check, 0), cleanup);
+ LY_CHECK_GOTO(ret = lyd_validate_subtree(op_node, &type_check, &type_meta_check, &when_check, 0, diff), cleanup);
/* finish incompletely validated terminal values/attributes and when conditions on the full tree */
LY_CHECK_GOTO(ret = lyd_validate_unres((struct lyd_node **)&tree, &when_check, &type_check, &type_meta_check,
- LYD_JSON, lydjson_resolve_prefix, NULL), cleanup);
+ LYD_JSON, lydjson_resolve_prefix, NULL, diff), cleanup);
/* perform final validation of the operation/notification */
lyd_validate_obsolete(op_node);
diff --git a/src/validation.h b/src/validation.h
index 35492af..d6d1f17 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -31,10 +31,11 @@
* @param[in] format Format of the unresolved data.
* @param[in] get_prefix_clb Format-specific getter to resolve prefixes.
* @param[in] parser_data Parser's data for @p get_prefix_clb.
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_unres(struct lyd_node **tree, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *meta_types,
- LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data);
+ LYD_FORMAT format, ly_clb_resolve_prefix get_prefix_clb, void *parser_data, struct lyd_node **diff);
/**
* @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.
@@ -44,9 +45,11 @@
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
-LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod);
+LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
+ struct lyd_node **diff);
/**
* @brief Perform all remaining validation tasks, the data tree must be final when calling this function.
@@ -71,9 +74,11 @@
* @param[in] node_types Set to add nodes with unresolved types into.
* @param[in] node_when Set to add nodes with "when" conditions into.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_defaults_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
- const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when, int val_opts);
+ const struct lys_module *mod, struct ly_set *node_types, struct ly_set *node_when,
+ int val_opts, struct lyd_node **diff);
#endif /* LY_VALIDATION_H_ */
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c
index a282659..612bf88 100644
--- a/tests/utests/data/test_diff.c
+++ b/tests/utests/data/test_diff.c
@@ -842,7 +842,7 @@
assert_non_null(mod);
st->first = NULL;
- assert_int_equal(lyd_validate_module(&st->first, mod, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_module(&st->first, mod, 0, NULL), LY_SUCCESS);
assert_ptr_not_equal(st->first, NULL);
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(st->ctx, xml2, LYD_XML, 0, LYD_VALIDATE_PRESENT, &st->second));
assert_non_null(st->second);
diff --git a/tests/utests/data/test_merge.c b/tests/utests/data/test_merge.c
index 0c77cea..4ad6c9f 100644
--- a/tests/utests/data/test_merge.c
+++ b/tests/utests/data/test_merge.c
@@ -282,7 +282,7 @@
/* merge them */
assert_int_equal(lyd_merge(&st->target, st->source, 0), LY_SUCCESS);
- assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
/* check the result */
lyd_print_mem(&printed, st->target, LYD_XML, LYD_PRINT_WITHSIBLINGS);
@@ -324,7 +324,7 @@
/* merge them */
assert_int_equal(lyd_merge(&st->target, st->source, 0), LY_SUCCESS);
- assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
/* check the result */
lyd_print_mem(&printed, st->target, LYD_XML, LYD_PRINT_WITHSIBLINGS);
@@ -394,7 +394,7 @@
/* merge them */
assert_int_equal(lyd_merge(&st->target, st->source, LYD_MERGE_EXPLICIT), LY_SUCCESS);
- assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
/* check the result */
lyd_print_mem(&printed, st->target, LYD_XML, LYD_PRINT_WITHSIBLINGS);
@@ -473,7 +473,7 @@
/* merge them */
assert_int_equal(lyd_merge(&st->target, st->source, LYD_MERGE_EXPLICIT), LY_SUCCESS);
- assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
/* check the result */
lyd_print_mem(&printed, st->target, LYD_XML, LYD_PRINT_WITHSIBLINGS);
@@ -531,7 +531,7 @@
/* merge them */
assert_int_equal(lyd_merge(&st->target, st->source, 0), LY_SUCCESS);
- assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&st->target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
/* check the result */
lyd_print_mem(&printed, st->target, LYD_XML, LYD_PRINT_WITHSIBLINGS);
@@ -566,13 +566,13 @@
st->target = lyd_new_path(NULL, st->ctx, "/merge-dflt:top/c", "c_dflt", 0);
assert_non_null(st->target);
- assert_int_equal(lyd_validate(&(st->target), NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&(st->target), NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
st->source = lyd_new_path(NULL, st->ctx, "/merge-dflt:top/a", "a_val", 0);
assert_non_null(st->source);
tmp = lyd_new_path(st->source, st->ctx, "/merge-dflt:top/b", "b_val", 0);
assert_non_null(tmp);
- assert_int_equal(lyd_validate(&(st->source), NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&(st->source), NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
assert_int_equal(lyd_merge(&st->target, st->source, LYD_MERGE_DESTRUCT), LY_SUCCESS);
st->source = NULL;
@@ -608,13 +608,13 @@
st->target = lyd_new_path(NULL, st->ctx, "/merge-dflt:top/c", "c_dflt", 0);
assert_non_null(st->target);
- assert_int_equal(lyd_validate(&(st->target), NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&(st->target), NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
st->source = lyd_new_path(NULL, st->ctx, "/merge-dflt:top/a", "a_val", 0);
assert_non_null(st->source);
tmp = lyd_new_path(st->source, st->ctx, "/merge-dflt:top/b", "b_val", 0);
assert_non_null(tmp);
- assert_int_equal(lyd_validate(&(st->source), NULL, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&(st->source), NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS);
assert_int_equal(lyd_merge(&st->target, st->source, LYD_MERGE_EXPLICIT), LY_SUCCESS);
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index 4bcd777..da68a40 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -1019,7 +1019,7 @@
*state = test_defaults;
char *str;
- struct lyd_node *tree, *node;
+ struct lyd_node *tree, *node, *diff;
const struct lys_module *mod = ly_ctx_get_module_latest(ctx, "f");
struct ly_out *out;
@@ -1027,8 +1027,9 @@
/* get defaults */
tree = NULL;
- assert_int_equal(lyd_validate_module(&tree, mod, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_module(&tree, mod, 0, &diff), LY_SUCCESS);
assert_non_null(tree);
+ assert_non_null(diff);
/* check all defaults exist */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1049,11 +1050,32 @@
"</cont>");
ly_out_reset(out);
+ /* check diff */
+ lyd_print(out, diff, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+ assert_string_equal(str,
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def1</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def2</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def3</ll1>"
+ "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt1</ll2>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt2</ll2>"
+ "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">"
+ "<ll1 yang:operation=\"create\">def1</ll1>"
+ "<ll1 yang:operation=\"create\">def2</ll1>"
+ "<ll1 yang:operation=\"create\">def3</ll1>"
+ "<d yang:operation=\"create\">15</d>"
+ "<ll2 yang:operation=\"create\">dflt1</ll2>"
+ "<ll2 yang:operation=\"create\">dflt2</ll2>"
+ "</cont>"
+ );
+ ly_out_reset(out);
+ lyd_free_siblings(diff);
+
/* create another explicit case and validate */
node = lyd_new_term(NULL, mod, "l", "value");
assert_non_null(node);
assert_int_equal(lyd_insert_sibling(tree, node), LY_SUCCESS);
- assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
/* check data tree */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1072,6 +1094,16 @@
"<l xmlns=\"urn:tests:f\">value</l>");
ly_out_reset(out);
+ /* check diff */
+ lyd_print(out, diff, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+ assert_string_equal(str,
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def1</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def2</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def3</ll1>"
+ );
+ ly_out_reset(out);
+ lyd_free_siblings(diff);
+
/* create explicit leaf-list and leaf and validate */
node = lyd_new_term(NULL, mod, "d", "15");
assert_non_null(node);
@@ -1079,7 +1111,7 @@
node = lyd_new_term(NULL, mod, "ll2", "dflt2");
assert_non_null(node);
assert_int_equal(lyd_insert_sibling(tree, node), LY_SUCCESS);
- assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
/* check data tree */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1097,12 +1129,22 @@
"<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
ly_out_reset(out);
+ /* check diff */
+ lyd_print(out, diff, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+ assert_string_equal(str,
+ "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt1</ll2>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt2</ll2>"
+ );
+ ly_out_reset(out);
+ lyd_free_siblings(diff);
+
/* create first explicit container, which should become implicit */
node = lyd_new_inner(NULL, mod, "cont");
assert_non_null(node);
assert_int_equal(lyd_insert_before(tree, node), LY_SUCCESS);
tree = tree->prev;
- assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
/* check data tree */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1120,11 +1162,14 @@
"<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
ly_out_reset(out);
+ /* check diff */
+ assert_null(diff);
+
/* create second explicit container, which should become implicit, so the first tree node should be removed */
node = lyd_new_inner(NULL, mod, "cont");
assert_non_null(node);
assert_int_equal(lyd_insert_after(tree, node), LY_SUCCESS);
- assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
/* check data tree */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1142,11 +1187,14 @@
"<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
ly_out_reset(out);
+ /* check diff */
+ assert_null(diff);
+
/* similar changes for nested defaults */
assert_non_null(lyd_new_term(tree, NULL, "ll1", "def3"));
assert_non_null(lyd_new_term(tree, NULL, "d", "5"));
assert_non_null(lyd_new_term(tree, NULL, "ll2", "non-dflt"));
- assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT), LY_SUCCESS);
+ assert_int_equal(lyd_validate(&tree, ctx, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS);
/* check data tree */
lyd_print(out, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_IMPL_TAG);
@@ -1161,6 +1209,21 @@
"<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
ly_out_reset(out);
+ /* check diff */
+ lyd_print(out, diff, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
+ assert_string_equal(str,
+ "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">"
+ "<ll1 yang:operation=\"delete\">def1</ll1>"
+ "<ll1 yang:operation=\"delete\">def2</ll1>"
+ "<ll1 yang:operation=\"delete\">def3</ll1>"
+ "<d yang:operation=\"delete\">15</d>"
+ "<ll2 yang:operation=\"delete\">dflt1</ll2>"
+ "<ll2 yang:operation=\"delete\">dflt2</ll2>"
+ "</cont>"
+ );
+ ly_out_reset(out);
+ lyd_free_siblings(diff);
+
lyd_free_siblings(tree);
ly_out_free(out, NULL, 1);
@@ -1178,7 +1241,7 @@
/* get empty data */
tree = NULL;
- assert_int_equal(lyd_validate_module(&tree, mod, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_module(&tree, mod, 0, NULL), LY_SUCCESS);
assert_null(tree);
/* disabled by f1 */
@@ -1194,7 +1257,7 @@
assert_int_equal(lys_feature_enable(mod, "f1"), LY_SUCCESS);
/* get data with default container */
- assert_int_equal(lyd_validate_module(&tree, mod, 0), LY_SUCCESS);
+ assert_int_equal(lyd_validate_module(&tree, mod, 0, NULL), LY_SUCCESS);
assert_non_null(tree);
lyd_free_siblings(tree);
@@ -1256,17 +1319,17 @@
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY, 0, &tree));
assert_non_null(tree);
- assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT));
+ assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
logbuf_assert("Data are disabled by \"cont\" schema node if-feature. /g:cont");
assert_int_equal(lys_feature_enable(mod, "f1"), LY_SUCCESS);
- assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT));
+ assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
logbuf_assert("Data are disabled by \"b\" schema node if-feature. /g:cont/l");
assert_int_equal(lys_feature_enable(mod, "f2"), LY_SUCCESS);
- assert_int_equal(LY_SUCCESS, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT));
+ assert_int_equal(LY_SUCCESS, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
lyd_free_siblings(tree);
@@ -1298,7 +1361,7 @@
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY, 0, &tree));
assert_non_null(tree);
- assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE));
+ assert_int_equal(LY_EVALID, lyd_validate(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE, NULL));
logbuf_assert("Invalid state data node \"cont2\" found. /h:cont/cont2");
lyd_free_siblings(tree);
@@ -1359,7 +1422,7 @@
assert_non_null(op_tree);
/* missing leafref */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_RPC));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_RPC, NULL));
logbuf_assert("Invalid leafref value \"target\" - no target instance \"/lf3\" with the same value."
" /j:cont/l1[k='val1']/act/lf2");
ly_in_free(in, 0);
@@ -1373,7 +1436,7 @@
assert_non_null(tree);
/* disabled if-feature */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
logbuf_assert("Data are disabled by \"act\" schema node if-feature. /j:cont/l1[k='val1']/act");
mod = ly_ctx_get_module_latest(ctx, "j");
@@ -1381,7 +1444,7 @@
assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
/* input must false */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
logbuf_assert("Must condition \"../../lf1 = 'true'\" not satisfied. /j:cont/l1[k='val1']/act");
lyd_free_siblings(tree);
@@ -1394,7 +1457,7 @@
assert_non_null(tree);
/* success */
- assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC));
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
lys_feature_disable(mod, "feat1");
lyd_free_tree(op_tree);
@@ -1435,7 +1498,7 @@
ly_in_free(in, 0);
/* missing leafref */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_REPLY));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_REPLY, NULL));
logbuf_assert("Invalid leafref value \"target\" - no target instance \"/lf4\" with the same value."
" /j:cont/l1[k='val1']/act/lf2");
@@ -1448,7 +1511,7 @@
assert_non_null(tree);
/* disabled if-feature */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
logbuf_assert("Data are disabled by \"act\" schema node if-feature. /j:cont/l1[k='val1']/act");
mod = ly_ctx_get_module_latest(ctx, "j");
@@ -1456,7 +1519,7 @@
assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
/* input must false */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
logbuf_assert("Must condition \"../../lf1 = 'true2'\" not satisfied. /j:cont/l1[k='val1']/act");
lyd_free_siblings(tree);
@@ -1469,7 +1532,7 @@
assert_non_null(tree);
/* success */
- assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY));
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
lys_feature_disable(mod, "feat1");
lyd_free_tree(op_tree);