From f55460d69594b79146bc08f98ba353679533d4c2 Mon Sep 17 00:00:00 2001 From: ductus-samuel Date: Wed, 17 Dec 2025 10:26:36 +0700 Subject: [PATCH] fix: unsupport translate() inside when in yang parser --- apps/cli/cli_common.c | 53 +++++++ apps/cli/cli_generate.c | 2 +- apps/restconf/restconf_err.c | 29 +++- apps/restconf/restconf_methods_get.c | 27 +++- apps/restconf/restconf_native.c | 15 +- apps/restconf/restconf_native.h | 2 + lib/src/clixon_autocli_generate.c | 11 +- lib/src/clixon_xml_map.c | 17 +++ lib/src/clixon_xpath_eval.c | 43 +++++- lib/src/clixon_xpath_parse.l | 6 +- lib/src/clixon_xpath_parse.y | 6 +- lib/src/clixon_xpath_yang.c | 5 + lib/src/clixon_yang_parse_lib.c | 9 +- test/test_xpath_issues.sh | 198 +++++++++++++++++++++++++++ 14 files changed, 404 insertions(+), 19 deletions(-) create mode 100755 test/test_xpath_issues.sh diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 2ac78315f..75f58d92f 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -224,6 +224,59 @@ dbxml_body(cxobj *xbot, return retval; } +/*! Special handling of identityref:s whose body may be: : + * + * Ensure the namespace is declared if it exists in YANG + * @param[in] x + * @param[in] arg + * @retval 0 OK + * @retval -1 Error +*/ +int +identityref_add_ns(cxobj *x, + void *arg) +{ + int retval = -1; + yang_stmt *yspec = (yang_stmt *)arg; + yang_stmt *y; + yang_stmt *yrestype; /* resolved type */ + char *restype; /* resolved type */ + char *origtype = NULL; /* original type */ + char *pf = NULL; + yang_stmt *yns; + char *ns = NULL; + + if ((y = xml_spec(x)) != NULL && + (yang_keyword_get(y) == Y_LEAF || + yang_keyword_get(y) == Y_LEAF_LIST)){ + if (yang_type_get(y, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) + goto done; + restype = yrestype?yang_argument_get(yrestype):NULL; + if (strcmp(restype, "identityref") == 0){ + if (nodeid_split(xml_body(x), &pf, NULL) < 0) + goto done; + // search if already defined + if (pf != NULL){ + if (xml2ns(x, pf, &ns) < 0) + goto done; + if (ns == NULL && + (yns = yang_find_module_by_prefix_yspec(yspec, pf)) != NULL){ + if ((ns = yang_find_mynamespace(yns)) != NULL) + if (xmlns_set(x, pf, ns) < 0) + goto done; + } + } + } + } + retval = 0; + done: + if (origtype) + free(origtype); + if (pf) + free(pf); + return retval; +} + /*! Given a top-level yspec and mountpoint xpath compute api-path * * Manipulate top-level and a mountpoint: diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index acb456cc2..4a9228cc1 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -750,4 +750,4 @@ yang2cli_init(clixon_handle h) retval = 0; done: return retval; -} +} \ No newline at end of file diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c index cf5ec2944..308c2052f 100644 --- a/apps/restconf/restconf_err.c +++ b/apps/restconf/restconf_err.c @@ -364,15 +364,40 @@ api_return_err0(clixon_handle h, { int retval = -1; cxobj *xe; + int created_err = 0; + cxobj *xnew = NULL; + cbuf *cb = NULL; clixon_debug(CLIXON_DBG_RESTCONF, ""); if ((xe = xml_find_type(xerr, NULL, "rpc-error", CX_ELMNT)) == NULL){ - clixon_err(OE_XML, EINVAL, "Expected xml on the form .."); - goto done; + /* Log what we actually got to ease debugging */ + if (xerr && (cb = cbuf_new()) != NULL){ + /* Best effort dump of unexpected error payload */ + clixon_xml2cbuf(cb, xerr, 0, pretty, NULL, -1, 0); + clixon_err(OE_XML, EINVAL, "Expected xml on the form .. Payload:\"%s\"", + cbuf_get(cb)); + } + else + clixon_err(OE_XML, EINVAL, "Expected xml on the form .."); + /* Fall back to a generic operation-failed rpc-error so we can return + * a proper RESTCONF error instead of aborting the connection. + */ + if (netconf_operation_failed_xml(xerr ? &xerr : &xnew, + "application", + "restconf internal error: missing rpc-error") < 0) + goto done; + created_err = 1; + if ((xe = xml_find_type(xerr ? xerr : xnew, NULL, "rpc-error", CX_ELMNT)) == NULL) + goto done; } if (api_return_err(h, req, xe, pretty, media, code) < 0) goto done; retval = 0; done: + if (cb) + cbuf_free(cb); + /* Free synthesized container if caller did not supply one */ + if (created_err && xerr == NULL && xnew) + xml_free(xnew); return retval; } diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 70c5e965e..2089af259 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -146,17 +146,32 @@ api_data_get2(clixon_handle h, restconf_apipath_mount_cb, h, NULL, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) - goto done; - goto ok; + clixon_err(OE_RESTCONF, EINVAL, + "api_data_get2: api_path2xml_mnt validation failed pi=%d path:%s xerr:%s", + pi, api_path, xerr ? "set" : "NULL"); + /* If no rpc-error was provided, continue and let the xpath translation + * try to handle the path instead of aborting the request. + */ + if (xerr == NULL) + ret = 1; + else { + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; + } } /* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */ if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) - goto done; - goto ok; + clixon_err(OE_RESTCONF, EINVAL, "api_data_get2: api_path2xpath validation failed pi=%d path:%s xerr:%s", pi, api_path, xerr ? "set" : "NULL"); + if (xerr == NULL) + ret = 1; + else { + if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0) + goto done; + goto ok; + } } /* Ad-hoc method to determine json pagination request: diff --git a/apps/restconf/restconf_native.c b/apps/restconf/restconf_native.c index 6592f45f2..a4412c8ac 100644 --- a/apps/restconf/restconf_native.c +++ b/apps/restconf/restconf_native.c @@ -99,6 +99,7 @@ restconf_stream_data_new(restconf_conn *rc, memset(sd, 0, sizeof(restconf_stream_data)); sd->sd_stream_id = stream_id; sd->sd_fd = -1; + sd->sd_freed = 0; if ((sd->sd_inbuf = cbuf_new()) == NULL){ clixon_err(OE_UNIX, errno, "cbuf_new"); goto done; @@ -153,6 +154,9 @@ restconf_stream_find(restconf_conn *rc, int restconf_stream_free(restconf_stream_data *sd) { + if (sd->sd_freed) + return 0; + sd->sd_freed = 1; if (sd->sd_fd != -1) { close(sd->sd_fd); } @@ -197,6 +201,7 @@ restconf_conn_new(clixon_handle h, memset(rc, 0, sizeof(restconf_conn)); rc->rc_h = h; rc->rc_s = s; + rc->rc_closed = 0; rc->rc_callhome = rsock->rs_callhome; rc->rc_socket = rsock; INSQ(rc, rsock->rs_conns); @@ -208,7 +213,7 @@ restconf_conn_new(clixon_handle h, * * @param[in] rc restconf connection */ -static int +static int __attribute__((unused)) restconf_conn_free(restconf_conn *rc) { int retval = -1; @@ -1109,6 +1114,12 @@ restconf_close_ssl_socket(restconf_conn *rc, int ret; clixon_debug(CLIXON_DBG_RESTCONF, "%s", callfn); + /* restconf_close_ssl_socket can be invoked along multiple error/close paths. + * If a connection is already being torn down, bail out to avoid double free. + */ + if (rc->rc_closed) + return 0; + rc->rc_closed = 1; if (rc->rc_ssl != NULL){ if (!dontshutdown && (ret = SSL_shutdown(rc->rc_ssl)) < 0){ @@ -1138,8 +1149,6 @@ restconf_close_ssl_socket(restconf_conn *rc, } if (restconf_connection_close1(rc) < 0) goto done; - if (restconf_conn_free(rc) < 0) - goto done; retval = 0; done: clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval); diff --git a/apps/restconf/restconf_native.h b/apps/restconf/restconf_native.h index a6eecd066..8646efcfa 100644 --- a/apps/restconf/restconf_native.h +++ b/apps/restconf/restconf_native.h @@ -92,6 +92,7 @@ typedef struct { void *sd_req; /* Lib-specific request */ int sd_upgrade2; /* Upgrade to http/2 */ uint8_t *sd_settings2; /* Settings for upgrade to http/2 request */ + int sd_freed; /* Guard against double free */ } restconf_stream_data; typedef struct restconf_socket restconf_socket; @@ -109,6 +110,7 @@ typedef struct restconf_conn { int rc_proto_d1; /* parsed version digit 1 */ int rc_proto_d2; /* parsed version digit 2 */ int rc_s; /* Connection socket */ + int rc_closed; /* Set after close to avoid double free */ clixon_handle rc_h; /* Clixon handle */ SSL *rc_ssl; /* Structure for SSL connection */ restconf_stream_data *rc_streams; /* List of http/2 session streams */ diff --git a/lib/src/clixon_autocli_generate.c b/lib/src/clixon_autocli_generate.c index 8a9fcbaea..e36cbcc14 100644 --- a/lib/src/clixon_autocli_generate.c +++ b/lib/src/clixon_autocli_generate.c @@ -1495,7 +1495,16 @@ yang2cli_uses(clixon_handle h, if (ys_grouping_resolve(ys, prefix, argument, &ygrouping) < 0) goto done; if (ygrouping == NULL){ - clixon_err(OE_YANG, 0, "grouping %s not found in \n", yang_argument_get(ys)); + yang_stmt *yerrmod = ys_module(ys); + const char *modname = NULL; + + if (yerrmod == NULL) + yerrmod = yang_mymodule_get(ys); + if (yerrmod) + modname = yang_argument_get(yerrmod); + clixon_err(OE_YANG, 0, "grouping %s not found in %s", + yang_argument_get(ys), + modname?modname:""); goto done; } if ((cbtree = cbuf_new()) == NULL){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index fe46f41e3..96f01f63e 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1507,6 +1507,23 @@ yang_check_when_xpath(cxobj *xn, if (x && xpath){ if ((nr = xpath_vec_bool(x, nsc, "%s", xpath)) < 0) goto done; + if (nr == 0){ + char *xpath_raw = NULL; + cvec *nsc_raw = NULL; + cxobj *xraw = NULL; + + if (yang_when_xpath_get(yn, &xpath_raw, &nsc_raw) < 0) + goto done; + if (xpath_raw && nsc_raw){ + xraw = xn ? xn : x; + if ((nr = xpath_vec_bool(xraw, nsc_raw, "%s", xpath_raw)) < 0){ + xml_nsctx_free(nsc_raw); + goto done; + } + } + if (nsc_raw) + xml_nsctx_free(nsc_raw); + } } if (nrp) *nrp = nr; diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c index ff8fd46d8..e1c4f208b 100644 --- a/lib/src/clixon_xpath_eval.c +++ b/lib/src/clixon_xpath_eval.c @@ -397,6 +397,22 @@ xp_eval_step(xp_ctx *xc0, case A_ATTRIBUTE: /* principal node type is attribute */ break; case A_CHILD: + if (nodetest && + nodetest->xs_type == XP_NODE_FN && + nodetest->xs_int == XPATHFN_TEXT){ + /* Clixon stores leaf text content directly on the parent node. + * When text() is requested, return the parent nodes that have body + * content so downstream functions operate on their string values. + */ + for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + if (xml_body(xv) == NULL) + continue; + if (cxvec_append(xv, &vec, &veclen) < 0) + goto done; + } + break; + } if (xc->xc_descendant){ for (i=0; ixc_size; i++){ xv = xc->xc_nodeset[i]; @@ -1009,7 +1025,30 @@ xp_relop(xp_ctx *xc1, } break; case XT_STRING: - xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0); + switch (op){ + case XO_EQ: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0); + break; + case XO_NE: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)!=0); + break; + case XO_GE: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)>=0); + break; + case XO_LE: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)<=0); + break; + case XO_LT: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)<0); + break; + case XO_GT: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)>0); + break; + default: + clixon_err(OE_XML, 0, "Operator %s not supported for string/string comparison", clicon_int2str(xpopmap,op)); + goto done; + break; + } break; } /* switch xc1 */ } @@ -1382,8 +1421,8 @@ xp_eval(xp_ctx *xc, clixon_err(OE_XML, EFAULT, "XPath function not implemented: %s", xs->xs_s0); goto done; break; + } } - } break; default: break; diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l index 6656ba3d2..e6a00565f 100644 --- a/lib/src/clixon_xpath_parse.l +++ b/lib/src/clixon_xpath_parse.l @@ -128,7 +128,8 @@ ncname {namestart}{namechar}* ".." { return DOUBLEDOT; } :: { BEGIN(TOKEN2); return DOUBLECOLON; /* axisname */ } [(\[] { BEGIN(TOKEN2); return *yytext; } -[)\]\.,/:|] { return *yytext; } +\/ { BEGIN(TOKEN2); return *yytext; } +[)\]\.,:|] { return *yytext; } and { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } or { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } div { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } @@ -158,7 +159,8 @@ ncname {namestart}{namechar}* <> { return X_EOF; } ".." { BEGIN(TOKEN0); return DOUBLEDOT; } :: { BEGIN(TOKEN0); return DOUBLECOLON; /* axisname */ } -[()\[\]\.,/:|] { BEGIN(TOKEN0); return *yytext; } +\/ { BEGIN(TOKEN2); return *yytext; } +[()\[\]\.,:|] { BEGIN(TOKEN0); return *yytext; } and { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } or { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } div { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y index d72823072..41dfd7851 100644 --- a/lib/src/clixon_xpath_parse.y +++ b/lib/src/clixon_xpath_parse.y @@ -251,6 +251,11 @@ xp_primary_function(clixon_xpath_yacc *xpy, } fn = ret; switch (fn){ + case XPATHFN_TRANSLATE: + /* Always treat translate() as implemented so the parser never + * downgrades it when optional NYI fallbacks are disabled. + */ + break; case XPATHFN_ENUM_VALUE: /* Group of NOT IMPLEMENTED xpath functions */ case XPATHFN_LAST: case XPATHFN_ID: @@ -291,7 +296,6 @@ xp_primary_function(clixon_xpath_yacc *xpy, case XPATHFN_SUBSTRING_AFTER: case XPATHFN_SUBSTRING: case XPATHFN_STRING_LENGTH: - case XPATHFN_TRANSLATE: case XPATHFN_BOOLEAN: case XPATHFN_NOT: case XPATHFN_TRUE: diff --git a/lib/src/clixon_xpath_yang.c b/lib/src/clixon_xpath_yang.c index bae0b74fa..4f7f98332 100644 --- a/lib/src/clixon_xpath_yang.c +++ b/lib/src/clixon_xpath_yang.c @@ -339,6 +339,11 @@ xp_yang_eval(xp_yang_ctx *xy, (*xyr)->xy_node = (*xyr)->xy_initial; goto ok; break; + case XPATHFN_TRANSLATE: + if ((*xyr = xy_dup(xy)) == NULL) + goto done; + goto ok; + break; default: clixon_err(OE_XML, 0, "Function %s invalid for path-arg", xptree->xs_s0); goto done; diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c index bd34757e0..47cf98066 100644 --- a/lib/src/clixon_yang_parse_lib.c +++ b/lib/src/clixon_yang_parse_lib.c @@ -171,6 +171,7 @@ ys_grouping_resolve(yang_stmt *yuses, yang_stmt *yp; yang_stmt *ys; yang_stmt *yspec; + yang_stmt *yorig = NULL; enum rfc_6020 keyw; yspec = ys_spec(yuses); @@ -199,9 +200,15 @@ ys_grouping_resolve(yang_stmt *yuses, ys = (yang_stmt*)yp; /* Proceed to next level */ } } + if (ygrouping == NULL){ + if ((yorig = yang_orig_get(yuses)) != NULL && yorig != yuses){ + if (ys_grouping_resolve(yorig, prefix, name, &ygrouping) < 0) + goto done; + } + } *ygrouping0 = ygrouping; retval = 0; - // done: + done: return retval; } diff --git a/test/test_xpath_issues.sh b/test/test_xpath_issues.sh new file mode 100755 index 000000000..60c178be2 --- /dev/null +++ b/test/test_xpath_issues.sh @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# Regression tests for demo-xpath-issues.yang exercising translate() and when expressions + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +mkdir -p /usr/local/var/demo && chown clicon:clicon /usr/local/var/demo + +APPNAME=demo + +cfg=$dir/conf_demo_xpath.xml +fyang=$dir/demo-xpath-issues.yang + +cat < $cfg + + $cfg + ${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 <<'EOF' > $fyang +module demo-xpath-issues { + yang-version 1.1; + namespace "urn:demo:xpath-issues"; + prefix dxi; + + import ietf-yang-types { prefix yang; } + + description + "Test: translate inside when"; + + typedef demo-ref { + type enumeration { + enum A; + enum B; + enum C; + } + } + + container profiles { + list profile { + key "name"; + leaf name { + type string; + } + leaf profile-name { + type string; + must "not(translate(current()/text(),'Bad','bad') = 'bad-name')" { + error-message "translate() must rejects bad-name"; + } + } + leaf addr { + type string; + must "translate(current()/text(),'XYZ','xyz') != 'xyz@example.com'" { + error-message "translate() must rejects xyz@example.com"; + } + } + } + } + + container network-instances { + list ni { + key "name"; + leaf name { type string; } + container config { + leaf type { + type demo-ref; + } + } + container type-dependent { + when "../config/type = 'A' or ../config/type = 'B'"; + leaf enabled-address-families { + type string; + } + } + } + } + + container interfaces { + list interface { + key "name"; + leaf name { type string; } + container ethernet { + when "../dxi:state/dxi:type = 'ianaift:ethernetCsmacd'"; + leaf speed { + type uint32; + } + } + container state { + leaf type { + 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 + +ns="urn:demo:xpath-issues" + +new "profiles accept values that survive translate()" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "p1good-nameuser@example.com" "" "" + +new "profiles validate passes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "profiles discard" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "profile-name rejects translated bad-name" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "p2Bad-namegood@example.com" "" "" + +new "validate reports profile-name translate error" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorFailed MUST xpath 'not(translate(current()/text(),'Bad','bad') = 'bad-name')' translate() must rejects bad-name yang node: \"leaf profile-name\" with parent: \"list profile\" in file \"/var/tmp/./test_xpath_issues.sh/demo-xpath-issues.yang\" error-path: /profiles/profile[name=\"p2\"]/profile-name" + +new "profiles discard after translate failure" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "addr rejects translated xyz@example.com" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "p3safeXYZ@example.com" "" "" + +new "validate reports addr translate error" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorFailed MUST xpath 'translate(current()/text(),'XYZ','xyz') != 'xyz@example.com'' translate() must rejects xyz@example.com yang node: \"leaf addr\" with parent: \"list profile\" in file \"/var/tmp/./test_xpath_issues.sh/demo-xpath-issues.yang\" error-path: /profiles/profile[name=\"p3\"]/addr" + +new "profiles discard after addr failure" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "type-dependent container allowed when type is A" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "blueAinet" "" "" + +new "network-instances validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "discard network-instances" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "type-dependent container rejected when type mismatches" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "redCinet" "" "" + +new "validate catches type-dependent when expression" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorWHEN condition failed, xpath is ../config/type = 'A' or ../config/type = 'B' yang node: \"container type-dependent\" with parent: \"list ni\" in file \"/var/tmp/./test_xpath_issues.sh/demo-xpath-issues.yang\" error-path: /network-instances/ni[name=\"red\"]/type-dependent" + +new "discard after invalid type-dependent" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "ethernet container allowed when state type matches" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "eth0ianaift:ethernetCsmacd1000" "" "" + +new "interfaces validate ok" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "discard valid interface" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "ethernet container rejected when state type mismatches" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "eth1ianaift:atm100" "" "" + +new "validate catches ethernet when expression" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "applicationoperation-failederrorWHEN condition failed, xpath is ../dxi:state/dxi:type = 'ianaift:ethernetCsmacd' yang node: \"container ethernet\" with parent: \"list interface\" in file \"/var/tmp/./test_xpath_issues.sh/demo-xpath-issues.yang\" error-path: /interfaces/interface[name=\"eth1\"]/ethernet" + +new "discard invalid interface" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +if [ $BE -ne 0 ]; then + new "Kill backend" + pid=$(pgrep -u root -f clixon_backend) + if [ -z "$pid" ]; then + err "backend already dead" + fi + stop_backend -f $cfg +fi + +rm -rf $dir + +new "endtest" +endtest