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:
parent
68e132c275
commit
0193186272
11 changed files with 395 additions and 104 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
@ -143,6 +143,7 @@ unique_search_xpath(cxobj *x,
|
|||
* @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
|
||||
* @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.
|
||||
|
|
@ -151,7 +152,8 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,11 +761,36 @@ 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)
|
||||
{
|
||||
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;
|
||||
|
|
@ -817,9 +803,12 @@ 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)
|
||||
if (recursive)
|
||||
if (xml_tree_prune_flags1(x, flags, mask, 1, removed) < 0)
|
||||
goto done;
|
||||
xprev = x;
|
||||
}
|
||||
|
|
@ -828,7 +817,6 @@ xml_tree_prune_flags(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Change namespace of XML node
|
||||
*
|
||||
* @param[in] x XML node
|
||||
|
|
|
|||
|
|
@ -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>"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -43,6 +44,17 @@ module unique{
|
|||
type string;
|
||||
}
|
||||
}
|
||||
list user {
|
||||
description "";
|
||||
ordered-by user;
|
||||
key "name";
|
||||
leaf name {
|
||||
type string;
|
||||
}
|
||||
leaf value {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf-list b{
|
||||
type string;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue