Fixed: [Templates with nc:operation merge causes bad diffs to be shows](https://github.com/clicon/clixon-controller/issues/187)
This commit is contained in:
parent
b0cc1857c0
commit
ed226a990c
5 changed files with 117 additions and 22 deletions
|
|
@ -22,6 +22,10 @@ Planned: April 2025
|
||||||
* New `clixon-restconf@2025-02-01.yang` revision
|
* New `clixon-restconf@2025-02-01.yang` revision
|
||||||
* Added timeout parameter
|
* Added timeout parameter
|
||||||
|
|
||||||
|
### Corrected Bugs
|
||||||
|
|
||||||
|
* Fixed: [Templates with nc:operation "merge" causes bad diffs to be shows](https://github.com/clicon/clixon-controller/issues/187)
|
||||||
|
|
||||||
## 7.3.0
|
## 7.3.0
|
||||||
30 January 2025
|
30 January 2025
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -655,7 +655,8 @@ from_client_edit_config(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Must do before duplicate check */
|
/* Must do before duplicate check,
|
||||||
|
* should probably be done before minmax check above */
|
||||||
if (xml_sort_recurse(xc) < 0)
|
if (xml_sort_recurse(xc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Disable duplicate check in NETCONF messages. */
|
/* Disable duplicate check in NETCONF messages. */
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,5 @@ A collection of community links to usages of clixon. Please notify of changes or
|
||||||
|
|
||||||
Links:
|
Links:
|
||||||
|
|
||||||
* https://github.com/MontaVista-OpenSourceTechnology/clixon
|
* https://github.com/MontaVista-OpenSourceTechnology/clixon-backend-helper
|
||||||
* https://github.com/brunorijsman/yang-tutorial/tree/main/clixon
|
* https://github.com/brunorijsman/yang-tutorial/tree/main/clixon
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@
|
||||||
*/
|
*/
|
||||||
struct vec_order {
|
struct vec_order {
|
||||||
cxobj *vo_xml;
|
cxobj *vo_xml;
|
||||||
char **vo_strvec;
|
char **vo_strvec; /* vector of keys, 1 element if leaf-list, NULL if non-list */
|
||||||
size_t vo_slen; /* length of vo_strvec (is actually global to vector) */
|
size_t vo_slen; /* Length of vo_strvec (is actually global to vector) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! New element last in list, check if already exists if so return -1
|
/*! New element last in list, check if already exists if so return -1
|
||||||
|
|
@ -877,9 +877,10 @@ remove_duplicates_list(yang_stmt *y,
|
||||||
int *nr,
|
int *nr,
|
||||||
cxobj **xret)
|
cxobj **xret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int v;
|
cvec *cvk;
|
||||||
int i;
|
int v;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (nr)
|
if (nr)
|
||||||
*nr = 0;
|
*nr = 0;
|
||||||
|
|
@ -896,7 +897,7 @@ remove_duplicates_list(yang_stmt *y,
|
||||||
(*nr)++;
|
(*nr)++;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
cvec *cvk = NULL;
|
cvk = NULL;
|
||||||
if (yang_keyword_get(y) == Y_LEAF_LIST){
|
if (yang_keyword_get(y) == Y_LEAF_LIST){
|
||||||
if ((cvk = cvec_new(0)) == NULL){
|
if ((cvk = cvec_new(0)) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cvec_new");
|
clixon_err(OE_UNIX, errno, "cvec_new");
|
||||||
|
|
@ -922,6 +923,48 @@ remove_duplicates_list(yang_stmt *y,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Remove duplicates container or leaf
|
||||||
|
*
|
||||||
|
* @param[in] vec Ordered vector of string vectors
|
||||||
|
* @param[in] vlen Length of vec
|
||||||
|
* @param[in] rm 0: return 0 on first duplicate, 1: remove all duplicates
|
||||||
|
* @param[in] cvv Vector of keys (for error)
|
||||||
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
remove_duplicates_single(yang_stmt *y,
|
||||||
|
struct vec_order *vec,
|
||||||
|
size_t vlen,
|
||||||
|
int rm,
|
||||||
|
int *nr,
|
||||||
|
cxobj **xret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *x;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
if (nr)
|
||||||
|
*nr = 0;
|
||||||
|
for (v=1; v<vlen; v++){
|
||||||
|
x = vec[v-1].vo_xml;
|
||||||
|
if (rm){
|
||||||
|
if (xml_purge(x) < 0)
|
||||||
|
goto done;
|
||||||
|
if (nr)
|
||||||
|
(*nr)++;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (xret && netconf_minmax_elements_xml(xret, xml_parent(x), xml_name(x), 1) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Analyze sorted list: detect and potentially remove duplicates
|
/*! Analyze sorted list: detect and potentially remove duplicates
|
||||||
*
|
*
|
||||||
* @param[in] y YANG node of list segment
|
* @param[in] y YANG node of list segment
|
||||||
|
|
@ -946,12 +989,25 @@ vec_order_analyze(yang_stmt *y,
|
||||||
int nr = 0;
|
int nr = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (yang_find(y, Y_ORDERED_BY, "user") != NULL)
|
switch (yang_keyword_get(y)){
|
||||||
qsort(vec, vlen, sizeof(*vec), cmp_list_qsort);
|
case Y_LIST:
|
||||||
if ((ret = remove_duplicates_list(y, vec, vlen, rm, &nr, xret)) < 0)
|
case Y_LEAF_LIST:
|
||||||
goto done;
|
if (yang_find(y, Y_ORDERED_BY, "user") != NULL)
|
||||||
if (ret == 0) {
|
qsort(vec, vlen, sizeof(*vec), cmp_list_qsort);
|
||||||
goto fail;
|
if ((ret = remove_duplicates_list(y, vec, vlen, rm, &nr, xret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case Y_LEAF:
|
||||||
|
case Y_CONTAINER:
|
||||||
|
if (vlen > 1){
|
||||||
|
if (remove_duplicates_single(y, vec, vlen, rm, &nr, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (x && nr)
|
if (x && nr)
|
||||||
xml_vector_decrement(x, nr);
|
xml_vector_decrement(x, nr);
|
||||||
|
|
@ -1016,12 +1072,28 @@ xml_duplicate_detect1(cxobj *xt,
|
||||||
}
|
}
|
||||||
keyw = yang_keyword_get(y);
|
keyw = yang_keyword_get(y);
|
||||||
switch (keyw){
|
switch (keyw){
|
||||||
|
case Y_CONTAINER:
|
||||||
|
case Y_LEAF:
|
||||||
|
if (vlen > 0 && slen0 != 0){ /* Sanity check */
|
||||||
|
clixon_err(OE_YANG, 0, "Container vector mismatch %lu != 0", slen0);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((vec = realloc(vec, (vlen+1)*sizeof(*vec))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "cvec_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
vec[vlen].vo_slen = 0;
|
||||||
|
vec[vlen].vo_strvec = NULL;
|
||||||
|
vec[vlen].vo_xml = x;
|
||||||
|
vlen++;
|
||||||
|
slen0 = 0;
|
||||||
|
break;
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
if ((cvk = yang_cvec_get(y)) == NULL)
|
if ((cvk = yang_cvec_get(y)) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((clen = cvec_len(cvk)) == 0)
|
if ((clen = cvec_len(cvk)) == 0)
|
||||||
continue;
|
continue;
|
||||||
if (vec>0 && slen0 != clen){ /* Sanity check */
|
if (vlen > 0 && slen0 != clen){ /* Sanity check */
|
||||||
clixon_err(OE_YANG, 0, "List key vector mismatch %lu != %lu", slen0, clen);
|
clixon_err(OE_YANG, 0, "List key vector mismatch %lu != %lu", slen0, clen);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -1063,7 +1135,7 @@ xml_duplicate_detect1(cxobj *xt,
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
if (vec>0 && slen0 != 1){ /* Sanity check */
|
if (vlen > 0 && slen0 != 1){ /* Sanity check */
|
||||||
clixon_err(OE_YANG, 0, "Leaf-list key vector mismatch %lu != 1", slen0);
|
clixon_err(OE_YANG, 0, "Leaf-list key vector mismatch %lu != 1", slen0);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ EOF
|
||||||
# Example (the list server part) from RFC7950 Sec 7.8.3.1 w changed types
|
# Example (the list server part) from RFC7950 Sec 7.8.3.1 w changed types
|
||||||
cat <<EOF > $fyang
|
cat <<EOF > $fyang
|
||||||
module unique{
|
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 {
|
||||||
|
|
@ -63,6 +63,8 @@ module unique{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
container d{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -79,7 +81,7 @@ if [ $BE -ne 0 ]; then
|
||||||
start_backend -s init -f $cfg
|
start_backend -s init -f $cfg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "wait backend"
|
new "Wait backend 1"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
new "Add list entry"
|
new "Add list entry"
|
||||||
|
|
@ -326,6 +328,24 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS>
|
||||||
new "Check mix w empty"
|
new "Check mix w empty"
|
||||||
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/><value>foo</value></user><b/><b>bbb</b><b>ccc</b><buser>CCC</buser><buser>BBB</buser><buser>AAA</buser></c></data></rpc-reply>"
|
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/><value>foo</value></user><b/><b>bbb</b><b>ccc</b><buser>CCC</buser><buser>BBB</buser><buser>AAA</buser></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>"
|
||||||
|
|
||||||
|
# Note both unordered and duplicate
|
||||||
|
new "Container dups"
|
||||||
|
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>
|
||||||
|
</c>
|
||||||
|
<d xmlns=\"urn:example:clixon\"/>
|
||||||
|
<c xmlns=\"urn:example:clixon\">
|
||||||
|
<b>bbb</b>
|
||||||
|
</c>
|
||||||
|
</config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "Check no container 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>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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue