* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.

* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily.
* Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK.
  * This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath`
* Added new xml functions for specific types: `xml_child_nr_notype`, `xml_child_nr_notype`, `xml_child_i_type`, `xml_find_type`.
This commit is contained in:
Olof hagsand 2019-01-02 15:18:29 +01:00
parent 861300d6c0
commit 0baebc93fd
71 changed files with 2679 additions and 1573 deletions

View file

@ -140,6 +140,7 @@ clicon_err_reset(void)
* @param[in] err Error number, typically errno
* @param[in] suberr Sub-error number
* @param[in] reason Error string, format with argv
* @see clicon_err_reser Resetting the global error variables.
*/
int
clicon_err_fn(const char *fn,

View file

@ -55,10 +55,12 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_netconf_lib.h"
#include "clixon_json.h"
#include "clixon_json_parse.h"
@ -90,43 +92,31 @@ enum childtype{
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
};
/*! Number of children EXCEPT attributes
* @param[in] xn xml node
* @retval number of children in XML tree (except children of type CX_ATTR)
* @see xml_child_nr
*/
static int
xml_child_nr_noattr(cxobj *xn)
{
cxobj *x = NULL;
int nr = 0;
while ((x = xml_child_each(xn, x, -1)) != NULL) {
if (xml_type(x) != CX_ATTR)
nr++;
}
return nr;
}
/*! x is element and has exactly one child which in turn has none
* remove attributes from x
* Clone from clixon_xml_map.c
*/
static enum childtype
childtype(cxobj *x)
child_type(cxobj *x)
{
cxobj *xc1; /* the only child of x */
cxobj *xc; /* the only child of x */
int clen; /* nr of children */
clen = xml_child_nr_noattr(x);
clen = xml_child_nr_notype(x, CX_ATTR);
if (xml_type(x) != CX_ELMNT)
return -1; /* n/a */
if (clen == 0)
return NULL_CHILD;
if (clen > 1)
return ANY_CHILD;
xc1 = xml_child_i(x, 0); /* From here exactly one child */
if (xml_child_nr_noattr(xc1) == 0 && xml_type(xc1)==CX_BODY)
/* From here exactly one noattr child, get it */
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
break;
if (xc == NULL)
return -2; /* n/a */
if (xml_child_nr_notype(xc, CX_ATTR) == 0 && xml_type(xc)==CX_BODY)
return BODY_CHILD;
else
return ANY_CHILD;
@ -175,6 +165,9 @@ arraytype2str(enum array_element_type lt)
return "";
}
/*! Check typeof x in array
* Some complexity when x is in different namespaces
*/
static enum array_element_type
array_eval(cxobj *xprev,
cxobj *x,
@ -184,7 +177,10 @@ array_eval(cxobj *xprev,
int eqprev=0;
int eqnext=0;
yang_stmt *ys;
char *nsx; /* namespace of x */
char *ns2;
nsx = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
if (xml_type(x)!=CX_ELMNT){
array=BODY_ARRAY;
goto done;
@ -192,12 +188,18 @@ array_eval(cxobj *xprev,
ys = xml_spec(x);
if (xnext &&
xml_type(xnext)==CX_ELMNT &&
strcmp(xml_name(x),xml_name(xnext))==0)
eqnext++;
strcmp(xml_name(x),xml_name(xnext))==0){
ns2 = xml_find_type_value(xnext, NULL, "xmlns", CX_ATTR);
if (nsx && ns2 && strcmp(nsx,ns2)==0)
eqnext++;
}
if (xprev &&
xml_type(xprev)==CX_ELMNT &&
strcmp(xml_name(x),xml_name(xprev))==0)
eqprev++;
strcmp(xml_name(x),xml_name(xprev))==0){
ns2 = xml_find_type_value(xprev, NULL, "xmlns", CX_ATTR);
if (nsx && ns2 && strcmp(nsx,ns2)==0)
eqprev++;
}
if (eqprev && eqnext)
array = MIDDLE_ARRAY;
else if (eqprev)
@ -316,8 +318,8 @@ xml2json1_cbuf(cbuf *cb,
* module name
*/
prefix = xml_prefix(x);
if (xml2ns(x, prefix, &namespace) < 0)
goto done;
namespace = xml_find_type_value(x, prefix, "xmlns", CX_ATTR);
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
yspec = ys_spec(ys);
/* Find module name associated with namspace URI */
@ -325,7 +327,7 @@ xml2json1_cbuf(cbuf *cb,
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
modname = ymod->ys_argument;
}
childt = childtype(x);
childt = child_type(x);
if (pretty==2)
cprintf(cb, "#%s_array, %s_child ",
arraytype2str(arraytype),
@ -442,7 +444,7 @@ xml2json1_cbuf(cbuf *cb,
xc_arraytype,
level+1, pretty, 0, bodystr0) < 0)
goto done;
if (i<xml_child_nr_noattr(x)-1)
if (i<xml_child_nr_notype(x, CX_ATTR)-1)
cprintf(cb, ",%s", pretty?"\n":"");
}
switch (arraytype){
@ -532,10 +534,24 @@ xml2json_cbuf(cbuf *cb,
{
int retval = 1;
int level = 0;
char *prefix;
char *namespace;
cprintf(cb, "%*s{%s",
pretty?level*JSON_INDENT:0,"",
pretty?"\n":"");
/* If x is labelled with a default namespace, it should be translated
* to a module name.
* Harder if x has a prefix, then that should also be translated to associated
* module name
*/
prefix = xml_prefix(x);
if (xml2ns(x, prefix, &namespace) < 0)
goto done;
/* Some complexities in grafting namespace in existing trees to new */
if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL && namespace)
if (xmlns_set(x, prefix, namespace) < 0)
goto done;
if (xml2json1_cbuf(cb,
x,
NO_ARRAY,
@ -551,7 +567,7 @@ xml2json_cbuf(cbuf *cb,
return retval;
}
/*! Translate a vector of xml objects to JSON CLigen buffer.
/*! Translate a vector of xml objects to JSON Cligen buffer.
* This is done by adding a top pseudo-object, and add the vector as subs,
* and then not printing the top pseudo-object using the 'flat' option.
* @param[out] cb Cligen buffer to write to
@ -575,12 +591,21 @@ xml2json_cbuf_vec(cbuf *cb,
int i;
cxobj *xp = NULL;
cxobj *xc;
char *prefix;
char *namespace;
if ((xp = xml_new("", NULL, NULL)) == NULL)
if ((xp = xml_new("xml2json", NULL, NULL)) == NULL)
goto done;
/* Some complexities in grafting namespace in existing trees to new */
for (i=0; i<veclen; i++){
prefix = xml_prefix(vec[i]);
if (xml2ns(vec[i], prefix, &namespace) < 0)
goto done;
xc = xml_dup(vec[i]);
xml_addsub(xp, xc);
if (xml_find_type_value(xc, prefix, "xmlns", CX_ATTR) == NULL && namespace)
if (xmlns_set(xc, prefix, namespace) < 0)
goto done;
}
if (0){
cprintf(cb, "[%s", pretty?"\n":" ");
@ -677,6 +702,66 @@ xml2json_vec(FILE *f,
return retval;
}
/*! Translate from JSON module:name to XML name xmlns="uri" recursively
* @param[in] yspec Yang spec
* @param[in,out] x XML tree. Translate it in-line
* @param[out] xerr If namespace not set, create xml error tree
* @retval 0 OK (if xerr set see above)
* @retval -1 Error
* @note the opposite - xml2ns is made inline in xml2json1_cbuf
*/
int
json2xml_ns(yang_spec *yspec,
cxobj *x,
cxobj **xerr)
{
int retval = -1;
yang_stmt *ymod;
char *namespace0;
char *namespace;
char *name = NULL;
char *prefix = NULL;
cxobj *xc;
if (nodeid_split(xml_name(x), &prefix, &name) < 0)
goto done;
if (prefix != NULL){
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
if (netconf_unknown_namespace_xml(xerr, "application",
prefix,
"No yang module found corresponding to prefix") < 0)
goto done;
goto ok;
}
namespace = yang_find_mynamespace(ymod);
/* Get existing default namespace in tree */
if (xml2ns(x, NULL, &namespace0) < 0)
goto done;
/* Set xmlns="" default namespace attribute (if diff from default) */
if (namespace0==NULL || strcmp(namespace0, namespace))
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
/* Remove prefix from name */
if (xml_name_set(x, name) < 0)
goto done;
}
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL){
if (json2xml_ns(yspec, xc, xerr) < 0)
goto done;
if (*xerr != NULL)
break;
}
ok:
retval = 0;
done:
if (prefix)
free(prefix);
if (name)
free(name);
return retval;
}
/*! Parse a string containing JSON and return an XML tree
* @param[in] str Input string containing JSON
* @param[in] name Log string, typically filename

View file

@ -315,11 +315,12 @@ netconf_unknown_attribute(cbuf *cb,
* @param[in] message Error message
*/
static int
netconf_element_xml_common(cxobj **xret,
char *type,
char *tag,
char *element,
char *message)
netconf_common_xml(cxobj **xret,
char *type,
char *tag,
char *infotag,
char *element,
char *message)
{
int retval =-1;
cxobj *xerr;
@ -334,9 +335,9 @@ netconf_element_xml_common(cxobj **xret,
goto done;
if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
"<error-tag>%s</error-tag>"
"<error-info><bad-element>%s</bad-element></error-info>"
"<error-info><%s>%s</%s></error-info>"
"<error-severity>error</error-severity>",
type, tag, element) < 0)
type, tag, infotag, element, infotag) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
@ -363,7 +364,8 @@ netconf_missing_element(cbuf *cb,
int retval = -1;
cxobj *xret = NULL;
if (netconf_element_xml_common(&xret, type, "missing-element", element, message) < 0)
if (netconf_common_xml(&xret, type, "missing-element",
"bad-element", element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
goto done;
@ -387,7 +389,8 @@ netconf_missing_element_xml(cxobj **xret,
char *element,
char *message)
{
return netconf_element_xml_common(xret, type, "missing-element", element, message);
return netconf_common_xml(xret, type, "missing-element",
"bad-element", element, message);
}
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
@ -408,7 +411,8 @@ netconf_bad_element(cbuf *cb,
int retval = -1;
cxobj *xret = NULL;
if (netconf_element_xml_common(&xret, type, "bad-element", element, message) < 0)
if (netconf_common_xml(&xret, type, "bad-element",
"bad-element",element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
goto done;
@ -424,7 +428,7 @@ netconf_bad_element_xml(cxobj **xret,
char *element,
char *message)
{
return netconf_element_xml_common(xret, type, "bad-element", element, message);
return netconf_common_xml(xret, type, "bad-element", "bad-element", element, message);
}
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
@ -444,7 +448,8 @@ netconf_unknown_element(cbuf *cb,
int retval = -1;
cxobj *xret = NULL;
if (netconf_element_xml_common(&xret, type, "unknown-element", element, message) < 0)
if (netconf_common_xml(&xret, type, "unknown-element",
"bad-element", element, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
goto done;
@ -454,13 +459,23 @@ netconf_unknown_element(cbuf *cb,
xml_free(xret);
return retval;
}
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
*
* An unexpected element is present.
* @param[out] xret XML buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] element Bad element name
* @param[in] message Error message
*/
int
netconf_unknown_element_xml(cxobj **xret,
char *type,
char *element,
char *message)
{
return netconf_element_xml_common(xret, type, "unknown-element", element, message);
return netconf_common_xml(xret, type, "unknown-element",
"bad-element", element, message);
}
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
@ -474,35 +489,32 @@ netconf_unknown_element_xml(cxobj **xret,
int
netconf_unknown_namespace(cbuf *cb,
char *type,
char *info,
char *namespace,
char *message)
{
int retval = -1;
char *encstr = NULL;
int retval = -1;
cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-type>%s</error-type>"
"<error-tag>unknown-namespace</error-tag>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
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 (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
if (netconf_common_xml(&xret, type, "unknown-namespace",
"bad-namespace", namespace, message) < 0)
goto done;
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
goto done;
retval = 0;
done:
if (encstr)
free(encstr);
if (xret)
xml_free(xret);
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
int
netconf_unknown_namespace_xml(cxobj **xret,
char *type,
char *namespace,
char *message)
{
return netconf_common_xml(xret, type, "unknown-namespace",
"bad-namespace", namespace, message);
}
/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
@ -989,7 +1001,7 @@ netconf_trymerge(cxobj *x,
}
/*! Load ietf netconf yang module and set enabled features
* The features added are:
* The features added are (in order):
* candidate (8.3)
* validate (8.6)
* startup (8.7)

View file

@ -253,9 +253,8 @@ clicon_options_main(clicon_handle h,
clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix);
goto done;
}
#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
_CLICON_XML_NS_ITERATE = 1;
#endif
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = 0;
/* Read configfile first without yangspec, for bootstrapping */
if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
goto done;
@ -281,9 +280,8 @@ clicon_options_main(clicon_handle h,
xml_child_sort = 1;
else
xml_child_sort = 0;
#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
_CLICON_XML_NS_ITERATE = clicon_option_bool(h, "CLICON_XML_NS_ITERATE");
#endif
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
retval = 0;
done:
return retval;

View file

@ -351,7 +351,7 @@ clicon_rpc_edit_config(clicon_handle h,
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc");
cprintf(cb, "<rpc %s", DEFAULT_XMLNS);
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
cprintf(cb, "><edit-config><target><%s/></target>", db);
@ -787,7 +787,7 @@ clicon_rpc_create_subscription(clicon_handle h,
char *username;
username = clicon_username_get(h);
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription>"
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">"
"<stream>%s</stream>"
"<filter type=\"xpath\" select=\"%s\" />"
"</create-subscription></rpc>",

View file

@ -562,6 +562,54 @@ clicon_str2int(const map_str2int *mstab,
return -1;
}
/*! Split colon-separated node identifier into prefix and name
* @param[in] node-id
* @param[out] prefix Malloced string. May be NULL.
* @param[out] id Malloced identifier.
* @retval 0 OK
* @retval -1 Error
* @code
* char *prefix = NULL;
* char *id = NULL;
* if (nodeid_split(nodeid, &prefix, &id) < 0)
* goto done;
* if (prefix)
* free(prefix);
* if (id)
* free(id);
* @note caller need to free id and prefix after use
*/
int
nodeid_split(char *nodeid,
char **prefix,
char **id)
{
int retval = -1;
char *str;
if ((str = strchr(nodeid, ':')) == NULL){
if ((*id = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
else{
if ((*prefix = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
(*prefix)[str-nodeid] = '\0';
str++;
if ((*id = strdup(str)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! strndup() for systems without it, such as xBSD
*/
#ifndef HAVE_STRNDUP

View file

@ -62,6 +62,8 @@
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h" /* xml_spec_populate */
#include "clixon_xml_map.h" /* xml_spec_populate */
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h"
@ -133,9 +135,9 @@ struct xml{
* or rpc if no xmlns attribute specifies namespace.
* This is loose semantics of finding namespaces.
* And it is wrong, but is the way Clixon originally was written."
* @see CLICON_XML_NS_ITERATE clixon configure option
* @see CLICON_XML_NS_STRICT clixon configure option
*/
int _CLICON_XML_NS_ITERATE = 0;
int _CLICON_XML_NS_STRICT = 1;
/* Mapping between xml type <--> string */
static const map_str2int xsmap[] = {
@ -226,8 +228,7 @@ xml_prefix_set(cxobj *xn,
return 0;
}
/*! Given an xml tree return URI namespace: default or localname given
/*! Given an xml tree return URI namespace recursively : default or localname given
*
* Given an XML tree and a prefix (or NULL) return URI namespace.
* @param[in] x XML tree
@ -235,7 +236,7 @@ xml_prefix_set(cxobj *xn,
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
* @retval 0 OK
* @retval -1 Error
* @see xmlns_check XXX coordinate
* @see xmlns_check XXX can these be merged?
*/
int
xml2ns(cxobj *x,
@ -246,9 +247,9 @@ xml2ns(cxobj *x,
char *ns;
cxobj *xp;
if (prefix != NULL) /* xmlns:<prefix> */
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
else /* default ns */
else /* xmlns="<uri>" */
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
/* namespace not found, try parent */
@ -270,6 +271,40 @@ xml2ns(cxobj *x,
return retval;
}
/*! Add a namespace attribute to an XML node, either default or specific prefix
* @param[in] x XML tree
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
* @param[out] namespace URI namespace (or NULL). Will be copied
* @retval 0 OK
* @retval -1 Error
* @see xml2ns
*/
int
xmlns_set(cxobj *x,
char *prefix,
char *namespace)
{
int retval = -1;
cxobj *xa;
if (prefix != NULL){ /* xmlns:<prefix>="<uri>" */
if ((xa = xml_new(prefix, x, NULL)) == NULL)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
}
else{ /* xmlns="<uri>" */
if ((xa = xml_new("xmlns", x, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
}
if (xml_value_set(xa, namespace) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! See if xmlns:[<localname>=]<uri> exists, if so return <uri>
*
* @param[in] xn XML node
@ -483,6 +518,8 @@ xml_type_set(cxobj *xn,
/*! Get number of children
* @param[in] xn xml node
* @retval number of children in XML tree
* @see xml_child_nr_type
* @see xml_child_nr_notype
*/
int
xml_child_nr(cxobj *xn)
@ -490,10 +527,33 @@ xml_child_nr(cxobj *xn)
return xn->x_childvec_len;
}
/*! Get number of children of EXCEPT specific type
* @param[in] xn xml node
* @param[in] type XML type or -1 for all
* @retval number of typed children in XML tree (except type)
* @see xml_child_nr
* @see xml_child_nr_type
*/
int
xml_child_nr_notype(cxobj *xn,
enum cxobj_type type)
{
cxobj *x = NULL;
int nr = 0;
while ((x = xml_child_each(xn, x, -1)) != NULL) {
if (xml_type(x) != type)
nr++;
}
return nr;
}
/*! Get number of children of specific type
* @param[in] xn xml node
* @param[in] type XML type or -1 for all
* @retval number of typed children in XML tree
* @see xml_child_nr
* @see xml_child_nr_notype
*/
int
xml_child_nr_type(cxobj *xn,
@ -521,6 +581,28 @@ xml_child_i(cxobj *xn,
return NULL;
}
/*! Get a specific child of a specific type
* @param[in] xn xml node
* @param[in] i the number of the child of specific type
* @param[in] type Child type
* @retval child in XML tree, or NULL if no such child, or empty child
* @see xml_child_i
*/
cxobj *
xml_child_i_type(cxobj *xn,
int i,
enum cxobj_type type)
{
cxobj *x = NULL;
int it = 0;
while ((x = xml_child_each(xn, x, type)) != NULL) {
if (x->x_type == type && (i == it++))
return x;
}
return NULL;
}
/*! Set specific child
* @param[in] xn xml node
* @param[in] i the number of the child, eg order in children vector
@ -938,7 +1020,7 @@ xml_body_get(cxobj *xt)
return NULL;
}
/*! Find and return the value of an xml child of specific type
/*! Find and return the value of an xml child of specific type given prefix and name
*
* The value can be of an attribute only
* @param[in] xt xml tree node
@ -950,6 +1032,7 @@ xml_body_get(cxobj *xt)
* char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
* @endcode
* @note, make a copy of the return value to use it properly
* @see xml_find_type return the xml object
* @see xml_find_value where a body can be found as well
*/
char *
@ -957,6 +1040,32 @@ xml_find_type_value(cxobj *xt,
char *prefix,
char *name,
enum cxobj_type type)
{
cxobj *x;
if ((x = xml_find_type(xt, prefix, name, type)) != NULL)
return xml_value(x);
return NULL;
}
/*! Find and return the xml child of specific type given prefix and name
*
* The value can be of an attribute only
* @param[in] xt xml tree node
* @param[in] prefix Prefix (namespace local name) or NULL
* @param[in] name name of xml tree node (eg attr name or "body")
* @retval val Pointer to the name string
* @retval NULL No such node or no value in node
* @code
* cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
* @endcode
* @see xml_find_value where a body can be found as well
*/
cxobj *
xml_find_type(cxobj *xt,
char *prefix,
char *name,
enum cxobj_type type)
{
cxobj *x = NULL;
int pmatch; /* prefix match */
@ -969,7 +1078,7 @@ xml_find_type_value(cxobj *xt,
else
pmatch = 1;
if (pmatch && strcmp(name, xml_name(x)) == 0)
return xml_value(x);
return x;
}
return NULL;
}
@ -1397,6 +1506,8 @@ _xml_parse(const char *str,
goto done;
/* Sort the complete tree after parsing */
if (yspec){
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
@ -1592,7 +1703,7 @@ xml_parse_va(cxobj **xtop,
return retval;
}
/*! Copy single xml node without copying children
/*! Copy single xml node frm x0 to x1 without copying children
*/
int
xml_copy_one(cxobj *x0,

View file

@ -380,7 +380,8 @@ xmldb_get(clicon_handle h,
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[out] cbret Initialized cligen buffer or NULL. On exit contains XML or "".
* @retval 0 OK
* @retval 1 OK
* @retval 0 Failed, cbret contains error xml message
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
@ -388,8 +389,10 @@ xmldb_get(clicon_handle h,
* cxobj *xret = NULL;
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
* err;
* if (xmldb_put(xh, "running", OP_MERGE, xt, cbret) < 0)
* if ((ret = xmldb_put(xh, "running", OP_MERGE, xt, cbret)) < 0)
* err;
* if (ret==0)
* cbret contains netconf error message
* @endcode
* @note that you can add both config data and state data. In comparison,
* xmldb_get has a parameter to get config data only.

File diff suppressed because it is too large Load diff

View file

@ -130,6 +130,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
* @param[in] ya XML parser yacc handler struct
* @param[in] prefix Prefix, namespace, or NULL
* @param[in] localpart Name
* @note the call to xml_child_spec() may not have xmlns attribute read yet XXX
*/
static int
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
@ -138,12 +139,14 @@ xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
int retval = -1;
cxobj *x;
yang_stmt *y = NULL; /* yang node */
cxobj *xp; /* xml parent */
cxobj *xp; /* xml parent */
xp = ya->ya_xparent;
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
if ((x = xml_new(name, xp, NULL)) == NULL)
goto done;
if ((x = xml_new(name, xp, y)) == NULL)
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
goto done;
if (y && xml_spec_set(x, y) < 0)
goto done;
ya->ya_xelement = x;
retval = 0;
@ -168,9 +171,11 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
cxobj *xp; /* xml parent */
xp = ya->ya_xparent;
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
if ((x = xml_new(name, xp, NULL)) == NULL)
goto done;
if ((x = xml_new(name, xp, y)) == NULL)
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
goto done;
if (y && xml_spec_set(x, y) < 0)
goto done;
if (xml_prefix_set(x, prefix) < 0)
goto done;
@ -313,19 +318,15 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
char *attval)
{
int retval = -1;
cxobj *xa;
cxobj *xa = NULL;
#ifdef ENABLE_XMLNS
if (prefix && strcmp(prefix,"xmlns")==0)
fprintf(stderr, "PrefixedAttName NCNAME:%s = %s\n", name, attval);
if (prefix==NULL && strcmp(name,"xmlns")==0)
fprintf(stderr, "DefaultAttName = %s\n", attval);
#endif /* notyet */
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (prefix && xml_prefix_set(xa, prefix) < 0)
goto done;
if ((xa = xml_find_type(ya->ya_xelement, prefix, name, CX_ATTR)) == NULL){
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (prefix && xml_prefix_set(xa, prefix) < 0)
goto done;
}
if (xml_value_set(xa, attval) < 0)
goto done;
retval = 0;

View file

@ -61,6 +61,8 @@
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_xml_map.h"
#include "clixon_xml_sort.h"
/*
@ -68,7 +70,9 @@
*/
/* Sort and binary search of XML children
* Experimental
* XXX kludge since low-level functions xml_merge/xml_diff calls
* match_base_child without handle
* @see clicon_xml_sort
*/
int xml_child_sort = 1;
@ -83,7 +87,7 @@ int xml_child_sort = 1;
* xmlns and xmlns:ns are used.
*/
int
xml_child_spec(char *name,
xml_child_spec(cxobj *x,
cxobj *xp,
yang_spec *yspec,
yang_stmt **yresult)
@ -93,8 +97,9 @@ xml_child_spec(char *name,
yang_stmt *yparent; /* parent yang */
yang_stmt *ymod = NULL;
yang_stmt *yi;
int i;
char *name;
name = xml_name(x);
if (xp && (yparent = xml_spec(xp)) != NULL){
if (yparent->ys_keyword == Y_RPC){
if ((yi = yang_find((yang_node*)yparent, Y_INPUT, NULL)) != NULL)
@ -108,12 +113,9 @@ xml_child_spec(char *name,
goto done;
if (ymod != NULL)
y = yang_find_schemanode((yang_node*)ymod, name);
if (y == NULL && _CLICON_XML_NS_ITERATE){
for (i=0; i<yspec->yp_len; i++){
ymod = yspec->yp_stmt[i];
if ((y = yang_find_schemanode((yang_node*)ymod, name)) != NULL)
break;
}
if (y == NULL && !_CLICON_XML_NS_STRICT){
if (xml_yang_find_non_strict(x, yspec, &y) < 0) /* schemanode */
goto done;
}
}
else
@ -265,6 +267,7 @@ xml_cmp1(cxobj *x,
* Assume populated by yang spec.
* @param[in] x0 XML node
* @param[in] arg Dummy so it can be called by xml_apply()
* @see xml_order XXX: how do they relate?
*/
int
xml_sort(cxobj *x,
@ -560,9 +563,10 @@ xml_sort_verify(cxobj *x0,
}
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[in] xml_sort Value of CLICON_XML_SORT option
* param[out] x0cp Matching base tree child (if any)
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
500K xml_child_each/cvec_each calls.
@ -575,6 +579,7 @@ int
match_base_child(cxobj *x0,
cxobj *x1c,
cxobj **x0cp,
int xml_sort,
yang_stmt *yc)
{
int retval = -1;
@ -632,7 +637,7 @@ match_base_child(cxobj *x0,
break;
}
/* Get match. Sorting mode(optimized) or not?*/
if (xml_child_sort==0)
if (xml_sort==0)
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
else{
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
@ -640,12 +645,6 @@ match_base_child(cxobj *x0,
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
}
else{
#if 1 /* This is just a warning, but a catcher for when xml tree is not
populated with yang spec. If you see this, a previous invacation of,
for example xml_spec_populate() may be missing
*/
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
#endif
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
}
}

View file

@ -469,94 +469,6 @@ yang_match(yang_node *yn,
}
return match;
}
#ifdef NOTYET
/*! Prototype more generic than yang_find_datanode and yang_find_schemanode
*/
yang_stmt *
yang_find_class(yang_node *yn,
char *argument,
yang_class class)
{
yang_stmt *ys = NULL;
yang_stmt *yc = NULL;
yang_stmt *ysmatch = NULL;
int i, j;
int ok;
for (i=0; i<yn->yn_len; i++){
ys = yn->yn_stmt[i];
switch(class){
case YC_NONE:
ok = 1;
break;
case YC_DATANODE:
ok = yang_datanode(ys);
break;
case YC_DATADEFINITION:
ok = yang_datadefinition(ys);
break;
case YC_SCHEMANODE:
ok = yang_schemanode(ys);
break;
}
if (!ok)
continue;
switch(class){
case YC_NONE:
if (argument == NULL)
ysmatch = ys;
else
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
ysmatch = ys;
if (ysmatch)
goto match;
break;
case YC_DATANODE:
case YC_DATADEFINITION:
if (argument == NULL)
ysmatch = ys;
else
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
ysmatch = ys;
if (ysmatch)
goto match;
break;
case YC_SCHEMANODE:
if (ys->ys_keyword == Y_CHOICE){ /* Look for its children */
for (j=0; j<ys->ys_len; j++){
yc = ys->ys_stmt[j];
if (yc->ys_keyword == Y_CASE) /* Look for its children */
ysmatch = yang_find_class((yang_node*)yc, argument, class);
else{
if (yang_schemanode(yc)){
if (argument == NULL)
ysmatch = yc;
else
if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
ysmatch = yc;
}
}
if (ysmatch)
goto match;
}
} /* Y_CHOICE */
else{
if (argument == NULL)
ysmatch = ys;
else
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
ysmatch = ys;
if (ysmatch)
goto match;
}
break;
} /* switch */
} /* for */
match:
return ysmatch;
}
#endif /* NOTYET */
/*! Find child data node with matching argument (container, leaf, etc)
*
@ -658,69 +570,6 @@ yang_find_schemanode(yang_node *yn,
return ysmatch;
}
/*! Find first matching data node in all modules in a yang spec (prefixes)
*
* @param[in] ysp Yang specification
* @param[in] nodeid Name of node. If NULL match first
* @param[in] class See yang_class for class of yang nodes
* A yang specification has modules as children which in turn can have
* syntax-nodes as children. This function goes through all the modules to
* look for nodes. Note that if a child to a module is a choice,
* the search is made recursively made to the choice's children.
* @note works for import prefix, but not work for generic XML parsing where
* xmlns and xmlns:ns are used.
* @see yang_find_top_ns
*/
yang_stmt *
yang_find_topnode(yang_spec *ysp,
char *nodeid,
yang_class class)
{
yang_stmt *ymod = NULL; /* module */
yang_stmt *yres = NULL; /* result */
char *prefix = NULL;
char *id = NULL;
int i;
if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
goto done;
if (prefix){
if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL ||
(ymod = yang_find((yang_node*)ysp, Y_SUBMODULE, prefix)) != NULL){
if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
goto ok;
goto done;
}
}
else /* No prefix given - loop through and find first */
for (i=0; i<ysp->yp_len; i++){
ymod = ysp->yp_stmt[i];
switch (class){
case YC_NONE:
if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
goto ok;
break;
case YC_DATANODE:
if ((yres = yang_find_datanode((yang_node*)ymod, id)) != NULL)
goto ok;
break;
case YC_SCHEMANODE:
if ((yres = yang_find_schemanode((yang_node*)ymod, id)) != NULL)
goto ok;
break;
case YC_DATADEFINITION:
break; /* nyi */
}
}
ok:
done:
if (prefix)
free(prefix);
if (id)
free(id);
return yres;
}
/*! Given a yang statement, find the prefix associated to this module
* @param[in] ys Yang statement in module tree (or module itself)
* @retval NULL Not found
@ -775,7 +624,6 @@ yang_find_mynamespace(yang_stmt *ys)
return namespace;
}
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
* @retval 0 not found
* @retval 1 found
@ -972,60 +820,15 @@ yarg_prefix(yang_stmt *ys)
return prefix;
}
/*! Split yang node identifier into prefix and identifer.
* @param[in] node-id
* @param[out] prefix Malloced string. May be NULL.
* @param[out] id Malloced identifier.
* @retval 0 OK
* @retval -1 Error
* @code
* char *prefix = NULL;
* char *id = NULL;
* if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
* goto done;
* if (prefix)
* free(prefix);
* if (id)
* free(id);
* @note caller need to free id and prefix after use
*/
int
yang_nodeid_split(char *nodeid,
char **prefix,
char **id)
{
int retval = -1;
char *str;
if ((str = strchr(nodeid, ':')) == NULL){
if ((*id = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
else{
if ((*prefix = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
(*prefix)[str-nodeid] = '\0';
str++;
if ((*id = strdup(str)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Given a yang statement and a prefix, return yang module to that prefix
/*! Given a yang statement and a prefix, return yang module to that relative prefix
* Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement
* @param[in] prefix prefix
* @retval ymod Yang module statement if found
* @retval NULL not found
* @node Prefixes are relative to the module they are defined
* @see yang_find_module_by_name
* @see yang_find_module_by_namespace
*/
yang_stmt *
yang_find_module_by_prefix(yang_stmt *ys,
@ -1081,12 +884,14 @@ yang_find_module_by_prefix(yang_stmt *ys,
return ymod;
}
/*! Given a yang statement and a namespace, return yang module
/*! Given a yang spec and a namespace, return yang module
*
* @param[in] yspec A yang specification
* @param[in] namespace namespace
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_name
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_namespace(yang_spec *yspec,
@ -1104,6 +909,28 @@ yang_find_module_by_namespace(yang_spec *yspec,
return ymod;
}
/*! Given a yang spec and a module name, return yang module
*
* @param[in] yspec A yang specification
* @param[in] name Name of module
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_namespace
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_name(yang_spec *yspec,
char *name)
{
yang_stmt *ymod = NULL;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL)
if ((ymod->ys_keyword == Y_MODULE || ymod->ys_keyword == Y_SUBMODULE) &&
strcmp(ymod->ys_argument, name)==0)
return ymod;
return NULL;
}
/*! string is quoted if it contains space or tab, needs double '' */
static int inline
quotedstring(char *s)
@ -1541,7 +1368,7 @@ ys_populate_feature(clicon_handle h,
if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
continue;
/* get m and f from configuration feature rules */
if (yang_nodeid_split(xml_body(xc), &m, &f) < 0)
if (nodeid_split(xml_body(xc), &m, &f) < 0)
goto done;
if (m && f &&
(strcmp(m,"*")==0 ||
@ -1558,7 +1385,8 @@ ys_populate_feature(clicon_handle h,
}
cv_name_set(cv, feature);
cv_bool_set(cv, found);
clicon_debug(1, "%s %s:%s %d", __FUNCTION__, module, feature, found);
if (found)
clicon_debug(1, "%s %s:%s", __FUNCTION__, module, feature);
ys->ys_cv = cv;
ok:
retval = 0;
@ -1936,7 +1764,7 @@ yang_parse_str(char *str,
}
/*! Parse yang spec from an open file descriptor
* @param[in] fd File descriptor containing the YANG file as ASCII characters
* @param[in] fd File descriptor containing the YANG file as ASCII characters
* @param[in] name For debug, eg filename
* @param[in] ysp Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
@ -2083,7 +1911,7 @@ yang_parse_filename(const char *filename,
int fd = -1;
struct stat st;
// clicon_debug(1, "%s %s", __FUNCTION__, filename);
clicon_debug(1, "%s %s", __FUNCTION__, filename);
if (stat(filename, &st) < 0){
clicon_err(OE_YANG, errno, "%s not found", filename);
goto done;
@ -2258,7 +2086,7 @@ yang_features(clicon_handle h,
while (i<yt->ys_len){ /* Note, children may be removed */
ys = yt->ys_stmt[i];
if (ys->ys_keyword == Y_IF_FEATURE){
if (yang_nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
if (nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
goto done;
/* Specifically need to handle? strcmp(prefix, myprefix)) */
if (prefix == NULL)
@ -2680,7 +2508,6 @@ yang_abs_schema_nodeid(yang_spec *yspec,
char *id;
char *prefix = NULL;
yang_stmt *yprefix;
yang_stmt *ys;
/* check absolute schema_nodeid */
if (schema_nodeid[0] != '/'){
@ -2719,21 +2546,6 @@ yang_abs_schema_nodeid(yang_spec *yspec,
}
}
}
if (ymod == NULL){ /* Try find id from topnode without prefix XXX remove?*/
if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){
clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id);
goto done;
}
if ((ymod = ys_module(ys)) == NULL){
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found2", prefix,id);
goto done;
}
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yprefix->ys_argument, prefix) != 0){
clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id);
goto done;
}
}
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0)
goto done;
ok: /* yres may not be set */

View file

@ -81,7 +81,7 @@
int
yang_modules_init(clicon_handle h)
{
int retval = -1;
int retval = -1;
yang_spec *yspec;
yspec = clicon_dbspec_yang(h);
@ -203,7 +203,7 @@ yang_modules_state_get(clicon_handle h,
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
else
cprintf(cb,"<namespace></namespace>");
cprintf(cb, "<conformance-type>implement</conformance-type>");
/* This follows order in rfc 7895: feature, conformance-type, submodules */
yc = NULL;
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
@ -211,6 +211,14 @@ yang_modules_state_get(clicon_handle h,
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
break;
default:
break;
}
}
cprintf(cb, "<conformance-type>implement</conformance-type>");
yc = NULL;
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
case Y_SUBMODULE:
cprintf(cb,"<submodule>");
cprintf(cb,"<name>%s</name>", yc->ys_argument);