Optimized search performance for large lists by sorting and binary search
This commit is contained in:
parent
b743b0a080
commit
4b92dbdc10
28 changed files with 1405 additions and 701 deletions
|
|
@ -64,7 +64,7 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$
|
|||
|
||||
SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
clixon_string.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
|
|
|
|||
|
|
@ -382,6 +382,11 @@ clicon_options_main(clicon_handle h)
|
|||
/* Read configfile */
|
||||
if (clicon_option_readfile_xml(copt, configfile, yspec) < 0)
|
||||
goto done;
|
||||
/* Specific option handling */
|
||||
if (clicon_option_bool(h, "CLICON_XML_SORT") == 1)
|
||||
xml_child_sort = 1;
|
||||
else
|
||||
xml_child_sort = 0;
|
||||
}
|
||||
else {
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
|
@ -409,6 +414,7 @@ clicon_options_main(clicon_handle h)
|
|||
/*! Check if a clicon option has a value
|
||||
* @param[in] h clicon_handle
|
||||
* @param[in] name option name
|
||||
* @retval
|
||||
*/
|
||||
int
|
||||
clicon_option_exists(clicon_handle h,
|
||||
|
|
|
|||
|
|
@ -82,21 +82,37 @@
|
|||
/*! xml tree node, with name, type, parent, children, etc
|
||||
* Note that this is a private type not visible from externally, use
|
||||
* access functions.
|
||||
* A word on ordering of x_children:
|
||||
* If there is no yang specification, xml children are ordered as they are entered.
|
||||
* If there is a yang specification (and the appropriate functions are called) the
|
||||
* xml children are ordered as follows:
|
||||
* 1) After yang specification order.
|
||||
* 2) list and leaf-list are sorted alphabetically unless ordered-by user.
|
||||
* Example:
|
||||
* container c{
|
||||
* leaf a;
|
||||
* leaf-list x;
|
||||
* }
|
||||
* then regardless in which order the xml is entered, it will be sorted as follows:
|
||||
* <c>
|
||||
* <a/>
|
||||
* <x>a</<x>
|
||||
* <x>b</<x>
|
||||
* </c>
|
||||
*/
|
||||
struct xml{
|
||||
char *x_name; /* name of node */
|
||||
char *x_namespace; /* namespace, if any */
|
||||
struct xml *x_up; /* parent node in hierarchy if any */
|
||||
struct xml **x_childvec; /* vector of children nodes */
|
||||
int x_childvec_len; /* length of vector */
|
||||
int x_childvec_len;/* length of vector */
|
||||
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
||||
char *x_value; /* attribute and body nodes have values */
|
||||
int _x_vector_i; /* internal use: xml_child_each */
|
||||
int x_flags; /* Flags according to XML_FLAG_* above */
|
||||
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
|
||||
reference, dont free */
|
||||
cg_var *x_cv; /* If body this contains the typed value */
|
||||
clicon_hash_t *x_hash; /* Hash of children */
|
||||
cg_var *x_cv; /* If body this contains the typed value */
|
||||
};
|
||||
|
||||
/* Mapping between xml type <--> string */
|
||||
|
|
@ -108,10 +124,6 @@ static const map_str2int xsmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Hash for XML trees list entries
|
||||
* Experimental XXX DOES NOT WORK
|
||||
*/
|
||||
int xml_child_hash = 0;
|
||||
|
||||
/*! Translate from xml type in enum form to string keyword
|
||||
* @param[in] type Xml type
|
||||
|
|
@ -475,11 +487,11 @@ xml_child_append(cxobj *x,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Set a a childvec to a speciufic size, fill with children after
|
||||
/*! Set a a childvec to a specific size, fill with children after
|
||||
* @code
|
||||
* xml_childvec_set(x, 2);
|
||||
* xml_child_i(x, 0) = xc0;
|
||||
* xml_child_i(x, 1) = xc1;
|
||||
* xml_child_i_set(x, 0, xc0)
|
||||
* xml_child_i_set(x, 1, xc1);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
|
|
@ -502,9 +514,9 @@ xml_childvec_get(cxobj *x)
|
|||
|
||||
/*! Create new xml node given a name and parent. Free with xml_free().
|
||||
*
|
||||
* @param[in] name Name of new
|
||||
* @param[in] xp The parent where the new xml node should be inserted
|
||||
* @param[in] spec Yang stmt or NULL.
|
||||
* @param[in] name Name of XML node
|
||||
* @param[in] xp The parent where the new xml node will be appended
|
||||
* @param[in] spec Yang statement of this XML or NULL.
|
||||
* @retval xml Created xml object if successful. Free with xml_free()
|
||||
* @retval NULL Error and clicon_err() called
|
||||
* @code
|
||||
|
|
@ -536,8 +548,6 @@ xml_new(char *name,
|
|||
if (xp && xml_child_append(xp, x) < 0)
|
||||
return NULL;
|
||||
x->x_spec = spec; /* Can be NULL */
|
||||
if (xml_child_hash && spec && xml_hash_add(x) < 0)
|
||||
return NULL;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
@ -655,8 +665,6 @@ xml_purge(cxobj *xc)
|
|||
int i;
|
||||
cxobj *xp;
|
||||
|
||||
if (xml_child_hash)
|
||||
xml_hash_rm_entry(xc);
|
||||
if ((xp = xml_parent(xc)) != NULL){
|
||||
/* Find child order i in parent*/
|
||||
for (i=0; i<xml_child_nr(xp); i++)
|
||||
|
|
@ -928,8 +936,6 @@ xml_free(cxobj *x)
|
|||
x->x_childvec[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (x->x_hash)
|
||||
hash_free(x->x_hash);
|
||||
if (x->x_childvec)
|
||||
free(x->x_childvec);
|
||||
free(x);
|
||||
|
|
@ -1830,276 +1836,93 @@ xml_operation2str(enum operation_type op)
|
|||
}
|
||||
}
|
||||
|
||||
/*! Given an XML object and a child name, return yang stmt of child
|
||||
* If no xml parent, find root yang stmt matching name
|
||||
* @param[in] name Name of child
|
||||
* @param[in] xp XML parent, can be NULL.
|
||||
* @param[in] yspec Yang specification (top level)
|
||||
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
|
||||
/*! Help function to qsort for sorting entries in xml child vector
|
||||
* @param[in] arg1
|
||||
* @param[in] arg2
|
||||
* @retval 0 If equal
|
||||
* @retval <0 if arg1 is less than arg2
|
||||
* @retval >0 if arg1 is greater than arg2
|
||||
* @note must be in clixon_xml.c since it uses internal (hidden) struct xml
|
||||
*/
|
||||
int
|
||||
xml_child_spec(char *name,
|
||||
cxobj *xp,
|
||||
yang_spec *yspec,
|
||||
yang_stmt **yresult)
|
||||
{
|
||||
yang_stmt *y; /* result yang node */
|
||||
yang_stmt *yparent; /* parent yang */
|
||||
|
||||
if (xp && (yparent = xml_spec(xp)) != NULL)
|
||||
y = yang_find_datanode((yang_node*)yparent, name);
|
||||
else if (yspec)
|
||||
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
|
||||
else
|
||||
y = NULL;
|
||||
*yresult = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Return yang hash
|
||||
* Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.
|
||||
*/
|
||||
clicon_hash_t *
|
||||
xml_hash(cxobj *x)
|
||||
{
|
||||
return x->x_hash;
|
||||
}
|
||||
|
||||
static int
|
||||
xml_hash_init(cxobj *x)
|
||||
xml_cmp(const void* arg1,
|
||||
const void* arg2)
|
||||
{
|
||||
if ((x->x_hash = hash_init()) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! XML remove hash only. Not in parent.
|
||||
* @param[in] x XML object
|
||||
* eg same as xml_hash_op(x, -1)
|
||||
*/
|
||||
int
|
||||
xml_hash_rm_only(cxobj *x)
|
||||
{
|
||||
if (x && x->x_hash){
|
||||
hash_free(x->x_hash);
|
||||
x->x_hash = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compute hash key for xml entry
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[out] key
|
||||
* key: yangtype+x1name
|
||||
* LEAFLIST: b0
|
||||
* LIST: b2vec+b0 -> x0c
|
||||
*/
|
||||
int
|
||||
xml_hash_key(cxobj *x,
|
||||
yang_stmt *y,
|
||||
cbuf *key)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ykey;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
char *keyname;
|
||||
char *b;
|
||||
char *str;
|
||||
|
||||
switch (y->ys_keyword){
|
||||
case Y_CONTAINER: str = "c"; break;
|
||||
case Y_LEAF: str = "e"; break;
|
||||
case Y_LEAF_LIST: str = "l"; break;
|
||||
case Y_LIST: str = "i"; break;
|
||||
default:
|
||||
str = "xx"; break;
|
||||
break;
|
||||
}
|
||||
cprintf(key, "%s%s", str, xml_name(x));
|
||||
switch (y->ys_keyword){
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if ((b = xml_body(x)) == NULL){
|
||||
cbuf_reset(key);
|
||||
goto ok;
|
||||
}
|
||||
cprintf(key, "%s", xml_body(x));
|
||||
break;
|
||||
case Y_LIST: /* Match with key values */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((b = xml_find_body(x, keyname)) == NULL){
|
||||
cbuf_reset(key);
|
||||
goto ok;
|
||||
}
|
||||
cprintf(key, "/%s", b);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! XML hash add. Create hash and add key/value to parent
|
||||
*
|
||||
* @param[in] arg add flag. If 1, else if 0 remove.
|
||||
* Typically called for a whole tree.
|
||||
*/
|
||||
int
|
||||
xml_hash_op(cxobj *x,
|
||||
void *arg)
|
||||
{
|
||||
int add = (intptr_t)arg;
|
||||
#if 1
|
||||
if (add)
|
||||
return xml_hash_add(x);
|
||||
else
|
||||
return xml_hash_rm_entry(x);
|
||||
#else
|
||||
struct xml *x1 = *(struct xml**)arg1;
|
||||
struct xml *x2 = *(struct xml**)arg2;
|
||||
yang_stmt *y1;
|
||||
yang_stmt *y2;
|
||||
int yi1;
|
||||
int yi2;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
int equal = 0;
|
||||
char *b1;
|
||||
char *b2;
|
||||
char *keyname;
|
||||
|
||||
int retval = -1;
|
||||
cxobj *xp;
|
||||
clicon_hash_t *ph;
|
||||
yang_stmt *y;
|
||||
cbuf *key = NULL; /* cligen buffer hash key */
|
||||
|
||||
|
||||
if (xml_hash(x)==NULL){
|
||||
if (add)
|
||||
xml_hash_init(x);
|
||||
if (x1 == NULL){
|
||||
if (x2 == NULL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else if (!add)
|
||||
xml_hash_rm_only(x);
|
||||
if ((xp = xml_parent(x)) == NULL)
|
||||
goto ok;
|
||||
if ((ph = xml_hash(xp))==NULL)
|
||||
goto ok;
|
||||
if ((y = xml_spec(x)) == NULL)
|
||||
goto ok;
|
||||
if ((key = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
else if (x2 == NULL)
|
||||
return 1;
|
||||
y1 = xml_spec(x1);
|
||||
y2 = xml_spec(x2);
|
||||
if (y1==NULL || y2==NULL)
|
||||
return 0; /* just ignore */
|
||||
if (y1 != y2){
|
||||
yi1 = yang_order(y1);
|
||||
yi2 = yang_order(y2);
|
||||
if ((equal = yi1-yi2) != 0)
|
||||
return equal;
|
||||
}
|
||||
if (xml_hash_key(x, y, key) < 0)
|
||||
goto done;
|
||||
if (cbuf_len(key)){
|
||||
// fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x);
|
||||
if (add){
|
||||
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL)
|
||||
/* Now y1=y2, same Yang spec, can only be list or leaf-list,
|
||||
* sort according to key
|
||||
*/
|
||||
if (yang_find((yang_node*)y1, Y_ORDERED_BY, "user") != NULL)
|
||||
return 0; /* Ordered by user: maintain existing order */
|
||||
switch (y1->ys_keyword){
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
equal = strcmp(xml_body(x1), xml_body(x2));
|
||||
break;
|
||||
case Y_LIST: /* Match with key values
|
||||
* Use Y_LIST cache (see struct yang_stmt)
|
||||
*/
|
||||
cvk = y1->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
b1 = xml_find_body(x1, keyname);
|
||||
b2 = xml_find_body(x2, keyname);
|
||||
if ((equal = strcmp(b1,b2)) != 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (hash_del(ph, cbuf_get(key)) < 0)
|
||||
goto done;
|
||||
equal = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (key)
|
||||
cbuf_free(key);
|
||||
return retval;
|
||||
#endif
|
||||
return equal;
|
||||
}
|
||||
|
||||
/*! XML hash add. Create hash and add key/value to parent
|
||||
*
|
||||
* @param[in] x XML object
|
||||
* eg same as xml_hash_op(x, 1)
|
||||
/*! Sort children of an XML node
|
||||
* Assume populated by yang spec.
|
||||
* @param[in] x0 XML node
|
||||
* @param[in] arg Dummy so it can be called by xml_apply()
|
||||
* @note must be in clixon_xml.c since it uses internal (hidden) struct xml
|
||||
*/
|
||||
int
|
||||
xml_hash_add(cxobj *x)
|
||||
xml_sort(cxobj *x,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xp;
|
||||
clicon_hash_t *ph;
|
||||
yang_stmt *y;
|
||||
yang_stmt *yp;
|
||||
cbuf *key = NULL; /* cligen buffer hash key */
|
||||
|
||||
if ((ph = xml_hash(x))==NULL){
|
||||
xml_hash_init(x);
|
||||
ph = xml_hash(x);
|
||||
}
|
||||
if ((xp = xml_parent(x)) == NULL)
|
||||
goto ok;
|
||||
yp = xml_spec(xp);
|
||||
if (yp && yp->ys_keyword != Y_LIST)
|
||||
goto ok;
|
||||
if ((y = xml_spec(x)) == NULL)
|
||||
goto ok;
|
||||
if ((key = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_hash_key(x, y, key) < 0)
|
||||
goto done;
|
||||
if (cbuf_len(key)){
|
||||
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (key)
|
||||
cbuf_free(key);
|
||||
return retval;
|
||||
qsort(x->x_childvec, x->x_childvec_len, sizeof(struct xml*), xml_cmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! XML hash rm. Create hash and add key/value to parent
|
||||
*
|
||||
* @param[in] x XML object
|
||||
* eg same as xml_hash_op(x, 0)
|
||||
*/
|
||||
int
|
||||
xml_hash_rm_entry(cxobj *x)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xp;
|
||||
clicon_hash_t *ph;
|
||||
yang_stmt *y;
|
||||
cbuf *key = NULL; /* cligen buffer hash key */
|
||||
|
||||
if (xml_hash(x)!=NULL)
|
||||
xml_hash_rm_only(x);
|
||||
if ((xp = xml_parent(x)) == NULL)
|
||||
goto ok;
|
||||
if ((ph = xml_hash(xp))==NULL)
|
||||
goto ok;
|
||||
if ((y = xml_spec(x)) == NULL)
|
||||
goto ok;
|
||||
if ((key = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_hash_key(x, y, key) < 0)
|
||||
goto done;
|
||||
if (cbuf_len(key)){
|
||||
if (hash_del(ph, cbuf_get(key)) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (key)
|
||||
cbuf_free(key);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn this on to get a xml parse and pretty print test program
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@
|
|||
#include "clixon_xsl.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
/* Something to do with reverse engineering of junos syntax? */
|
||||
|
|
@ -590,136 +591,6 @@ cvec2xml_1(cvec *cvv,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Given child tree x1c, find matching child in base tree x0
|
||||
* param[in] x0 Base tree node
|
||||
* param[in] x1c Modification tree child
|
||||
* param[in] yc Yang spec of tree child
|
||||
* 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.
|
||||
The outer loop is large for large lists
|
||||
The inner loop is small
|
||||
Major time in xml_find_body()
|
||||
Can one do a binary search in the x0 list?
|
||||
*/
|
||||
int
|
||||
match_base_child(cxobj *x0,
|
||||
cxobj *x1c,
|
||||
cxobj **x0cp,
|
||||
yang_stmt *yc)
|
||||
{
|
||||
int retval = -1;
|
||||
char *x1cname;
|
||||
cxobj *x0c = NULL; /* x0 child */
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
char *b0;
|
||||
char *b1;
|
||||
yang_stmt *ykey;
|
||||
char *keyname;
|
||||
int equal;
|
||||
char **b1vec = NULL;
|
||||
int i;
|
||||
cxobj **p;
|
||||
cbuf *key = NULL; /* cligen buffer hash key */
|
||||
size_t vlen;
|
||||
|
||||
if (xml_child_hash){
|
||||
*x0cp = NULL; /* return value */
|
||||
if (xml_hash(x0) == NULL)
|
||||
goto nohash;
|
||||
if ((key = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done1;
|
||||
}
|
||||
if (xml_hash_key(x1c, yc, key) < 0)
|
||||
goto done;
|
||||
x0c = NULL;
|
||||
if (cbuf_len(key))
|
||||
if ((p = hash_value(xml_hash(x0), cbuf_get(key), &vlen)) != NULL){
|
||||
assert(vlen == sizeof(x0c));
|
||||
x0c = *p;
|
||||
}
|
||||
// fprintf(stderr, "%s get %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x0c);
|
||||
*x0cp = x0c;
|
||||
retval = 0;
|
||||
done1:
|
||||
if (key)
|
||||
cbuf_free(key);
|
||||
return retval;
|
||||
}
|
||||
nohash:
|
||||
*x0cp = NULL; /* return value */
|
||||
x1cname = xml_name(x1c);
|
||||
switch (yc->ys_keyword){
|
||||
case Y_CONTAINER: /* Equal regardless */
|
||||
case Y_LEAF: /* Equal regardless */
|
||||
x0c = xml_find(x0, x1cname);
|
||||
break;
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if ((b1 = xml_body(x1c)) == NULL)
|
||||
goto ok;
|
||||
x0c = xml_find_body_obj(x0, x1cname, b1);
|
||||
break;
|
||||
case Y_LIST: /* Match with key values */
|
||||
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, yc->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL; i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL)
|
||||
i++;
|
||||
if ((b1vec = calloc(i, sizeof(b1))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
cvi = NULL; i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
|
||||
goto ok; /* not found */
|
||||
b1vec[i++] = b1;
|
||||
}
|
||||
/* Iterate over x0 tree to (1) find a child that matches name
|
||||
(2) that have keys that matches */
|
||||
x0c = NULL;
|
||||
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
|
||||
equal = 0;
|
||||
if (strcmp(xml_name(x0c), x1cname))
|
||||
continue;
|
||||
/* Must be inner loop */
|
||||
cvi = NULL; i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
b1 = b1vec[i++];
|
||||
equal = 0;
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
|
||||
break; /* error case */
|
||||
if (strcmp(b0, b1))
|
||||
break; /* stop as soon as inequal key found */
|
||||
equal=1; /* reaches here for all keynames, x0c is found. */
|
||||
}
|
||||
if (equal) /* x0c and x1c equal, otherwise look for other */
|
||||
break;
|
||||
} /* while x0c */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ok:
|
||||
*x0cp = x0c;
|
||||
retval = 0;
|
||||
done:
|
||||
if (b1vec)
|
||||
free(b1vec);
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find next yang node, either start from yang_spec or some yang-node
|
||||
* @param[in] y Node spec or sny yang-node
|
||||
|
|
@ -895,7 +766,6 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
|||
cbuf *cb)
|
||||
{
|
||||
yang_node *yp; /* parent */
|
||||
yang_stmt *ykey;
|
||||
int i;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
int retval = -1;
|
||||
|
|
@ -926,14 +796,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
|||
|
||||
switch (ys->ys_keyword){
|
||||
case Y_LIST:
|
||||
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
if (cvec_len(cvk))
|
||||
cprintf(cb, "=");
|
||||
/* Iterate over individual keys */
|
||||
|
|
@ -951,8 +814,6 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
|||
} /* switch */
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1370,13 +1231,16 @@ xml_order(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
j0 = 0;
|
||||
/* Go through xml children and ensure they are same order as yspec children */
|
||||
/* Go through yang node's children and ensure that the
|
||||
* xml children follow this order.
|
||||
* Do not order the list or leaf-list children (have same name).
|
||||
*/
|
||||
for (i=0; i<y->ys_len; i++){
|
||||
yc = y->ys_stmt[i];
|
||||
if (!yang_datanode(yc))
|
||||
continue;
|
||||
yname = yc->ys_argument;
|
||||
/* First go thru xml children with same name */
|
||||
/* First go thru xml children with same name in rest of list */
|
||||
for (; j0<xml_child_nr(xt); j0++){
|
||||
xc = xml_child_i(xt, j0);
|
||||
if (xml_type(xc) != CX_ELMNT)
|
||||
|
|
@ -1528,7 +1392,6 @@ api_path2xpath_cvv(yang_spec *yspec,
|
|||
yang_stmt *y = NULL;
|
||||
char *val;
|
||||
char *v;
|
||||
yang_stmt *ykey;
|
||||
cg_var *cvi;
|
||||
|
||||
for (i=offset; i<cvec_len(cvv); i++){
|
||||
|
|
@ -1559,17 +1422,7 @@ api_path2xpath_cvv(yang_spec *yspec,
|
|||
*v = '\0';
|
||||
v++;
|
||||
}
|
||||
/* Find keys */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
cprintf(xpath, "/%s", name);
|
||||
|
|
@ -1647,7 +1500,6 @@ api_path2xml_vec(char **vec,
|
|||
char *name;
|
||||
char *restval = NULL;
|
||||
char *restval_enc;
|
||||
yang_stmt *ykey;
|
||||
cxobj *xn = NULL; /* new */
|
||||
cxobj *xb; /* body */
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
|
|
@ -1709,15 +1561,7 @@ api_path2xml_vec(char **vec,
|
|||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
/* Get the yang list key */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
if (valvec){
|
||||
free(valvec);
|
||||
valvec = NULL;
|
||||
|
|
@ -1754,10 +1598,6 @@ api_path2xml_vec(char **vec,
|
|||
if (xml_value_set(xb, val2) <0)
|
||||
goto done;
|
||||
}
|
||||
if (cvk){
|
||||
cvec_free(cvk);
|
||||
cvk = NULL;
|
||||
}
|
||||
break;
|
||||
default: /* eg Y_CONTAINER, Y_LEAF */
|
||||
if ((x = xml_new(name, x0, y)) == NULL)
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
|
||||
void
|
||||
|
|
|
|||
476
lib/src/clixon_xml_sort.c
Normal file
476
lib/src/clixon_xml_sort.c
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* XML search functions when used with YANG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <syslog.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clixon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_string.h"
|
||||
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
|
||||
/* Sort and binary search of XML children
|
||||
* Experimental
|
||||
*/
|
||||
int xml_child_sort = 1;
|
||||
|
||||
|
||||
/*! Given an XML object and a child name, return yang stmt of child
|
||||
* If no xml parent, find root yang stmt matching name
|
||||
* @param[in] name Name of child
|
||||
* @param[in] xp XML parent, can be NULL.
|
||||
* @param[in] yspec Yang specification (top level)
|
||||
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
|
||||
*/
|
||||
int
|
||||
xml_child_spec(char *name,
|
||||
cxobj *xp,
|
||||
yang_spec *yspec,
|
||||
yang_stmt **yresult)
|
||||
{
|
||||
yang_stmt *y; /* result yang node */
|
||||
yang_stmt *yparent; /* parent yang */
|
||||
|
||||
if (xp && (yparent = xml_spec(xp)) != NULL)
|
||||
y = yang_find_datanode((yang_node*)yparent, name);
|
||||
else if (yspec)
|
||||
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
|
||||
else
|
||||
y = NULL;
|
||||
*yresult = y;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] yangi Yang order
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
* @param[out] userorder If set, this yang order is user ordered, linear search
|
||||
* @retval 0 If equal (or userorder set)
|
||||
* @retval <0 if arg1 is less than arg2
|
||||
* @retval >0 if arg1 is greater than arg2
|
||||
*/
|
||||
static int
|
||||
xml_cmp1(cxobj *x,
|
||||
yang_stmt *y,
|
||||
char *name,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval,
|
||||
int *userorder)
|
||||
{
|
||||
char *b;
|
||||
int i;
|
||||
char *keyname;
|
||||
char *key;
|
||||
|
||||
/* Check if same yang spec (order in yang stmt list) */
|
||||
switch (keyword){
|
||||
case Y_CONTAINER: /* Match with name */
|
||||
case Y_LEAF: /* Match with name */
|
||||
return strcmp(name, xml_name(x));
|
||||
break;
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL)
|
||||
*userorder=1;
|
||||
b=xml_body(x);
|
||||
return strcmp(keyval[0], b);
|
||||
break;
|
||||
case Y_LIST: /* Match with array of key values */
|
||||
if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL)
|
||||
*userorder=1;
|
||||
for (i=0; i<keynr; i++){
|
||||
keyname = keyvec[i];
|
||||
key = keyval[i];
|
||||
if ((b = xml_find_body(x, keyname)) == NULL)
|
||||
break; /* error case */
|
||||
return strcmp(key, b);
|
||||
}
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0; /* should not reach here */
|
||||
}
|
||||
|
||||
static cxobj *
|
||||
xml_search_userorder(cxobj *x0,
|
||||
yang_stmt *y,
|
||||
char *name,
|
||||
int yangi,
|
||||
int mid,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval)
|
||||
{
|
||||
int i;
|
||||
cxobj *xc;
|
||||
|
||||
for (i=mid+1; i<xml_child_nr(x0); i++){ /* First increment */
|
||||
xc = xml_child_i(x0, i);
|
||||
y = xml_spec(xc);
|
||||
if (yangi!=yang_order(y))
|
||||
break;
|
||||
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, NULL) == 0)
|
||||
return xc;
|
||||
}
|
||||
for (i=mid-1; i>=0; i--){ /* Then decrement */
|
||||
xc = xml_child_i(x0, i);
|
||||
y = xml_spec(xc);
|
||||
if (yangi!=yang_order(y))
|
||||
break;
|
||||
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, NULL) == 0)
|
||||
return xc;
|
||||
}
|
||||
return NULL; /* Not found */
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] yangi Yang order
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
* @param[in] low Lower bound of childvec search interval
|
||||
* @param[in] upper Lower bound of childvec search interval
|
||||
*/
|
||||
static cxobj *
|
||||
xml_search1(cxobj *x0,
|
||||
char *name,
|
||||
int yangi,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval,
|
||||
int low,
|
||||
int upper)
|
||||
{
|
||||
int mid;
|
||||
int cmp;
|
||||
cxobj *xc;
|
||||
yang_stmt *y;
|
||||
int userorder= 0;
|
||||
|
||||
if (upper < low)
|
||||
return NULL; /* not found */
|
||||
mid = (low + upper) / 2;
|
||||
if (mid >= xml_child_nr(x0)) /* beyond range */
|
||||
return NULL;
|
||||
xc = xml_child_i(x0, mid);
|
||||
assert(y = xml_spec(xc));
|
||||
if ((cmp = yangi-yang_order(y)) == 0){
|
||||
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, &userorder);
|
||||
if (userorder && cmp) /* Look inside this yangi order */
|
||||
return xml_search_userorder(x0, y, name, yangi, mid, keyword, keynr, keyvec, keyval);
|
||||
}
|
||||
if (cmp == 0)
|
||||
return xc;
|
||||
else if (cmp < 0)
|
||||
return xml_search1(x0, name, yangi, keyword,
|
||||
keynr, keyvec, keyval, low, mid-1);
|
||||
else
|
||||
return xml_search1(x0, name, yangi, keyword,
|
||||
keynr, keyvec, keyval, mid+1, upper);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] yangi yang child order
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
*/
|
||||
cxobj *
|
||||
xml_search(cxobj *x0,
|
||||
char *name,
|
||||
int yangi,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval)
|
||||
{
|
||||
return xml_search1(x0, name, yangi, keyword, keynr, keyvec, keyval,
|
||||
0, xml_child_nr(x0));
|
||||
}
|
||||
|
||||
/*! Find matching xml child given name and optional key values
|
||||
* container: x0, y->keyword, name
|
||||
* list: x0, y->keyword, y->key, name
|
||||
*
|
||||
* The function needs a vector of key values (or single for LEAF_LIST).
|
||||
* What format?
|
||||
* 1) argc/argv:: "val1","val2" <<==
|
||||
* 2) cv-list?
|
||||
* 3) va-list?
|
||||
*
|
||||
* yc - LIST (interface) -
|
||||
* ^
|
||||
* |
|
||||
* x0-->x0c-->(name=interface)+->x(name=name)->xb(value="eth0") <==this is
|
||||
* |
|
||||
* v
|
||||
* x1c->name (interface)
|
||||
* x1c->x(name=name)->xb(value="eth0")
|
||||
*
|
||||
* CONTAINER:name
|
||||
* LEAF: name
|
||||
* LEAFLIST: name/body... #b0
|
||||
* LIST: name/key0/key1... #b2vec+b0 -> x0c
|
||||
|
||||
* <interface><name>eth0</name></interface>
|
||||
* <interface><name>eth1</name></interface>
|
||||
* <interface><name>eth2</name></interface>
|
||||
* @param[in] x0 XML node. Find child of this node.
|
||||
* @param[in] keyword Yang keyword. Relevant: container, list, leaf, leaf_list
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
* @param[out] xp Return value on success, pointer to XML child node
|
||||
* @note If keyword is:
|
||||
* - list, keyvec and keyval should be an array with keynr length
|
||||
* - leaf_list, keyval should be 1 and keyval should contain one element
|
||||
* - otherwise, keyval should be 0 and keyval and keyvec should be both NULL.
|
||||
*/
|
||||
cxobj *
|
||||
xml_match(cxobj *x0,
|
||||
char *name,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval)
|
||||
{
|
||||
char *key;
|
||||
char *keyname;
|
||||
char *b0;
|
||||
cxobj *x = NULL;
|
||||
int equal;
|
||||
int i;
|
||||
|
||||
x = NULL;
|
||||
switch (keyword){
|
||||
case Y_CONTAINER: /* Match with name */
|
||||
case Y_LEAF: /* Match with name */
|
||||
if (keynr != 0){
|
||||
clicon_err(OE_XML, EINVAL, "Expected no key argument to CONTAINER or LEAF");
|
||||
goto ok;
|
||||
}
|
||||
x = xml_find(x0, name);
|
||||
break;
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if (keynr != 1)
|
||||
goto ok;
|
||||
x = xml_find_body_obj(x0, name, keyval[0]);
|
||||
break;
|
||||
case Y_LIST: /* Match with array of key values */
|
||||
i = 0;
|
||||
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL){
|
||||
equal = 0;
|
||||
if (strcmp(xml_name(x), name))
|
||||
continue;
|
||||
/* Must be inner loop */
|
||||
for (i=0; i<keynr; i++){
|
||||
keyname = keyvec[i];
|
||||
key = keyval[i];
|
||||
equal = 0;
|
||||
if ((b0 = xml_find_body(x, keyname)) == NULL)
|
||||
break; /* error case */
|
||||
if (strcmp(b0, key))
|
||||
break; /* stop as soon as inequal key found */
|
||||
equal=1; /* reaches here for all keynames, x is found. */
|
||||
}
|
||||
if (equal) /* x matches, oyherwise look for other */
|
||||
break;
|
||||
} /* while x */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ok:
|
||||
return x;
|
||||
}
|
||||
/*! Given child tree x1c, find matching child in base tree x0
|
||||
* param[in] x0 Base tree node
|
||||
* param[in] x1c Modification tree child
|
||||
* param[in] yc Yang spec of tree child
|
||||
* 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.
|
||||
The outer loop is large for large lists
|
||||
The inner loop is small
|
||||
Major time in xml_find_body()
|
||||
Can one do a binary search in the x0 list?
|
||||
*/
|
||||
int
|
||||
match_base_child(cxobj *x0,
|
||||
cxobj *x1c,
|
||||
cxobj **x0cp,
|
||||
yang_stmt *yc)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
char *b1;
|
||||
char *keyname;
|
||||
char **keyval = NULL;
|
||||
char **keyvec = NULL;
|
||||
char keynr = 0;
|
||||
int i;
|
||||
|
||||
*x0cp = NULL; /* return value */
|
||||
switch (yc->ys_keyword){
|
||||
case Y_CONTAINER: /* Equal regardless */
|
||||
case Y_LEAF: /* Equal regardless */
|
||||
break;
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
keynr = 1;
|
||||
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
if ((keyval[0] = xml_body(x1c)) == NULL)
|
||||
goto ok;
|
||||
break;
|
||||
case Y_LIST: /* Match with key values */
|
||||
cvk = yc->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
/* Count number of key indexes */
|
||||
cvi = NULL; keynr = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL)
|
||||
keynr++;
|
||||
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
if ((keyvec = calloc(keynr+1, sizeof(char*))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
cvi = NULL; i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
keyvec[i] = keyname;
|
||||
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
|
||||
goto ok; /* not found */
|
||||
keyval[i++] = b1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Get match */
|
||||
{
|
||||
yang_node *y0;
|
||||
int yorder;
|
||||
|
||||
if ((y0 = yc->ys_parent) == NULL)
|
||||
goto done;
|
||||
/* XXX: No we cant do this. on uppermost layer it can look like this:
|
||||
* config
|
||||
* ximport-----ymod = ietf
|
||||
* interfaces--ymod = example
|
||||
* Which means yang order can be different for different children in the
|
||||
* same childvec.
|
||||
*/
|
||||
if (xml_child_sort==0)
|
||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||
else{
|
||||
#if 1
|
||||
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
|
||||
yorder = yang_order(yc);
|
||||
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
||||
}
|
||||
else{
|
||||
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
|
||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||
}
|
||||
#else
|
||||
cxobj *xx;
|
||||
|
||||
|
||||
|
||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||
if (xml_child_nr(x0) && xml_spec(xml_child_i(x0,0))!=NULL){
|
||||
xx = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
||||
if (xx!=*x0cp){
|
||||
clicon_log(LOG_WARNING, "%s mismatch", __FUNCTION__);
|
||||
fprintf(stderr, "mismatch\n");
|
||||
xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (keyval)
|
||||
free(keyval);
|
||||
if (keyvec)
|
||||
free(keyvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ yn_each(yang_node *yn,
|
|||
*
|
||||
* @param[in] yn Yang node, current context node.
|
||||
* @param[in] keyword if 0 match any keyword
|
||||
* @param[in] argument String compare w wrgument. if NULL, match any.
|
||||
* @param[in] argument String compare w argument. if NULL, match any.
|
||||
* This however means that if you actually want to match only a yang-stmt with
|
||||
* argument==NULL you cannot, but I have not seen any such examples.
|
||||
* @see yang_find_datanode
|
||||
|
|
@ -384,8 +384,8 @@ yang_find(yang_node *yn,
|
|||
char *argument)
|
||||
{
|
||||
yang_stmt *ys = NULL;
|
||||
int i;
|
||||
int match = 0;
|
||||
int i;
|
||||
int match = 0;
|
||||
|
||||
for (i=0; i<yn->yn_len; i++){
|
||||
ys = yn->yn_stmt[i];
|
||||
|
|
@ -558,6 +558,54 @@ yang_find_myprefix(yang_stmt *ys)
|
|||
return prefix;
|
||||
}
|
||||
|
||||
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
||||
* @retval 0 not found
|
||||
* @retval 1 found
|
||||
*/
|
||||
static int
|
||||
order1(yang_node *yp,
|
||||
yang_stmt *y,
|
||||
int *index)
|
||||
{
|
||||
yang_stmt *ys;
|
||||
int i;
|
||||
|
||||
for (i=0; i<yp->yn_len; i++){
|
||||
ys = yp->yn_stmt[i];
|
||||
if (!yang_datanode(ys))
|
||||
continue;
|
||||
if (ys==y)
|
||||
return 1;
|
||||
(*index)++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Return order of yang statement y in parents child vector
|
||||
* @retval i Order of child with specified argument
|
||||
* @retval -1 Not found
|
||||
*/
|
||||
int
|
||||
yang_order(yang_stmt *y)
|
||||
{
|
||||
yang_node *yp;
|
||||
yang_node *ypp;
|
||||
yang_node *yn;
|
||||
int i;
|
||||
int j=0;
|
||||
|
||||
yp = y->ys_parent;
|
||||
if (yp->yn_keyword == Y_MODULE ||yp->yn_keyword == Y_SUBMODULE){
|
||||
ypp = yp->yn_parent;
|
||||
for (i=0; i<ypp->yn_len; i++){
|
||||
yn = (yang_node*)ypp->yn_stmt[i];
|
||||
if (order1(yn, y, &j) == 1)
|
||||
return j;
|
||||
}
|
||||
}
|
||||
order1(yp, y, &j);
|
||||
return j;
|
||||
}
|
||||
|
||||
/*! Reset flag in complete tree, arg contains flag */
|
||||
static int
|
||||
|
|
@ -880,6 +928,20 @@ ys_populate_leaf(yang_stmt *ys,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
ys_populate_list(yang_stmt *ys,
|
||||
void *arg)
|
||||
{
|
||||
yang_stmt *ykey;
|
||||
|
||||
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL)
|
||||
return 0;
|
||||
cvec_free(ys->ys_cvec);
|
||||
if ((ys->ys_cvec = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Populate range and length statements
|
||||
*
|
||||
* Create cvec variables "range_min" and "range_max". Assume parent is type.
|
||||
|
|
@ -1061,6 +1123,10 @@ ys_populate(yang_stmt *ys,
|
|||
if (ys_populate_leaf(ys, arg) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (ys_populate_list(ys, arg) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_RANGE:
|
||||
case Y_LENGTH:
|
||||
if (ys_populate_range(ys, arg) < 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue