Merge branch 'master' of https://github.com/clicon/clixon
This commit is contained in:
commit
af720e8f28
58 changed files with 1527 additions and 770 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue