diff --git a/CHANGELOG.md b/CHANGELOG.md index 44268286..0e621e4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Clixon Changelog -## 4.3.1 (Expected: January 2020) +## 4.3.1 (Expected: February 2020) ### Corrected Bugs * Compile option `VALIDATE_STATE_XML` introduced in `include/custom.h` to control whether code for state data validation is compiled or not. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 238a5341..59605028 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -266,17 +266,11 @@ client_statedata(clicon_handle h, cxobj **xret) { int retval = -1; - cxobj **xvec = NULL; - size_t xlen; - int i; yang_stmt *yspec; yang_stmt *ymod; int ret; char *namespace; -#ifdef VALIDATE_STATE_XML - cxobj *xerr = NULL; -#endif - + if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_YANG, ENOENT, "No yang spec"); goto done; @@ -325,81 +319,9 @@ client_statedata(clicon_handle h, goto done; if (ret == 0) goto fail; - -#ifdef VALIDATE_STATE_XML - /* Check XML from state callback by validating it. return internal - * error with error cause - * Only if config has been read, disable validation on state-only - */ - if (content != CONTENT_NONCONFIG){ - if ((ret = xml_yang_validate_all_top(h, *xret, &xerr)) < 0) - goto done; - if (ret > 0 && (ret = xml_yang_validate_add(h, *xret, &xerr)) < 0) - goto done; - if (ret == 0){ - cxobj *xe; - cxobj *xb; - -#if 1 - if (debug){ - cbuf *ccc=cbuf_new(); - if (clicon_xml2cbuf(ccc, *xret, 0, 0, -1) < 0) - goto done; - clicon_debug(1, "%s FAIL: %s", __FUNCTION__, cbuf_get(ccc)); - cbuf_free(ccc); - } -#endif - if ((xe = xpath_first(xerr, NULL, "//error-tag")) != NULL && - (xb = xml_body_get(xe))){ - if (xml_value_set(xb, "operation-failed") < 0) - goto done; - } - if ((xe = xpath_first(xerr, NULL, "//error-message")) != NULL && - (xb = xml_body_get(xe))){ - if (xml_value_append(xb, " Internal error, state callback returned invalid XML") < 0) - goto done; - } - if (*xret){ - xml_free(*xret); - *xret = NULL; - } - *xret = xerr; - xerr = NULL; - goto fail; - } - } -#endif /* VALIDATE_STATE_XML */ - - /* Code complex to filter out anything that is outside of xpath - * Actually this is a safety catch, should really be done in plugins - * and modules_state functions. - */ - if (xpath_vec(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) - goto done; - /* If vectors are specified then mark the nodes found and - * then filter out everything else, - * otherwise return complete tree. - */ - if (xvec != NULL){ - for (i=0; i + * Function reused from both from_client_get() and from_client_get_config + * @param[in] yspec + * @param[in] db + * @param[in] xpath + * @param[in] username + * @param[in] content + * @param[in] depth * @param[out] cbret Return xml tree, eg ..., - * The set of namespace declarations are those in scope on the - * element. - */ - else - if (xml_nsctx_node(xfilter, &nsc) < 0) - goto done; - if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0) - goto done; - if (nsc) - xml_nsctx_free(nsc); - nsc = nsc1; - } /* Note xret can be pruned by nacm below (and change name), * so zero-copy cant be used * Also, must use external namespace context here due to 0?depth+1:depth) < 0) goto done; } cprintf(cbret, ""); ok: retval = 0; done: - if (xpath) - free(xpath); - if (xnacm) - xml_free(xnacm); if (xvec) free(xvec); + if (xnacm) + xml_free(xnacm); + if (xret) + xml_free(xret); + return retval; +} + +/*! Retrieve all or part of a specified configuration. + * + * @param[in] h Clicon handle + * @param[in] xe Request: + * @param[out] cbret Return xml tree, eg ..., + * The set of namespace declarations are those in scope on the + * element. + */ + else + if (xml_nsctx_node(xfilter, &nsc) < 0) + goto done; + if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0) + goto done; + if (nsc) + xml_nsctx_free(nsc); + nsc = nsc1; + } + /* Clixon extensions: depth */ + if ((attr = xml_find_value(xe, "depth")) != NULL){ + char *reason = NULL; + if ((ret = parse_int32(attr, &depth, &reason)) < 0){ + clicon_err(OE_XML, errno, "parse_int32"); + goto done; + } + if (ret == 0){ + if (netconf_bad_attribute(cbret, "application", + "depth", "Unrecognized value of depth attribute") < 0) + goto done; + goto ok; + } + } + if ((ret = client_config_only(h, nsc, yspec, db, xpath, username, -1, cbret)) < 0) + goto done; + ok: + retval = 0; + done: + if (xpath) + free(xpath); if (nsc) xml_nsctx_free(nsc); if (cbx) cbuf_free(cbx); - if (xret) - xml_free(xret); return retval; } @@ -547,7 +520,6 @@ from_client_edit_config(clicon_handle h, cxobj *xc; cxobj *x; enum operation_type operation = OP_MERGE; - int non_config = 0; yang_stmt *yspec; cbuf *cbx = NULL; /* Assist cbuf */ @@ -953,6 +925,12 @@ from_client_get(clicon_handle h, netconf_content content = CONTENT_ALL; int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */ yang_stmt *yspec; + int i; +#ifdef VALIDATE_STATE_XML + cxobj *xerr = NULL; + cxobj *xr; + cxobj *xb; +#endif username = clicon_username_get(h); if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -977,10 +955,10 @@ from_client_get(clicon_handle h, xml_nsctx_free(nsc); nsc = nsc1; } - /* Clixon extensions: depth and content */ + /* Clixon extensions: content */ if ((attr = xml_find_value(xe, "content")) != NULL) content = netconf_content_str2int(attr); - + /* Clixon extensions: depth */ if ((attr = xml_find_value(xe, "depth")) != NULL){ char *reason = NULL; if ((ret = parse_int32(attr, &depth, &reason)) < 0){ @@ -994,29 +972,105 @@ from_client_get(clicon_handle h, goto ok; } } - if (content != CONTENT_NONCONFIG){ - /* Get config - * Note xret can be pruned by nacm below and change name and - * metrged with state data, so zero-copy cant be used - * Also, must use external namespace context here due to 0 && (ret = xml_yang_validate_add(h, xret, &xerr)) < 0) + goto done; + if (ret == 0){ +#if 1 + if (debug){ + cbuf *ccc=cbuf_new(); + if (clicon_xml2cbuf(ccc, xret, 0, 0, -1) < 0) + goto done; + clicon_debug(1, "%s FAIL: %s", __FUNCTION__, cbuf_get(ccc)); + cbuf_free(ccc); + } +#endif + if ((xr = xpath_first(xerr, NULL, "//error-tag")) != NULL && + (xb = xml_body_get(xr))){ + if (xml_value_set(xb, "operation-failed") < 0) + goto done; + } + if ((xr = xpath_first(xerr, NULL, "//error-message")) != NULL && + (xb = xml_body_get(xr))){ + if (xml_value_append(xb, " Internal error, state callback returned invalid XML") < 0) + goto done; + } + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + goto done; + goto ok; + } +#endif /* VALIDATE_STATE_XML */ + if (content == CONTENT_NONCONFIG){ /* state only, all config should be removed now */ + /* Keep state data only, remove everything that is not config. Note that state data + * may be a sub-part in a config tree, we need to traverse to find all + */ + if (xml_apply(xret, CX_ELMNT, xml_non_config_data, NULL) < 0) + goto done; + if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0) + goto done; + if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) + goto done; + } + /* Code complex to filter out anything that is outside of xpath + * Actually this is a safety catch, should really be done in plugins + * and modules_state functions. + */ + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + goto done; + /* If vectors are specified then mark the nodes found and + * then filter out everything else, + * otherwise return complete tree. + */ + if (xvec != NULL){ + for (i=0; i