* More precise Yang validation and better error messages
* For Example, adding bad-, missing-, or unknown-element error messages, etc instead of operation-failed * Removed delete-config support for candidate db since it is not supported in RFC6241. * Switched the order of `error-type` and `error-tag` in all netconf and restconf error messages to comply to RFC order. * Added example_rpc RPC to example backend * Renamed xml_namespace[_set]() to xml_prefix[_set]() * Some restconf error messages contained "rpc-reply" or "rpc-error" which have now been removed. * Netconf/Restconf RPC extra input arguments are ignored (https://github.com/clicon/clixon/issues/47)
This commit is contained in:
parent
03e618b1e5
commit
f872c7e295
45 changed files with 807 additions and 405 deletions
|
|
@ -315,7 +315,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
* Harder if x has a prefix, then that should also be translated to associated
|
||||
* module name
|
||||
*/
|
||||
prefix = xml_namespace(x);
|
||||
prefix = xml_prefix(x);
|
||||
if (xml2ns(x, prefix, &namespace) < 0)
|
||||
goto done;
|
||||
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ netconf_in_use(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>in-use</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>in-use</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -119,8 +119,8 @@ netconf_invalid_value(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>invalid-value</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>invalid-value</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -159,8 +159,8 @@ netconf_too_big(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>too-big</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>too-big</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -200,8 +200,8 @@ netconf_missing_attribute(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>missing-attribute</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>missing-attribute</error-tag>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
|
|
@ -241,8 +241,8 @@ netconf_bad_attribute(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>bad-attribute</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>bad-attribute</error-tag>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
|
|
@ -283,8 +283,8 @@ netconf_unknown_attribute(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>unknown-attribute</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>unknown-attribute</error-tag>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
|
|
@ -318,18 +318,18 @@ netconf_unknown_attribute(cbuf *cb,
|
|||
int
|
||||
netconf_missing_element(cbuf *cb,
|
||||
char *type,
|
||||
char *info,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>missing-element</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-tag>missing-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
|
|
@ -355,24 +355,24 @@ netconf_missing_element(cbuf *cb,
|
|||
* pattern mismatch.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] info bad-element xml
|
||||
* @param[in] elemnt Bad element name
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
int
|
||||
netconf_bad_element(cbuf *cb,
|
||||
char *type,
|
||||
char *info,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>bad-element</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-tag>bad-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
|
|
@ -397,24 +397,24 @@ netconf_bad_element(cbuf *cb,
|
|||
* An unexpected element is present.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] info bad-element xml
|
||||
* @param[in] element Bad element name
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
int
|
||||
netconf_unknown_element(cbuf *cb,
|
||||
char *type,
|
||||
char *info,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>unknown-element</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-tag>unknown-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
|
|
@ -452,8 +452,8 @@ netconf_unknown_namespace(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>unknown-namespace</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>unknown-namespace</error-tag>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, info) <0)
|
||||
|
|
@ -478,7 +478,8 @@ netconf_unknown_namespace(cbuf *cb,
|
|||
|
||||
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* An expected element is missing.
|
||||
* Access to the requested protocol operation or data model is denied because
|
||||
* authorization failed.
|
||||
* @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
|
||||
|
|
@ -492,8 +493,8 @@ netconf_access_denied(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>access-denied</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>access-denied</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -517,7 +518,8 @@ netconf_access_denied(cbuf *cb,
|
|||
|
||||
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* An expected element is missing.
|
||||
* Access to the requested protocol operation or data model is denied because
|
||||
* authorization failed.
|
||||
* @param[out] xret Error XML tree
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
|
|
@ -538,8 +540,8 @@ netconf_access_denied_xml(cxobj **xret,
|
|||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-tag>access-denied</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
|
||||
"<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>",
|
||||
|
|
@ -567,8 +569,8 @@ netconf_lock_denied(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>lock-denied</error-tag>"
|
||||
"<error-type>protocol</error-type>"
|
||||
"<error-tag>lock-denied</error-tag>"
|
||||
"<error-info>%s</error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
info) <0)
|
||||
|
|
@ -593,7 +595,7 @@ netconf_lock_denied(cbuf *cb,
|
|||
|
||||
/*! Create Netconf resource-denied error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* An expected element is missing.
|
||||
* Request could not be completed because of insufficient resources.
|
||||
* @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
|
||||
|
|
@ -607,8 +609,8 @@ netconf_resource_denied(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>resource-denied</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>resource-denied</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -647,8 +649,8 @@ netconf_rollback_failed(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>rollback-failed</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>rollback-failed</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -686,8 +688,8 @@ netconf_data_exists(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>data-exists</error-tag>"
|
||||
"<error-type>application</error-type>"
|
||||
"<error-tag>data-exists</error-tag>"
|
||||
"<error-severity>error</error-severity>") <0)
|
||||
goto err;
|
||||
if (message){
|
||||
|
|
@ -724,8 +726,8 @@ netconf_data_missing(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>data-missing</error-tag>"
|
||||
"<error-type>application</error-type>"
|
||||
"<error-tag>data-missing</error-tag>"
|
||||
"<error-severity>error</error-severity>") <0)
|
||||
goto err;
|
||||
if (message){
|
||||
|
|
@ -763,8 +765,8 @@ netconf_operation_not_supported(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-not-supported</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>operation-not-supported</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -803,8 +805,8 @@ netconf_operation_failed(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
|
|
@ -850,8 +852,8 @@ netconf_operation_failed_xml(cxobj **xret,
|
|||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>%s</error-type>"
|
||||
if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
|
||||
"<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>",
|
||||
|
|
@ -879,8 +881,8 @@ netconf_malformed_message(cbuf *cb,
|
|||
char *encstr = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>malformed-message</error-tag>"
|
||||
"<error-type>rpc</error-type>"
|
||||
"<error-tag>malformed-message</error-tag>"
|
||||
"<error-severity>error</error-severity>") <0)
|
||||
goto err;
|
||||
if (message){
|
||||
|
|
@ -925,8 +927,8 @@ netconf_malformed_message_xml(cxobj **xret,
|
|||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-tag>malformed-message</error-tag>"
|
||||
"<error-type>rpc</error-type>"
|
||||
if (xml_parse_va(&xerr, NULL, "<error-type>rpc</error-type>"
|
||||
"<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>",
|
||||
|
|
@ -990,7 +992,6 @@ netconf_module_load(clicon_handle h)
|
|||
clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Enable features (hardcoded here) */
|
||||
if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:candidate</CLICON_FEATURE>", yspec, &xc) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ parse_configfile(clicon_handle h,
|
|||
char *name;
|
||||
char *body;
|
||||
clicon_hash_t *copt = clicon_options(h);
|
||||
cbuf *cbret = NULL;
|
||||
int ret;
|
||||
|
||||
if (filename == NULL || !strlen(filename)){
|
||||
clicon_err(OE_UNIX, 0, "Not specified");
|
||||
|
|
@ -167,8 +169,16 @@ parse_configfile(clicon_handle h,
|
|||
}
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_yang_validate_add, NULL) < 0)
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = xml_yang_validate_add(xc, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
|
||||
goto done;
|
||||
}
|
||||
while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) {
|
||||
name = xml_name(x);
|
||||
body = xml_body(x);
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ clicon_rpc_delete_config(clicon_handle h,
|
|||
char *username;
|
||||
|
||||
username = clicon_username_get(h);
|
||||
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><delete-config><target><%s/></target></delete-config></rpc>",
|
||||
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><edit-config><target><%s/></target><default-operation>none</default-operation><config operation=\"delete\"/></edit-config></rpc>",
|
||||
username?username:"", db)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
|
|
|
|||
|
|
@ -196,10 +196,9 @@ xml_name_set(cxobj *xn,
|
|||
/*! Get namespace of xnode
|
||||
* @param[in] xn xml node
|
||||
* @retval namespace of xml node
|
||||
* XXX change to xml_localname
|
||||
*/
|
||||
char*
|
||||
xml_namespace(cxobj *xn)
|
||||
xml_prefix(cxobj *xn)
|
||||
{
|
||||
return xn->x_prefix;
|
||||
}
|
||||
|
|
@ -209,11 +208,10 @@ xml_namespace(cxobj *xn)
|
|||
* @param[in] localname new namespace, null-terminated string, copied by function
|
||||
* @retval -1 on error with clicon-err set
|
||||
* @retval 0 OK
|
||||
* XXX change to xml_localname_set
|
||||
*/
|
||||
int
|
||||
xml_namespace_set(cxobj *xn,
|
||||
char *localname)
|
||||
xml_prefix_set(cxobj *xn,
|
||||
char *localname)
|
||||
{
|
||||
if (xn->x_prefix){
|
||||
free(xn->x_prefix);
|
||||
|
|
@ -288,7 +286,7 @@ xmlns_check(cxobj *xn,
|
|||
char *xns;
|
||||
|
||||
while ((x = xml_child_each(xn, x, CX_ATTR)) != NULL)
|
||||
if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 &&
|
||||
if ((xns = xml_prefix(x)) && strcmp(xns, "xmlns")==0 &&
|
||||
strcmp(xml_name(x), nsn) == 0)
|
||||
return xml_value(x);
|
||||
return NULL;
|
||||
|
|
@ -311,7 +309,7 @@ xml_localname_check(cxobj *xn,
|
|||
yang_stmt *ys = xml_spec(xn);
|
||||
|
||||
/* No namespace name - comply */
|
||||
if ((nsn = xml_namespace(xn)) == NULL)
|
||||
if ((nsn = xml_prefix(xn)) == NULL)
|
||||
return 0;
|
||||
/* Check if NSN defined in same node */
|
||||
if (xmlns_check(xn, nsn) != NULL)
|
||||
|
|
@ -965,7 +963,7 @@ xml_find_type_value(cxobj *xt,
|
|||
char *xprefix; /* xprefix */
|
||||
|
||||
while ((x = xml_child_each(xt, x, type)) != NULL) {
|
||||
xprefix = xml_namespace(x);
|
||||
xprefix = xml_prefix(x);
|
||||
if (prefix)
|
||||
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
|
||||
else
|
||||
|
|
@ -1121,7 +1119,7 @@ clicon_xml2file(FILE *f,
|
|||
if (x == NULL)
|
||||
goto ok;
|
||||
name = xml_name(x);
|
||||
namespace = xml_namespace(x);
|
||||
namespace = xml_prefix(x);
|
||||
switch(xml_type(x)){
|
||||
case CX_BODY:
|
||||
if ((val = xml_value(x)) == NULL) /* incomplete tree */
|
||||
|
|
@ -1246,7 +1244,7 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
char *val;
|
||||
|
||||
name = xml_name(x);
|
||||
namespace = xml_namespace(x);
|
||||
namespace = xml_prefix(x);
|
||||
switch(xml_type(x)){
|
||||
case CX_BODY:
|
||||
if ((val = xml_value(x)) == NULL) /* incomplete tree */
|
||||
|
|
@ -1333,10 +1331,10 @@ xmltree2cbuf(cbuf *cb,
|
|||
cprintf(cb, " ");
|
||||
if (xml_type(x) != CX_BODY)
|
||||
cprintf(cb, "%s", xml_type2str(xml_type(x)));
|
||||
if (xml_namespace(x)==NULL)
|
||||
if (xml_prefix(x)==NULL)
|
||||
cprintf(cb, " %s", xml_name(x));
|
||||
else
|
||||
cprintf(cb, " %s:%s", xml_namespace(x), xml_name(x));
|
||||
cprintf(cb, " %s:%s", xml_prefix(x), xml_name(x));
|
||||
if (xml_value(x))
|
||||
cprintf(cb, " value:\"%s\"", xml_value(x));
|
||||
if (x->x_flags)
|
||||
|
|
@ -1612,8 +1610,8 @@ xml_copy_one(cxobj *x0,
|
|||
if ((s = xml_name(x0))) /* malloced string */
|
||||
if ((xml_name_set(x1, s)) < 0)
|
||||
return -1;
|
||||
if ((s = xml_namespace(x0))) /* malloced string */
|
||||
if ((xml_namespace_set(x1, s)) < 0)
|
||||
if ((s = xml_prefix(x0))) /* malloced string */
|
||||
if ((xml_prefix_set(x1, s)) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@
|
|||
#include "clixon_xpath.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
|
|
@ -230,10 +231,15 @@ 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
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
validate_leafref(cxobj *xt,
|
||||
yang_stmt *ytype)
|
||||
yang_stmt *ytype,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ypath;
|
||||
|
|
@ -247,8 +253,9 @@ validate_leafref(cxobj *xt,
|
|||
if ((leafrefbody = xml_body(xt)) == NULL)
|
||||
goto ok;
|
||||
if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){
|
||||
clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument);
|
||||
goto done;
|
||||
if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0)
|
||||
goto done;
|
||||
|
|
@ -260,9 +267,9 @@ validate_leafref(cxobj *xt,
|
|||
break;
|
||||
}
|
||||
if (i==xlen){
|
||||
clicon_err(OE_DB, 0, "Leafref validation failed, no such leaf: %s",
|
||||
leafrefbody);
|
||||
goto done;
|
||||
if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -270,6 +277,9 @@ validate_leafref(cxobj *xt,
|
|||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Validate xml node of type identityref, ensure value is a defined identity
|
||||
|
|
@ -285,14 +295,18 @@ 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
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
* @see ys_populate_identity where the derived types are set
|
||||
* @see RFC7950 Sec 9.10.2:
|
||||
|
||||
*/
|
||||
static int
|
||||
validate_identityref(cxobj *xt,
|
||||
yang_stmt *ys,
|
||||
yang_stmt *ytype)
|
||||
yang_stmt *ytype,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
char *node;
|
||||
|
|
@ -305,37 +319,46 @@ validate_identityref(cxobj *xt,
|
|||
* Always add default prefix because derived identifiers are stored with
|
||||
* prefixes in the base identifiers derived-list.
|
||||
*/
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((node = xml_body(xt)) == NULL)
|
||||
return 0;
|
||||
if (strchr(node, ':') == NULL){
|
||||
prefix = yang_find_myprefix(ys);
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%s:%s", prefix, node);
|
||||
node = cbuf_get(cb);
|
||||
}
|
||||
/* This is the type's base reference */
|
||||
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){
|
||||
clicon_err(OE_DB, 0, "Identityref validation failed, no base");
|
||||
goto done;
|
||||
if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* This is the actual base identity */
|
||||
if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
|
||||
clicon_err(OE_DB, 0, "Identityref validation failed, no base identity");
|
||||
goto done;
|
||||
if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* Here check if node is in the derived node list of the base identity */
|
||||
if (cvec_find(ybaseid->ys_cvec, node) == NULL){
|
||||
clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument);
|
||||
goto done;
|
||||
cbuf_reset(cb);
|
||||
cprintf(cb, "Identityref validation failed, %s not derived from %s",
|
||||
node, ybaseid->ys_argument);
|
||||
if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
retval = 0;
|
||||
retval = 1;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Validate an RPC node
|
||||
|
|
@ -377,24 +400,33 @@ validate_identityref(cxobj *xt,
|
|||
* the same order as they are defined within the "output" statement.
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_rpc(cxobj *xrpc)
|
||||
xml_yang_validate_rpc(cxobj *xrpc,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yn=NULL; /* rpc name */
|
||||
cxobj *xn; /* rpc name */
|
||||
yang_stmt *yi=NULL; /* input name */
|
||||
cxobj *xi; /* input name */
|
||||
int ret;
|
||||
|
||||
assert(strcmp(xml_name(xrpc), "rpc")==0);
|
||||
if (strcmp(xml_name(xrpc), "rpc")){
|
||||
clicon_err(OE_XML, EINVAL, "Expected RPC");
|
||||
goto done;
|
||||
}
|
||||
xn = NULL;
|
||||
/* 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)
|
||||
goto fail;
|
||||
xi = NULL;
|
||||
while ((xi = xml_child_each(xn, xi, CX_ELMNT)) != NULL) {
|
||||
if ((yi = xml_spec(xi)) == NULL)
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = xml_yang_validate_all(xn, cbret)) < 0)
|
||||
goto fail;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
|
||||
goto fail;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
// ok: /* pass validation */
|
||||
retval = 1;
|
||||
|
|
@ -408,14 +440,24 @@ xml_yang_validate_rpc(cxobj *xrpc)
|
|||
/*! Validate a single XML node with yang specification for added entry
|
||||
* 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
|
||||
* @retval 0 Valid OK
|
||||
* @retval -1 Validation failed
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[out] cbret Error buffer
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* cbuf *cbret = cbuf_new();
|
||||
* if ((ret = xml_yang_validate_add(x, cbret)) < 0)
|
||||
* err;
|
||||
* if (ret == 0)
|
||||
* fail;
|
||||
* @endcode
|
||||
* @see xml_yang_validate_all
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_add(cxobj *xt,
|
||||
void *arg)
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv = NULL;
|
||||
|
|
@ -424,11 +466,14 @@ xml_yang_validate_add(cxobj *xt,
|
|||
int i;
|
||||
yang_stmt *ys;
|
||||
char *body;
|
||||
int ret;
|
||||
cxobj *x;
|
||||
|
||||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){
|
||||
switch (ys->ys_keyword){
|
||||
case Y_RPC:
|
||||
case Y_INPUT:
|
||||
case Y_LIST:
|
||||
/* fall thru */
|
||||
|
|
@ -440,9 +485,9 @@ xml_yang_validate_add(cxobj *xt,
|
|||
if (yang_config(yc)==0)
|
||||
continue;
|
||||
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
||||
clicon_err(OE_CFG, 0,"Missing mandatory variable: %s",
|
||||
yc->ys_argument);
|
||||
goto done;
|
||||
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -463,12 +508,11 @@ xml_yang_validate_add(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
if ((ys_cv_validate(cv, ys, &reason)) != 1){
|
||||
clicon_err(OE_DB, 0,
|
||||
"validation of %s failed %s",
|
||||
ys->ys_argument, reason?reason:"");
|
||||
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
|
||||
goto done;
|
||||
if (reason)
|
||||
free(reason);
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -476,28 +520,43 @@ xml_yang_validate_add(cxobj *xt,
|
|||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_add(x, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (cv)
|
||||
cv_free(cv);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! 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[in] arg Not used
|
||||
* @retval -1 Validation failed
|
||||
* @retval 0 Validation OK
|
||||
* @param[out] cbret Error buffer
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
* @see xml_yang_validate_add
|
||||
* @code
|
||||
* if (xml_apply(x, CX_ELMNT, (xml_applyfn_t*)xml_yang_validate_all, 0) < 0)
|
||||
* cxobj *x;
|
||||
* cbuf *cbret = cbuf_new();
|
||||
* if ((ret = xml_yang_validate_all(x, cbret)) < 0)
|
||||
* err;
|
||||
* if (ret == 0)
|
||||
* fail;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_all(cxobj *xt,
|
||||
void *arg)
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys; /* yang node */
|
||||
|
|
@ -505,13 +564,24 @@ xml_yang_validate_all(cxobj *xt,
|
|||
yang_stmt *ye; /* yang must error-message */
|
||||
char *xpath;
|
||||
int nr;
|
||||
|
||||
int ret;
|
||||
cxobj *x;
|
||||
|
||||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
if ((ys = xml_spec(xt)) != NULL &&
|
||||
yang_config(ys) != 0){
|
||||
ys=xml_spec(xt);
|
||||
if (ys==NULL){
|
||||
if (netconf_unknown_element(cbret, "application", xml_name(xt), NULL) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (ys != NULL && yang_config(ys) != 0){
|
||||
/* Node-specific validation */
|
||||
switch (ys->ys_keyword){
|
||||
case Y_ANYXML:
|
||||
case Y_ANYDATA:
|
||||
goto ok;
|
||||
break;
|
||||
case Y_LEAF:
|
||||
/* fall thru */
|
||||
case Y_LEAF_LIST:
|
||||
|
|
@ -520,11 +590,11 @@ xml_yang_validate_all(cxobj *xt,
|
|||
*/
|
||||
if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
|
||||
if (strcmp(yc->ys_argument, "leafref") == 0){
|
||||
if (validate_leafref(xt, yc) < 0)
|
||||
if (validate_leafref(xt, yc, cbret) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(yc->ys_argument, "identityref") == 0){
|
||||
if (validate_identityref(xt, ys, yc) < 0)
|
||||
if (validate_identityref(xt, ys, yc, cbret) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -568,11 +638,11 @@ xml_yang_validate_all(cxobj *xt,
|
|||
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL)
|
||||
clicon_err(OE_DB, 0, "%s", ye->ys_argument);
|
||||
else
|
||||
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
|
||||
goto done;
|
||||
ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL);
|
||||
if (netconf_operation_failed(cbret, "application",
|
||||
ye?ye->ys_argument:"must xpath validation failed") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
|
||||
|
|
@ -581,14 +651,42 @@ xml_yang_validate_all(cxobj *xt,
|
|||
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
|
||||
goto done;
|
||||
if (netconf_operation_failed(cbret, "application",
|
||||
"when xpath validation failed") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_all(x, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
int
|
||||
xml_yang_validate_all_top(cxobj *xt,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int ret;
|
||||
cxobj *x;
|
||||
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_all(x, cbret)) < 1)
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
||||
|
|
@ -1310,6 +1408,11 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
|
||||
/*! Add default values (if not set)
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] arg Ignored
|
||||
* Typically called in a recursive apply function:
|
||||
* @code
|
||||
* xml_apply(xt, CX_ELMNT, xml_default, NULL);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_default(cxobj *xt,
|
||||
|
|
@ -1328,7 +1431,8 @@ xml_default(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
/* Check leaf defaults */
|
||||
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){
|
||||
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST ||
|
||||
ys->ys_keyword == Y_INPUT){
|
||||
for (i=0; i<ys->ys_len; i++){
|
||||
y = ys->ys_stmt[i];
|
||||
if (y->ys_keyword != Y_LEAF)
|
||||
|
|
@ -1493,9 +1597,9 @@ xml_spec_populate_rpc(clicon_handle h,
|
|||
yang_stmt *y=NULL; /* yang node */
|
||||
yang_stmt *ymod=NULL; /* yang module */
|
||||
yang_stmt *yi = NULL; /* input */
|
||||
yang_stmt *ya = NULL; /* arg */
|
||||
// yang_stmt *ya = NULL; /* arg */
|
||||
cxobj *x;
|
||||
cxobj *xi;
|
||||
// cxobj *xi;
|
||||
int i;
|
||||
|
||||
if ((strcmp(xml_name(xrpc), "rpc"))!=0){
|
||||
|
|
@ -1521,11 +1625,18 @@ xml_spec_populate_rpc(clicon_handle h,
|
|||
if (y){
|
||||
xml_spec_set(x, y);
|
||||
if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){
|
||||
/* kludge rpc -> input */
|
||||
xml_spec_set(x, yi);
|
||||
#if 1
|
||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
#else
|
||||
xi = NULL;
|
||||
while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) {
|
||||
if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL)
|
||||
xml_spec_set(xi, ya);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
|
|||
goto done;
|
||||
if ((x = xml_new(name, xp, y)) == NULL)
|
||||
goto done;
|
||||
if (xml_namespace_set(x, prefix) < 0)
|
||||
if (xml_prefix_set(x, prefix) < 0)
|
||||
goto done;
|
||||
ya->ya_xelement = x;
|
||||
retval = 0;
|
||||
|
|
@ -223,9 +223,9 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
|
|||
xml_name(x), name);
|
||||
goto done;
|
||||
}
|
||||
if (xml_namespace(x)!=NULL){
|
||||
if (xml_prefix(x)!=NULL){
|
||||
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s",
|
||||
xml_namespace(x), xml_name(x), name);
|
||||
xml_prefix(x), xml_name(x), name);
|
||||
goto done;
|
||||
}
|
||||
/* Strip pretty-print. Ad-hoc algorithm
|
||||
|
|
@ -263,16 +263,16 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
|||
|
||||
if (strcmp(xml_name(x), name)){
|
||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
|
||||
xml_namespace(x),
|
||||
xml_prefix(x),
|
||||
xml_name(x),
|
||||
namespace,
|
||||
name);
|
||||
goto done;
|
||||
}
|
||||
if (xml_namespace(x)==NULL ||
|
||||
strcmp(xml_namespace(x), namespace)){
|
||||
if (xml_prefix(x)==NULL ||
|
||||
strcmp(xml_prefix(x), namespace)){
|
||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
|
||||
xml_namespace(x),
|
||||
xml_prefix(x),
|
||||
xml_name(x),
|
||||
namespace,
|
||||
name);
|
||||
|
|
@ -324,7 +324,7 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
|
|||
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (prefix && xml_namespace_set(xa, prefix) < 0)
|
||||
if (prefix && xml_prefix_set(xa, prefix) < 0)
|
||||
goto done;
|
||||
if (xml_value_set(xa, attval) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -118,6 +118,9 @@ xml_child_spec(char *name,
|
|||
}
|
||||
else
|
||||
y = NULL;
|
||||
/* kludge rpc -> input */
|
||||
if (y && y->ys_keyword == Y_RPC && yang_find((yang_node*)y, Y_INPUT, NULL))
|
||||
y = yang_find((yang_node*)y, Y_INPUT, NULL);
|
||||
*yresult = y;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -862,7 +862,7 @@ ys_module_by_xml(yang_spec *ysp,
|
|||
|
||||
if (ymodp)
|
||||
*ymodp = NULL;
|
||||
prefix = xml_namespace(xt);
|
||||
prefix = xml_prefix(xt);
|
||||
if (prefix){
|
||||
/* Get namespace for prefix */
|
||||
if (xml2ns(xt, prefix, &namespace) < 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue