diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbbaead9..35649cc3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,11 +41,21 @@ Expected: May 2022
Users may have to change how they access the system
+* Netconf data-not-unique info changed to return schema nodes instead of XML for RFC7950 compliance
* CLI reconnects to backend if backend restarts with a warning
* Note that edits to the candidate database or locks will be lost
* To force the CLI to exit if backend restarts, undef `PROTO_RESTART_RECONNECT`
* This is an effect of the fix of [Broken pipe error seen in client (cli) when backend restarts and CLICON_SOCK is recreated](https://github.com/clicon/clixon/issues/312), the CLI behavior on backend restart is changed.
+### Minor features
+
+* YANG unique: added single descendant node ids as special case
+ * This means that two variants are supported:
+ * unique "a b c", ie multiple direct chidlren
+ * unique "a/b/c", ie single descendants
+ * RFC 7950 Sec 7.8.3 is somewhat unclear
+ * The combination is not supported
+
### Corrected Bugs
* Fixed: [Broken pipe error seen in client (cli) when backend restarts and CLICON_SOCK is recreated](https://github.com/clicon/clixon/issues/312)
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index c8b14a35..4c3c89b1 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -1275,7 +1275,7 @@ netconf_malformed_message_xml(cxobj **xret,
* "unique" constraint is invalidated.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] x List element containing duplicate
- * @param[in] cvk List of comonents in x that are non-unique
+ * @param[in] cvk List of components in x that are non-unique
* @see RFC7950 Sec 15.1
*/
int
@@ -1321,13 +1321,9 @@ netconf_data_not_unique_xml(cxobj **xret,
goto done;
}
while ((cvi = cvec_each(cvk, cvi)) != NULL){
- if ((xi = xml_find(x, cv_string_get(cvi))) == NULL)
- continue; /* ignore, shouldnt happen */
- clicon_xml2cbuf(cb, xi, 0, 0, -1);
if (clixon_xml_parse_va(YB_NONE, NULL, &xinfo, NULL,
- "%s", cbuf_get(cb)) < 0)
+ "%s", cv_string_get(cvi)) < 0)
goto done;
- cbuf_reset(cb);
}
}
retval = 0;
diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c
index aa4cb6a8..d0f942ec 100644
--- a/lib/src/clixon_validate.c
+++ b/lib/src/clixon_validate.c
@@ -710,6 +710,67 @@ check_mandatory(cxobj *xt,
* @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.
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed (cbret set)
+ * @retval -1 Error
+ */
+static int
+unique_search_one(cxobj *x,
+ char *xpath,
+ char ***svec,
+ size_t *slen)
+{
+ int retval = -1;
+ cxobj **xvec = NULL;
+ size_t xveclen;
+ int i;
+ int s;
+ cxobj *xi;
+ char *bi;
+
+ /* Collect tuples */
+ if (xpath_vec(x, NULL, "%s", &xvec, &xveclen, xpath) < 0)
+ goto done;
+ for (i=0; imandatory$LIST]]>]]>" "^applicationoperation-faileddata-not-uniqueerrorbar]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOmandatory$LIST]]>]]>" "^applicationoperation-faileddata-not-uniqueerroruk]]>]]>$"
new "netconf choice ok"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO42xxx]]>]]>" "^4242xxx]]>]]>$"
diff --git a/test/test_unique.sh b/test/test_unique.sh
index b926633e..16bed7fe 100755
--- a/test/test_unique.sh
+++ b/test/test_unique.sh
@@ -109,7 +109,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerroripport]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
@@ -138,7 +138,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerroripport]]>]]>$"
new "make it valid by deleting port from smtp entry"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOnonesmtp25
@@ -163,7 +163,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.1]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerrorip]]>]]>$"
new "make valid by replacing IP of http entry"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOnonehttp178.23.34.1
@@ -204,7 +204,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
new "netconf validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerror192.0.2.125]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerroripport]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
diff --git a/test/test_unique_descendant.sh b/test/test_unique_descendant.sh
new file mode 100755
index 00000000..4640d712
--- /dev/null
+++ b/test/test_unique_descendant.sh
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+# Yang list unique tests using descendant schema node identifiers
+# That is, not only direct descendants as the example in RFC7890 7.8.3.1
+
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exi{t 0; else return 0; fi
+
+APPNAME=example
+
+cfg=$dir/conf_yang.xml
+fyang=$dir/unique.yang
+
+cat < $cfg
+
+ $cfg
+ ${YANG_INSTALLDIR}
+ $fyang
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ /usr/local/var/$APPNAME
+
+EOF
+
+# Example (the list server part) from RFC7950 Sec 7.8.3.1 w changed types
+cat < $fyang
+module unique{
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix un;
+ list outer{
+ key name;
+ leaf name{
+ type string;
+ }
+ unique c/inner/value;
+ container c{
+ list inner{
+ key name;
+ leaf name{
+ type string;
+ }
+ leaf value{
+ type string;
+ }
+ }
+ }
+ }
+}
+EOF
+
+new "test params: -f $cfg"
+
+if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ new "start backend -s init -f $cfg"
+ # start new backend
+ start_backend -s init -f $cfg
+fi
+
+new "wait backend"
+wait_backend
+
+RPC=$(cat<replace
+
+ x
+ afoo
+ bbar
+
+
+
+ y
+ afie
+ bfum
+
+
+]]>]]>
+EOF
+ )
+
+# RFC test two-field caes
+new "Add valid example"
+expecteof "$clixon_netconf -qf $cfg" 0 "${RPC}" "^]]>]]>$"
+
+new "netconf validate ok"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+
+RPC=$(cat<replace
+
+ x
+ afoo
+ bfoo
+
+
+
+ y
+ afie
+ bfum
+
+
+]]>]]>
+EOF
+ )
+
+new "Add invalid example"
+expecteof "$clixon_netconf -qf $cfg" 0 "${RPC}" "^]]>]]>$"
+
+new "netconf validate same inner (should fail)"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerrorc/inner/value]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+
+RPC=$(cat<replace
+
+ x
+ afoo
+ bbar
+
+
+
+ y
+ afie
+ bbar
+
+
+]]>]]>
+EOF
+ )
+
+new "Add invalid example"
+expecteof "$clixon_netconf -qf $cfg" 0 "${RPC}" "^]]>]]>$"
+
+new "netconf validate same in different outers (should fail)"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-faileddata-not-uniqueerrorc/inner/value]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+
+if [ $BE -ne 0 ]; then
+ new "Kill backend"
+ # Check if premature kill
+ pid=$(pgrep -u root -f clixon_backend)
+ if [ -z "$pid" ]; then
+ err "backend already dead"
+ fi
+ # kill backend
+ stop_backend -f $cfg
+fi
+
+
+rm -rf $dir
+
+unset RPC
+
+new "endtest"
+endtest