diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ecbb7b..6158dbd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Expected: September 2022 ### Corrected Bugs +* Fixed: [YANG min-elements within non-presence container does not work](https://github.com/clicon/clixon/issues/355) * Fixed: [Issues with ietf-snmp modules](https://github.com/clicon/clixon/issues/353) * Fixed: [Missing/no namespace error in YANG augments with default values](https://github.com/clicon/clixon/issues/354) * Fixed: [Validation of mandatory in choice/case does not work in some cases](https://github.com/clicon/clixon/issues/349) diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index edfb219c..f8403583 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -1223,6 +1223,52 @@ check_min_max(cxobj *xp, goto done; } +/*! Check if there is any empty list (no x elements) and check min-elements + * Note recurse for non-presence container + * @param[in] xt XML node + * @param[in] yt YANG node + * @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 + */ +static int +check_empty_list_minmax(cxobj *xt, + yang_stmt *ye, + cxobj **xret) +{ + int retval = -1; + int ret; + yang_stmt *yprev = NULL; + + if (yang_config(ye) == 1){ + if(yang_keyword_get(ye) == Y_CONTAINER && + yang_find(ye, Y_PRESENCE, NULL) == NULL){ + yprev = NULL; + while ((yprev = yn_each(ye, yprev)) != NULL) { + if ((ret = check_empty_list_minmax(xt, yprev, xret)) < 0) + goto done; + if (ret == 0) + goto fail; + } + } + else if (yang_keyword_get(ye) == Y_LIST || + yang_keyword_get(ye) == Y_LEAF_LIST){ + /* Check if the list length violates min/max */ + if ((ret = check_min_max(xt, ye, 0, xret)) < 0) + goto done; + if (ret == 0) + goto fail; + } + } + retval = 1; + done: + return retval; + fail: + retval = 0; + goto done; +} + /*! Detect unique constraint for duplicates from parent node and minmax * @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 @@ -1289,7 +1335,7 @@ xml_yang_check_list_unique_minmax(cxobj *xt, * o Otherwise, it is enforced if the ancestor node exists. */ yt = xml_spec(xt); /* If yt == NULL, then no gap-analysis is done */ - /* Traverse all elemenents */ + /* Traverse all elements */ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { if ((y = xml_spec(x)) == NULL) continue; @@ -1335,14 +1381,10 @@ xml_yang_check_list_unique_minmax(cxobj *xt, ye = yn_each(yt, ye); if (ye && ych != ye) do { - if (yang_config(ye) == 1 && - (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST)){ - /* Check if the list length violates min/max */ - if ((ret = check_min_max(xt, ye, 0, xret)) < 0) - goto done; - if (ret == 0) - goto fail; - } + if ((ret = check_empty_list_minmax(xt, ye, xret)) < 0) + goto done; + if (ret == 0) + goto fail; ye = yn_each(yt, ye); } while(ye != NULL && /* to avoid livelock (shouldnt happen) */ ye != ych); @@ -1389,17 +1431,14 @@ xml_yang_check_list_unique_minmax(cxobj *xt, /* Check if there is any empty list between after last non-empty list * Note, does not detect empty lists within choice/case (too complicated) */ - if ((ye = yn_each(yt, ye)) != NULL) + if ((ye = yn_each(yt, ye)) != NULL){ do { - if (yang_config(ye) == 1 && - (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST)){ - /* Check if the list length violates min/max */ - if ((ret = check_min_max(xt, ye, 0, xret)) < 0) - goto done; - if (ret == 0) - goto fail; - } + if ((ret = check_empty_list_minmax(xt, ye, xret)) < 0) + goto done; + if (ret == 0) + goto fail; } while((ye = yn_each(yt, ye)) != NULL); + } retval = 1; done: return retval; diff --git a/test/test_minmax.sh b/test/test_minmax.sh index cc84c832..55ea8fc2 100755 --- a/test/test_minmax.sh +++ b/test/test_minmax.sh @@ -97,6 +97,24 @@ module $APPNAME{ min-elements 1; max-elements 2; } + container c3{ + list b2{ + description "RFC7950 7.7.5 : it is enforced if the ancestor node exists."; + key kb; + leaf kb { + type string; + } + container b2c { + leaf-list b2ll{ + min-elements 1; + type string; + } + } + leaf-list b3ll{ + type string; + } + } + } } EOF @@ -244,6 +262,21 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "protocoloperation-failedtoo-few-elementserror/c/a1" +new "delete c" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "none" "" "" + +new "add empty list entry with a min-element leaf within a non-presence container" +expecteof_netconf "$clixon_netconf -qf $cfg -D 1 -l s" 0 "$DEFAULTHELLO" "0" "" "" + +new "minmax: validate should fail, there should be a b2ll trailing" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "protocoloperation-failedtoo-few-elementserror/c3/b2\[kb=\"0\"\]/b2ll" "" + +new "add b3ll after missing b2ll" +expecteof_netconf "$clixon_netconf -qf $cfg -D 1 -l s" 0 "$DEFAULTHELLO" "042" "" "" + +new "minmax: validate should fail, there should be a b2ll before b3ll" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "protocoloperation-failedtoo-few-elementserror/c3/b2\[kb=\"0\"\]/b2ll" "" + if [ $BE -ne 0 ]; then new "Kill backend" # Check if premature kill