From 0193186272b89597c10c43e35bbe1155939a3067 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 23 Nov 2024 10:51:04 +0100 Subject: [PATCH] Fixed: [Change CLICON_NETCONF_DUPLICATE_ALLOW to remove duplicates](https://github.com/clicon/clixon-controller/issues/160) C-API: Removed xml_tree_prune_flagged --- CHANGELOG.md | 2 + apps/backend/backend_client.c | 23 +-- apps/backend/backend_commit.c | 3 +- lib/clixon/clixon_validate_minmax.h | 1 + lib/clixon/clixon_xml_map.h | 2 +- lib/src/clixon_validate_minmax.c | 180 +++++++++++++++++++-- lib/src/clixon_xml_map.c | 86 +++++----- test/test_minmax.sh | 2 +- test/test_netconf_duplicate.sh | 184 ++++++++++++++++++++-- test/test_unique.sh | 8 +- yang/clixon/clixon-config@2024-11-01.yang | 8 +- 11 files changed, 395 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a925b2b..5311f68b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index dbe5680b..b4f892c9 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -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; diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 1a8076fc..6a89dc0b 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -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) diff --git a/lib/clixon/clixon_validate_minmax.h b/lib/clixon/clixon_validate_minmax.h index fe754911..0c6f0f61 100644 --- a/lib/clixon/clixon_validate_minmax.h +++ b/lib/clixon/clixon_validate_minmax.h @@ -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_ */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 882e0a24..b58b12ce 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.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); diff --git a/lib/src/clixon_validate_minmax.c b/lib/src/clixon_validate_minmax.c index 396575d6..04a68655 100644 --- a/lib/src/clixon_validate_minmax.c +++ b/lib/src/clixon_validate_minmax.c @@ -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 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; +} diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 633d6c1f..d6cc7207 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -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 diff --git a/test/test_minmax.sh b/test/test_minmax.sh index c104e71c..ea42e209 100755 --- a/test/test_minmax.sh +++ b/test/test_minmax.sh @@ -144,7 +144,7 @@ new "minmax: minimal validate ok" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" new "minmax: maximal: 2x a1,b1" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace01unbounded0101unbounded101" "" "" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace01unbounded0101unbounded10" "" "" new "minmax: validate ok" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" diff --git a/test/test_netconf_duplicate.sh b/test/test_netconf_duplicate.sh index c77a076c..c8bada6b 100755 --- a/test/test_netconf_duplicate.sh +++ b/test/test_netconf_duplicate.sh @@ -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" " " "" "applicationoperation-faileddata-not-uniqueerror/rpc/edit-config/config/c/server[name=\"one\"]/name" +new "Add user sorted list with duplicate" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace + + one + foo + + + two + bar + + + one + foo + +" "" "applicationoperation-faileddata-not-uniqueerror/rpc/edit-config/config/c/user[name=\"one\"]/name" + new "Add leaf-list with duplicate" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace one @@ -105,6 +133,130 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" " one " "" "applicationoperation-faileddata-not-uniqueerror/rpc/edit-config/config/c/b[.=\"one\"]/one" +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" "replace + + bbb + foo + + + bbb + bar + +" "" "" + +new "Check no duplicates" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "bbbbar" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Add complex list with duplicate" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace + + aaa + foo + + + bbb + foo + + + ccc + foo + + + bbb + bar + + + ccc + bar + + + bbb + fie + + + ddd + foo + +" "" "" + +new "Check complex no duplicates" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "aaafoobbbfiecccbardddfoo" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Add user sorted list with duplicate" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace + + aaa + foo + + + bbb + foo + + + ccc + foo + + + bbb + bar + + + ccc + bar + + + bbb + fie + + + ddd + foo + +" "" "" + +new "Check user sorted no duplicates" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "aaafoocccbarbbbfiedddfoo" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Add leaf-list with duplicate" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "replace + aaa + aaa + bbb +" "" "" + +new "Check leaf-list no duplicates" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "aaabbb" + + if [ $BE -ne 0 ]; then new "Kill backend" # Check if premature kill diff --git a/test/test_unique.sh b/test/test_unique.sh index ab45662f..9acc70a6 100755 --- a/test/test_unique.sh +++ b/test/test_unique.sh @@ -199,13 +199,7 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" " 192.0.2.1 25 -" "" "" - -new "netconf validate (should fail) example3" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-faileddata-not-uniqueerror/c/server[name=\"smtp\"]/ip/c/server[name=\"smtp\"]/port" - -new "netconf discard-changes" -expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" +" "" "applicationoperation-faileddata-not-uniqueerror/rpc/edit-config/config/c/server[name=\"smtp\"]/ip/rpc/edit-config/config/c/server[name=\"smtp\"]/port" if [ $BE -ne 0 ]; then new "Kill backend" diff --git a/yang/clixon/clixon-config@2024-11-01.yang b/yang/clixon/clixon-config@2024-11-01.yang index 57598cba..427221a0 100644 --- a/yang/clixon/clixon-config@2024-11-01.yang +++ b/yang/clixon/clixon-config@2024-11-01.yang @@ -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 */