Fixed: [Change CLICON_NETCONF_DUPLICATE_ALLOW to remove duplicates](https://github.com/clicon/clixon-controller/issues/160)

C-API: Removed xml_tree_prune_flagged
This commit is contained in:
Olof hagsand 2024-11-23 10:51:04 +01:00
parent 68e132c275
commit 0193186272
11 changed files with 395 additions and 104 deletions

View file

@ -25,6 +25,7 @@ Expected: January 2025
* New `system-only-config` extension
* New `ca_system_only` backend callback for reading system-only data
* New `clixon-config@2024-11-01.yang` revision
* Changed: `CLICON_NETCONF_DUPLICATE_ALLOW` to not only check but remove duplicates
* Added: `CLICON_XMLDB_SYSTEM_ONLY_CONFIG`
### C/CLI-API changes on existing features
@ -37,6 +38,7 @@ Developers may need to change their code
### Corrected Bugs
* Fixed: [Change CLICON_NETCONF_DUPLICATE_ALLOW to remove duplicates](https://github.com/clicon/clixon-controller/issues/160)
* Fixed: Segv in canonical xpath transform
* Fixed: [Error with submodules and feature Interaction](https://github.com/clicon/clixon-controller/issues/158)
* Fixed: [Expansion removes the double quote](https://github.com/clicon/clixon/issues/524)

View file

@ -657,10 +657,20 @@ from_client_edit_config(clixon_handle h,
*/
if ((ret = xml_yang_validate_minmax(xc, 1, &xret)) < 0)
goto done;
/* Disable duplicate check in NETCONF messages.*/
if (clicon_option_bool(h, "CLICON_NETCONF_DUPLICATE_ALLOW"))
;
else if (ret == 1 && (ret = xml_yang_validate_unique_recurse(xc, &xret)) < 0)
if (ret == 0){
if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0)
goto done;
goto ok;
}
/* Must do before duplicate check */
if (xml_sort_recurse(xc) < 0)
goto done;
/* Disable duplicate check in NETCONF messages. */
if (clicon_option_bool(h, "CLICON_NETCONF_DUPLICATE_ALLOW")){
if ((ret = xml_duplicate_remove_recurse(xc, &xret)) < 0)
goto done;
}
else if ((ret = xml_yang_validate_unique_recurse(xc, &xret)) < 0)
goto done;
/* xmldb_put (difflist handling) requires list keys */
if (ret == 1 && (ret = xml_yang_validate_list_key_only(xc, &xret)) < 0)
@ -670,11 +680,6 @@ from_client_edit_config(clixon_handle h,
goto done;
goto ok;
}
/* Cant do this earlier since we dont have a yang spec to
* the upper part of the tree, until we get the "config" tree.
*/
if (xml_sort_recurse(xc) < 0)
goto done;
if ((ret = xmldb_put(h, target, operation, xc, username, cbret)) < 0){
if (netconf_operation_failed(cbret, "protocol", clixon_err_reason())< 0)
goto done;

View file

@ -312,9 +312,8 @@ startup_common(clixon_handle h,
clixon_err(OE_XML, EFAULT, "Yang sort error");
}
/* clear XML tree of defaults */
if (xml_tree_prune_flagged(xt, XML_FLAG_DEFAULT, 1) < 0)
if (xml_tree_prune_flags(xt, XML_FLAG_DEFAULT, XML_FLAG_DEFAULT) < 0)
goto done;
if (xmldb_dump(h, stdout, xt, FORMAT_XML,
clicon_option_bool(h, "CLICON_XMLDB_PRETTY"),
WITHDEFAULTS_REPORT_ALL, 0, NULL) < 0)

View file

@ -47,5 +47,6 @@ int xml_yang_validate_minmax(cxobj *xt, int presence, cxobj **xret);
int xml_yang_validate_minmax_recurse(cxobj *xt, cxobj **xret);
int xml_yang_validate_unique(cxobj *xt, cxobj **xret);
int xml_yang_validate_unique_recurse(cxobj *xt, cxobj **xret);
int xml_duplicate_remove_recurse(cxobj *xt, cxobj **xret);
#endif /* _CLIXON_VALIDATE_MINMAX_H_ */

View file

@ -68,8 +68,8 @@ int xml_diff(cxobj *x0, cxobj *x1,
cxobj ***changed_x0, cxobj ***changed_x1, int *changedlen);
int xml_tree_equal(cxobj *x0, cxobj *x1);
int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark);
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);
int xml_tree_prune_flags1(cxobj *xt, int flags, int mask, int recurse, int *removed);
int xml_namespace_change(cxobj *x, char *ns, char *prefix);
int xml_sanity(cxobj *x, void *arg);
int xml_non_config_data(cxobj *xt, cxobj **xerr);

View file

@ -79,7 +79,7 @@
#include "clixon_xml_bind.h"
#include "clixon_validate_minmax.h"
/*! New element last in list, check if already exists if sp return -1
/*! New element last in list, check if already exists if so return -1
*
* @param[in] vec Vector of existing entries (new is last)
* @param[in] i1 The new entry is placed at vec[i1]
@ -139,19 +139,21 @@ unique_search_xpath(cxobj *x,
/*! New element last in list, return error if already exists
*
* @param[in] vec Vector of existing entries (new is last)
* @param[in] i1 The new entry is placed at vec[i1]
* @param[in] vlen Length of vec
* @param[in] vec Vector of existing entries (new is last)
* @param[in] i1 The new entry is placed at vec[i1]
* @param[in] vlen Length of vec
* @param[in] sorted Sorted by system, ie sorted by key, otherwise no assumption
* @retval 0 OK, entry is unique
* @retval -1 Duplicate detected
* @param[out] dupl Index of duplicated element (if retval = -1)
* @retval 0 OK, entry is unique
* @retval -1 Duplicate detected
* @note This is currently quadratic complexity. It could be improved by inserting new element sorted and binary search.
*/
static int
check_insert_duplicate(char **vec,
int i1,
int vlen,
int sorted)
int sorted,
int *dupl)
{
int i;
int v;
@ -168,6 +170,8 @@ check_insert_duplicate(char **vec,
return 0;
}
/* here we have passed thru all keys of previous element and they are all equal */
if (dupl)
*dupl = i;
return -1;
}
else{
@ -180,7 +184,13 @@ check_insert_duplicate(char **vec,
if (v==vlen) /* duplicate */
break;
}
return i==i1?0:-1;
if (i<i1){
if (dupl)
*dupl = i;
return -1;
}
else
return 0;
}
}
@ -190,6 +200,7 @@ check_insert_duplicate(char **vec,
* @param[in] xt The parent of x (a list)
* @param[in] y Its yang spec (Y_LIST)
* @param[in] yu A yang unique (Y_UNIQUE) for unique schema node ids or (Y_LIST) for list keys
* @param[in] mark If set, a flag mask to mark (prior) duplicate object with
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
@ -213,12 +224,14 @@ check_unique_list_direct(cxobj *x,
cxobj *xt,
yang_stmt *y,
yang_stmt *yu,
int mark,
cxobj **xret)
{
int retval = -1;
cg_var *cvi; /* unique node name */
cxobj *xi;
char **vec = NULL; /* 2xmatrix */
cxobj **xvec = NULL;
int clen;
int i;
int v;
@ -226,6 +239,7 @@ check_unique_list_direct(cxobj *x,
int sorted;
char *str;
cvec *cvk;
int dupl;
/* If list and is sorted by system, then it is assumed elements are in key-order which is optimized
* Other cases are "unique" constraint or list sorted by user which is quadratic in complexity
@ -239,15 +253,21 @@ check_unique_list_direct(cxobj *x,
/* No keys: no checks necessary */
goto ok;
}
/* x need not be child 0, which could make the vector larger than necessary */
if ((vec = calloc(clen*xml_child_nr(xt), sizeof(char*))) == NULL){
clixon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) == NULL){
clixon_err(OE_UNIX, errno, "calloc");
goto done;
}
/* A vector is built with key-values, for each iteration check "backward" in the vector
* for duplicates
*/
i = 0; /* x element index */
do {
xvec[i] = x;
cvi = NULL;
v = 0; /* index in each tuple */
/* XXX Quadratic if clen > 1 */
@ -267,7 +287,9 @@ check_unique_list_direct(cxobj *x,
}
if (cvi==NULL){
/* Last element (i) is newly inserted, see if it is already there */
if (check_insert_duplicate(vec, i, clen, sorted) < 0){
if (check_insert_duplicate(vec, i, clen, sorted, &dupl) < 0){
if (mark)
xml_flag_set(xvec[dupl], mark);
if (xret && netconf_data_not_unique_xml(xret, x, cvk) < 0)
goto done;
goto fail;
@ -280,6 +302,8 @@ check_unique_list_direct(cxobj *x,
/* It would be possible to cache vec here as an optimization */
retval = 1;
done:
if (xvec)
free(xvec);
if (vec)
free(vec);
return retval;
@ -294,6 +318,7 @@ check_unique_list_direct(cxobj *x,
* @param[in] xt The parent of x (a list)
* @param[in] y Its yang spec (Y_LIST)
* @param[in] yu A yang unique (Y_UNIQUE) for unique schema node ids or (Y_LIST) for list keys
* @param[in] mark If set, a flag mask to mark (prior) duplicate object with
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (xret set)
@ -317,6 +342,7 @@ check_unique_list(cxobj *x,
cxobj *xt,
yang_stmt *y,
yang_stmt *yu,
uint16_t mark,
cxobj **xret)
{
int retval = -1;
@ -333,7 +359,7 @@ check_unique_list(cxobj *x,
/* Check if multiple direct children */
cvk = yang_cvec_get(yu);
if (cvec_len(cvk) > 1){
retval = check_unique_list_direct(x, xt, y, yu, xret);
retval = check_unique_list_direct(x, xt, y, yu, mark, xret);
goto done;
}
cvi = cvec_i(cvk, 0);
@ -343,7 +369,7 @@ check_unique_list(cxobj *x,
}
/* Check if direct schmeanode-id , ie not xpath */
if (index(xpath0, '/') == NULL){
retval = check_unique_list_direct(x, xt, y, yu, xret);
retval = check_unique_list_direct(x, xt, y, yu, mark, xret);
goto done;
}
/* Here proper xpath with at least one slash (can there be a descendant schemanodeid w/o slash?) */
@ -484,6 +510,7 @@ check_empty_list_minmax(cxobj *xt,
* @param[in] x XML LIST node
* @param[in] xt XML parent
* @param[in] y YANG of x
* @param[in] mark If set, a flag mask to mark (prior) duplicate object with
* @param[out] xret Error as XML if ret = 0
* @retval 1 Validation OK
* @retval 0 Validation failed (xret set)
@ -493,6 +520,7 @@ static int
xml_yang_minmax_new_list(cxobj *x,
cxobj *xt,
yang_stmt *y,
int mark,
cxobj **xret)
{
int retval = -1;
@ -503,7 +531,7 @@ xml_yang_minmax_new_list(cxobj *x,
/* Here new (first element) of lists only
* First check unique keys direct children
*/
if ((ret = check_unique_list_direct(x, xt, y, y, xret)) < 0)
if ((ret = check_unique_list_direct(x, xt, y, y, mark, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -520,7 +548,7 @@ xml_yang_minmax_new_list(cxobj *x,
* 1) multiple direct children (no prefixes), eg "a b"
* 2) single xpath with canonical prefixes, eg "/ex:a/ex:b"
*/
if ((ret = check_unique_list(x, xt, y, yu, xret)) < 0)
if ((ret = check_unique_list(x, xt, y, yu, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -538,6 +566,7 @@ xml_yang_minmax_new_list(cxobj *x,
* @param[in] x0 XML LIST node
* @param[in] xt XML parent
* @param[in] y0 YANG of x0
* @param[in] mark If set, a flag mask to mark (prior) duplicate object with
* @param[out] xret Error as XML if ret = 0
* @retval 1 Validation OK
* @retval 0 Validation failed (xret set)
@ -548,6 +577,7 @@ static int
xml_yang_minmax_new_leaf_list(cxobj *x0,
cxobj *xt,
yang_stmt *y0,
int mark,
cxobj **xret)
{
int retval = -1;
@ -567,6 +597,8 @@ xml_yang_minmax_new_leaf_list(cxobj *x0,
if ((bj = xml_body(xj)) == NULL)
continue;
if (bi && bj && strcmp(bi, bj) == 0){
if (mark)
xml_flag_set(xi, mark);
if ((cvv = cvec_new(0)) == NULL){
clixon_err(OE_UNIX, errno, "cvec_new");
goto done;
@ -758,7 +790,7 @@ xml_yang_validate_minmax(cxobj *xt,
/* new list check */
if (ret){
if (keyw == Y_LIST){
if ((ret = xml_yang_minmax_new_list(x, xt, y, xret)) < 0)
if ((ret = xml_yang_minmax_new_list(x, xt, y, 0, xret)) < 0)
goto done;
}
#ifdef NOTYET /* XXX This is enforced in xml_yang_validate_unique instead */
@ -926,13 +958,13 @@ xml_yang_validate_unique(cxobj *xt,
/* new list check */
switch (keyw){
case Y_LIST:
if ((ret = xml_yang_minmax_new_list(x, xt, y, xret)) < 0)
if ((ret = xml_yang_minmax_new_list(x, xt, y, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
break;
case Y_LEAF_LIST:
if ((ret = xml_yang_minmax_new_leaf_list(x, xt, y, xret)) < 0)
if ((ret = xml_yang_minmax_new_leaf_list(x, xt, y, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1002,3 +1034,119 @@ xml_yang_validate_unique_recurse(cxobj *xt,
retval = 0;
goto done;
}
/*! YANG unique check and remove duplicates, keep last
*
* Assume xt:s children are sorted and yang populated.
* @param[in] xt XML parent (may have lists w unique constraints as child)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 2 Locally abort this subtree, continue with others
* @retval 1 Abort, dont continue with others, return 1 to end user
* @retval 0 OK, continue
* @retval -1 Error, aborted at first error encounter, return -1 to end user
* @see xml_yang_validate_minmax which include these unique tests
*/
static int
xml_duplicate_remove(cxobj *xt,
void *arg)
{
int retval = -1;
cxobj **xret = (cxobj **)arg;
cxobj *x;
yang_stmt *y;
yang_stmt *yprev;
enum rfc_6020 keyw;
int again;
int ret;
again = 1;
while (again){
again = 0;
yprev = NULL;
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if ((y = xml_spec(x)) == NULL)
continue;
keyw = yang_keyword_get(y);
if (keyw == Y_LIST || keyw == Y_LEAF_LIST){
if (y == yprev){ /* equal: continue, assume list check does look-forward */
continue;
}
/* new list check */
switch (keyw){
case Y_LIST:
if ((ret = xml_yang_minmax_new_list(x, xt, y, XML_FLAG_DEL, xret)) < 0)
goto done;
if (ret == 0){
if (xml_tree_prune_flags1(xt, XML_FLAG_DEL, XML_FLAG_DEL, 0, &again) < 0)
goto done;
if (again){
if (xret && *xret){
xml_free(*xret);
*xret = NULL;
}
break;
}
goto fail;
}
break;
case Y_LEAF_LIST:
if ((ret = xml_yang_minmax_new_leaf_list(x, xt, y, XML_FLAG_DEL, xret)) < 0)
goto done;
if (ret == 0){
if (xml_tree_prune_flags1(xt, XML_FLAG_DEL, XML_FLAG_DEL, 0, &again) < 0)
goto done;
if (again){
if (xret && *xret){
xml_free(*xret);
*xret = NULL;
}
break;
}
goto fail;
}
break;
default:
break;
}
if (again)
break;
yprev = y;
}
}
}
retval = 0;
done:
return retval;
fail:
retval = 1;
goto done;
}
/*! Recursive YANG unique check and remove duplicates, keep last
*
* @param[in] xt XML parent (may have lists w unique constraints as child)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (xret set)
* @retval -1 Error
* @see xml_yang_validate_unique_recurse
*/
int
xml_duplicate_remove_recurse(cxobj *xt,
cxobj **xret)
{
int retval = -1;
int ret;
if ((ret = xml_apply0(xt, CX_ELMNT, xml_duplicate_remove, xret)) < 0)
goto done;
if (ret == 1)
goto fail;
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -676,7 +676,7 @@ xml_tree_equal(cxobj *x0,
* xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
* @endcode
* @note This function seems a little too complex semantics
* @see xml_tree_prune_flagged for a simpler variant
* @see xml_tree_prune_flags1 for a simpler variant
*/
int
xml_tree_prune_flagged_sub(cxobj *xt,
@ -750,46 +750,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
return retval;
}
/*! Prune everything that passes test
*
* @param[in] xt XML tree with some node marked
* @param[in] flag Which flag to test for
* @param[in] test 1: test that flag is set, 0: test that flag is not set
* @retval 0 OK
* @retval -1 Error
* The function removes all branches that does not pass test
* @code
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1);
* @endcode
*/
int
xml_tree_prune_flagged(cxobj *xt,
int flag,
int test)
{
int retval = -1;
cxobj *x;
cxobj *xprev;
x = NULL;
xprev = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, flag) == (test?flag:0)){ /* Pass test means purge */
if (xml_purge(x) < 0)
goto done;
x = xprev;
continue;
}
if (xml_tree_prune_flagged(x, flag, test) < 0)
goto done;
xprev = x;
}
retval = 0;
done:
return retval;
}
/*! Prune everything that passes test
/*! Prune everything that passes test recursively
*
* @param[in] xt XML tree with some node marked
* @param[in] flags Flags set
@ -800,15 +761,40 @@ xml_tree_prune_flagged(cxobj *xt,
* @code
* xml_tree_prune_flags(xt, XML_FLAG_MARK, XML_FLAG_MARK|XML_FLAG_DEFAULT);
* @endcode
* @see xml_tree_prune_flags1
*/
int
xml_tree_prune_flags(cxobj *xt,
int flags,
int mask)
int flags,
int mask)
{
int retval = -1;
cxobj *x;
cxobj *xprev;
return xml_tree_prune_flags1(xt, flags, mask, 1, NULL);
}
/*! Prune everything that passes test
*
* @param[in] xt XML tree with some node marked
* @param[in] flags Flags set
* @param[in] mask Which flags to test for
* @param[in] recurse 0: one level only, 1: recursive
* @param[out] removed Number of entities removed
* @retval 0 OK
* @retval -1 Error
* The function removes all branches that does pass test
* @code
* xml_tree_prune_flags1(xt, XML_FLAG_MARK, XML_FLAG_MARK|XML_FLAG_DEFAULT, 1);
* @endcode
*/
int
xml_tree_prune_flags1(cxobj *xt,
int flags,
int mask,
int recursive,
int *removed)
{
int retval = -1;
cxobj *x;
cxobj *xprev;
x = NULL;
xprev = NULL;
@ -817,10 +803,13 @@ xml_tree_prune_flags(cxobj *xt,
if (xml_purge(x) < 0)
goto done;
x = xprev;
if (removed)
(*removed)++;
continue;
}
if (xml_tree_prune_flags(x, flags, mask) < 0)
goto done;
if (recursive)
if (xml_tree_prune_flags1(x, flags, mask, 1, removed) < 0)
goto done;
xprev = x;
}
retval = 0;
@ -828,7 +817,6 @@ xml_tree_prune_flags(cxobj *xt,
return retval;
}
/*! Change namespace of XML node
*
* @param[in] x XML node

View file

@ -144,7 +144,7 @@ new "minmax: minimal validate ok"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "minmax: maximal: 2x a1,b1"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\"><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>1</b1><b1>0</b1><b0>1</b0></c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\"><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>1</b1><b1>0</b1></c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "minmax: validate ok"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"

View file

@ -2,6 +2,7 @@
# Detect duplicates in incoming edit-configs (not top-level)
# Both list and leaf-list
# See https://github.com/clicon/clixon-controller/issues/107
# Also, check CLICON_NETCONF_DUPLICATE_ALLOW
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -31,22 +32,33 @@ module unique{
yang-version 1.1;
namespace "urn:example:clixon";
prefix un;
container c{
presence "trigger"; // force presence container to trigger error
list server {
description "";
key "name";
leaf name {
type string;
}
leaf value {
type string;
}
}
leaf-list b{
type string;
}
}
container c{
presence "trigger"; // force presence container to trigger error
list server {
description "";
key "name";
leaf name {
type string;
}
leaf value {
type string;
}
}
list user {
description "";
ordered-by user;
key "name";
leaf name {
type string;
}
leaf value {
type string;
}
}
leaf-list b{
type string;
}
}
}
EOF
@ -98,6 +110,22 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
</server>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity><error-info><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/rpc/edit-config/config/c/server[name=\"one\"]/name</non-unique></error-info></rpc-error></rpc-reply>"
new "Add user sorted list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<user>
<name>one</name>
<value>foo</value>
</user>
<user>
<name>two</name>
<value>bar</value>
</user>
<user>
<name>one</name>
<value>foo</value>
</user>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity><error-info><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/rpc/edit-config/config/c/user[name=\"one\"]/name</non-unique></error-info></rpc-error></rpc-reply>"
new "Add leaf-list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<b>one</b>
@ -105,6 +133,130 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
<b>one</b>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity><error-info><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/rpc/edit-config/config/c/b[.=\"one\"]/one</non-unique></error-info></rpc-error></rpc-reply>"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
fi
# Check CLICON_NETCONF_DUPLICATE_ALLOW
if [ $BE -ne 0 ]; then
new "start backend -s init -f $cfg"
# start new backend
start_backend -s init -f $cfg -o CLICON_NETCONF_DUPLICATE_ALLOW=true
fi
new "Wait backend 2"
wait_backend
new "Add list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<server>
<name>bbb</name>
<value>foo</value>
</server>
<server>
<name>bbb</name>
<value>bar</value>
</server>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check no duplicates"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><c xmlns=\"urn:example:clixon\"><server><name>bbb</name><value>bar</value></server></c></data></rpc-reply>"
new "netconf discard-changes"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add complex list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<server>
<name>aaa</name>
<value>foo</value>
</server>
<server>
<name>bbb</name>
<value>foo</value>
</server>
<server>
<name>ccc</name>
<value>foo</value>
</server>
<server>
<name>bbb</name>
<value>bar</value>
</server>
<server>
<name>ccc</name>
<value>bar</value>
</server>
<server>
<name>bbb</name>
<value>fie</value>
</server>
<server>
<name>ddd</name>
<value>foo</value>
</server>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check complex no duplicates"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><c xmlns=\"urn:example:clixon\"><server><name>aaa</name><value>foo</value></server><server><name>bbb</name><value>fie</value></server><server><name>ccc</name><value>bar</value></server><server><name>ddd</name><value>foo</value></server></c></data></rpc-reply>"
new "netconf discard-changes"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add user sorted list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<user>
<name>aaa</name>
<value>foo</value>
</user>
<user>
<name>bbb</name>
<value>foo</value>
</user>
<user>
<name>ccc</name>
<value>foo</value>
</user>
<user>
<name>bbb</name>
<value>bar</value>
</user>
<user>
<name>ccc</name>
<value>bar</value>
</user>
<user>
<name>bbb</name>
<value>fie</value>
</user>
<user>
<name>ddd</name>
<value>foo</value>
</user>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check user sorted no duplicates"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><c xmlns=\"urn:example:clixon\"><user><name>aaa</name><value>foo</value></user><user><name>ccc</name><value>bar</value></user><user><name>bbb</name><value>fie</value></user><user><name>ddd</name><value>foo</value></user></c></data></rpc-reply>"
new "netconf discard-changes"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add leaf-list with duplicate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns=\"urn:example:clixon\">
<b>aaa</b>
<b>aaa</b>
<b>bbb</b>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Check leaf-list no duplicates"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><c xmlns=\"urn:example:clixon\"><b>aaa</b><b>bbb</b></c></data></rpc-reply>"
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill

View file

@ -199,13 +199,7 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
<ip>192.0.2.1</ip>
<port>25</port>
</server>
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "netconf validate (should fail) example3"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity><error-info><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/c/server[name=\"smtp\"]/ip</non-unique><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/c/server[name=\"smtp\"]/port</non-unique></error-info></rpc-error></rpc-reply>"
new "netconf discard-changes"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><discard-changes/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
</c></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity><error-info><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/rpc/edit-config/config/c/server[name=\"smtp\"]/ip</non-unique><non-unique xmlns=\"urn:ietf:params:xml:ns:yang:1\">/rpc/edit-config/config/c/server[name=\"smtp\"]/port</non-unique></error-info></rpc-error></rpc-reply>"
if [ $BE -ne 0 ]; then
new "Kill backend"

View file

@ -53,6 +53,7 @@ module clixon-config {
description
"Added options:
CLICON_XMLDB_SYSTEM_ONLY_CONFIG
Changed: CLICON_NETCONF_DUPLICATE_ALLOW to not only check but remove duplicates
Released in Clixon 7.3";
}
revision 2024-08-01 {
@ -72,7 +73,7 @@ module clixon-config {
CLICON_YANG_SCHEMA_MOUNT_SHARE: Share same YANGs of equal moint-points.
CLICON_SOCK_PRIO: Enable socket event priority
CLICON_XMLDB_MULTI: Split datastore into multiple sub files
CLICON_CLI_OUTPUT_FORMAT: Default CLI output format
CLICON_CLI_OUTPUT_FORMAT: Defauldirt CLI output format
CLICON_AUTOLOCK: Implicit locks
Released in Clixon 7.1";
}
@ -791,10 +792,11 @@ module clixon-config {
type boolean;
default false;
description
"Disable duplicate check in NETCONF messages.
"Remove duplicates in incoming NETCONF messages instead of signaling errors.
In Clixon 7.0, a stricter check of duplicate entries in incoming NETCONF messages was made.
More specifically: lists and leaf-lists with non-unique entries.
Enable to disable this check, and to allow duplicates in incoming NETCONF messages.
Enable to disable this check, and to REMOVE duplicates in incoming NETCONF messages.
When duplicates are removed, only the latest entry is kept.
Note that this is an error by such a client, but there is some legacy code that uses this";
}
/* HTTP and Restconf */