experimental xml hash for better performance

This commit is contained in:
Olof hagsand 2017-09-18 20:53:49 +02:00
parent 687641e944
commit 7a7bfc48a4
18 changed files with 612 additions and 481 deletions

View file

@ -1,7 +1,10 @@
# Clixon CHANGELOG # Clixon CHANGELOG
## 3.3.3 Upcoming ## 3.3.3 Upcoming
* netconf client was limited to 8K byte messages. Now limit is 2^32, (but needs scaling improvement) * netconf client was limited to 8K byte messages. Now limit is 2^32,
* Added event_poll function.
* Added experimental xml hash for better performance of large lists.
To enable, set XML_CHILD_HASH in clixon_custom.h
## 3.3.2 Aug 27 2017 ## 3.3.2 Aug 27 2017

View file

@ -121,8 +121,7 @@ process_incoming_packet(clicon_handle h,
if (xpath_first(xreq, "//hello") != NULL) if (xpath_first(xreq, "//hello") != NULL)
; ;
else{ else{
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\ clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropped");
ed");
goto done; goto done;
} }
if (!isrpc){ /* hello */ if (!isrpc){ /* hello */
@ -204,7 +203,6 @@ netconf_input_cb(int s,
if (len == 0){ /* EOF */ if (len == 0){ /* EOF */
cc_closed++; cc_closed++;
close(s); close(s);
clicon_log(LOG_ERR, "read close", __FUNCTION__);
retval = 0; retval = 0;
goto done; goto done;
} }

View file

@ -363,6 +363,11 @@ text_get(xmldb_handle xh,
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done; goto done;
#if (XML_CHILD_HASH==1)
/* Add hash */
if (xml_apply0(xt, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
if (debug>1) if (debug>1)
clicon_xml2file(stderr, xt, 0, 1); clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt; *xtop = xt;
@ -380,90 +385,6 @@ text_get(xmldb_handle xh,
return retval; return retval;
} }
/*! Given a modification tree, check existing matching child in the base tree
* 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)
*/
static int
match_base_child(cxobj *x0,
cxobj *x1c,
yang_stmt *yc,
cxobj **x0cp)
{
int retval = -1;
cxobj *x0c = NULL;
char *keyname;
cvec *cvk = NULL;
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *cname;
int ok;
char *x1bstr; /* body string */
cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
x1bstr = xml_body(x1c);
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(cname, xml_name(x0c)) == 0 &&
strcmp(xml_body(x0c), x1bstr)==0)
break;
}
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;
x0c = NULL;
/* 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?
*/
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x0c), cname))
continue;
cvi = NULL;
ok = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
ok = 1; /* if we come here */
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break;
ok = 2; /* and reaches here for all keynames, x0c is found. */
}
if (ok == 2)
break;
}
break;
default: /* Just match with name */
x0c = xml_find(x0, cname);
break;
}
*x0cp = x0c;
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Modify a base tree x0 with x1 with yang spec y according to operation op /*! Modify a base tree x0 with x1 with yang spec y according to operation op
* @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL * @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
@ -538,6 +459,10 @@ text_modify(cxobj *x0,
if (xml_value_set(x0b, x1bstr) < 0) if (xml_value_set(x0b, x1bstr) < 0)
goto done; goto done;
} }
#if (XML_CHILD_HASH==1)
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
break; break;
case OP_DELETE: case OP_DELETE:
if (x0==NULL){ if (x0==NULL){
@ -545,8 +470,9 @@ text_modify(cxobj *x0,
goto done; goto done;
} }
case OP_REMOVE: /* fall thru */ case OP_REMOVE: /* fall thru */
if (x0) if (x0){
xml_purge(x0); xml_purge(x0);
}
break; break;
default: default:
break; break;
@ -571,8 +497,9 @@ text_modify(cxobj *x0,
if (y0->yn_keyword == Y_ANYXML){ if (y0->yn_keyword == Y_ANYXML){
if (op == OP_NONE) if (op == OP_NONE)
break; break;
if (x0) if (x0){
xml_purge(x0); xml_purge(x0);
}
if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL) if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL)
goto done; goto done;
if (xml_copy(x1, x0) < 0) if (xml_copy(x1, x0) < 0)
@ -585,7 +512,10 @@ text_modify(cxobj *x0,
if (op==OP_NONE) if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */ xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
} }
#if 0 /* Find x1c in x0 */ #if (XML_CHILD_HASH==1)
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
/* First pass: mark existing children in base */ /* First pass: mark existing children in base */
/* Loop through children of the modification tree */ /* Loop through children of the modification tree */
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){ if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
@ -603,7 +533,7 @@ text_modify(cxobj *x0,
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
x0c = NULL; x0c = NULL;
if (match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
x0vec[i++] = x0c; x0vec[i++] = x0c;
} }
@ -611,29 +541,11 @@ text_modify(cxobj *x0,
x1c = NULL; x1c = NULL;
i = 0; i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
yc = yang_find_datanode(y0, x1cname); yc = yang_find_datanode(y0, x1cname);
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op) < 0) if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op) < 0)
goto done; goto done;
} }
#else
i = 0; i = i; /* XXX */
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
clicon_err(OE_YANG, errno, "No yang node found: %s", x1cname);
goto done;
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0)
goto done;
}
#endif
break; break;
case OP_DELETE: case OP_DELETE:
if (x0==NULL){ if (x0==NULL){
@ -712,7 +624,7 @@ text_modify_top(cxobj *x0,
goto done; goto done;
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0) if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0)
goto done; goto done;
@ -831,6 +743,12 @@ text_put(xmldb_handle xh,
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
#if (XML_CHILD_HASH==1)
/* Add hash */
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
/* /*
* Modify base tree x with modification x1 * Modify base tree x with modification x1
*/ */

View file

@ -45,5 +45,11 @@ int strverscmp (__const char *__s1, __const char *__s2);
/* Replace the current clixon.conf.cpp.cpp options with XML file with Yang /* Replace the current clixon.conf.cpp.cpp options with XML file with Yang
* Why not use the config mechanisms that CLixon uses for its own config-file? * Why not use the config mechanisms that CLixon uses for its own config-file?
* Experimental
*/ */
#define CONFIGFILE_XML 0 #define CONFIGFILE_XML 0
/* Hash for XML trees list entries
* Experimental
*/
#define XML_CHILD_HASH 0

View file

@ -118,6 +118,7 @@ char *xml_body(cxobj *xn);
cxobj *xml_body_get(cxobj *xn); cxobj *xml_body_get(cxobj *xn);
char *xml_find_value(cxobj *xn_parent, char *name); char *xml_find_value(cxobj *xn_parent, char *name);
char *xml_find_body(cxobj *xn, char *name); char *xml_find_body(cxobj *xn, char *name);
cxobj *xml_find_body_obj(cxobj *xt, char *name, char *val);
int xml_free(cxobj *xn); int xml_free(cxobj *xn);
@ -146,5 +147,12 @@ int xml_body_int32(cxobj *xb, int32_t *val);
int xml_body_uint32(cxobj *xb, uint32_t *val); int xml_body_uint32(cxobj *xb, uint32_t *val);
int xml_operation(char *opstr, enum operation_type *op); int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op); char *xml_operation2str(enum operation_type op);
#if (XML_CHILD_HASH==1)
clicon_hash_t *xml_hash(cxobj *x);
int xml_hash_init(cxobj *x);
int xml_hash_rm(cxobj *x);
int xml_hash_key(cxobj *x, yang_stmt *y, cbuf *key);
int xml_hash_op(cxobj *x, void *arg);
#endif
#endif /* _CLIXON_XML_H */ #endif /* _CLIXON_XML_H */

View file

@ -65,6 +65,7 @@ int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath); int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
int schemanode, cxobj **xpathp, yang_node **ypathp); int schemanode, cxobj **xpathp, yang_node **ypathp);
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec); int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
int yang_enum_int_value(cxobj *node, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -94,6 +94,7 @@
#include "clixon_hash.h" #include "clixon_hash.h"
#define HASH_SIZE 1031 /* Number of hash buckets. Should be a prime */ #define HASH_SIZE 1031 /* Number of hash buckets. Should be a prime */
#define align4(s) (((s)/4)*4 + 4)
/*! A very simplistic algorithm to calculate a hash bucket index /*! A very simplistic algorithm to calculate a hash bucket index
*/ */
@ -235,7 +236,7 @@ hash_add(clicon_hash_t *hash,
} }
/* Make copy of lvalue */ /* Make copy of lvalue */
newval = malloc (vlen+3); /* XXX: qdbm needs aligned mallocs? */ newval = malloc(align4(vlen+3)); /* XXX: qdbm needs aligned mallocs? */
if (newval == NULL){ if (newval == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno)); clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch; goto catch;

