* 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:
parent
861300d6c0
commit
0baebc93fd
71 changed files with 2679 additions and 1573 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue