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 `system-only-config` extension
|
||||||
* New `ca_system_only` backend callback for reading system-only data
|
* New `ca_system_only` backend callback for reading system-only data
|
||||||
* New `clixon-config@2024-11-01.yang` revision
|
* 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`
|
* Added: `CLICON_XMLDB_SYSTEM_ONLY_CONFIG`
|
||||||
|
|
||||||
### C/CLI-API changes on existing features
|
### C/CLI-API changes on existing features
|
||||||
|
|
@ -37,6 +38,7 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Corrected Bugs
|
### 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: Segv in canonical xpath transform
|
||||||
* Fixed: [Error with submodules and feature Interaction](https://github.com/clicon/clixon-controller/issues/158)
|
* 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)
|
* 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)
|
if ((ret = xml_yang_validate_minmax(xc, 1, &xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Disable duplicate check in NETCONF messages.*/
|
if (ret == 0){
|
||||||
if (clicon_option_bool(h, "CLICON_NETCONF_DUPLICATE_ALLOW"))
|
if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0)
|
||||||
;
|
goto done;
|
||||||
else if (ret == 1 && (ret = xml_yang_validate_unique_recurse(xc, &xret)) < 0)
|
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;
|
goto done;
|
||||||
/* xmldb_put (difflist handling) requires list keys */
|
/* xmldb_put (difflist handling) requires list keys */
|
||||||
if (ret == 1 && (ret = xml_yang_validate_list_key_only(xc, &xret)) < 0)
|
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 done;
|
||||||
goto ok;
|
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 ((ret = xmldb_put(h, target, operation, xc, username, cbret)) < 0){
|
||||||
if (netconf_operation_failed(cbret, "protocol", clixon_err_reason())< 0)
|
if (netconf_operation_failed(cbret, "protocol", clixon_err_reason())< 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -312,9 +312,8 @@ startup_common(clixon_handle h,
|
||||||
clixon_err(OE_XML, EFAULT, "Yang sort error");
|
clixon_err(OE_XML, EFAULT, "Yang sort error");
|
||||||
}
|
}
|
||||||
/* clear XML tree of defaults */
|
/* 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;
|
goto done;
|
||||||
|
|
||||||
if (xmldb_dump(h, stdout, xt, FORMAT_XML,
|
if (xmldb_dump(h, stdout, xt, FORMAT_XML,
|
||||||
clicon_option_bool(h, "CLICON_XMLDB_PRETTY"),
|
clicon_option_bool(h, "CLICON_XMLDB_PRETTY"),
|
||||||
WITHDEFAULTS_REPORT_ALL, 0, NULL) < 0)
|
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_minmax_recurse(cxobj *xt, cxobj **xret);
|
||||||
int xml_yang_validate_unique(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_yang_validate_unique_recurse(cxobj *xt, cxobj **xret);
|
||||||
|
int xml_duplicate_remove_recurse(cxobj *xt, cxobj **xret);
|
||||||
|
|
||||||
#endif /* _CLIXON_VALIDATE_MINMAX_H_ */
|
#endif /* _CLIXON_VALIDATE_MINMAX_H_ */
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@ int xml_diff(cxobj *x0, cxobj *x1,
|
||||||
cxobj ***changed_x0, cxobj ***changed_x1, int *changedlen);
|
cxobj ***changed_x0, cxobj ***changed_x1, int *changedlen);
|
||||||
int xml_tree_equal(cxobj *x0, cxobj *x1);
|
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_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_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_namespace_change(cxobj *x, char *ns, char *prefix);
|
||||||
int xml_sanity(cxobj *x, void *arg);
|
int xml_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
#include "clixon_xml_bind.h"
|
#include "clixon_xml_bind.h"
|
||||||
#include "clixon_validate_minmax.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] vec Vector of existing entries (new is last)
|
||||||
* @param[in] i1 The new entry is placed at vec[i1]
|
* @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
|
/*! New element last in list, return error if already exists
|
||||||
*
|
*
|
||||||
* @param[in] vec Vector of existing entries (new is last)
|
* @param[in] vec Vector of existing entries (new is last)
|
||||||
* @param[in] i1 The new entry is placed at vec[i1]
|
* @param[in] i1 The new entry is placed at vec[i1]
|
||||||
* @param[in] vlen Length of vec
|
* @param[in] vlen Length of vec
|
||||||
* @param[in] sorted Sorted by system, ie sorted by key, otherwise no assumption
|
* @param[in] sorted Sorted by system, ie sorted by key, otherwise no assumption
|
||||||
* @retval 0 OK, entry is unique
|
* @param[out] dupl Index of duplicated element (if retval = -1)
|
||||||
* @retval -1 Duplicate detected
|
* @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.
|
* @note This is currently quadratic complexity. It could be improved by inserting new element sorted and binary search.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_insert_duplicate(char **vec,
|
check_insert_duplicate(char **vec,
|
||||||
int i1,
|
int i1,
|
||||||
int vlen,
|
int vlen,
|
||||||
int sorted)
|
int sorted,
|
||||||
|
int *dupl)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int v;
|
int v;
|
||||||
|
|
@ -168,6 +170,8 @@ check_insert_duplicate(char **vec,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* here we have passed thru all keys of previous element and they are all equal */
|
/* here we have passed thru all keys of previous element and they are all equal */
|
||||||
|
if (dupl)
|
||||||
|
*dupl = i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
@ -180,7 +184,13 @@ check_insert_duplicate(char **vec,
|
||||||
if (v==vlen) /* duplicate */
|
if (v==vlen) /* duplicate */
|
||||||
break;
|
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] xt The parent of x (a list)
|
||||||
* @param[in] y Its yang spec (Y_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] 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
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (cbret set)
|
* @retval 0 Validation failed (cbret set)
|
||||||
|
|
@ -213,12 +224,14 @@ check_unique_list_direct(cxobj *x,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
yang_stmt *y,
|
yang_stmt *y,
|
||||||
yang_stmt *yu,
|
yang_stmt *yu,
|
||||||
|
int mark,
|
||||||
cxobj **xret)
|
cxobj **xret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cg_var *cvi; /* unique node name */
|
cg_var *cvi; /* unique node name */
|
||||||
cxobj *xi;
|
cxobj *xi;
|
||||||
char **vec = NULL; /* 2xmatrix */
|
char **vec = NULL; /* 2xmatrix */
|
||||||
|
cxobj **xvec = NULL;
|
||||||
int clen;
|
int clen;
|
||||||
int i;
|
int i;
|
||||||
int v;
|
int v;
|
||||||
|
|
@ -226,6 +239,7 @@ check_unique_list_direct(cxobj *x,
|
||||||
int sorted;
|
int sorted;
|
||||||
char *str;
|
char *str;
|
||||||
cvec *cvk;
|
cvec *cvk;
|
||||||
|
int dupl;
|
||||||
|
|
||||||
/* If list and is sorted by system, then it is assumed elements are in key-order which is optimized
|
/* 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
|
* 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 */
|
/* No keys: no checks necessary */
|
||||||
goto ok;
|
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){
|
if ((vec = calloc(clen*xml_child_nr(xt), sizeof(char*))) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "calloc");
|
clixon_err(OE_UNIX, errno, "calloc");
|
||||||
goto done;
|
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
|
/* A vector is built with key-values, for each iteration check "backward" in the vector
|
||||||
* for duplicates
|
* for duplicates
|
||||||
*/
|
*/
|
||||||
i = 0; /* x element index */
|
i = 0; /* x element index */
|
||||||
do {
|
do {
|
||||||
|
xvec[i] = x;
|
||||||
cvi = NULL;
|
cvi = NULL;
|
||||||
v = 0; /* index in each tuple */
|
v = 0; /* index in each tuple */
|
||||||
/* XXX Quadratic if clen > 1 */
|
/* XXX Quadratic if clen > 1 */
|
||||||
|
|
@ -267,7 +287,9 @@ check_unique_list_direct(cxobj *x,
|
||||||
}
|
}
|
||||||
if (cvi==NULL){
|
if (cvi==NULL){
|
||||||
/* Last element (i) is newly inserted, see if it is already there */
|
/* 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)
|
if (xret && netconf_data_not_unique_xml(xret, x, cvk) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -280,6 +302,8 @@ check_unique_list_direct(cxobj *x,
|
||||||
/* It would be possible to cache vec here as an optimization */
|
/* It would be possible to cache vec here as an optimization */
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
if (vec)
|
if (vec)
|
||||||
free(vec);
|
free(vec);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -294,6 +318,7 @@ check_unique_list_direct(cxobj *x,
|
||||||
* @param[in] xt The parent of x (a list)
|
* @param[in] xt The parent of x (a list)
|
||||||
* @param[in] y Its yang spec (Y_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] 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
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (xret set)
|
* @retval 0 Validation failed (xret set)
|
||||||
|
|
@ -317,6 +342,7 @@ check_unique_list(cxobj *x,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
yang_stmt *y,
|
yang_stmt *y,
|
||||||
yang_stmt *yu,
|
yang_stmt *yu,
|
||||||
|
uint16_t mark,
|
||||||
cxobj **xret)
|
cxobj **xret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -333,7 +359,7 @@ check_unique_list(cxobj *x,
|
||||||
/* Check if multiple direct children */
|
/* Check if multiple direct children */
|
||||||
cvk = yang_cvec_get(yu);
|
cvk = yang_cvec_get(yu);
|
||||||
if (cvec_len(cvk) > 1){
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
cvi = cvec_i(cvk, 0);
|
cvi = cvec_i(cvk, 0);
|
||||||
|
|
@ -343,7 +369,7 @@ check_unique_list(cxobj *x,
|
||||||
}
|
}
|
||||||
/* Check if direct schmeanode-id , ie not xpath */
|
/* Check if direct schmeanode-id , ie not xpath */
|
||||||
if (index(xpath0, '/') == NULL){
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Here proper xpath with at least one slash (can there be a descendant schemanodeid w/o slash?) */
|
/* 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] x XML LIST node
|
||||||
* @param[in] xt XML parent
|
* @param[in] xt XML parent
|
||||||
* @param[in] y YANG of x
|
* @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
|
* @param[out] xret Error as XML if ret = 0
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (xret set)
|
* @retval 0 Validation failed (xret set)
|
||||||
|
|
@ -493,6 +520,7 @@ static int
|
||||||
xml_yang_minmax_new_list(cxobj *x,
|
xml_yang_minmax_new_list(cxobj *x,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
yang_stmt *y,
|
yang_stmt *y,
|
||||||
|
int mark,
|
||||||
cxobj **xret)
|
cxobj **xret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -503,7 +531,7 @@ xml_yang_minmax_new_list(cxobj *x,
|
||||||
/* Here new (first element) of lists only
|
/* Here new (first element) of lists only
|
||||||
* First check unique keys direct children
|
* 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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -520,7 +548,7 @@ xml_yang_minmax_new_list(cxobj *x,
|
||||||
* 1) multiple direct children (no prefixes), eg "a b"
|
* 1) multiple direct children (no prefixes), eg "a b"
|
||||||
* 2) single xpath with canonical prefixes, eg "/ex:a/ex: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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -538,6 +566,7 @@ xml_yang_minmax_new_list(cxobj *x,
|
||||||
* @param[in] x0 XML LIST node
|
* @param[in] x0 XML LIST node
|
||||||
* @param[in] xt XML parent
|
* @param[in] xt XML parent
|
||||||
* @param[in] y0 YANG of x0
|
* @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
|
* @param[out] xret Error as XML if ret = 0
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (xret set)
|
* @retval 0 Validation failed (xret set)
|
||||||
|
|
@ -548,6 +577,7 @@ static int
|
||||||
xml_yang_minmax_new_leaf_list(cxobj *x0,
|
xml_yang_minmax_new_leaf_list(cxobj *x0,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
yang_stmt *y0,
|
yang_stmt *y0,
|
||||||
|
int mark,
|
||||||
cxobj **xret)
|
cxobj **xret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -567,6 +597,8 @@ xml_yang_minmax_new_leaf_list(cxobj *x0,
|
||||||
if ((bj = xml_body(xj)) == NULL)
|
if ((bj = xml_body(xj)) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if (bi && bj && strcmp(bi, bj) == 0){
|
if (bi && bj && strcmp(bi, bj) == 0){
|
||||||
|
if (mark)
|
||||||
|
xml_flag_set(xi, mark);
|
||||||
if ((cvv = cvec_new(0)) == NULL){
|
if ((cvv = cvec_new(0)) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cvec_new");
|
clixon_err(OE_UNIX, errno, "cvec_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -758,7 +790,7 @@ xml_yang_validate_minmax(cxobj *xt,
|
||||||
/* new list check */
|
/* new list check */
|
||||||
if (ret){
|
if (ret){
|
||||||
if (keyw == Y_LIST){
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
#ifdef NOTYET /* XXX This is enforced in xml_yang_validate_unique instead */
|
#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 */
|
/* new list check */
|
||||||
switch (keyw){
|
switch (keyw){
|
||||||
case Y_LIST:
|
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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case Y_LEAF_LIST:
|
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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1002,3 +1034,119 @@ xml_yang_validate_unique_recurse(cxobj *xt,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
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);
|
* xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note This function seems a little too complex semantics
|
* @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
|
int
|
||||||
xml_tree_prune_flagged_sub(cxobj *xt,
|
xml_tree_prune_flagged_sub(cxobj *xt,
|
||||||
|
|
@ -750,46 +750,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Prune everything that passes test
|
/*! Prune everything that passes test recursively
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*
|
*
|
||||||
* @param[in] xt XML tree with some node marked
|
* @param[in] xt XML tree with some node marked
|
||||||
* @param[in] flags Flags set
|
* @param[in] flags Flags set
|
||||||
|
|
@ -800,15 +761,40 @@ xml_tree_prune_flagged(cxobj *xt,
|
||||||
* @code
|
* @code
|
||||||
* xml_tree_prune_flags(xt, XML_FLAG_MARK, XML_FLAG_MARK|XML_FLAG_DEFAULT);
|
* xml_tree_prune_flags(xt, XML_FLAG_MARK, XML_FLAG_MARK|XML_FLAG_DEFAULT);
|
||||||
* @endcode
|
* @endcode
|
||||||
|
* @see xml_tree_prune_flags1
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_tree_prune_flags(cxobj *xt,
|
xml_tree_prune_flags(cxobj *xt,
|
||||||
int flags,
|
int flags,
|
||||||
int mask)
|
int mask)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
return xml_tree_prune_flags1(xt, flags, mask, 1, NULL);
|
||||||
cxobj *x;
|
}
|
||||||
cxobj *xprev;
|
|
||||||
|
/*! 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;
|
x = NULL;
|
||||||
xprev = NULL;
|
xprev = NULL;
|
||||||
|
|
@ -817,10 +803,13 @@ xml_tree_prune_flags(cxobj *xt,
|
||||||
if (xml_purge(x) < 0)
|
if (xml_purge(x) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
x = xprev;
|
x = xprev;
|
||||||
|
if (removed)
|
||||||
|
(*removed)++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (xml_tree_prune_flags(x, flags, mask) < 0)
|
if (recursive)
|
||||||
goto done;
|
if (xml_tree_prune_flags1(x, flags, mask, 1, removed) < 0)
|
||||||
|
goto done;
|
||||||
xprev = x;
|
xprev = x;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -828,7 +817,6 @@ xml_tree_prune_flags(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Change namespace of XML node
|
/*! Change namespace of XML node
|
||||||
*
|
*
|
||||||
* @param[in] x 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>"
|
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"
|
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"
|
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>"
|
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)
|
# Detect duplicates in incoming edit-configs (not top-level)
|
||||||
# Both list and leaf-list
|
# Both list and leaf-list
|
||||||
# See https://github.com/clicon/clixon-controller/issues/107
|
# 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)
|
# Magic line must be first in script (see README.md)
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
@ -31,22 +32,33 @@ module unique{
|
||||||
yang-version 1.1;
|
yang-version 1.1;
|
||||||
namespace "urn:example:clixon";
|
namespace "urn:example:clixon";
|
||||||
prefix un;
|
prefix un;
|
||||||
container c{
|
container c{
|
||||||
presence "trigger"; // force presence container to trigger error
|
presence "trigger"; // force presence container to trigger error
|
||||||
list server {
|
list server {
|
||||||
description "";
|
description "";
|
||||||
key "name";
|
key "name";
|
||||||
leaf name {
|
leaf name {
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
leaf value {
|
leaf value {
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
leaf-list b{
|
list user {
|
||||||
type string;
|
description "";
|
||||||
}
|
ordered-by user;
|
||||||
}
|
key "name";
|
||||||
|
leaf name {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf-list b{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -98,6 +110,22 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
||||||
</server>
|
</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>"
|
</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"
|
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\">
|
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>
|
<b>one</b>
|
||||||
|
|
@ -105,6 +133,130 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
||||||
<b>one</b>
|
<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>"
|
</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
|
if [ $BE -ne 0 ]; then
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
|
||||||
|
|
@ -199,13 +199,7 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
||||||
<ip>192.0.2.1</ip>
|
<ip>192.0.2.1</ip>
|
||||||
<port>25</port>
|
<port>25</port>
|
||||||
</server>
|
</server>
|
||||||
</c></config></edit-config></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>"
|
||||||
|
|
||||||
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>"
|
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ module clixon-config {
|
||||||
description
|
description
|
||||||
"Added options:
|
"Added options:
|
||||||
CLICON_XMLDB_SYSTEM_ONLY_CONFIG
|
CLICON_XMLDB_SYSTEM_ONLY_CONFIG
|
||||||
|
Changed: CLICON_NETCONF_DUPLICATE_ALLOW to not only check but remove duplicates
|
||||||
Released in Clixon 7.3";
|
Released in Clixon 7.3";
|
||||||
}
|
}
|
||||||
revision 2024-08-01 {
|
revision 2024-08-01 {
|
||||||
|
|
@ -72,7 +73,7 @@ module clixon-config {
|
||||||
CLICON_YANG_SCHEMA_MOUNT_SHARE: Share same YANGs of equal moint-points.
|
CLICON_YANG_SCHEMA_MOUNT_SHARE: Share same YANGs of equal moint-points.
|
||||||
CLICON_SOCK_PRIO: Enable socket event priority
|
CLICON_SOCK_PRIO: Enable socket event priority
|
||||||
CLICON_XMLDB_MULTI: Split datastore into multiple sub files
|
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
|
CLICON_AUTOLOCK: Implicit locks
|
||||||
Released in Clixon 7.1";
|
Released in Clixon 7.1";
|
||||||
}
|
}
|
||||||
|
|
@ -791,10 +792,11 @@ module clixon-config {
|
||||||
type boolean;
|
type boolean;
|
||||||
default false;
|
default false;
|
||||||
description
|
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.
|
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.
|
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";
|
Note that this is an error by such a client, but there is some legacy code that uses this";
|
||||||
}
|
}
|
||||||
/* HTTP and Restconf */
|
/* HTTP and Restconf */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue