diff --git a/CHANGELOG.md b/CHANGELOG.md index f98f1db0..3f6d347b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ Expected: February 2020 ### Corrected Bugs +* Fixed: Search function checked only own not for config false statement, should have checked all ancestors. * Fixed: Some restconf errors were wrongly formatted such as: `{"ietf-restconf:errors":{"error":{"rpc-error":` . There should be no `"rpc-error"` level. * Fixed: Enabling modstate (CLICON_XMLDB_MODSTATE), changing a revision on a yang, and restarting made the backend daemon exit at start (thanks Matt) * Also: ensure to load `ietf-yang-library.yang ` if CLICON_XMLDB_MODSTATE is set diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index cf31bb33..06349b7e 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -216,6 +216,7 @@ int yang_desc_schema_nodeid(yang_stmt *yn, char *schema_nodeid, enum rfc_6020 keyword, yang_stmt **yres); int yang_mandatory(yang_stmt *ys); int yang_config(yang_stmt *ys); +int yang_config_ancestor(yang_stmt *ys); int yang_features(clicon_handle h, yang_stmt *yt); cvec *yang_arg2cvec(yang_stmt *ys, char *delimi); int yang_key_match(yang_stmt *yn, char *name); diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 080a3637..ab0744bf 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -611,6 +611,46 @@ xml_search_indexvar_binary_pos(cxobj *x1, done: return retval; } + +static int +xml_search_indexvar(cxobj *xp, + cxobj *x1, + int yangi, + int low, + int upper, + char *indexvar, + cxobj ***xvec, + size_t *xlen) +{ + int retval = -1; + clixon_xvec *ivec = NULL; + int ilen; + int pos; + int eq = 0; + + /* Check if (exactly one) explicit indexes in cvk */ + if (xml_search_vector_get(xp, indexvar, &ivec) < 0) + goto done; + if (ivec){ + ilen = clixon_xvec_len(ivec); + if ((pos = xml_search_indexvar_binary_pos(x1, indexvar, + ivec, 0, + ilen, ilen, &eq)) < 0) + goto done; + if (eq){ /* Found */ + if (cxvec_append(clixon_xvec_i(ivec,pos), xvec, xlen) < 0) + goto done; + /* there may be more? */ + if (search_multi_equals_xvec(ivec, x1, yangi, pos, + 0, xvec, xlen) < 0) + goto done; + } + } + retval = 0; + done: + return retval; +} + #endif /* XML_EXPLICIT_INDEX */ /*! Find XML child under xp matching x1 using binary search @@ -657,40 +697,13 @@ xml_search_binary(cxobj *xp, /* Here is right yang order == same yang? */ if (cmp == 0){ #ifdef XML_EXPLICIT_INDEX - if (indexvar != NULL){ - clixon_xvec *ivec = NULL; - int ilen; - int pos; - int eq = 0; - /* Check if (exactly one) explicit indexes in cvk */ - if (xml_search_vector_get(xp, indexvar, &ivec) < 0) + if (indexvar){ + if (xml_search_indexvar(xp, x1, yangi, low, upper, indexvar, xvec, xlen) < 0) goto done; - if (ivec){ - ilen = clixon_xvec_len(ivec); -#if 1 /* XXX This needs some heavy testing */ - if ((pos = xml_search_indexvar_binary_pos(x1, indexvar, - ivec, 0, - ilen, ilen, &eq)) < 0) - goto done; - if (eq){ /* Found */ - if (cxvec_append(clixon_xvec_i(ivec,pos), xvec, xlen) < 0) - goto done; - /* there may be more? */ - if (search_multi_equals_xvec(ivec, x1, yangi, pos, - 0, xvec, xlen) < 0) - goto done; - } -#else - if (xml_search_indexvar_binary(x1, indexvar, - ivec, 0, - ilen, ilen, - xvec, xlen) < 0) - goto done; -#endif - } goto ok; } #endif + /* >0 means search upper interval, <0 lower interval, = 0 is equal */ cmp = xml_cmp(x1, xc, 0, skip1, NULL); if (cmp && !sorted){ /* Ordered by user (if not equal) */ retval = xml_find_keys_notsorted(xp, x1, yangi, mid, skip1, xvec, xlen); @@ -753,7 +766,7 @@ xml_search_yang(cxobj *xp, if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa) != CX_ATTR) break; /* Find if non-config and if ordered-by-user */ - if (yang_config(yc)==0) + if (yang_config_ancestor(yc)==0) sorted = 0; else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL); @@ -989,7 +1002,7 @@ xml_insert(cxobj *xp, if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR) break; /* Find if non-config and if ordered-by-user */ - if (yang_config(y)==0) + if (yang_config_ancestor(y)==0) userorder = 1; else if (yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST) userorder = (yang_find(y, Y_ORDERED_BY, "user") != NULL); @@ -1026,7 +1039,7 @@ xml_sort_verify(cxobj *x0, yang_stmt *ys; /* Abort sort if non-config (=state) data */ - if ((ys = xml_spec(x0)) != 0 && yang_config(ys)==0){ + if ((ys = xml_spec(x0)) != 0 && yang_config_ancestor(ys)==0){ retval = 1; goto done; } diff --git a/lib/src/clixon_xpath_optimize.c b/lib/src/clixon_xpath_optimize.c index 426da62a..05551137 100644 --- a/lib/src/clixon_xpath_optimize.c +++ b/lib/src/clixon_xpath_optimize.c @@ -249,7 +249,7 @@ xpath_list_optimize_fn(xpath_tree *xt, if ((yp = xml_spec(xv)) == NULL) goto ok; /* or if not config data (state data should not be ordered) */ - if (yang_config(yp) == 0) + if (yang_config_ancestor(yp) == 0) goto ok; /* Check yang and that only a list with key as index is a special case can do bin search * That is, ONLY check optimize cases of this type:_x[_y='_z'] diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index d89156ab..98537417 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -2346,11 +2346,10 @@ yang_mandatory(yang_stmt *ys) } /*! Return config state of this node - * config statement is default true. - * @note a node with config=false may not have a sub statement where - * config=true. And this function does not check the status of a parent. - * @retval 0 if node has a config sub-statement and it is false - * @retval 1 node has not config sub-statement or it is true + * @param[in] ys Yang statement + * @retval 0 If node has a config sub-statement and it is false + * @retval 1 If node has not config sub-statement or it is true + * @see yang_config_ancestor which also takes ancestors into account, which you should normally do. */ int yang_config(yang_stmt *ys) @@ -2365,6 +2364,26 @@ yang_config(yang_stmt *ys) return 1; } +/*! Return config state of this node taking parent into account + * + * config statement is default true. + * @param[in] ys Yang statement + * @retval 0 Node or one of its ancestor has config false + * @retval 1 Neither node nor any of its ancestors has config false + */ +int +yang_config_ancestor(yang_stmt *ys) +{ + yang_stmt *yp; + + yp = ys; + do { + if (yang_config(yp) == 0) + return 0; + } while((yp = yang_parent_get(yp)) != NULL); + return 1; +} + /*! Given a yang node, translate the argument string to a cv vector * * @param[in] ys Yang statement diff --git a/test/test_instance_id.sh b/test/test_instance_id.sh index 10972939..3cb8b1bf 100755 --- a/test/test_instance_id.sh +++ b/test/test_instance_id.sh @@ -12,6 +12,9 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi # Number of tests to generate XML for max=9 +# How many times to run ech test (go through some random numbers) +rep=10 + # XML file (alt provide it in stdin after xpath) for (( i=1; i<$max; i++ )); do eval xml$i=$dir/xml$i.xml @@ -155,6 +158,7 @@ module modb{ } EOF +# This make it non-deterministic, some numbers may not work,... rnd=$(( ( RANDOM % $nr ) )) # Single string key @@ -165,45 +169,42 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml1 -new "instance-id single string key k1=a$rnd" -echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"]" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id single string key k1=a$rnd" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" -new "instance-id single string key /x1" -echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1)" 0 "0: a0foo0a1foo1" # Assume at least two elements + new "instance-id single string key /x1" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1)" 0 "0: a0foo0a1foo1" # Assume at least two elements -new "instance-id position specific position 5" -echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[5]" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[5])" 0 "0: a13foo13" # sort alphanumerivc wrong 1,10,2 + new "instance-id position specific position 5" +# expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[5])" 0 "0: a13foo13" # sort alphanumerivc wrong 1,10,2 -new "instance-id single string key omit key" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y)" 0 '^0: a0foo0 + new "instance-id single string key omit key" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y)" 0 '^0: a0foo0 1: a0foo0' -# Fails and error handling -new "instance-id single string search non-index" -echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"]" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"] )" 0 "a$rndfoo$rnd$" + # Fails and error handling + new "instance-id single string search non-index" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"] )" 0 "a$rndfoo$rnd$" -new "instance-id single string search non-index (two variables, index first)" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"][a:z=\"foo$rnd\"] )" 0 "a$rndfoo$rnd$" + new "instance-id single string search non-index (two variables, index first)" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=\"a$rnd\"][a:z=\"foo$rnd\"] )" 0 "a$rndfoo$rnd$" -new "instance-id single string search non-index (two variables, index last)" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"][a:k1=\"a$rnd\"] )" 0 "a$rndfoo$rnd$" + new "instance-id single string search non-index (two variables, index last)" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:z=\"foo$rnd\"][a:k1=\"a$rnd\"] )" 0 "a$rndfoo$rnd$" -new "instance-id single string wrong module, notfound" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /xxx:x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' + new "instance-id single string wrong module, notfound" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /xxx:x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' -new "instance-id single string no module, notfound" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' + new "instance-id single string no module, notfound" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /x1/a:y[a:k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' -new "instance-id single string no sub-prefixes, notfound" -echo "$clixon_util_path -f $xml1 -y $ydir -p /a:x1/y[k1=\"a$rnd\"]" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/y[k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' + new "instance-id single string no sub-prefixes, notfound" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/y[k1=\"a$rnd\"] 2> /dev/null)" 255 '^$' -new "instance-id single string two keys, notfound" -expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=a$rnd][a:k2=a$rnd] 2> /dev/null)" 255 '^$' + new "instance-id single string two keys, notfound" + expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /a:x1/a:y[a:k1=a$rnd][a:k2=a$rnd] 2> /dev/null)" 255 '^$' +done # Single int key new "generate list with $nr single int key to $xml2" @@ -213,9 +214,10 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml2 -new "instance-id single int key k1=$rnd" -echo "$clixon_util_path -f $xml2 -y $ydir -p /a:x2/a:y[a:k1=\"$rnd\"]" -expectpart "$($clixon_util_path -f $xml2 -y $ydir -p /a:x2/a:y[a:k1=\"$rnd\"])" 0 "^0: $rndfoo$rnd$" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id single int key k1=$rnd" + expectpart "$($clixon_util_path -f $xml2 -y $ydir -p /a:x2/a:y[a:k1=\"$rnd\"])" 0 "^0: $rndfoo$rnd$" +done # Double string key new "generate list with $nr double string keys to $xml3 (two k2 entries per k1 key)" @@ -229,23 +231,21 @@ echo -n "a0foo0" >> $xml3 echo -n "a1foo1" >> $xml3 echo -n '' >> $xml3 -new "instance-id double string key k1=a$rnd k2=b$rnd" -echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a$rnd\"][k2=\"b$rnd\"]" -expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a$rnd\"][k2=\"b$rnd\"])" 0 "0: a$rndb$rndfoob$rnd" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id double string key k1=a$rnd k2=b$rnd" + expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a$rnd\"][k2=\"b$rnd\"])" 0 "0: a$rndb$rndfoob$rnd" -new "instance-id double string key k1=a$rnd, - empty k2 string" -echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"][k2=\"\"]" -expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"][k2=\"\"])" 0 "0: a1foo1" + new "instance-id double string key k1=a$rnd, - empty k2 string" + expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"][k2=\"\"])" 0 "0: a1foo1" -new "instance-id double string key k1=a$rnd, - no k2 string - three matches" -echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"]" -expecteq "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"])" 0 "0: a1foo1 + new "instance-id double string key k1=a$rnd, - no k2 string - three matches" + expecteq "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[k1=\"a1\"])" 0 "0: a1foo1 1: a1a1foo1 2: a1b1foob1" -new "instance-id double string specific position 5" -echo "$clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[5]" -expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[5])" 0 "0: a1b1foob1" # sort alphanumerivc wrong 1,10,2 + new "instance-id double string specific position 5" + expectpart "$($clixon_util_path -f $xml3 -y $ydir -p /a:x3/a:y[5])" 0 "0: a1b1foob1" # sort alphanumerivc wrong 1,10,2 +done # Leaf-list new "generate leaf-list int keys to $xml4" @@ -255,9 +255,10 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml4 -new "instance-id leaf-list k1=a$rnd" -echo "$clixon_util_path -f $xml4 -y $ydir -p /a:x4/a:y[.=\"a$rnd\"]" -expectpart "$($clixon_util_path -f $xml4 -y $ydir -p /a:x4/a:y[.=\"a$rnd\"])" 0 "^0: a$rnd$" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id leaf-list k1=a$rnd" + expectpart "$($clixon_util_path -f $xml4 -y $ydir -p /a:x4/a:y[.=\"a$rnd\"])" 0 "^0: a$rnd$" +done # Single string key direct under root new "generate list with $nr single string key to $xml5" @@ -265,9 +266,11 @@ echo -n '' > $xml5 for (( i=0; i<$nr; i++ )); do echo -n "a$ifoo$i" >> $xml5 done - -new "instance-id direct under root single string key k1=a$rnd" -expectpart "$($clixon_util_path -f $xml5 -y $ydir -p /a:x5[k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" + +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id direct under root single string key k1=a$rnd" + expectpart "$($clixon_util_path -f $xml5 -y $ydir -p /a:x5[k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +done # Depth and augment # Deep augmented xml path @@ -282,13 +285,14 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml6 -new "instance-id double string key b$rnd,b$rnd in mod b" -echo "$clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"]" -expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"])" 0 "0: b$rndb$rndfoo$rnda0a0foo0a1a1foo1a2a2foo2" - -new "instance-id double string key a$rnd,b$rnd in modb + augmented in moda" -expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"]/a:y[k1=\"a1\"][k2=\"a1\"]/a:z[.=\"foo1\"])" 0 "0: foo1" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id double string key b$rnd,b$rnd in mod b" + expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"])" 0 "0: b$rndb$rndfoo$rnda0a0foo0a1a1foo1a2a2foo2" + new "instance-id double string key a$rnd,b$rnd in modb + augmented in moda" + expectpart "$($clixon_util_path -f $xml6 -y $ydir -p /b:x6/b:yy[kk1=\"b$rnd\"][kk2=\"b$rnd\"]/a:y[k1=\"a1\"][k2=\"a1\"]/a:z[.=\"foo1\"])" 0 "0: foo1" +done + # Single list ordered by user new "generate list with $nr single string key to $xml7" echo -n '' > $xml7 @@ -297,9 +301,10 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml7 -new "instance-id single string key k1=a$rnd ordered by user" -echo "$clixon_util_path -f $xml7 -y $ydir -p /a:x7/a:y[a:k1=\"a$rnd\"]" -expectpart "$($clixon_util_path -f $xml7 -y $ydir -p /a:x7/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +for (( ii=0; ii<$rep; ii++ )); do + new "instance-id single string key k1=a$rnd ordered by user" + expectpart "$($clixon_util_path -f $xml7 -y $ydir -p /a:x7/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +done # Single list state data (non-config) new "generate list with $nr single string key to $xml8" @@ -309,8 +314,11 @@ for (( i=0; i<$nr; i++ )); do done echo -n '' >> $xml8 -new "instance-id single string key k1=a$rnd ordered by user" -expectpart "$($clixon_util_path -f $xml8 -y $ydir -p /a:x8/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +for (( ii=0; ii<$rep; ii++ )); do + rnd=$(( ( RANDOM % $nr ) )) + new "instance-id single string key k1=a$rnd ordered by user" + expectpart "$($clixon_util_path -f $xml8 -y $ydir -p /a:x8/a:y[a:k1=\"a$rnd\"])" 0 "^0: a$rndfoo$rnd$" +done rm -rf $dir diff --git a/util/clixon_util_path.c b/util/clixon_util_path.c index 7e9a3740..6b935dda 100644 --- a/util/clixon_util_path.c +++ b/util/clixon_util_path.c @@ -72,7 +72,7 @@ usage(char *argv0) "\t-p \tPATH string\n" "\t-y \tYang filename or dir (load all files)\n" "\t-Y \tYang dirs (can be several)\n" - "\t-n \tRepeat the call n times(for profiling)\n" + "\t-n \tRepeat the call n times(for profiling)\n" "and the following extra rules:\n" "\tif -f is not given, XML input is expected on stdin\n" "\tif -p is not given, is expected as the first line on stdin\n"