diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index 4e0c195b9..8fa38f036 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -181,7 +181,10 @@ validate_leafref_err(char *xpath, cbuf *cbm = NULL; int exist = 0; char *msg = NULL; + char *body; + if ((body = xml_body(xt)) == NULL) + body = ""; if ((cbi = cbuf_new()) == NULL){ clixon_err(OE_UNIX, errno, "cbuf_new"); goto done; @@ -195,12 +198,12 @@ validate_leafref_err(char *xpath, goto done; } cprintf(cbm, "%s", msg); - cprintf(cbi, "%s", xml_body(xt)); + cprintf(cbi, "%s", body); } else{ if (validate_errmsg(&cbm, xt, yt) < 0) goto done; - cprintf(cbi, "<%s>%s", xml_name(xt), xml_body(xt), xml_name(xt)); + cprintf(cbi, "<%s>%s", xml_name(xt), body, xml_name(xt)); } if (xret && netconf_missing_yang_xml(xret, xpath, "instance-required", cbuf_get(cbi), cbuf_get(cbm)) < 0) goto done; @@ -631,8 +634,10 @@ validate_leafref(cxobj *xt, clixon_err(OE_YANG, 0, "No argument for Y_PATH"); goto done; } + /* Empty value is a value: it must also refer to an existing (empty) instance, + * see https://github.com/clicon/clixon/issues/647 */ if ((leafrefbody = xml_body(xt)) == NULL) - goto ok; + leafrefbody = ""; if (xml_nsctx_yang(yt, &nsc) < 0) goto done; #ifdef LEAFREF_OPTIMIZE @@ -667,7 +672,8 @@ validate_leafref(cxobj *xt, &leafref_opt.lc_bin_x0) < 0) goto done; } - if (leafref_opt.lc_bin_search){ + /* Binary search requires a non-empty key, use linear search for empty value */ + if (leafref_opt.lc_bin_search && *leafrefbody != '\0'){ if ((ret = leafref_opt_search(xpath, xt, leafref_opt.lc_bin_x0, ytype, xret)) < 0) goto done; if (ret == 0) @@ -679,7 +685,7 @@ validate_leafref(cxobj *xt, for (i = 0; i < xlen; i++) { x = xvec[i]; if ((leafbody = xml_body(x)) == NULL) - continue; + leafbody = ""; if (strcmp(leafbody, leafrefbody) == 0) break; } diff --git a/test/test_leafref_empty.sh b/test/test_leafref_empty.sh new file mode 100755 index 000000000..5493e49f9 --- /dev/null +++ b/test/test_leafref_empty.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +# Yang leafref with empty value +# An empty value must refer to an existing instance with empty value, as any other value +# See https://github.com/clicon/clixon/issues/647 + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +cfg=$dir/conf_yang.xml +fyang=$dir/leafref.yang + +cat < $cfg + + $cfg + $dir + ${YANG_INSTALLDIR} + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/run/$APPNAME.sock + /usr/local/var/run/$APPNAME.pidfile + /usr/local/var/$APPNAME + +EOF + +cat < $fyang +module leafref{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + container groups { + leaf-list name { + type string; + } + } + list zone { + key name; + leaf name { + type string; + } + leaf label { + type string; + } + } + container test { + list object { + key id; + leaf id { + type string; + } + leaf group { + description "leafref to leaf-list"; + type leafref { + path "/ex:groups/ex:name"; + } + } + leaf zone { + description "leafref to list key"; + type leafref { + path "/ex:zone/ex:name"; + } + } + leaf label { + description "leafref to non-key list leaf"; + type leafref { + path "/ex:zone/ex:label"; + } + } + } + } +} +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_backend -s init -f $cfg +fi + +new "wait backend" +wait_backend + +new "netconf set groups and zones" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "g1g2z1z2z3" "" "" + +new "netconf commit groups and zones" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set non-empty refs" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1g2z2" "" "" + +new "netconf validate non-empty refs ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to leaf-list" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to leaf-list not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:groups/ex:name" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to list key" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to list key not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:zone/ex:name" "" + +new "netconf discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf set empty leafref to non-key leaf" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "o1" "" "" + +new "netconf validate empty leafref to non-key leaf not ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "applicationdata-missinginstance-required/ex:zone/ex:label" "" + +new "netconf set referred non-key leaf to empty value" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "z3" "" "" + +new "netconf validate empty leafref to existing empty value ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "netconf discard-changes" +expecteof_netconf "$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 + +new "endtest" +endtest