From 1eb78a78f842a12e793e93fe5d98ebbe4a81f99f Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 17 Oct 2022 13:16:08 +0200 Subject: [PATCH] Fixed: [YANG when condition evaluated as false combined with a mandatory leaf does not work](https://github.com/clicon/clixon/issues/380) Replaced yang_mandatory() with yang_xml_mandatory() by extending existing it with when check --- CHANGELOG.md | 1 + lib/clixon/clixon_xml_map.h | 1 + lib/clixon/clixon_yang.h | 1 - lib/src/clixon_validate.c | 58 ++++--- lib/src/clixon_xml_map.c | 83 +++++++++- lib/src/clixon_yang.c | 59 ------- test/test_when_mandatory.sh | 309 ++++++++++++++++++++++++++++++++++++ 7 files changed, 424 insertions(+), 88 deletions(-) create mode 100755 test/test_when_mandatory.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e2d57bf..43f98a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ Developers may need to change their code ### Corrected Bugs +* Fixed: [YANG when condition evaluated as false combined with a mandatory leaf does not work](https://github.com/clicon/clixon/issues/380) * Fixed: [Trying to change the "read-only" node through snmpset](https://github.com/clicon/clixon/issues/376) * Fixed: [Trying to change the "config false" node through snmpset](https://github.com/clicon/clixon/issues/377) * Fixed by returning `SNMP_ERR_NOTWRITABLE` when trying to reserve object diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 400481bf..031b44f7 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -75,6 +75,7 @@ int yang_enum2valstr(yang_stmt *ytype, char *enumstr, char **valstr); int yang_enum_int_value(cxobj *node, int32_t *val); int xml_copy_marked(cxobj *x0, cxobj *x1); int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp); +int yang_xml_mandatory(cxobj *xt, yang_stmt *ys); int xml_rpc_isaction(cxobj *xn); int xml_find_action(cxobj *xn, int top, cxobj **xap); int purge_tagged_nodes(cxobj *xn, char *ns, char *name, char *value, int keepnode); diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index ce72361f..38b294a8 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -272,7 +272,6 @@ int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn, int f int yang_datanode(yang_stmt *ys); int yang_abs_schema_nodeid(yang_stmt *ys, char *schema_nodeid, yang_stmt **yres); int yang_desc_schema_nodeid(yang_stmt *yn, char *schema_nodeid, 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); diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index 7c45390a..229e7413 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -613,16 +613,20 @@ check_list_key(cxobj *xt, * @retval -1 Error */ static int -choice_mandatory_check(yang_stmt *ycase, +choice_mandatory_check(cxobj *xt, + yang_stmt *ycase, cxobj **xret) { int retval = -1; yang_stmt *yc = NULL; cbuf *cb = NULL; int fail = 0; + int ret; while ((yc = yn_each(ycase, yc)) != NULL) { - if (yang_mandatory(yc)){ + if ((ret = yang_xml_mandatory(xt, yc)) < 0) + goto done; + if (ret == 1){ if (yang_flag_get(yc, YANG_FLAG_MARK)) yang_flag_reset(yc, YANG_FLAG_MARK); else if (fail == 0){ @@ -728,16 +732,20 @@ check_mandatory_case(cxobj *xt, if ((y = xml_spec(x)) != NULL && yang_ancestor_child(y, yc, &ym, &ycnew) != 0 && yang_keyword_get(ycnew) == Y_CASE){ - if (ym && yang_mandatory(ym)){ + if (ym){ + if ((ret = yang_xml_mandatory(xt, ym)) < 0) + goto done; + if (ret == 1){ if (yang_flag_get(ym, YANG_FLAG_MARK) != 0){ clicon_debug(1, "%s Already marked, shouldnt happen", __FUNCTION__); } yang_flag_set(ym, YANG_FLAG_MARK); + } } if (ycase != NULL){ if (ycnew != ycase){ /* End of case, new case */ /* Check and clear marked mandatory */ - if ((ret = choice_mandatory_check(ycase, xret)) < 0) + if ((ret = choice_mandatory_check(xt, ycase, xret)) < 0) goto done; if (ret == 0) goto fail; @@ -749,7 +757,7 @@ check_mandatory_case(cxobj *xt, } else if (ycase != NULL){ /* End of case */ /* Check and clear marked mandatory */ - if ((ret = choice_mandatory_check(ycase, xret)) < 0) + if ((ret = choice_mandatory_check(xt, ycase, xret)) < 0) goto done; if (ret == 0) goto fail; @@ -757,7 +765,7 @@ check_mandatory_case(cxobj *xt, } } if (ycase){ - if ((ret = choice_mandatory_check(ycase, xret)) < 0) + if ((ret = choice_mandatory_check(xt, ycase, xret)) < 0) goto done; if (ret == 0) goto fail; @@ -806,7 +814,7 @@ check_mandatory(cxobj *xt, while ((yc = yn_each(yt, yc)) != NULL) { /* Choice is more complex because of choice/case structure and possibly hierarchical */ if (yang_keyword_get(yc) == Y_CHOICE){ - if (yang_mandatory(yc)){ + if (yang_xml_mandatory(xt, yc)){ x = NULL; while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { if ((y = xml_spec(x)) != NULL && @@ -829,7 +837,9 @@ check_mandatory(cxobj *xt, if (ret == 0) goto fail; } - if (!yang_mandatory(yc)) /* Rest of yangs are immediate children */ + if ((ret = yang_xml_mandatory(xt, yc)) < 0) /* Rest of yangs are immediate children */ + goto done; + if (ret == 0) continue; switch (yang_keyword_get(yc)){ case Y_CONTAINER: @@ -1130,6 +1140,22 @@ xml_yang_validate_all(clicon_handle h, goto fail; } if (yang_config(yt) != 0){ + if (yang_check_when_xpath(xt, xml_parent(xt), yt, &hit, &nr, &xpath) < 0) + goto done; + if (hit && nr == 0){ + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + cprintf(cb, "Failed WHEN condition of %s in module %s (WHEN xpath is %s)", + xml_name(xt), + yang_argument_get(ys_module(yt)), + xpath); + if (xret && netconf_operation_failed_xml(xret, "application", + cbuf_get(cb)) < 0) + goto done; + goto fail; + } if ((ret = check_mandatory(xt, yt, xret)) < 0) goto done; if (ret == 0) @@ -1204,22 +1230,6 @@ xml_yang_validate_all(clicon_handle h, nsc = NULL; } } - if (yang_check_when_xpath(xt, xml_parent(xt), yt, &hit, &nr, &xpath) < 0) - goto done; - if (hit && nr == 0){ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - cprintf(cb, "Failed WHEN condition of %s in module %s (WHEN xpath is %s)", - xml_name(xt), - yang_argument_get(ys_module(yt)), - xpath); - if (xret && netconf_operation_failed_xml(xret, "application", - cbuf_get(cb)) < 0) - goto done; - goto fail; - } } x = NULL; while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 729df365..98943745 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -2089,10 +2089,14 @@ xml_copy_marked(cxobj *x0, /*! Check when condition * - * @param[in] h Clixon handle - * @param[in] xn XML node, can be NULL, in which case it is added as dummy under xp - * @param[in] xp XML parent - * @param[in] ys Yang node + * @param[in] xn XML node, can be NULL, in which case it is added as dummy under xp + * @param[in] xp XML parent + * @param[in] yn Yang node + * @param[out] hit when statement found + * @param[out] nrp 1: when stmt evaluates to true + * @param[out] xpathp when stmts xpath + * @retval 0 OK + * @retval -1 Error * First variants of WHEN: Augmented and uses when using special info in node * Second variant of when, actual "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ @@ -2155,6 +2159,77 @@ yang_check_when_xpath(cxobj *xn, return retval; } +/*! Check if node is (recursively) mandatory also checking when conditional + * + * @param[in] xn Optional XML node + * @param[in] xp XML parent + * @param[in] ys YANG node + * @retval 1 Recursively contains a mandatory node + * @retval 0 Does not contain a mandatory node + * @retval -1 Error + * @see RFC7950 Sec 3: + * o mandatory node: A mandatory node is one of: + * 1) A leaf, choice, anydata, or anyxml node with a "mandatory" + * statement with the value "true". + * 2) # see below + * 3) A container node without a "presence" statement and that has at + * least one mandatory node as a child. + */ +int +yang_xml_mandatory(cxobj *xt, + yang_stmt *ys) +{ + int retval = -1; + yang_stmt *ym; + cg_var *cv; + enum rfc_6020 keyw; + cxobj *xs = NULL; + int ret; + yang_stmt *yc; + int hit; + int nr; + + /* Create dummy xs if not exist */ + if ((xs = xml_new(yang_argument_get(ys), xt, CX_ELMNT)) == NULL) + goto done; + xml_spec_set(xs, ys); + if (yang_check_when_xpath(xs, xt, ys, &hit, &nr, NULL) < 0) + goto done; + if (hit && !nr){ + retval = 0; + goto done; + } + keyw = yang_keyword_get(ys); + if (keyw == Y_LEAF || keyw == Y_CHOICE || keyw == Y_ANYDATA || keyw == Y_ANYXML){ + if ((ym = yang_find(ys, Y_MANDATORY, NULL)) != NULL){ + if ((cv = yang_cv_get(ym)) != NULL){ /* shouldnt happen */ + retval = cv_bool_get(cv); + goto done; + } + } + } + /* 3) A container node without a "presence" statement and that has at + * least one mandatory node as a child. */ + else if (keyw == Y_CONTAINER && + yang_find(ys, Y_PRESENCE, NULL) == NULL){ + yc = NULL; + while ((yc = yn_each(ys, yc)) != NULL) { + if ((ret = yang_xml_mandatory(xs, yc)) < 0) + goto done; + if (ret == 1) + goto mandatory; + } + } + retval = 0; /* Does not contain mandatory node */ + done: + if (xs != NULL) + xml_purge(xs); + return retval; + mandatory: + retval = 1; + goto done; +} + /*! Is XML node (ie under ) an action, ie name action and belong to YANG_XML_NAMESPACE? * @param[in] xn XML node * @retval 1 Yes, an action diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 5e47ed26..e56bd91b 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -3213,65 +3213,6 @@ yang_desc_schema_nodeid(yang_stmt *yn, return retval; } -/*! Check if this leaf is mandatory or not - * Note: one can cache this value in ys_cvec instead of functionally evaluating it. - * @retval 1 yang statement is leaf and it has a mandatory sub-stmt with value true - * @retval 0 The negation of conditions for return value 1. - * @see RFC7950 Sec 3: - * o mandatory node: A mandatory node is one of: - * 1) A leaf, choice, anydata, or anyxml node with a "mandatory" - * statement with the value "true". - * 2) # see below - * 3) A container node without a "presence" statement and that has at - * least one mandatory node as a child. - * - * @note There is also this statement - * 2) A list or leaf-list node with a "min-elements" statement with a - * value greater than zero. - * which we ignore here since: - * (a) it does not consider the XML siblings and therefore returns false positives - * (b) where the actual check is catched by check_list_unique_minmax() - */ -int -yang_mandatory(yang_stmt *ys) -{ - yang_stmt *ym; - cg_var *cv; - - /* 1) A leaf, choice, anydata, or anyxml node with a "mandatory" - * statement with the value "true". */ - if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_CHOICE || - ys->ys_keyword == Y_ANYDATA || ys->ys_keyword == Y_ANYXML){ - if ((ym = yang_find(ys, Y_MANDATORY, NULL)) != NULL){ - if ((cv = yang_cv_get(ym)) != NULL) /* shouldnt happen */ - return cv_bool_get(cv); - } - } -#if 0 /* See note above */ - /* 2) A list or leaf-list node with a "min-elements" statement with a - * value greater than zero. */ - else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){ - if ((ym = yang_find(ys, Y_MIN_ELEMENTS, NULL)) != NULL){ - cv = yang_cv_get(ym); - return cv_uint32_get(cv) > 0; /* Not true if XML considered */ - } - } -#endif - /* 3) A container node without a "presence" statement and that has at - * least one mandatory node as a child. */ - else if (ys->ys_keyword == Y_CONTAINER && - yang_find(ys, Y_PRESENCE, NULL) == NULL){ - yang_stmt *yc; - int i; - for (i=0; iys_len; i++){ - yc = ys->ys_stmt[i]; - if (yang_mandatory(yc)) - return 1; - } - } - return 0; -} - /*! Return config state of this node * @param[in] ys Yang statement * @retval 0 If node has a config sub-statement and it is false diff --git a/test/test_when_mandatory.sh b/test/test_when_mandatory.sh new file mode 100755 index 00000000..3deac9cd --- /dev/null +++ b/test/test_when_mandatory.sh @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +# Yang when conditional and mandatory +# Testing of validation phase. +# Variants: +# - One level and two levels of non-presence containers +# - when-condition true and false +# - empty vs extra leaf in non-presence container +# - mandatory leaf present or not + +# 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/test.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 + +cat < $fyang +module $APPNAME{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + identity routing-protocol { + description + "Base identity from which routing protocol identities are + derived."; + } + identity direct { + base routing-protocol; + description + "Routing pseudo-protocol which provides routes to directly + connected networks."; + } + identity static { + base routing-protocol; + description + "Static routing pseudo-protocol."; + } + list x { + key "type"; + leaf type { + type identityref { + base routing-protocol; + } + } + container y { + when "../type='static'" { + description + "This container is only valid for the 'static' + routing protocol."; + } + leaf name{ + type string; + mandatory true; + } + leaf extra{ + type string; + } + } + } + list x2 { + key "type"; + leaf type { + type identityref { + base routing-protocol; + } + } + container y2 { + when "../type='static'" { + description + "This container is only valid for the 'static' + routing protocol."; + } + container y3 { + leaf name{ + type string; + mandatory true; + } + leaf extra{ + type string; + } + } + } + } + list x3 { + key "type"; + leaf type { + type identityref { + base routing-protocol; + } + } + container y2 { + container y3 { + when "../../type='static'" { + description + "This container is only valid for the 'static' + routing protocol."; + } + leaf name{ + type string; + mandatory true; + } + leaf extra{ + 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_backend -s init -f $cfg +fi + +new "wait backend" +wait_backend + +new "First: have mandatory leaf under an empty when-conditioned ccntainer" + +new "when false + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "direct" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "static" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementyerrorMandatory variable of x in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "statica" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Second: have mandatory leaf under a non-empty when-conditioned ccntainer" + +new "when false + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "directb" "" "" + +new "validate fail extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorFailed WHEN condition of y in module example (WHEN xpath is ../type='static')" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticb" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementnameerrorMandatory variable of y in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticab" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Third: have mandatory leaf under two empty when-conditioned ccntainer" +new "when false + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "direct" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "static" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementy2errorMandatory variable of x2 in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "statica" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Fourth: have mandatory leaf under two non-empty when-conditioned ccntainer" + +new "when false + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "directb" "" "" + +new "validate fail extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorFailed WHEN condition of y2 in module example (WHEN xpath is ../type='static')" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticb" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementnameerrorMandatory variable of y3 in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticab" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Fifth: have mandatory leaf under one empty and one when-conditioned ccntainer" +new "when false + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "direct" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "static" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementy2errorMandatory variable of x3 in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "statica" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Sixth: have mandatory leaf under one non-empty and one when-conditioned ccntainer" + +new "when false + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "directb" "" "" + +new "validate fail extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorFailed WHEN condition of y3 in module example (WHEN xpath is ../../type='static')" + +new "when: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when true + no name + extra" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticb" "" "" + +new "validate fail" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementnameerrorMandatory variable of y3 in module example" + +new "when true + name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "staticab" "" "" + +new "validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "when: 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