This commit is contained in:
Olof hagsand 2019-06-18 14:50:30 +02:00
commit af720e8f28
58 changed files with 1527 additions and 770 deletions

View file

@ -78,6 +78,7 @@
#include "clixon_datastore_write.h"
#include "clixon_datastore_read.h"
/*! Translate from symbolic database name to actual filename in file-system
* @param[in] th text handle handle
* @param[in] db Symbolic database name, eg "candidate", "running"
@ -446,3 +447,21 @@ xmldb_create(clicon_handle h,
close(fd);
return retval;
}
/*! Create an XML database. If it exists already, delete it before creating
* Utility function.
* @param[in] h Clixon handle
* @param[in] db Symbolic database name, eg "candidate", "running"
*/
int
xmldb_db_reset(clicon_handle h,
char *db)
{
if (xmldb_exists(h, db) == 1){
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
return -1;
}
if (xmldb_create(h, db) < 0)
return -1;
return 0;
}

View file

@ -641,7 +641,8 @@ xmldb_get_zerocopy(clicon_handle h,
* @param[in] db Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @retval 0 OK
* @retval -1 Error
* @code
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0)
* err;

View file

@ -154,17 +154,10 @@ text_modify(clicon_handle h,
goto fail;
permit = 1;
}
// int iamkey=0;
#ifdef USE_XML_INSERT
/* Add new xml node but without parent - insert when node fully
copied (see changed conditional below) */
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
#else
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
#endif
changed++;
/* Copy xmlns attributes */
@ -210,16 +203,14 @@ text_modify(clicon_handle h,
}
}
}
#ifdef USE_XML_INSERT
if (changed){
if (xml_insert(x0p, x0) < 0)
goto done;
}
#endif
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
@ -295,17 +286,12 @@ text_modify(clicon_handle h,
goto fail;
permit = 1;
}
#ifdef USE_XML_INSERT
/* Add new xml node but without parent - insert when node fully
* copied (see changed conditional below)
* Note x0 may dangle cases if exit before changed conditional
*/
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
#else
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
#endif
changed++;
/* Copy xmlns attributes */
x1a = NULL;
@ -367,16 +353,14 @@ text_modify(clicon_handle h,
if (ret == 0)
goto fail;
}
#ifdef USE_XML_INSERT
if (changed){
if (xml_insert(x0p, x0) < 0)
goto done;
}
#endif
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
@ -396,17 +380,11 @@ text_modify(clicon_handle h,
break;
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
#ifndef USE_XML_INSERT
if (changed)
xml_sort(x0p, h);
#endif
retval = 1;
done:
#ifdef USE_XML_INSERT
/* Remove dangling added objects */
if (changed && x0 && xml_parent(x0)==NULL)
xml_purge(x0);
#endif
if (x0vec)
free(x0vec);
return retval;
@ -488,7 +466,7 @@ text_modify_top(clicon_handle h,
I.e., curl -u andy:bar -sS -X DELETE http://localhost/restconf/data
*/
case OP_DELETE:
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
break;

View file

@ -32,6 +32,8 @@
***** END LICENSE BLOCK *****
* Netconf library functions. See RFC6241
* Functions to generate a netconf error message come in two forms: xml-tree and
* cbuf. XML tree is preferred.
*/
#ifdef HAVE_CONFIG_H
@ -63,6 +65,8 @@
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xml_map.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
@ -70,7 +74,7 @@
* The request requires a resource that already is in use.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
int
netconf_in_use(cbuf *cb,
@ -109,7 +113,7 @@ netconf_in_use(cbuf *cb,
* The request specifies an unacceptable value for one or more parameters.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
int
netconf_invalid_value(cbuf *cb,
@ -149,7 +153,7 @@ netconf_invalid_value(cbuf *cb,
* too large for the implementation to handle.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "transport", "rpc", "application", "protocol"
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
int
netconf_too_big(cbuf *cb,
@ -189,7 +193,7 @@ netconf_too_big(cbuf *cb,
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] info bad-attribute or bad-element xml
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
int
netconf_missing_attribute(cbuf *cb,
@ -230,7 +234,7 @@ netconf_missing_attribute(cbuf *cb,
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] info bad-attribute or bad-element xml
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
int
netconf_bad_attribute(cbuf *cb,
@ -313,7 +317,7 @@ netconf_unknown_attribute(cbuf *cb,
* @param[in] type Error type: "application" or "protocol"
* @param[in] tag Error tag
* @param[in] element bad-element xml
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
*/
static int
netconf_common_xml(cxobj **xret,
@ -323,8 +327,9 @@ netconf_common_xml(cxobj **xret,
char *element,
char *message)
{
int retval =-1;
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
@ -340,11 +345,17 @@ netconf_common_xml(cxobj **xret,
"<error-severity>error</error-severity>",
type, tag, infotag, element, infotag) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
encstr) < 0)
goto done;
}
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
@ -551,7 +562,7 @@ netconf_access_denied(cbuf *cb,
* authorization failed.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
* @code
* cxobj *xret = NULL;
* if (netconf_access_denied_xml(&xret, "protocol", "Unauthorized") < 0)
@ -565,9 +576,10 @@ netconf_access_denied_xml(cxobj **xret,
char *type,
char *message)
{
int retval =-1;
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
@ -580,11 +592,17 @@ netconf_access_denied_xml(cxobj **xret,
"<error-tag>access-denied</error-tag>"
"<error-severity>error</error-severity>", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
encstr) < 0)
goto done;
}
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
@ -752,38 +770,81 @@ netconf_data_exists(cbuf *cb,
* does not exist. For example, a "delete" operation was attempted on
* data that does not exist.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatiry choice
* @param[in] message Error message
*/
int
netconf_data_missing(cbuf *cb,
char *missing_choice,
char *message)
{
int retval = -1;
char *encstr = NULL;
cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>"
"<error-severity>error</error-severity>") <0)
goto err;
if (netconf_data_missing_xml(&xret, missing_choice, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
goto done;
retval = 0;
done:
if (xret)
xml_free(xret);
return retval;
}
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A
*
* Request could not be completed because the relevant data model content
* does not exist. For example, a "delete" operation was attempted on
* data that does not exist.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatiry choice
* @param[in] message Error message
*/
int
netconf_data_missing_xml(cxobj **xret,
char *missing_choice,
char *message)
{
int retval = -1;
char *encstr = NULL;
cxobj *xerr;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_parse_va(&xerr, NULL,
"<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>") < 0)
goto done;
if (missing_choice) /* NYI: RFC7950: 15.6 <error-path> */
if (xml_parse_va(&xerr, NULL,
"<error-app-tag>missing-choice</error-app-tag>"
"<error-info><missing-choice>%s</missing-choice></error-info>",
missing_choice) < 0)
goto done;
if (xml_parse_va(&xerr, NULL,
"<error-severity>error</error-severity>") < 0)
goto done;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
goto err;
if (xml_parse_va(&xerr, NULL,
"<error-message>%s</error-message>", encstr) < 0)
goto done;
}
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
*
* Request could not be completed because the requested operation is not
@ -858,7 +919,7 @@ netconf_operation_failed(cbuf *cb,
* some reason not covered by any other error condition.
* @param[out] xret Error XML tree
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
* @code
* cxobj *xret = NULL;
* if (netconf_operation_failed_xml(&xret, "protocol", "Unauthorized") < 0)
@ -874,6 +935,7 @@ netconf_operation_failed_xml(cxobj **xret,
{
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
@ -887,11 +949,17 @@ netconf_operation_failed_xml(cxobj **xret,
"<error-tag>operation-failed</error-tag>"
"<error-severity>error</error-severity>", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
encstr) < 0)
goto done;
}
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
@ -929,7 +997,7 @@ netconf_malformed_message(cbuf *cb,
* For example, the message is not well-formed XML or it uses an
* invalid character set.
* @param[out] xret Error XML tree
* @param[in] message Error message
* @param[in] message Error message (will be XML encoded)
* @note New in :base:1.1
* @code
* cxobj *xret = NULL;
@ -943,8 +1011,9 @@ int
netconf_malformed_message_xml(cxobj **xret,
char *message)
{
int retval =-1;
int retval =-1;
cxobj *xerr;
char *encstr = NULL;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
@ -958,11 +1027,17 @@ netconf_malformed_message_xml(cxobj **xret,
"<error-tag>malformed-message</error-tag>"
"<error-severity>error</error-severity>") < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
encstr) < 0)
goto done;
}
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
@ -970,79 +1045,97 @@ netconf_malformed_message_xml(cxobj **xret,
*
* A NETCONF operation would result in configuration data where a
* "unique" constraint is invalidated.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] x List element containing duplicate
* @param[in] cvk List of comonents in x that are non-unique
* @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
* @see RFC7950 Sec 15.1
*/
int
netconf_data_not_unique(cbuf *cb,
cxobj *x,
cvec *cvk)
netconf_data_not_unique_xml(cxobj **xret,
cxobj *x,
cvec *cvk)
{
int retval = -1;
cg_var *cvi = NULL;
cxobj *xi;
cxobj *xerr;
cxobj *xinfo;
cbuf *cb = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-type>protocol</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-app-tag>data-not-unique</error-app-tag>"
"<error-severity>error</error-severity>"
"<error-info>") < 0)
goto err;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
if ((xi = xml_find(x, cv_string_get(cvi))) == NULL)
continue; /* ignore, shouldnt happen */
cprintf(cb, "<non-unique>");
clicon_xml2cbuf(cb, xi, 0, 0);
cprintf(cb, "</non-unique>");
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-type>protocol</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-app-tag>data-not-unique</error-app-tag>"
"<error-severity>error</error-severity>") < 0)
goto done;
if (cvec_len(cvk)){
if ((xinfo = xml_new("error-info", xerr, NULL)) == NULL)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
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);
if (xml_parse_va(&xinfo, NULL, "<non-unique>%s</non-unique>", cbuf_get(cb)) < 0)
goto done;
cbuf_reset(cb);
}
}
if (cprintf(cb, "</error-info></rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf too-many/few-elements err msg according to RFC 7950 15.2/15.3
*
* A NETCONF operation would result in configuration data where a
list or a leaf-list would have too many entries, the following error
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] x List element containing duplicate
* @param[in] max If set, return too-many, otherwise too-few
* @see RFC7950 Sec 15.1
*/
int
netconf_minmax_elements(cbuf *cb,
cxobj *x,
int max)
netconf_minmax_elements_xml(cxobj **xret,
cxobj *x,
int max)
{
int retval = -1;
int retval = -1;
cxobj *xerr;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-type>protocol</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-app-tag>too-%s-elements</error-app-tag>"
"<error-severity>error</error-severity>"
"<error-path>%s</error-path>"
"</rpc-error></rpc-reply>",
max?"many":"few",
xml_name(x)) < 0) /* XXX should be xml2xpath */
goto err;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-type>protocol</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-app-tag>too-%s-elements</error-app-tag>"
"<error-severity>error</error-severity>"
"<error-path>%s</error-path>",
max?"many":"few",
xml_name(x)) < 0) /* XXX should be xml2xpath */
goto done;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Help function: merge - check yang - if error make netconf errmsg
* @param[in] x XML tree
* @param[in] yspec Yang spec
@ -1156,3 +1249,41 @@ netconf_db_find(cxobj *xn,
return db;
}
/*! Generate netconf error msg to cbuf to use in string printout or logs
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
* @param[out] cberr Translation from netconf err to cbuf. Free with cbuf_free.
* @retval 0 OK, with cberr set
* @retval -1 Error
* @code
* cbuf *cb = NULL;
* if (netconf_err2cb(xerr, &cb) < 0)
* err;
* printf("%s", cbuf_get(cb));
* @endcode
* @see clicon_rpc_generate_error
*/
int
netconf_err2cb(cxobj *xerr,
cbuf **cberr)
{
int retval = -1;
cbuf *cb = NULL;
cxobj *x;
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((x=xpath_first(xerr, "error-type"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-message"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-info"))!=NULL)
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
*cberr = cb;
retval = 0;
done:
return retval;
}

View file

@ -72,6 +72,7 @@
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
/* Mapping between Clicon startup modes string <--> constants,
@ -89,6 +90,7 @@ static const map_str2int startup_mode_map[] = {
* @param[in] dbglevel Debug level
* @retval 0 OK
* @retval -1 Error
* @note CLICON_FEATURE and CLICON_YANG_DIR are treated specially since they are lists
*/
int
clicon_option_dump(clicon_handle h,
@ -101,6 +103,7 @@ clicon_option_dump(clicon_handle h,
void *val;
size_t klen;
size_t vlen;
cxobj *x = NULL;
if (clicon_hash_keys(hash, &keys, &klen) < 0)
goto done;
@ -115,7 +118,22 @@ clicon_option_dump(clicon_handle h,
else
clicon_debug(dbglevel, "%s = NULL", keys[i]);
}
retval = 0;
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree
* Since they are lists they are placed in the config tree.
*/
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_YANG_DIR") != 0)
continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
}
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_FEATURE") != 0)
continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
}
retval = 0;
done:
if (keys)
free(keys);
@ -145,6 +163,7 @@ parse_configfile(clicon_handle h,
char *body;
clicon_hash_t *copt = clicon_options(h);
cbuf *cbret = NULL;
cxobj *xret = NULL;
int ret;
if (filename == NULL || !strlen(filename)){
@ -194,13 +213,11 @@ parse_configfile(clicon_handle h,
}
if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0)
goto done;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((ret = xml_yang_validate_add(h, xc, cbret)) < 0)
if ((ret = xml_yang_validate_add(h, xc, &xret)) < 0)
goto done;
if (ret == 0){
if (netconf_err2cb(xret, &cbret) < 0)
goto done;
clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
goto done;
}
@ -234,6 +251,8 @@ parse_configfile(clicon_handle h,
done:
if (cbret)
cbuf_free(cbret);
if (xret)
xml_free(xret);
if (xt)
xml_free(xt);
if (f)
@ -243,6 +262,7 @@ parse_configfile(clicon_handle h,
/*! Add configuration option overriding file setting
* Add to clicon_options hash, and to clicon_conf_xml tree
* Assumes clicon_conf_xml_set has been called
* @param[in] h Clicon handle
* @param[in] name Name of configuration option (see clixon-config.yang)
* @param[in] value String value

View file

@ -71,6 +71,7 @@
#include "clixon_proto.h"
#include "clixon_err.h"
#include "clixon_err_string.h"
#include "clixon_netconf_lib.h"
#include "clixon_proto_client.h"
/*! Send internal netconf rpc from client to backend
@ -224,29 +225,22 @@ clicon_rpc_netconf_xml(clicon_handle h,
}
/*! Generate and log clicon error function call from Netconf error message
* @param[in] prefix Print this string (if given) before: "<prefix>: <error>"
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
*/
int
clicon_rpc_generate_error(char *format,
clicon_rpc_generate_error(char *prefix,
cxobj *xerr)
{
int retval = -1;
cbuf *cb = NULL;
cxobj *x;
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
if (netconf_err2cb(xerr, &cb) < 0)
goto done;
}
if ((x=xpath_first(xerr, "error-type"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-message"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-info"))!=NULL)
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
clicon_log(LOG_ERR, "%s: %s", format, cbuf_get(cb));
if (prefix)
clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb));
else
clicon_log(LOG_ERR, "%s", cbuf_get(cb));
retval = 0;
done:
if (cb)

View file

@ -67,6 +67,7 @@
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_yang_module.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_xml_changelog.h"
#include "clixon_xpath_ctx.h"
@ -424,8 +425,9 @@ clixon_xml_changelog_init(clicon_handle h)
int fd = -1;
cxobj *xt = NULL;
yang_stmt *yspec;
cbuf *cbret = NULL;
int ret;
cxobj *xret = NULL;
cbuf *cbret = NULL;
yspec = clicon_dbspec_yang(h);
if ((filename = clicon_option_str(h, "CLICON_XML_CHANGELOG_FILE")) != NULL){
@ -437,15 +439,13 @@ clixon_xml_changelog_init(clicon_handle h)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
if ((ret = xml_yang_validate_all(h, xt, &xret)) < 0)
goto done;
}
if ((ret = xml_yang_validate_all(h, xt, cbret)) < 0)
goto done;
if (ret==1 && (ret = xml_yang_validate_add(h, xt, cbret)) < 0)
if (ret==1 && (ret = xml_yang_validate_add(h, xt, &xret)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (netconf_err2cb(xret, &cbret) < 0)
goto done;
clicon_err(OE_YANG, 0, "validation failed: %s", cbuf_get(cbret));
goto done;
}
@ -455,12 +455,14 @@ clixon_xml_changelog_init(clicon_handle h)
}
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
if (xret)
xml_free(xret);
if (fd != -1)
close(fd);
if (xt)
xml_free(xt);
if (cbret)
cbuf_free(cbret);
return retval;
}

View file

@ -248,7 +248,7 @@ xml2cli(FILE *f,
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ytype Yang type statement belonging to the XML node
* @param[out] cbret Error buffer
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed
* @retval -1 Error
@ -256,7 +256,7 @@ xml2cli(FILE *f,
static int
validate_leafref(cxobj *xt,
yang_stmt *ytype,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
yang_stmt *ypath;
@ -270,7 +270,7 @@ validate_leafref(cxobj *xt,
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
goto done;
goto fail;
}
@ -284,12 +284,12 @@ validate_leafref(cxobj *xt,
break;
}
if (i==xlen){
if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
if (netconf_bad_element_xml(xret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
goto done;
goto fail;
}
ok:
retval = 0;
retval = 1;
done:
if (xvec)
free(xvec);
@ -312,7 +312,7 @@ validate_leafref(cxobj *xt,
* @param[in] xt XML leaf node of type identityref
* @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type field of type identityref
* @param[out] cbret Error buffer
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed
* @retval -1 Error
@ -324,7 +324,8 @@ static int
validate_identityref(cxobj *xt,
yang_stmt *ys,
yang_stmt *ytype,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
char *node;
@ -350,13 +351,13 @@ validate_identityref(cxobj *xt,
}
/* This is the type's base reference */
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
goto done;
goto fail;
}
/* This is the actual base identity */
if ((ybaseid = yang_find_identity(ybaseref, yang_argument_get(ybaseref))) == NULL){
if (netconf_missing_element(cbret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
goto done;
goto fail;
}
@ -367,7 +368,7 @@ validate_identityref(cxobj *xt,
cbuf_reset(cb);
cprintf(cb, "Identityref validation failed, %s not derived from %s",
node, yang_argument_get(ybaseid));
if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0)
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cb)) < 0)
goto done;
goto fail;
}
@ -413,10 +414,12 @@ xml_yang_root(cxobj *x,
}
/*! Validate an RPC node
* @param[in] xt XML node to be validated
* @retval 1 Validation OK
* @retval 0 Validation failed
* @retval -1 Error
* @param[in] h Clicon handle
* @param[in] xrpc XML node to be validated
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed
* @retval -1 Error
* rfc7950
* 7.14.2
* If a leaf in the input tree has a "mandatory" statement with the
@ -454,8 +457,8 @@ xml_yang_root(cxobj *x,
*/
int
xml_yang_validate_rpc(clicon_handle h,
cxobj *xrpc,
cbuf *cbret)
cxobj *xrpc,
cxobj **xret)
{
int retval = -1;
yang_stmt *yn=NULL; /* rpc name */
@ -469,15 +472,15 @@ xml_yang_validate_rpc(clicon_handle h,
/* xn is name of rpc, ie <rcp><xn/></rpc> */
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
if ((yn = xml_spec(xn)) == NULL){
if (netconf_unknown_element(cbret, "application", xml_name(xn), NULL) < 0)
if (netconf_unknown_element_xml(xret, "application", xml_name(xn), NULL) < 0)
goto done;
goto fail;
}
if ((retval = xml_yang_validate_all(h, xn, cbret)) < 1)
if ((retval = xml_yang_validate_all(h, xn, xret)) < 1)
goto done; /* error or validation fail */
if ((retval = xml_yang_validate_add(h, xn, cbret)) < 1)
if ((retval = xml_yang_validate_add(h, xn, xret)) < 1)
goto done; /* error or validation fail */
if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
if (xml_apply0(xn, CX_ELMNT, xml_default, h) < 0)
goto done;
}
// ok: /* pass validation */
@ -492,7 +495,7 @@ xml_yang_validate_rpc(clicon_handle h,
/*! Check if an xml node is a part of a choice and have >1 siblings
* @param[in] xt XML node to be validated
* @param[in] yt xt:s yang statement
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
@ -501,7 +504,7 @@ xml_yang_validate_rpc(clicon_handle h,
static int
check_choice(cxobj *xt,
yang_stmt *yt,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
yang_stmt *y;
@ -552,7 +555,7 @@ check_choice(cxobj *xt,
continue; /* not choice */
break;
}
if (netconf_bad_element(cbret, "application", xml_name(x), "Element in choice statement already exists") < 0)
if (netconf_bad_element_xml(xret, "application", xml_name(x), "Element in choice statement already exists") < 0)
goto done;
goto fail;
} /* while */
@ -569,7 +572,7 @@ check_choice(cxobj *xt,
/*! Check if an xml node lacks mandatory children
* @param[in] xt XML node to be validated
* @param[in] yt xt:s yang statement
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
@ -577,7 +580,8 @@ check_choice(cxobj *xt,
static int
check_mandatory(cxobj *xt,
yang_stmt *yt,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
int i;
@ -600,7 +604,7 @@ check_mandatory(cxobj *xt,
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
if (netconf_missing_element(cbret, "application", keyname, "Mandatory key") < 0)
if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
goto done;
goto fail;
}
@ -623,7 +627,7 @@ check_mandatory(cxobj *xt,
break; /* got it */
}
if (x == NULL){
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
if (netconf_missing_element_xml(xret, "application", yc->ys_argument, "Mandatory variable") < 0)
goto done;
goto fail;
}
@ -640,17 +644,7 @@ check_mandatory(cxobj *xt,
if (x == NULL){
/* @see RFC7950: 15.6 Error Message for Data That Violates
* a Mandatory "choice" Statement */
if (cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>"
"<error-app-tag>missing-choice</error-app-tag>"
#ifdef NYI
// "<error-path></error-path>"
#endif
"<error-info><missing-choice>%s</missing-choice></error-info>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>",
yc->ys_argument) <0)
if (netconf_data_missing_xml(xret, yc->ys_argument, NULL) < 0)
goto done;
goto fail;
}
@ -667,10 +661,14 @@ check_mandatory(cxobj *xt,
goto done;
}
/*!
* @param[out] xret Error XML tree. Free with xml_free after use
*/
static int
check_list_key(cxobj *xt,
yang_stmt *yt,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
int i;
@ -690,7 +688,7 @@ check_list_key(cxobj *xt,
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
if (netconf_missing_element(cbret, "application", keyname, "Mandatory key") < 0)
if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
goto done;
goto fail;
}
@ -739,7 +737,7 @@ check_insert_duplicate(char **vec,
* @param[in] xt The parent of x
* @param[in] y Its yang spec (Y_LIST)
* @param[in] yu A yang unique spec (Y_UNIQUE)
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
@ -750,7 +748,8 @@ check_unique_list(cxobj *x,
cxobj *xt,
yang_stmt *y,
yang_stmt *yu,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
cvec *cvk; /* unique vector */
@ -784,7 +783,7 @@ check_unique_list(cxobj *x,
if (cvi==NULL){
/* Last element (i) is newly inserted, see if it is already there */
if (check_insert_duplicate(vec, i, vlen) < 0){
if (netconf_data_not_unique(cbret, x, cvk) < 0)
if (netconf_data_not_unique_xml(xret, x, cvk) < 0)
goto done;
goto fail;
}
@ -807,7 +806,7 @@ check_unique_list(cxobj *x,
* @param[in] x One x (the last) of a specific lis
* @param[in] y Yang spec of x
* @param[in] nr Number of elements (like x) in thlist
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
@ -817,7 +816,7 @@ static int
check_min_max(cxobj *x,
yang_stmt *y,
int nr,
cbuf *cbret)
cxobj **xret)
{
int retval = -1;
yang_stmt *ymin; /* yang min */
@ -827,7 +826,7 @@ check_min_max(cxobj *x,
if ((ymin = yang_find(y, Y_MIN_ELEMENTS, NULL)) != NULL){
cv = yang_cv_get(ymin);
if (nr < cv_uint32_get(cv)){
if (netconf_minmax_elements(cbret, x, 0) < 0)
if (netconf_minmax_elements_xml(xret, x, 0) < 0)
goto done;
goto fail;
}
@ -836,7 +835,7 @@ check_min_max(cxobj *x,
cv = yang_cv_get(ymax);
if (cv_uint32_get(cv) > 0 && /* 0 means unbounded */
nr > cv_uint32_get(cv)){
if (netconf_minmax_elements(cbret, x, 1) < 0)
if (netconf_minmax_elements_xml(xret, x, 1) < 0)
goto done;
goto fail;
}
@ -851,9 +850,9 @@ check_min_max(cxobj *x,
/*! Detect unique constraint for duplicates from parent node and minmax
* @param[in] xt XML parent (may have lists w unique constraints as child)
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval 0 Validation failed (xret set)
* @retval -1 Error
* Assume xt:s children are sorted and yang populated.
* The function does two different things of the children of an XML node:
@ -889,8 +888,8 @@ check_min_max(cxobj *x,
* are not allowed.
*/
static int
check_list_unique_minmax(cxobj *xt,
cbuf *cbret)
check_list_unique_minmax(cxobj *xt,
cxobj **xret)
{
int retval = -1;
cxobj *x = NULL;
@ -932,7 +931,7 @@ check_list_unique_minmax(cxobj *xt,
}
else {
/* Check if the list length violates min/max */
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
if ((ret = check_min_max(xp, yp, nr, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -953,7 +952,7 @@ check_list_unique_minmax(cxobj *xt,
do {
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
/* Check if the list length violates min/max */
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -973,7 +972,7 @@ check_list_unique_minmax(cxobj *xt,
* its first element x, its yang spec y, its parent xt, and
* a unique yang spec yu,
*/
if ((ret = check_unique_list(x, xt, y, yu, cbret)) < 0)
if ((ret = check_unique_list(x, xt, y, yu, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -984,7 +983,7 @@ check_list_unique_minmax(cxobj *xt,
*/
if (yp){
/* Check if the list length violates min/max */
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
if ((ret = check_min_max(xp, yp, nr, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -996,7 +995,7 @@ check_list_unique_minmax(cxobj *xt,
do {
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
/* Check if the list length violates min/max */
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1014,14 +1013,14 @@ check_list_unique_minmax(cxobj *xt,
* 1. Check if mandatory leafs present as subs.
* 2. Check leaf values, eg int ranges and string regexps.
* @param[in] xt XML node to be validated
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
* @code
* cxobj *x;
* cbuf *cbret = cbuf_new();
* if ((ret = xml_yang_validate_add(h, x, cbret)) < 0)
* cbuf *xret = NULL;
* if ((ret = xml_yang_validate_add(h, x, &xret)) < 0)
* err;
* if (ret == 0)
* fail;
@ -1032,8 +1031,8 @@ check_list_unique_minmax(cxobj *xt,
*/
int
xml_yang_validate_add(clicon_handle h,
cxobj *xt,
cbuf *cbret)
cxobj *xt,
cxobj **xret)
{
int retval = -1;
cg_var *cv = NULL;
@ -1047,11 +1046,11 @@ xml_yang_validate_add(clicon_handle h,
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
if ((ret = check_choice(xt, yt, cbret)) < 0)
if ((ret = check_choice(xt, yt, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
if ((ret = check_mandatory(xt, yt, cbret)) < 0)
if ((ret = check_mandatory(xt, yt, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1073,21 +1072,21 @@ xml_yang_validate_add(clicon_handle h,
* are considered as "" */
cvtype = cv_type_get(cv);
if (cv_isint(cvtype) || cvtype == CGV_BOOL || cvtype == CGV_DEC64){
if (netconf_bad_element(cbret, "application", yt->ys_argument, "Invalid NULL value") < 0)
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, "Invalid NULL value") < 0)
goto done;
goto fail;
}
}
else{
if (cv_parse1(body, cv, &reason) != 1){
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, reason) < 0)
goto done;
goto fail;
}
}
if ((ys_cv_validate(h, cv, yt, &reason)) != 1){
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, reason) < 0)
goto done;
goto fail;
}
@ -1098,7 +1097,7 @@ xml_yang_validate_add(clicon_handle h,
}
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if ((ret = xml_yang_validate_add(h, x, cbret)) < 0)
if ((ret = xml_yang_validate_add(h, x, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1116,11 +1115,12 @@ xml_yang_validate_add(clicon_handle h,
}
/*! Some checks done only at edit_config, eg keys in lists
* @param[out] xret Error XML tree. Free with xml_free after use
*/
int
xml_yang_validate_list_key_only(clicon_handle h,
cxobj *xt,
cbuf *cbret)
cxobj *xt,
cxobj **xret)
{
int retval = -1;
yang_stmt *yt; /* yang spec of xt going in */
@ -1130,14 +1130,14 @@ xml_yang_validate_list_key_only(clicon_handle h,
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
if ((ret = check_list_key(xt, yt, cbret)) < 0)
if ((ret = check_list_key(xt, yt, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if ((ret = xml_yang_validate_list_key_only(h, x, cbret)) < 0)
if ((ret = xml_yang_validate_list_key_only(h, x, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1154,17 +1154,18 @@ xml_yang_validate_list_key_only(clicon_handle h,
/*! Validate a single XML node with yang specification for all (not only added) entries
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
* @param[in] xt XML node to be validated
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
* @param[out] xret Error XML tree (if retval=0). Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval -1 Error
* @code
* cxobj *x;
* cbuf *cbret = cbuf_new();
* if ((ret = xml_yang_validate_all(x, cbret)) < 0)
* cbuf *xret = NULL;
* if ((ret = xml_yang_validate_all(h, x, &xret)) < 0)
* err;
* if (ret == 0)
* fail;
* xml_free(xret);
* @endcode
* @see xml_yang_validate_add
* @see xml_yang_validate_rpc
@ -1172,8 +1173,8 @@ xml_yang_validate_list_key_only(clicon_handle h,
*/
int
xml_yang_validate_all(clicon_handle h,
cxobj *xt,
cbuf *cbret)
cxobj *xt,
cxobj **xret)
{
int retval = -1;
yang_stmt *ys; /* yang node */
@ -1188,7 +1189,7 @@ xml_yang_validate_all(clicon_handle h,
and !Node has a config sub-statement and it is false */
ys=xml_spec(xt);
if (ys==NULL){
if (netconf_unknown_element(cbret, "application", xml_name(xt), NULL) < 0)
if (netconf_unknown_element_xml(xret, "application", xml_name(xt), NULL) < 0)
goto done;
goto fail;
}
@ -1207,12 +1208,16 @@ xml_yang_validate_all(clicon_handle h,
*/
if ((yc = yang_find(ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, yc, cbret) < 0)
if ((ret = validate_leafref(xt, yc, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
else if (strcmp(yc->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, yc, cbret) < 0)
if ((ret = validate_identityref(xt, ys, yc, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
}
break;
@ -1230,7 +1235,7 @@ xml_yang_validate_all(clicon_handle h,
goto done;
if (!nr){
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
if (netconf_operation_failed(cbret, "application",
if (netconf_operation_failed_xml(xret, "application",
ye?ye->ys_argument:"must xpath validation failed") < 0)
goto done;
goto fail;
@ -1242,7 +1247,7 @@ xml_yang_validate_all(clicon_handle h,
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done;
if (!nr){
if (netconf_operation_failed(cbret, "application",
if (netconf_operation_failed_xml(xret, "application",
"when xpath validation failed") < 0)
goto done;
goto fail;
@ -1251,7 +1256,7 @@ xml_yang_validate_all(clicon_handle h,
}
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if ((ret = xml_yang_validate_all(h, x, cbret)) < 0)
if ((ret = xml_yang_validate_all(h, x, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1259,7 +1264,7 @@ xml_yang_validate_all(clicon_handle h,
/* Check unique and min-max after choice test for example*/
if (yang_config(ys) != 0){
/* Checks if next level contains any unique list constraints */
if ((ret = check_list_unique_minmax(xt, cbret)) < 0)
if ((ret = check_list_unique_minmax(xt, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1274,24 +1279,25 @@ xml_yang_validate_all(clicon_handle h,
}
/*! Translate a single xml node to a cligen variable vector. Note not recursive
* @param[out] xret Error XML tree (if ret == 0). Free with xml_free after use
* @retval 1 Validation OK
* @retval 0 Validation failed (cbret set)
* @retval 0 Validation failed (xret set)
* @retval -1 Error
*/
int
xml_yang_validate_all_top(clicon_handle h,
cxobj *xt,
cbuf *cbret)
cxobj *xt,
cxobj **xret)
{
int ret;
cxobj *x;
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if ((ret = xml_yang_validate_all(h, x, cbret)) < 1)
if ((ret = xml_yang_validate_all(h, x, xret)) < 1)
return ret;
}
if ((ret = check_list_unique_minmax(xt, cbret)) < 1)
if ((ret = check_list_unique_minmax(xt, xret)) < 1)
return ret;
return 1;
}
@ -2053,7 +2059,7 @@ xml_tree_prune_flagged(cxobj *xt,
*/
int
xml_default(cxobj *xt,
void *arg)
void *arg)
{
int retval = -1;
yang_stmt *ys;
@ -2079,13 +2085,8 @@ xml_default(cxobj *xt,
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
if (!xml_find(xt, y->ys_argument)){
#ifdef USE_XML_INSERT
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
goto done;
#else
if ((xc = xml_new(y->ys_argument, xt, y)) == NULL)
goto done;
#endif
xml_flag_set(xc, XML_FLAG_DEFAULT);
if ((xb = xml_new("body", xc, NULL)) == NULL)
goto done;
@ -2098,18 +2099,12 @@ xml_default(cxobj *xt,
goto done;
free(str);
added++;
#ifdef USE_XML_INSERT
if (xml_insert(xt, xc) < 0)
goto done;
#endif
}
}
}
}
#ifndef USE_XML_INSERT
if (added)
xml_sort(xt, NULL);
#endif
retval = 0;
done:
return retval;
@ -2206,7 +2201,9 @@ xml_spec_populate_rpc(clicon_handle h,
if (yrpc){
xml_spec_set(x, yrpc);
if ((yi = yang_find(yrpc, Y_INPUT, NULL)) != NULL){
/* kludge rpc -> input XXX THIS HIDES AN ERROR IN xml_spec_populate */
/* xml_spec_populate need to have parent with yang spec for
* recursive population to work. Therefore, assign input yang
* to rpc level although not 100% intuitive */
xml_spec_set(x, yi);
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
@ -2497,7 +2494,7 @@ api_path2xml_vec(char **vec,
else{
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
goto done;
if (nvalvec > cvec_len(cvk)){
if (nvalvec != cvec_len(cvk)){
clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
goto fail;
}

View file

@ -301,7 +301,38 @@ ys_free1(yang_stmt *ys)
return 0;
}
/*! Free a tree of yang statements recursively */
/*! Remove child i from parent yp (dont free)
* @param[in] yp Parent node
* @param[in] i Order of child to remove
* @retval NULL No such node, nothing done
* @retval yc returned orphaned yang node
* @see ys_free Deallocate yang node
* @note Do not call this in a loop of yang children (unless you know what you are doing)
*/
static yang_stmt *
ys_prune(yang_stmt *yp,
int i)
{
size_t size;
yang_stmt *yc = NULL;
if (i >= yp->ys_len)
goto done;
size = (yp->ys_len - i - 1)*sizeof(struct yang_stmt *);
yc = yp->ys_stmt[i];
memmove(&yp->ys_stmt[i],
&yp->ys_stmt[i+1],
size);
yc = yp->ys_stmt[yp->ys_len--] = NULL;;
done:
return yc;
}
/*! Free a yang statement tree recursively
* @param[in] ys Yang node to remove and all its children recursively
* @note does not remove yang node from tree
* @see ys_prune Remove from parent
*/
int
ys_free(yang_stmt *ys)
{
@ -1363,7 +1394,6 @@ ys_populate_leaf(clicon_handle h,
/* 3b. If not default value, indicate empty cv. */
cv_flag_set(cv, V_UNSET); /* no value (no default) */
}
/* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
if (yparent && yparent->ys_keyword == Y_LIST){
if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
@ -1822,23 +1852,25 @@ ys_populate_unknown(clicon_handle h,
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
*
* We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree
* See ys_parse_sub for first pass and what can be assumed
* @param[in] ys Yang statement
* @param[in] h Clicon handle
* Preferably run this command using yang_apply
* Done in 2nd pass after complete parsing to be sure to have a complete
* parse-tree
* After this pass, cv:s are set for LEAFs and LEAF-LISTs
* @see ys_parse_sub for first pass and what can be assumed
* @see ys_populate2 for after grouping expand and augment
* (there may be more functions (all?) that may be moved to ys_populate2)
*/
int
ys_populate(yang_stmt *ys,
void *arg)
{
int retval = -1;
int retval = -1;
clicon_handle h = (clicon_handle)arg;
switch(ys->ys_keyword){
case Y_LEAF:
case Y_LEAF_LIST:
if (ys_populate_leaf(h, ys) < 0)
goto done;
break;
case Y_LIST:
if (ys_populate_list(h, ys) < 0)
goto done;
@ -1851,11 +1883,6 @@ ys_populate(yang_stmt *ys,
if (ys_populate_length(h, ys) < 0)
goto done;
break;
case Y_MANDATORY: /* call yang_mandatory() to check if set */
case Y_CONFIG:
if (ys_parse(ys, CGV_BOOL) == NULL)
goto done;
break;
case Y_TYPE:
if (ys_populate_type(h, ys) < 0)
goto done;
@ -1880,9 +1907,42 @@ ys_populate(yang_stmt *ys,
return retval;
}
/*! Run after grouping expand and augment
* @see ys_populate run before grouping expand and augment
*/
static int
ys_populate2(yang_stmt *ys,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
switch(ys->ys_keyword){
case Y_LEAF:
case Y_LEAF_LIST:
if (ys_populate_leaf(h, ys) < 0)
goto done;
break;
case Y_MANDATORY: /* call yang_mandatory() to check if set */
case Y_CONFIG:
if (ys_parse(ys, CGV_BOOL) == NULL)
goto done;
break;
default:
break;
}
retval = 0;
done:
return retval;
}
/*! Resolve a grouping name from a point in the yang tree
* @retval 0 OK, but ygrouping determines if a grouping was resolved or not
* @retval -1 Error, with clicon_err called
* @param[in] ys Yang statement of "uses" statement doing the lookup
* @param[in] prefix Prefix of grouping to look for
* @param[in] name Name of grouping to look for
* @param[out] ygrouping0 A found grouping yang structure as result
* @retval 0 OK, ygrouping may be NULL
* @retval -1 Error, with clicon_err called
*/
static int
ys_grouping_resolve(yang_stmt *ys,
@ -1999,23 +2059,94 @@ yang_augment_spec(yang_stmt *ysp,
return retval;
}
/*! Macro expansion of grouping/uses done in step 2 of yang parsing
NOTE
RFC6020 says this:
Identifiers appearing inside the grouping are resolved relative to the scope in which the
grouping is defined, not where it is used. Prefix mappings, type names, grouping
names, and extension usage are evaluated in the hierarchy where the
"grouping" statement appears.
But it will be very difficult to generate keys etc with this semantics. So for now I
macro-expand them
*/
/*! Given a refine node, perform the refinement action on the target refine node
* The RFC is somewhat complicate in the rules for refine.
* Most nodes will be replaced, but some are added
* @param[in] yr Refine node
* @param[in] yt Refine target node (will be modified)
* @see RFC7950 Sec 7.13.2
* There may be some missed cornercases
*/
static int
yang_expand(yang_stmt *yn)
ys_do_refine(yang_stmt *yr,
yang_stmt *yt)
{
int retval = -1;
yang_stmt *yrc; /* refine child */
yang_stmt *yrc1;
yang_stmt *ytc; /* target child */
enum rfc_6020 keyw;
int i;
/* Loop through refine node children. First if remove do that first
* In some cases remove a set of nodes.
*/
yrc = NULL;
while ((yrc = yn_each(yr, yrc)) != NULL) {
keyw = yang_keyword_get(yrc);
switch (keyw){
case Y_DEFAULT: /* remove old, add new */
case Y_DESCRIPTION:
case Y_REFERENCE:
case Y_CONFIG:
case Y_MANDATORY:
case Y_PRESENCE:
case Y_MIN_ELEMENTS:
case Y_MAX_ELEMENTS:
case Y_EXTENSION:
/* Remove old matching, dont increment due to prune in loop */
for (i=0; i<yt->ys_len; ){
ytc = yt->ys_stmt[i];
if (keyw != yang_keyword_get(ytc)){
i++;
continue;
}
ys_prune(yt, i);
ys_free(ytc);
}
/* fall through and add if not found */
case Y_MUST: /* keep old, add new */
case Y_IF_FEATURE:
break;
default:
break;
}
}
/* Second, add the node(s) */
yrc = NULL;
while ((yrc = yn_each(yr, yrc)) != NULL) {
keyw = yang_keyword_get(yrc);
/* Make copy */
if ((yrc1 = ys_dup(yrc)) == NULL)
goto done;
if (yn_insert(yt, yrc1) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Macro expansion of grouping/uses done in step 2 of yang parsing
* RFC7950:
* Identifiers appearing inside the grouping are resolved
* relative to the scope in which the grouping is defined, not where it is
* used. Prefix mappings, type names, grouping names, and extension usage are
* evaluated in the hierarchy where the "grouping" statement appears.
* The identifiers defined in the grouping are not bound to a namespace
* until the contents of the grouping are added to the schema tree via a
* "uses" statement that does not appear inside a "grouping" statement,
* at which point they are bound to the namespace of the current module.
*/
static int
yang_expand_grouping(yang_stmt *yn)
{
int retval = -1;
yang_stmt *ys = NULL;
yang_stmt *ygrouping;
yang_stmt *yg;
yang_stmt *ygrouping; /* grouping original */
yang_stmt *ygrouping2; /* grouping copy */
yang_stmt *yg; /* grouping child */
yang_stmt *yr; /* refinement */
int glen;
int i;
int j;
@ -2034,27 +2165,32 @@ yang_expand(yang_stmt *yn)
prefix = yarg_prefix(ys); /* And this its prefix */
if (ys_grouping_resolve(ys, prefix, name, &ygrouping) < 0)
goto done;
if (prefix){
free(prefix);
prefix = NULL;
}
if (ygrouping == NULL){
clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
__FUNCTION__, ys->ys_argument, ys_module(ys)->ys_argument);
goto done;
break;
}
if (prefix)
free(prefix); /* XXX move up */
/* Check mark flag to see if this grouping (itself) has been expanded
If not, this needs to be done before we can insert it into
the 'uses' place */
if ((ygrouping->ys_flags & YANG_FLAG_MARK) == 0){
if (yang_expand(ygrouping) < 0)
if (yang_expand_grouping(ygrouping) < 0)
goto done;
ygrouping->ys_flags |= YANG_FLAG_MARK; /* Mark as expanded */
}
/* Make a copy of the grouping, then make refinements to this copy
*/
if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
goto done;
/* Replace ys with ygrouping,...
* First enlarge parent vector
*/
glen = ygrouping->ys_len;
glen = ygrouping2->ys_len;
/*
* yn is parent: the children of ygrouping replaces ys.
* Is there a case when glen == 0? YES AND THIS BREAKS
@ -2064,7 +2200,7 @@ yang_expand(yang_stmt *yn)
yn->ys_len += glen - 1;
if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yn->ys_len)*sizeof(yang_stmt *))) == 0){
clicon_err(OE_YANG, errno, "realloc");
return -1;
goto done;
}
/* Then move all existing elements up from i+1 (not uses-stmt) */
if (size)
@ -2072,16 +2208,42 @@ yang_expand(yang_stmt *yn)
&yn->ys_stmt[i+1],
size);
}
/* Iterate through refinements and modify grouping copy
* See RFC 7950 7.13.2 yrt is the refine target node
*/
yr = NULL;
while ((yr = yn_each(ys, yr)) != NULL) {
yang_stmt *yrt; /* refine target node */
if (yang_keyword_get(yr) != Y_REFINE)
continue;
/* Find a node */
if (yang_desc_schema_nodeid(ygrouping2,
yang_argument_get(yr),
-1,
&yrt) < 0)
goto done;
/* Not found, try next */
if (yrt == NULL)
continue;
/* Do the actual refinement */
if (ys_do_refine(yr, yrt) < 0)
goto done;
/* RFC: The argument is a string that identifies a node in the
* grouping. I interpret that as only one node --> break */
break;
}
/* Then copy and insert each child element */
for (j=0; j<glen; j++){
if ((yg = ys_dup(ygrouping->ys_stmt[j])) == NULL)
goto done;
yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
yn->ys_stmt[i+j] = yg;
yg->ys_parent = yn;
}
/* XXX: refine */
/* Remove 'uses' node */
ys_free(ys);
/* Remove the grouping copy */
ygrouping2->ys_len = 0;
ys_free(ygrouping2);
break; /* Note same child is re-iterated since it may be changed */
default:
i++;
@ -2091,7 +2253,7 @@ yang_expand(yang_stmt *yn)
/* Second pass since length may have changed */
for (i=0; i<yn->ys_len; i++){
ys = yn->ys_stmt[i];
if (yang_expand(ys) < 0)
if (yang_expand_grouping(ys) < 0)
goto done;
}
retval = 0;
@ -2441,8 +2603,8 @@ ys_schemanode_check(yang_stmt *ys,
if (yang_desc_schema_nodeid(yp, ys->ys_argument, -1, &yres) < 0)
goto done;
if (yres == NULL){
clicon_err(OE_YANG, 0, "schemanode sanity check of %d %s",
ys->ys_keyword,
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
yang_key2str(ys->ys_keyword),
ys->ys_argument);
goto done;
}
@ -2609,15 +2771,20 @@ yang_parse_post(clicon_handle h,
/* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
for (i=modnr; i<yspec->ys_len; i++){
if (yang_expand(yspec->ys_stmt[i]) < 0)
if (yang_expand_grouping(yspec->ys_stmt[i]) < 0)
goto done;
yang_apply(yspec->ys_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
}
/* 7: Top-level augmentation of all modules XXX: only new modules? */
/* 7: Top-level augmentation of all modules. (Augment also in uses) */
if (yang_augment_spec(yspec, modnr) < 0)
goto done;
/* 4: Go through parse tree and do 2nd step populate (eg default) */
for (i=modnr; i<yspec->ys_len; i++)
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate2, (void*)h) < 0)
goto done;
/* 8: sanity check of schemanode references, need more here */
for (i=modnr; i<yspec->ys_len; i++)
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
@ -2829,19 +2996,13 @@ yang_spec_load_dir(clicon_handle h,
* This is a failsafe in case anything else fails
*/
if (revm && rev0){
int size;
if (revm > rev0) /* Loaded module is older or eq -> remove ym */
ym = ym0;
for (j=0; j<yspec->ys_len; j++)
if (yspec->ys_stmt[j] == ym)
break;
size = (yspec->ys_len - j - 1)*sizeof(struct yang_stmt *);
memmove(&yspec->ys_stmt[j],
&yspec->ys_stmt[j+1],
size);
ys_prune(yspec, j);
ys_free(ym);
yspec->ys_len--;
yspec->ys_stmt[yspec->ys_len] = NULL;
}
}
if (yang_parse_post(h, yspec, modnr) < 0)
@ -3101,7 +3262,7 @@ yang_abs_schema_nodeid(yang_stmt *yspec,
* @param[out] yres First yang node matching schema nodeid
* @retval 0 OK
* @retval -1 Error, with clicon_err called
* @see yang_schema_nodeid
* @see yang_abs_schema_nodeid
* Used in yang: unique, refine, uses augment
*/
int
@ -3429,6 +3590,7 @@ yang_arg2cvec(yang_stmt *ys,
/*! Check if yang node yn has key-stmt as child which matches name
*
* The function looks at the LIST argument string (not actual children)
* @param[in] yn Yang node with sub-statements (look for a key child)
* @param[in] name Check if this name (eg "b") is a key in the yang key statement
*