View file

@ -47,6 +47,10 @@
#include <cligen/cligen.h> #include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_json_parse.h" #include "clixon_json_parse.h"

View file

@ -124,6 +124,10 @@ object.
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_json_parse.h" #include "clixon_json_parse.h"

View file

@ -66,6 +66,9 @@
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_sig.h" #include "clixon_sig.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xsl.h" #include "clixon_xsl.h"
@ -361,7 +364,7 @@ clicon_msg_rcv(int s,
goto done; goto done;
} }
memcpy(*msg, &hdr, hlen); memcpy(*msg, &hdr, hlen);
if ((len2 = read(s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){ if ((len2 = atomicio(read, s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__); clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
goto done; goto done;
} }

View file

@ -34,6 +34,10 @@
* XML support functions. * XML support functions.
*/ */
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -51,7 +55,11 @@
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_string.h" #include "clixon_string.h"
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"
@ -81,6 +89,9 @@ struct xml{
void *x_spec; /* Pointer to specification, eg yang, by void *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */ reference, dont free */
cg_var *x_cv; /* If body this contains the typed value */ cg_var *x_cv; /* If body this contains the typed value */
#if (XML_CHILD_HASH==1)
clicon_hash_t *x_hash; /* Hash of children */
#endif
}; };
/* Mapping between xml type <--> string */ /* Mapping between xml type <--> string */
@ -647,6 +658,9 @@ xml_purge(cxobj *xc)
int i; int i;
cxobj *xp; cxobj *xp;
#if (XML_CHILD_HASH==1)
xml_hash_op(xc, 0);
#endif
if ((xp = xml_parent(xc)) != NULL){ if ((xp = xml_parent(xc)) != NULL){
/* Find child order i in parent*/ /* Find child order i in parent*/
for (i=0; i<xml_child_nr(xp); i++) for (i=0; i<xml_child_nr(xp); i++)
@ -771,6 +785,9 @@ xml_rootchild(cxobj *xp,
* @retval NULL if no such node or no body in found node * @retval NULL if no such node or no body in found node
* Note, make a copy of the return value to use it properly * Note, make a copy of the return value to use it properly
* @see xml_find_body * @see xml_find_body
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb.x_value
*/ */
char * char *
xml_body(cxobj *xn) xml_body(cxobj *xn)
@ -782,12 +799,18 @@ xml_body(cxobj *xn)
return NULL; return NULL;
} }
/*! Get (first) body of xml node, note could be many
* @param[in] xt xml tree node
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb
*/
cxobj * cxobj *
xml_body_get(cxobj *xn) xml_body_get(cxobj *xt)
{ {
cxobj *xb = NULL; cxobj *xb = NULL;
while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL) while ((xb = xml_child_each(xt, xb, CX_BODY)) != NULL)
return xb; return xb;
return NULL; return NULL;
} }
@ -795,21 +818,26 @@ xml_body_get(cxobj *xn)
/*! Find and return the value of a sub xml node /*! Find and return the value of a sub xml node
* *
* The value can be of an attribute or body. * The value can be of an attribute or body.
* @param[in] xn xml tree node * @param[in] xt xml tree node
* @param[in] name name of xml tree nod (eg attr name or "body") * @param[in] name name of xml tree nod (eg attr name or "body")
* @retval The returned value as a pointer to the name string * @retval val Pointer to the name string
* @retval NULL if no such node or no value in found node * @retval NULL No such node or no value in node
* *
* Note, make a copy of the return value to use it properly * Note, make a copy of the return value to use it properly
* See also xml_find_body * See also xml_find_body
* Explaining picture:
* xt --> x
* x_name=name
* return x_value
*/ */
char * char *
xml_find_value(cxobj *x_up, xml_find_value(cxobj *xt,
char *name) char *name)
{ {
cxobj *x; cxobj *x = NULL;
if ((x = xml_find(x_up, name)) != NULL) while ((x = xml_child_each(xt, x, -1)) != NULL)
if (strcmp(name, xml_name(x)) == 0)
return xml_value(x); return xml_value(x);
return NULL; return NULL;
} }
@ -821,18 +849,56 @@ xml_find_value(cxobj *x_up,
* @retval NULL if no such node or no body in found node * @retval NULL if no such node or no body in found node
* @note, make a copy of the return value to use it properly * @note, make a copy of the return value to use it properly
* @see xml_find_value * @see xml_find_value
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name return x_value
*/ */
char * char *
xml_find_body(cxobj *xn, xml_find_body(cxobj *xt,
char *name) char *name)
{ {
cxobj *x; cxobj *x=NULL;
if ((x = xml_find(xn, name)) != NULL) while ((x = xml_child_each(xt, x, -1)) != NULL)
if (strcmp(name, xml_name(x)) == 0)
return xml_body(x); return xml_body(x);
return NULL; return NULL;
} }
/*! Find xml object with matching name and value.
*
* This can be useful if x is a leaf-list with many subs with same name,
* but you need to pick the object with a specific value
* @param[in] xt XML tree
* @param[in] name Name of child (there can be many with same name)
* @param[in] val Value. Must be equal to body of child.
* @retval x Child with matching name and body
*
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name x_value=val
* return x
*/
cxobj *
xml_find_body_obj(cxobj *xt,
char *name,
char *val)
{
cxobj *x = NULL;
char *bstr;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(x)))
continue;
if ((bstr = xml_body(x)) == NULL)
continue;
if (strcmp(bstr, val) == 0)
break; /* x is returned */
}
return x;
}
/*! Free an xl sub-tree recursively, but do not remove it from parent /*! Free an xl sub-tree recursively, but do not remove it from parent
* @param[in] x the xml tree to be freed. * @param[in] x the xml tree to be freed.
* @see xml_purge where x is also removed from parent * @see xml_purge where x is also removed from parent
@ -856,6 +922,10 @@ xml_free(cxobj *x)
xml_free(xc); xml_free(xc);
x->x_childvec[i] = NULL; x->x_childvec[i] = NULL;
} }
#if (XML_CHILD_HASH==1)
if (x->x_hash)
hash_free(x->x_hash);
#endif
if (x->x_childvec) if (x->x_childvec)
free(x->x_childvec); free(x->x_childvec);
free(x); free(x);
@ -995,7 +1065,7 @@ clicon_xml2cbuf(cbuf *cb,
*/ */
int int
xml_parse(char *str, xml_parse(char *str,
cxobj *x_up) cxobj *xt)
{ {
int retval = -1; int retval = -1;
struct xml_parse_yacc_arg ya = {0,}; struct xml_parse_yacc_arg ya = {0,};
@ -1004,7 +1074,7 @@ xml_parse(char *str,
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
return -1; return -1;
} }
ya.ya_xparent = x_up; ya.ya_xparent = xt;
ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */ ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */
if (clixon_xml_parsel_init(&ya) < 0) if (clixon_xml_parsel_init(&ya) < 0)
goto done; goto done;
@ -1641,6 +1711,160 @@ xml_operation2str(enum operation_type op)
} }
} }
#if (XML_CHILD_HASH==1)
/*! 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;
}
int
xml_hash_init(cxobj *x)
{
if ((x->x_hash = hash_init()) < 0)
return -1;
return 0;
}
int
xml_hash_rm(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 -1: rm only hash 0: rm entry, 1: add
* Typically called for a whole tree.
*/
int
xml_hash_op(cxobj *x,
void *arg)
{
int retval = -1;
cxobj *xp;
clicon_hash_t *ph;
yang_stmt *y;
cbuf *key = NULL; /* cligen buffer hash key */
int op = (int)arg;
if (xml_hash(x)==NULL){
if (op==1)
xml_hash_init(x);
}
else if (op==-1|| op==0)
xml_hash_rm(x);
if (op==-1)
goto ok;
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)){
// fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x);
if (op == 1){
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL)
goto done;
}
else
if (hash_del(ph, cbuf_get(key)) < 0)
goto done;
}
ok:
retval = 0;
done:
if (key)
cbuf_free(key);
return retval;
}
#endif
/* /*
* Turn this on to get a xml parse and pretty print test program * Turn this on to get a xml parse and pretty print test program
* Usage: xpath * Usage: xpath

View file

@ -587,271 +587,262 @@ cvec2xml_1(cvec *cvv,
return retval; return retval;
} }
/*! Return 1 if value is a body of one of the named children of xt */ /*! Given child tree x1c, find matching child in base tree x0
static int * param[in] x0 Base tree node
xml_is_body(cxobj *xt, * param[in] x1c Modification tree child
char *name, * param[in] yc Yang spec of tree child
char *val) * param[out] x0cp Matching base tree child (if any)
{ * @note XXX: room for optimization? on 1K calls we have 1M body calls and
cxobj *x; 500K xml_child_each/cvec_each calls.
char *bx; The outer loop is large for large lists
The inner loop is small
x = NULL; Major time in xml_find_body()
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { Can one do a binary search in the x0 list?
if (strcmp(name, xml_name(x)))
continue;
if ((bx = xml_body(x)) == NULL)
continue;
if (strcmp(xml_body(x), val) == 0)
return 1;
}
return 0;
}
/*! Recursive help function to compute differences between two xml trees
* @see dbdiff_vector.
*/ */
static int int
xml_diff1(yang_stmt *ys, match_base_child(cxobj *x0,
cxobj *xt1, cxobj *x1c,
cxobj *xt2, cxobj **x0cp,
cxobj ***first, yang_stmt *yc)
size_t *firstlen,
cxobj ***second,
size_t *secondlen,
cxobj ***changed1,
cxobj ***changed2,
size_t *changedlen)
{ {
int retval = -1; int retval = -1;
cxobj *x1 = NULL; char *x1cname;
cxobj *x2 = NULL; cxobj *x0c = NULL; /* x0 child */
yang_stmt *y;
yang_stmt *ykey;
char *name;
cg_var *cvi;
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *keyname; char *keyname;
int equal; int equal;
char *body1; char **b1vec = NULL;
char *body2; int i;
#if (XML_CHILD_HASH==1)
cxobj **p;
cbuf *key = NULL; /* cligen buffer hash key */
size_t vlen;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec"); *x0cp = NULL; /* return value */
/* Check nodes present in xt1 and xt2 + nodes only in xt1 if (xml_hash(x0) == NULL)
* Loop over xt1 goto nohash;
*/ if ((key = cbuf_new()) == NULL){
x1 = NULL; clicon_err(OE_XML, errno, "cbuf_new");
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){ goto done1;
name = xml_name(x1);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name, 0);
else
y = yang_find_datanode((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
} }
switch (y->ys_keyword){ if (xml_hash_key(x1c, yc, key) < 0)
case Y_LIST: goto done;
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){ 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:
#endif /* XML_CHILD_HASH */
*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", clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument); __FUNCTION__, yc->ys_argument);
goto done; goto done;
} }
/* The value is a list of keys: <key>[ <key>]* */ /* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done; goto done;
/* Iterate over xt2 tree to (1) find a child that matches name cvi = NULL; i = 0;
(2) that have keys that matches */ while ((cvi = cvec_each(cvk, cvi)) != NULL)
equal = 0; i++;
x2 = NULL; if ((b1vec = calloc(i, sizeof(b1))) == NULL){
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){ clicon_err(OE_UNIX, errno, "calloc");
if (strcmp(xml_name(x2), name)) goto done;
continue; }
cvi = NULL; cvi = NULL; i = 0;
equal = 0;
/* (2) Match keys between x1 and x2 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); keyname = cv_string_get(cvi);
if ((body1 = xml_find_body(x1, keyname)) == NULL) if ((b1 = xml_find_body(x1c, keyname)) == NULL){
continue; /* may be error */ clicon_err(OE_UNIX, errno, "key %s not found", keyname);
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body1, body2)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if (equal){
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
}
else
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
case Y_LEAF:
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
body1 = xml_body(x1);
body2 = xml_body(x2);
if (body1 == NULL || body2 == NULL) /* empty type */
break;
if (strcmp(xml_body(x1), xml_body(x2))){
if (cxvec_append(x1, changed1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2, changed2, changedlen) < 0)
goto done; goto done;
} }
break; b1vec[i++] = b1;
case Y_LEAF_LIST: }
if ((body1 = xml_body(x1)) == NULL) /* 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; continue;
if (!xml_is_body(xt2, name, body1)) /* where body is */ /* Must be inner loop */
if (cxvec_append(x1, first, firstlen) < 0) cvi = NULL; i = 0;
goto done; 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; break;
default: default:
break; break;
} }
} /* while xt1 */ ok:
/* Check nodes present only in xt2 *x0cp = x0c;
* Loop over xt2
*/
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
name = xml_name(x2);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name, 0);
else
y = yang_find_datanode((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
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;
/* Iterate over xt1 tree to (1) find a child that matches name
(2) that have keys that matches */
equal = 0;
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x1), name))
continue;
cvi = NULL;
equal = 0;
/* (2) Match keys between x2 and x1 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body2, body1)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if (!equal)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF:
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF_LIST:
body2 = xml_body(x2);
if (!xml_is_body(xt1, name, body2)) /* where body is */
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
retval = 0; retval = 0;
done: done:
if (b1vec)
free(b1vec);
if (cvk) if (cvk)
cvec_free(cvk); cvec_free(cvk);
return retval; return retval;
} }
/*! Find next yang node, either start from yang_spec or some yang-node
* @param[in] y Node spec or sny yang-node
* @param[in] name Name of childnode to find
* @retval ys yang statement
* @retval NULL Error: no node found
*/
static yang_stmt *
yang_next(yang_node *y,
char *name)
{
yang_stmt *ys;
if (y->yn_keyword == Y_SPEC)
ys = yang_find_topnode((yang_spec*)y, name, 0);
else
ys = yang_find_datanode(y, name);
if (ys == NULL)
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
return ys;
}
/*! Recursive help function to compute differences between two xml trees
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[out] x1vec Pointervector to XML nodes existing in only first tree
* @param[out] x1veclen Length of first vector
* @param[out] x2vec Pointervector to XML nodes existing in only second tree
* @param[out] x2veclen Length of x2vec vector
* @param[out] changed_x1 Pointervector to XML nodes changed orig value
* @param[out] changed_x2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *x1,
cxobj *x2,
cxobj ***x1vec,
size_t *x1veclen,
cxobj ***x2vec,
size_t *x2veclen,
cxobj ***changed_x1,
cxobj ***changed_x2,
size_t *changedlen)
{
int retval = -1;
cxobj *x1c = NULL; /* x1 child */
cxobj *x2c = NULL; /* x2 child */
yang_stmt *yc;
char *b1;
char *b2;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in x1 and x2 + nodes only in x1
* Loop over x1
* XXX: room for improvement. Compare with match_base_child()
*/
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x1c))) == NULL)
goto done;
if (match_base_child(x2, x1c, &x2c, yc) < 0)
goto done;
if (x2c == NULL){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
}
else{
if (yc->ys_keyword == Y_LEAF){
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
break;
if ((b2 = xml_body(x2c)) == NULL) /* empty type */
break;
if (strcmp(b1, b2)){
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
goto done;
}
}
if (xml_diff1(yc, x1c, x2c,
x1vec, x1veclen,
x2vec, x2veclen,
changed_x1, changed_x2, changedlen)< 0)
goto done;
}
} /* while x1 */
/* Check nodes present only in x2
* Loop over x2
*/
x2c = NULL;
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x2c))) == NULL)
goto done;
if (match_base_child(x1, x2c, &x1c, yc) < 0)
goto done;
if (x1c == NULL)
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
goto done;
} /* while x1 */
retval = 0;
done:
return retval;
}
/*! Compute differences between two xml trees /*! Compute differences between two xml trees
* @param[in] yspec Yang specification * @param[in] yspec Yang specification
* @param[in] xt1 First XML tree * @param[in] x1 First XML tree
* @param[in] xt2 Second XML tree * @param[in] x2 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree * @param[out] first Pointervector to XML nodes existing in only first tree
* @param[out] firstlen Length of first vector * @param[out] firstlen Length of first vector
* @param[out] second Pointervector to XML nodes existing in only second tree * @param[out] second Pointervector to XML nodes existing in only second tree
* @param[out] secondlen Length of second vector * @param[out] secondlen Length of second vector
* @param[out] changed1 Pointervector to XML nodes changed value * @param[out] changed1 Pointervector to XML nodes changed orig value
* @param[out] changed2 Pointervector to XML nodes changed value * @param[out] changed2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector * @param[out] changedlen Length of changed vector
* All xml vectors should be freed after use. * All xml vectors should be freed after use.
* Bot xml trees should be freed with xml_free() * Bot xml trees should be freed with xml_free()
*/ */
int int
xml_diff(yang_spec *yspec, xml_diff(yang_spec *yspec,
cxobj *xt1, cxobj *x1,
cxobj *xt2, cxobj *x2,
cxobj ***first, cxobj ***first,
size_t *firstlen, size_t *firstlen,
cxobj ***second, cxobj ***second,
@ -865,19 +856,19 @@ xml_diff(yang_spec *yspec,
*firstlen = 0; *firstlen = 0;
*secondlen = 0; *secondlen = 0;
*changedlen = 0; *changedlen = 0;
if (xt1 == NULL && xt2 == NULL) if (x1 == NULL && x2 == NULL)
return 0; return 0;
if (xt2 == NULL){ if (x2 == NULL){
if (cxvec_append(xt1, first, firstlen) < 0) if (cxvec_append(x1, first, firstlen) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (xt1 == NULL){ if (x1 == NULL){
if (cxvec_append(xt1, second, secondlen) < 0) if (cxvec_append(x1, second, secondlen) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (xml_diff1((yang_stmt*)yspec, xt1, xt2, if (xml_diff1((yang_stmt*)yspec, x1, x2,
first, firstlen, first, firstlen,
second, secondlen, second, secondlen,
changed1, changed2, changedlen) < 0) changed1, changed2, changedlen) < 0)
@ -1833,82 +1824,6 @@ api_path2xml(char *api_path,
return retval; return retval;
} }
/*! Given a modification tree, check existing matching child in the base tree
* 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)
*/
static int
match_base_child(cxobj *x0,
cxobj *x1c,
yang_stmt *yc,
cxobj **x0cp)
{
int retval = -1;
cxobj *x0c = NULL;
char *keyname;
cvec *cvk = NULL;
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *cname;
int ok;
char *x1bstr; /* body string */
cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
x1bstr = xml_body(x1c);
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(cname, xml_name(x0c)) == 0 &&
strcmp(xml_body(x0c), x1bstr)==0)
break;
}
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;
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x0c), cname))
continue;
cvi = NULL;
ok = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
ok = 1; /* if we come here */
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break;
ok = 2; /* and reaches here for all keynames, x0c is found. */
}
if (ok == 2)
break;
}
break;
default: /* Just match with name */
x0c = xml_find(x0, cname);
break;
}
*x0cp = x0c;
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Merge a base tree x0 with x1 with yang spec y /*! Merge a base tree x0 with x1 with yang spec y
* @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x0 Base xml tree (can be NULL in add scenarios)
@ -1975,7 +1890,7 @@ xml_merge1(cxobj *x0,
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
x0c = NULL; x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0) if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done; goto done;
@ -2012,7 +1927,7 @@ xml_merge(cxobj *x0,
goto done; goto done;
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0) if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done; goto done;

View file

@ -48,6 +48,10 @@
#include <cligen/cligen.h> #include <cligen/cligen.h>
/* clicon */ /* clicon */
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"

View file

@ -93,6 +93,7 @@ in
#include <fnmatch.h> #include <fnmatch.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h> #include <assert.h>
#include <syslog.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -107,7 +108,6 @@ in
/* Constants */ /* Constants */
#define XPATH_VEC_START 128 #define XPATH_VEC_START 128
/* /*
* Types * Types
*/ */
@ -646,10 +646,8 @@ xpath_find(cxobj *xcur,
struct xpath_predicate *xp; struct xpath_predicate *xp;
if (xe == NULL){ if (xe == NULL){
/* append */
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags)) if (flags==0x0 || xml_flag(xv, flags))
cxvec_append(xv, vec2, vec2len); cxvec_append(xv, vec2, vec2len);
} }
@ -693,12 +691,13 @@ xpath_find(cxobj *xcur,
goto done; goto done;
} }
} }
else else{
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
x = NULL; x = NULL;
while ((x = xml_child_each(xv, x, -1)) != NULL) { while ((x = xml_child_each(xv, x, -1)) != NULL) {
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){ if (fnmatch(xe->xe_str, xml_name(x), 0) == 0)
{
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags)); clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
if (flags==0x0 || xml_flag(x, flags)) if (flags==0x0 || xml_flag(x, flags))
if (cxvec_append(x, &vec1, &vec1len) < 0) if (cxvec_append(x, &vec1, &vec1len) < 0)
@ -706,6 +705,7 @@ xpath_find(cxobj *xcur,
} }
} }
} }
}
free(vec0); free(vec0);
vec0 = vec1; vec0 = vec1;
vec0len = vec1len; vec0len = vec1len;
@ -718,7 +718,10 @@ xpath_find(cxobj *xcur,
default: default:
break; break;
} }
/* remove duplicates */ /* remove duplicates
* This is cycle-heavy and I dont know when it is needed?
*/
if (0)
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
for (j=i+1; j<vec0len; j++){ for (j=i+1; j<vec0len; j++){
if (vec0[i] == vec0[j]){ if (vec0[i] == vec0[j]){
@ -732,6 +735,7 @@ xpath_find(cxobj *xcur,
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0) if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done; goto done;
} }
if (xpath_find(xcur, xe->xe_next, descendants, if (xpath_find(xcur, xe->xe_next, descendants,
vec0, vec0len, flags, vec0, vec0len, flags,
vec2, vec2len) < 0) vec2, vec2len) < 0)
@ -910,8 +914,8 @@ xpath_first0(cxobj *xcur,
* ... * ...
* } * }
* @endcode * @endcode
* Note that the returned pointer points into the original tree so should not be freed * @note the returned pointer points into the original tree so should not be freed after use.
* after use. * @note return value does not see difference between error and not found
* @see also xpath_vec. * @see also xpath_vec.
*/ */
cxobj * cxobj *
@ -1024,8 +1028,8 @@ xpath_each(cxobj *xcur,
* } * }
* free(vec); * free(vec);
* @endcode * @endcode
* @Note that although the returned vector must be freed after use, the returned xml * @note Although the returned vector must be freed after use,
* trees need not be. * the returned xml trees should not.
* @see also xpath_first, xpath_each. * @see also xpath_first, xpath_each.
*/ */
int int

View file

@ -46,8 +46,6 @@ expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth/0/0" "^$"
new "cli show configuration" new "cli show configuration"
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$"
new "cli failed validate" new "cli failed validate"
expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable" expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"

View file

@ -51,7 +51,9 @@ fi
new "leafref base config" new "leafref base config"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces> expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces>
<interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address></ipv4> <ipv4><address><ip>192.0.2.2</ip></address></ipv4></interface><interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" <interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface>
<interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface>
</interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref get config" new "leafref get config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>' expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'

View file

@ -83,7 +83,7 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/><
new "netconf commit" new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config replace" new "netconf edit config replace XXX is merge?"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get replaced config" new "netconf get replaced config"

View file

@ -29,12 +29,13 @@ module ietf-ip{
leaf a { leaf a {
type string; type string;
} }
} leaf b {
container z {
leaf-list c {
type string; type string;
} }
} }
leaf-list c {
type string;
}
} }
} }
EOF EOF
@ -54,20 +55,57 @@ if [ $? -ne 0 ]; then
err err
fi fi
new "generate large config" new "generate large list config"
echo -n "<rpc><edit-config><target><candidate/></target><config><x>" > $fconfig echo -n "<rpc><edit-config><target><candidate/></target><config><x>" > $fconfig
for (( i=0; i<$number; i++ )); do for (( i=0; i<$number; i++ )); do
echo -n "<y><a>$i</a></y>" >> $fconfig echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
done done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
new "netconf edit large config" new "netconf edit large config"
expecteof_file "time $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" < $fconfig expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit large config again"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
rm $fconfig rm $fconfig
new "netconf commit large config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add small config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit small config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get large config" new "netconf get large config"
expecteof "time $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a></y><y><a>1</a>" expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>"
new "generate large leaf-list config"
echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x>" > $fconfig
for (( i=0; i<$number; i++ )); do
echo -n "<c>$i</c>" >> $fconfig
done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
new "netconf replace large list-leaf config"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
rm $fconfig
new "netconf commit large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><c>x</c></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><c>0</c><c>1</c>"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive