experimental xml hash for better performance
This commit is contained in:
parent
687641e944
commit
7a7bfc48a4
18 changed files with 612 additions and 481 deletions
|
|
@ -1,7 +1,10 @@
|
|||
# Clixon CHANGELOG
|
||||
|
||||
## 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
|
||||
|
||||
|
|
|
|||
|
|
@ -121,8 +121,7 @@ process_incoming_packet(clicon_handle h,
|
|||
if (xpath_first(xreq, "//hello") != NULL)
|
||||
;
|
||||
else{
|
||||
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
|
||||
ed");
|
||||
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropped");
|
||||
goto done;
|
||||
}
|
||||
if (!isrpc){ /* hello */
|
||||
|
|
@ -204,7 +203,6 @@ netconf_input_cb(int s,
|
|||
if (len == 0){ /* EOF */
|
||||
cc_closed++;
|
||||
close(s);
|
||||
clicon_log(LOG_ERR, "read close", __FUNCTION__);
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,6 +363,11 @@ text_get(xmldb_handle xh,
|
|||
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||
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)
|
||||
clicon_xml2file(stderr, xt, 0, 1);
|
||||
*xtop = xt;
|
||||
|
|
@ -380,90 +385,6 @@ text_get(xmldb_handle xh,
|
|||
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
|
||||
* @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
|
||||
|
|
@ -538,6 +459,10 @@ text_modify(cxobj *x0,
|
|||
if (xml_value_set(x0b, x1bstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
#if (XML_CHILD_HASH==1)
|
||||
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
break;
|
||||
case OP_DELETE:
|
||||
if (x0==NULL){
|
||||
|
|
@ -545,8 +470,9 @@ text_modify(cxobj *x0,
|
|||
goto done;
|
||||
}
|
||||
case OP_REMOVE: /* fall thru */
|
||||
if (x0)
|
||||
if (x0){
|
||||
xml_purge(x0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -571,8 +497,9 @@ text_modify(cxobj *x0,
|
|||
if (y0->yn_keyword == Y_ANYXML){
|
||||
if (op == OP_NONE)
|
||||
break;
|
||||
if (x0)
|
||||
if (x0){
|
||||
xml_purge(x0);
|
||||
}
|
||||
if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL)
|
||||
goto done;
|
||||
if (xml_copy(x1, x0) < 0)
|
||||
|
|
@ -585,7 +512,10 @@ text_modify(cxobj *x0,
|
|||
if (op==OP_NONE)
|
||||
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 */
|
||||
/* Loop through children of the modification tree */
|
||||
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 */
|
||||
x0c = NULL;
|
||||
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
||||
goto done;
|
||||
x0vec[i++] = x0c;
|
||||
}
|
||||
|
|
@ -611,29 +541,11 @@ text_modify(cxobj *x0,
|
|||
x1c = NULL;
|
||||
i = 0;
|
||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||
x1cname = xml_name(x1c);
|
||||
yc = yang_find_datanode(y0, x1cname);
|
||||
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op) < 0)
|
||||
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;
|
||||
case OP_DELETE:
|
||||
if (x0==NULL){
|
||||
|
|
@ -712,7 +624,7 @@ text_modify_top(cxobj *x0,
|
|||
goto done;
|
||||
}
|
||||
/* 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;
|
||||
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0)
|
||||
goto done;
|
||||
|
|
@ -831,6 +743,12 @@ text_put(xmldb_handle xh,
|
|||
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* Why not use the config mechanisms that CLixon uses for its own config-file?
|
||||
* Experimental
|
||||
*/
|
||||
#define CONFIGFILE_XML 0
|
||||
|
||||
/* Hash for XML trees list entries
|
||||
* Experimental
|
||||
*/
|
||||
#define XML_CHILD_HASH 0
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ char *xml_body(cxobj *xn);
|
|||
cxobj *xml_body_get(cxobj *xn);
|
||||
char *xml_find_value(cxobj *xn_parent, 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);
|
||||
|
||||
|
|
@ -146,5 +147,12 @@ int xml_body_int32(cxobj *xb, int32_t *val);
|
|||
int xml_body_uint32(cxobj *xb, uint32_t *val);
|
||||
int xml_operation(char *opstr, 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 */
|
||||
|
|
|
|||
|
|
@ -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_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
||||
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 yang_enum_int_value(cxobj *node, int32_t *val);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
#include "clixon_hash.h"
|
||||
|
||||
#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
|
||||
*/
|
||||
|
|
@ -235,7 +236,7 @@ hash_add(clicon_hash_t *hash,
|
|||
}
|
||||
|
||||
/* 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){
|
||||
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
|
||||
goto catch;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@
|
|||
|
||||
#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_xml.h"
|
||||
#include "clixon_json_parse.h"
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ object.
|
|||
|
||||
#include "clixon_err.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_json_parse.h"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_sig.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
|
|
@ -361,8 +364,8 @@ clicon_msg_rcv(int s,
|
|||
goto done;
|
||||
}
|
||||
memcpy(*msg, &hdr, hlen);
|
||||
if ((len2 = read(s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
|
||||
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
|
||||
if ((len2 = atomicio(read, s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
|
||||
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
if (len2 != mlen - sizeof(hdr)){
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@
|
|||
* XML support functions.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -51,7 +55,11 @@
|
|||
#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_parse.h"
|
||||
|
||||
|
|
@ -81,6 +89,9 @@ struct xml{
|
|||
void *x_spec; /* Pointer to specification, eg yang, by
|
||||
reference, dont free */
|
||||
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 */
|
||||
|
|
@ -647,6 +658,9 @@ xml_purge(cxobj *xc)
|
|||
int i;
|
||||
cxobj *xp;
|
||||
|
||||
#if (XML_CHILD_HASH==1)
|
||||
xml_hash_op(xc, 0);
|
||||
#endif
|
||||
if ((xp = xml_parent(xc)) != NULL){
|
||||
/* Find child order i in parent*/
|
||||
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
|
||||
* Note, make a copy of the return value to use it properly
|
||||
* @see xml_find_body
|
||||
* Explaining picture:
|
||||
* xt --> xb (x_type=CX_BODY)
|
||||
* return xb.x_value
|
||||
*/
|
||||
char *
|
||||
xml_body(cxobj *xn)
|
||||
|
|
@ -782,12 +799,18 @@ xml_body(cxobj *xn)
|
|||
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 *
|
||||
xml_body_get(cxobj *xn)
|
||||
xml_body_get(cxobj *xt)
|
||||
{
|
||||
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 NULL;
|
||||
}
|
||||
|
|
@ -795,22 +818,27 @@ xml_body_get(cxobj *xn)
|
|||
/*! Find and return the value of a sub xml node
|
||||
*
|
||||
* 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")
|
||||
* @retval The returned value as a pointer to the name string
|
||||
* @retval NULL if no such node or no value in found node
|
||||
* @retval val Pointer to the name string
|
||||
* @retval NULL No such node or no value in node
|
||||
*
|
||||
* Note, make a copy of the return value to use it properly
|
||||
* See also xml_find_body
|
||||
* Explaining picture:
|
||||
* xt --> x
|
||||
* x_name=name
|
||||
* return x_value
|
||||
*/
|
||||
char *
|
||||
xml_find_value(cxobj *x_up,
|
||||
xml_find_value(cxobj *xt,
|
||||
char *name)
|
||||
{
|
||||
cxobj *x;
|
||||
cxobj *x = NULL;
|
||||
|
||||
if ((x = xml_find(x_up, name)) != NULL)
|
||||
return xml_value(x);
|
||||
while ((x = xml_child_each(xt, x, -1)) != NULL)
|
||||
if (strcmp(name, xml_name(x)) == 0)
|
||||
return xml_value(x);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -821,18 +849,56 @@ xml_find_value(cxobj *x_up,
|
|||
* @retval NULL if no such node or no body in found node
|
||||
* @note, make a copy of the return value to use it properly
|
||||
* @see xml_find_value
|
||||
* Explaining picture:
|
||||
* xt --> x --> bx (x_type=CX_BODY)
|
||||
* x_name=name return x_value
|
||||
|
||||
*/
|
||||
char *
|
||||
xml_find_body(cxobj *xn,
|
||||
xml_find_body(cxobj *xt,
|
||||
char *name)
|
||||
{
|
||||
cxobj *x;
|
||||
cxobj *x=NULL;
|
||||
|
||||
if ((x = xml_find(xn, name)) != NULL)
|
||||
return xml_body(x);
|
||||
while ((x = xml_child_each(xt, x, -1)) != NULL)
|
||||
if (strcmp(name, xml_name(x)) == 0)
|
||||
return xml_body(x);
|
||||
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
|
||||
* @param[in] x the xml tree to be freed.
|
||||
* @see xml_purge where x is also removed from parent
|
||||
|
|
@ -856,6 +922,10 @@ xml_free(cxobj *x)
|
|||
xml_free(xc);
|
||||
x->x_childvec[i] = NULL;
|
||||
}
|
||||
#if (XML_CHILD_HASH==1)
|
||||
if (x->x_hash)
|
||||
hash_free(x->x_hash);
|
||||
#endif
|
||||
if (x->x_childvec)
|
||||
free(x->x_childvec);
|
||||
free(x);
|
||||
|
|
@ -995,7 +1065,7 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
*/
|
||||
int
|
||||
xml_parse(char *str,
|
||||
cxobj *x_up)
|
||||
cxobj *xt)
|
||||
{
|
||||
int retval = -1;
|
||||
struct xml_parse_yacc_arg ya = {0,};
|
||||
|
|
@ -1004,7 +1074,7 @@ xml_parse(char *str,
|
|||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
ya.ya_xparent = x_up;
|
||||
ya.ya_xparent = xt;
|
||||
ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */
|
||||
if (clixon_xml_parsel_init(&ya) < 0)
|
||||
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
|
||||
* Usage: xpath
|
||||
|
|
|
|||
|
|
@ -587,271 +587,262 @@ cvec2xml_1(cvec *cvv,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Return 1 if value is a body of one of the named children of xt */
|
||||
static int
|
||||
xml_is_body(cxobj *xt,
|
||||
char *name,
|
||||
char *val)
|
||||
{
|
||||
cxobj *x;
|
||||
char *bx;
|
||||
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
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
|
||||
xml_diff1(yang_stmt *ys,
|
||||
cxobj *xt1,
|
||||
cxobj *xt2,
|
||||
cxobj ***first,
|
||||
size_t *firstlen,
|
||||
cxobj ***second,
|
||||
size_t *secondlen,
|
||||
cxobj ***changed1,
|
||||
cxobj ***changed2,
|
||||
size_t *changedlen)
|
||||
/*! 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;
|
||||
cxobj *x1 = NULL;
|
||||
cxobj *x2 = NULL;
|
||||
yang_stmt *y;
|
||||
yang_stmt *ykey;
|
||||
char *name;
|
||||
cg_var *cvi;
|
||||
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 *body1;
|
||||
char *body2;
|
||||
char **b1vec = NULL;
|
||||
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");
|
||||
/* Check nodes present in xt1 and xt2 + nodes only in xt1
|
||||
* Loop over xt1
|
||||
*/
|
||||
x1 = NULL;
|
||||
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
|
||||
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);
|
||||
*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:
|
||||
#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",
|
||||
__FUNCTION__, yc->ys_argument);
|
||||
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);
|
||||
/* 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){
|
||||
clicon_err(OE_UNIX, errno, "key %s not found", keyname);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
/* Iterate over xt2 tree to (1) find a child that matches name
|
||||
(2) that have keys that matches */
|
||||
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;
|
||||
x2 = NULL;
|
||||
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
|
||||
if (strcmp(xml_name(x2), name))
|
||||
continue;
|
||||
cvi = NULL;
|
||||
equal = 0;
|
||||
/* (2) Match keys between x1 and x2 */
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((body1 = xml_find_body(x1, keyname)) == NULL)
|
||||
continue; /* may be error */
|
||||
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;
|
||||
}
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
if ((body1 = xml_body(x1)) == NULL)
|
||||
if (strcmp(xml_name(x0c), x1cname))
|
||||
continue;
|
||||
if (!xml_is_body(xt2, name, body1)) /* where body is */
|
||||
if (cxvec_append(x1, first, firstlen) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} /* while xt1 */
|
||||
/* Check nodes present only in xt2
|
||||
* 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;
|
||||
/* Must be inner loop */
|
||||
cvi = NULL; i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
b1 = b1vec[i++];
|
||||
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;
|
||||
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 (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 */
|
||||
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
|
||||
* @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
|
||||
* @param[in] yspec Yang specification
|
||||
* @param[in] xt1 First XML tree
|
||||
* @param[in] xt2 Second XML tree
|
||||
* @param[in] x1 First XML tree
|
||||
* @param[in] x2 Second XML tree
|
||||
* @param[out] first Pointervector to XML nodes existing in only first tree
|
||||
* @param[out] firstlen Length of first vector
|
||||
* @param[out] second Pointervector to XML nodes existing in only second tree
|
||||
* @param[out] secondlen Length of second vector
|
||||
* @param[out] changed1 Pointervector to XML nodes changed value
|
||||
* @param[out] changed2 Pointervector to XML nodes changed value
|
||||
* @param[out] changed1 Pointervector to XML nodes changed orig value
|
||||
* @param[out] changed2 Pointervector to XML nodes changed wanted value
|
||||
* @param[out] changedlen Length of changed vector
|
||||
* All xml vectors should be freed after use.
|
||||
* Bot xml trees should be freed with xml_free()
|
||||
*/
|
||||
int
|
||||
xml_diff(yang_spec *yspec,
|
||||
cxobj *xt1,
|
||||
cxobj *xt2,
|
||||
cxobj *x1,
|
||||
cxobj *x2,
|
||||
cxobj ***first,
|
||||
size_t *firstlen,
|
||||
cxobj ***second,
|
||||
|
|
@ -865,19 +856,19 @@ xml_diff(yang_spec *yspec,
|
|||
*firstlen = 0;
|
||||
*secondlen = 0;
|
||||
*changedlen = 0;
|
||||
if (xt1 == NULL && xt2 == NULL)
|
||||
if (x1 == NULL && x2 == NULL)
|
||||
return 0;
|
||||
if (xt2 == NULL){
|
||||
if (cxvec_append(xt1, first, firstlen) < 0)
|
||||
if (x2 == NULL){
|
||||
if (cxvec_append(x1, first, firstlen) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (xt1 == NULL){
|
||||
if (cxvec_append(xt1, second, secondlen) < 0)
|
||||
if (x1 == NULL){
|
||||
if (cxvec_append(x1, second, secondlen) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (xml_diff1((yang_stmt*)yspec, xt1, xt2,
|
||||
if (xml_diff1((yang_stmt*)yspec, x1, x2,
|
||||
first, firstlen,
|
||||
second, secondlen,
|
||||
changed1, changed2, changedlen) < 0)
|
||||
|
|
@ -1833,82 +1824,6 @@ api_path2xml(char *api_path,
|
|||
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
|
||||
* @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 */
|
||||
x0c = NULL;
|
||||
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
|
||||
goto done;
|
||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
||||
goto done;
|
||||
|
|
@ -2012,7 +1927,7 @@ xml_merge(cxobj *x0,
|
|||
goto done;
|
||||
}
|
||||
/* 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;
|
||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@
|
|||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ in
|
|||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <syslog.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -107,7 +108,6 @@ in
|
|||
/* Constants */
|
||||
#define XPATH_VEC_START 128
|
||||
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
|
|
@ -449,8 +449,8 @@ xpath_exec(cxobj *xcur, char *xpath, cxobj **vec0, size_t vec0len,
|
|||
|
||||
/*! XPath predicate expression check
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] predicate_expression xpath expression as a string
|
||||
* @param[in] flags Extra xml flag checks that must match (apart from predicate)
|
||||
* @param[in] predicate_expression xpath expression as a string
|
||||
* @param[in] flags Extra xml flag checks that must match (apart from predicate)
|
||||
* @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered
|
||||
* @param[in,out] vec0len Length of vector or matches
|
||||
* On input, vec0 contains a list of xml nodes to match.
|
||||
|
|
@ -646,10 +646,8 @@ xpath_find(cxobj *xcur,
|
|||
struct xpath_predicate *xp;
|
||||
|
||||
if (xe == NULL){
|
||||
/* append */
|
||||
for (i=0; i<vec0len; i++){
|
||||
xv = vec0[i];
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
if (flags==0x0 || xml_flag(xv, flags))
|
||||
cxvec_append(xv, vec2, vec2len);
|
||||
}
|
||||
|
|
@ -693,19 +691,21 @@ xpath_find(cxobj *xcur,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
else{
|
||||
for (i=0; i<vec0len; i++){
|
||||
xv = vec0[i];
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, -1)) != NULL) {
|
||||
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
|
||||
if (flags==0x0 || xml_flag(x, flags))
|
||||
if (cxvec_append(x, &vec1, &vec1len) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0)
|
||||
{
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
|
||||
if (flags==0x0 || xml_flag(x, flags))
|
||||
if (cxvec_append(x, &vec1, &vec1len) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(vec0);
|
||||
vec0 = vec1;
|
||||
vec0len = vec1len;
|
||||
|
|
@ -718,20 +718,24 @@ xpath_find(cxobj *xcur,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
/* remove duplicates */
|
||||
for (i=0; i<vec0len; i++){
|
||||
for (j=i+1; j<vec0len; j++){
|
||||
if (vec0[i] == vec0[j]){
|
||||
memmove(vec0[j], vec0[j+1], (vec0len-j)*sizeof(cxobj*));
|
||||
vec0len--;
|
||||
/* remove duplicates
|
||||
* This is cycle-heavy and I dont know when it is needed?
|
||||
*/
|
||||
if (0)
|
||||
for (i=0; i<vec0len; i++){
|
||||
for (j=i+1; j<vec0len; j++){
|
||||
if (vec0[i] == vec0[j]){
|
||||
memmove(vec0[j], vec0[j+1], (vec0len-j)*sizeof(cxobj*));
|
||||
vec0len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
|
||||
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (xpath_find(xcur, xe->xe_next, descendants,
|
||||
vec0, vec0len, flags,
|
||||
vec2, vec2len) < 0)
|
||||
|
|
@ -910,8 +914,8 @@ xpath_first0(cxobj *xcur,
|
|||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
* Note that the returned pointer points into the original tree so should not be freed
|
||||
* after use.
|
||||
* @note the returned pointer points into the original tree so should not be freed after use.
|
||||
* @note return value does not see difference between error and not found
|
||||
* @see also xpath_vec.
|
||||
*/
|
||||
cxobj *
|
||||
|
|
@ -1024,8 +1028,8 @@ xpath_each(cxobj *xcur,
|
|||
* }
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* @Note that although the returned vector must be freed after use, the returned xml
|
||||
* trees need not be.
|
||||
* @note Although the returned vector must be freed after use,
|
||||
* the returned xml trees should not.
|
||||
* @see also xpath_first, xpath_each.
|
||||
*/
|
||||
int
|
||||
|
|
@ -1035,7 +1039,7 @@ xpath_vec(cxobj *xcur,
|
|||
size_t *veclen,
|
||||
...)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
va_list ap;
|
||||
size_t len;
|
||||
char *xpath;
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth/0/0" "^$"
|
|||
new "cli show configuration"
|
||||
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$"
|
||||
|
||||
|
||||
|
||||
new "cli failed validate"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ fi
|
|||
|
||||
new "leafref base config"
|
||||
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"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/><
|
|||
new "netconf commit"
|
||||
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>]]>]]>$"
|
||||
|
||||
new "netconf get replaced config"
|
||||
|
|
|
|||
|
|
@ -29,12 +29,13 @@ module ietf-ip{
|
|||
leaf a {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
container z {
|
||||
leaf-list c {
|
||||
leaf b {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf-list c {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
@ -54,20 +55,57 @@ if [ $? -ne 0 ]; then
|
|||
err
|
||||
fi
|
||||
|
||||
new "generate large config"
|
||||
new "generate large list config"
|
||||
echo -n "<rpc><edit-config><target><candidate/></target><config><x>" > $fconfig
|
||||
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
|
||||
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
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"
|
||||
# Check if still alive
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue