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