* 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:
Olof hagsand 2018-12-21 01:33:41 +01:00
parent 03e618b1e5
commit f872c7e295
45 changed files with 807 additions and 405 deletions

View file

@ -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 */

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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;
}

View file

@ -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
}
}
}

View file

@ -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;

View file

@ -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:

View file

@ -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)