From 7a7bfc48a457fd998f87ffdc4f12a35197530fd0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 18 Sep 2017 20:53:49 +0200 Subject: [PATCH] experimental xml hash for better performance --- CHANGELOG.md | 5 +- apps/netconf/netconf_main.c | 4 +- datastore/text/clixon_xmldb_text.c | 134 ++----- include/clixon_custom.h | 6 + lib/clixon/clixon_xml.h | 8 + lib/clixon/clixon_xml_map.h | 1 + lib/src/clixon_hash.c | 3 +- lib/src/clixon_json_parse.l | 4 + lib/src/clixon_json_parse.y | 4 + lib/src/clixon_proto.c | 7 +- lib/src/clixon_xml.c | 254 +++++++++++++- lib/src/clixon_xml_map.c | 547 ++++++++++++----------------- lib/src/clixon_xml_parse.l | 4 + lib/src/clixon_xsl.c | 52 +-- test/test_cli.sh | 2 - test/test_leafref.sh | 4 +- test/test_netconf.sh | 2 +- test/test_perf.sh | 52 ++- 18 files changed, 612 insertions(+), 481 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc1e746d..e5950989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index b1216698..990e89b6 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -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; } diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index fc0ee8e5..e95b6d8d 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -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: [ ]* */ - 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 */ diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 5f7a00fb..e1ba8e19 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -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 diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 207b8654..e70abe8e 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -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 */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 1cff0413..1a1ded10 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.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); diff --git a/lib/src/clixon_hash.c b/lib/src/clixon_hash.c index 3ad70036..d7bb94de 100644 --- a/lib/src/clixon_hash.c +++ b/lib/src/clixon_hash.c @@ -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; diff --git a/lib/src/clixon_json_parse.l b/lib/src/clixon_json_parse.l index 64ce04ae..a3545b68 100644 --- a/lib/src/clixon_json_parse.l +++ b/lib/src/clixon_json_parse.l @@ -47,6 +47,10 @@ #include +#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" diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y index 245e31be..26c6b998 100644 --- a/lib/src/clixon_json_parse.y +++ b/lib/src/clixon_json_parse.y @@ -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" diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index c95d502a..fbd73cb9 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -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)){ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index e04aec97..d733b0d3 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -34,6 +34,10 @@ * XML support functions. */ +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + #include #include #include @@ -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 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: [ ]* */ + 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 diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 2f1ad129..5356fa23 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -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: [ ]* */ + 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: [ ]* */ - 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: [ ]* */ - 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: [ ]* */ - 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; diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l index 3192d035..b52520e0 100644 --- a/lib/src/clixon_xml_parse.l +++ b/lib/src/clixon_xml_parse.l @@ -48,6 +48,10 @@ #include /* 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" diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index ff21d008..dad46b5b 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -93,6 +93,7 @@ in #include #include #include +#include /* cligen */ #include @@ -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; ixe_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; ixe_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; diff --git a/test/test_cli.sh b/test/test_cli.sh index e9ca8d3c..40853c37 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -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" diff --git a/test/test_leafref.sh b/test/test_leafref.sh index eea36dbc..030fa08b 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -51,7 +51,9 @@ fi new "leafref base config" expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" " -eth0 eth
192.0.2.1
192.0.2.2
lolo
127.0.0.1
]]>]]>" "^]]>]]>$" +eth0 eth
192.0.2.1
192.0.2.2
+lolo
127.0.0.1
+]]>]]>" "^]]>]]>$" new "leafref get config" expecteof "$clixon_netconf -qf $clixon_cf" ']]>]]>' '^eth0' diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 39fd6772..70cd15b3 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -83,7 +83,7 @@ expecteof "$clixon_netconf -qf $clixon_cf" "< new "netconf commit" expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" -new "netconf edit config replace" +new "netconf edit config replace XXX is merge?" expecteof "$clixon_netconf -qf $clixon_cf" "eth2ethmerge]]>]]>" "^]]>]]>$" new "netconf get replaced config" diff --git a/test/test_perf.sh b/test/test_perf.sh index f0654785..b804b355 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -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 "" > $fconfig for (( i=0; i<$number; i++ )); do - echo -n "$i" >> $fconfig + echo -n "$i$i" >> $fconfig done echo "]]>]]>" >> $fconfig new "netconf edit large config" -expecteof_file "time $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" < $fconfig +expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" + +new "netconf edit large config again" +expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" rm $fconfig +new "netconf commit large config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" + +new "netconf add small config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "xy]]>]]>" "^]]>]]>$" + +new "netconf commit small config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" + new "netconf get large config" -expecteof "time $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^01" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^0011" + + +new "generate large leaf-list config" +echo -n "replace" > $fconfig +for (( i=0; i<$number; i++ )); do + echo -n "$i" >> $fconfig +done +echo "]]>]]>" >> $fconfig + +new "netconf replace large list-leaf config" +expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" + +rm $fconfig + +new "netconf commit large leaf-list config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" + +new "netconf add small leaf-list config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "x]]>]]>" "^]]>]]>$" + +new "netconf commit small leaf-list config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" + +new "netconf get large leaf-list config" +expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^01" new "Kill backend" # Check if still alive