From 4b92dbdc10ba2606e73297ac350a26f96956d741 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 27 Dec 2017 11:34:47 +0100 Subject: [PATCH] Optimized search performance for large lists by sorting and binary search --- CHANGELOG.md | 9 + apps/cli/cli_generate.c | 11 +- datastore/keyvalue/clixon_keyvalue.c | 11 +- datastore/text/clixon_xmldb_text.c | 85 ++--- lib/clixon/clixon.h.in | 1 + lib/clixon/clixon_xml.h | 13 +- lib/clixon/clixon_xml_map.h | 1 - lib/clixon/clixon_xml_sort.h | 52 +++ lib/clixon/clixon_yang.h | 15 +- lib/src/Makefile.in | 2 +- lib/src/clixon_options.c | 6 + lib/src/clixon_xml.c | 367 ++++++--------------- lib/src/clixon_xml_map.c | 178 +--------- lib/src/clixon_xml_parse.y | 1 + lib/src/clixon_xml_sort.c | 476 +++++++++++++++++++++++++++ lib/src/clixon_yang.c | 72 +++- test/lib.sh | 1 - test/test_cli.sh | 54 +-- test/test_leafref.sh | 44 +-- test/test_netconf.sh | 84 ++--- test/test_order.sh | 177 ++++++++++ test/test_perf.sh | 49 +-- test/test_restconf.sh | 7 +- test/test_startup.sh | 38 ++- test/test_type.sh | 36 +- test/test_yang.sh | 48 +-- yang/Makefile.in | 2 +- yang/clixon-config@2017-12-27.yang | 266 +++++++++++++++ 28 files changed, 1405 insertions(+), 701 deletions(-) create mode 100644 lib/clixon/clixon_xml_sort.h create mode 100644 lib/src/clixon_xml_sort.c create mode 100755 test/test_order.sh create mode 100644 yang/clixon-config@2017-12-27.yang diff --git a/CHANGELOG.md b/CHANGELOG.md index 4452d678..703ddc73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## 3.4.0 (Upcoming) +* Optimized search performance for large lists by sorting and binary search. + * New CLICON_XML_SORT configuration option. Default is 1. Disable by setting to 0. + * New yang config file: clixon-config@2017-12-27.yang + * Added yang ordered-by user. The default (ordered-by system) will now sort lists and leaf-lists alphabetically to increase search performance. + * This replaces XML hash experimental code, ie xml_child_hash variables and all xml_hash_ functions have been removed. + + +* Cached keys in yang Y_LIST node as cligen vector, see ys_populate_list() + * Clixon_backend now returns -1/255 on error instead of 0. Useful for systemd restarts, for example. * Fixed bug that deletes running on startup if backup started with -m running. diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 7cdcfb92..1fa391ea 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -547,7 +547,6 @@ yang2cli_list(clicon_handle h, { yang_stmt *yc; yang_stmt *yd; - yang_stmt *ykey; yang_stmt *yleaf; int i; cg_var *cvi; @@ -568,13 +567,7 @@ yang2cli_list(clicon_handle h, cprintf(cbuf, "(\"%s\")", helptext); } /* Loop over all key variables */ - if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, 0, "List statement \"%s\" has no key", ys->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; + cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; /* Iterate over individual keys */ while ((cvi = cvec_each(cvk, cvi)) != NULL) { @@ -614,8 +607,6 @@ yang2cli_list(clicon_handle h, done: if (helptext) free(helptext); - if (cvk) - cvec_free(cvk); return retval; } diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index 35a65c40..00da273d 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -201,14 +201,7 @@ append_listkeys(cbuf *ckey, char *bodyenc; int i=0; - if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", - __FUNCTION__, ys->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; + cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; /* Iterate over individual keys */ while ((cvi = cvec_each(cvk, cvi)) != NULL) { @@ -230,8 +223,6 @@ append_listkeys(cbuf *ckey, } retval = 0; done: - if (cvk) - cvec_free(cvk); return retval; } diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index 375f5a86..c58059b0 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -276,7 +276,6 @@ text_setopt(xmldb_handle xh, return retval; } - /*! Ensure that xt only has a single sub-element and that is "config" */ static int @@ -446,6 +445,9 @@ text_get(xmldb_handle xh, if (singleconfigroot(xt, &xt) < 0) goto done; } + /* Sort XML children according to YANG and ordered-by XXX */ + if (xml_child_sort && xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) + goto done; } /* xt == NULL */ /* Here xt looks like: ... */ @@ -507,7 +509,10 @@ text_get(xmldb_handle xh, if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0) goto done; /* Order XML children according to YANG */ - if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0) + if (!xml_child_sort && xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0) + goto done; + /* XXX again just so default values are placed correctly */ + if (xml_child_sort && xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) goto done; if (debug>1) clicon_xml2file(stderr, xt, 0, 1); @@ -600,10 +605,6 @@ text_modify(cxobj *x0, if (xml_value_set(x0b, x1bstr) < 0) goto done; } - if (xml_child_hash && - xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0) - goto done; - break; case OP_DELETE: if (x0==NULL){ @@ -652,11 +653,7 @@ text_modify(cxobj *x0, goto done; if (op==OP_NONE) xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */ - } - if (xml_child_hash && - xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0) - goto done; /* First pass: mark existing children in base */ /* Loop through children of the modification tree */ if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){ @@ -826,6 +823,7 @@ text_put(xmldb_handle xh, struct text_handle *th = handle(xh); char *dbfile = NULL; int fd = -1; + FILE *f = NULL; cbuf *cb = NULL; yang_spec *yspec; cxobj *x0 = NULL; @@ -880,17 +878,14 @@ text_put(xmldb_handle xh, } /* Add yang specification backpointer to all XML nodes */ - /* XXX: where is thiscreated? Add yspec */ + /* XXX: where is this created? Add yspec */ if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - /* Add hash */ -#if 0 - if (xml_child_hash && - xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0) + if (xml_child_sort && xml_apply0(x1, CX_ELMNT, xml_sort, NULL) < 0) goto done; -#endif /* - * Modify base tree x with modification x1 + * Modify base tree x with modification x1. This is where the + * new tree is made. */ if (text_modify_top(x0, x1, yspec, op) < 0) goto done; @@ -907,6 +902,8 @@ text_put(xmldb_handle xh, /* Remove (prune) nodes that are marked (non-presence containers w/o children) */ if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0) goto done; + if (xml_child_sort && xml_apply0(x0, CX_ELMNT, xml_sort, NULL) < 0) + goto done; /* Write back to datastore cache if first time */ if (xmltree_cache){ @@ -926,42 +923,20 @@ text_put(xmldb_handle xh, goto done; } } -#if 1 - if (fd != -1) + if (fd != -1){ close(fd); - { - FILE *f; - if ((f = fopen(dbfile, "w")) == NULL){ - clicon_err(OE_CFG, errno, "Creating file %s", dbfile); - goto done; - } - if (xml_print(f, x0) < 0) - goto done; - fclose(f); + fd = -1; } -#else - // output: - /* Print out top-level xml tree after modification to file */ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); + if ((f = fopen(dbfile, "w")) == NULL){ + clicon_err(OE_CFG, errno, "Creating file %s", dbfile); goto done; - } - if (clicon_xml2cbuf(cb, x0, 0, 1) < 0) + } + if (xml_print(f, x0) < 0) goto done; - /* Reopen file in write mode */ - if (fd != -1) - close(fd); - if ((fd = open(dbfile, O_WRONLY | O_TRUNC, S_IRWXU)) < 0) { - clicon_err(OE_UNIX, errno, "open(%s)", dbfile); - goto done; - } - if (write(fd, cbuf_get(cb), cbuf_len(cb)) < 0){ - clicon_err(OE_UNIX, errno, "write(%s)", dbfile); - goto done; - } -#endif retval = 0; done: + if (f != NULL) + fclose(f); if (dbfile) free(dbfile); if (fd != -1) @@ -1020,7 +995,6 @@ text_copy(xmldb_handle xh, hash_add(th->th_dbs, to, &de0, sizeof(de0)); } } - } if (text_db2file(th, from, &fromfile) < 0) goto done; @@ -1175,7 +1149,8 @@ text_delete(xmldb_handle xh, struct text_handle *th = handle(xh); struct db_element *de = NULL; cxobj *xt = NULL; - + struct stat sb; + if (xmltree_cache){ if ((de = hash_value(th->th_dbs, db, NULL)) != NULL){ if ((xt = de->de_xml) != NULL){ @@ -1186,10 +1161,11 @@ text_delete(xmldb_handle xh, } if (text_db2file(th, db, &filename) < 0) goto done; - if (unlink(filename) < 0){ - clicon_err(OE_DB, errno, "unlink %s", filename); - goto done; - } + if (lstat(filename, &sb) == 0) + if (unlink(filename) < 0){ + clicon_err(OE_DB, errno, "unlink %s", filename); + goto done; + } retval = 0; done: if (filename) @@ -1303,7 +1279,8 @@ usage(char *argv0) } int -main(int argc, char **argv) +main(int argc, + char **argv) { cxobj *xt; cxobj *xn; diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 074dca5e..69bf4420 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 779c2dd1..59a0749b 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -80,10 +80,10 @@ typedef int (xml_applyfn_t)(cxobj *yn, void *arg); #define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */ #define XML_FLAG_NONE 0x10 /* Node is added as NONE */ -/* Hash for XML trees list entries +/* Sort and binary search of XML children * Experimental */ -extern int xml_child_hash; +extern int xml_child_sort; /* * Prototypes @@ -160,14 +160,7 @@ 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); -int xml_child_spec(char *name, cxobj *xp, yang_spec *yspec, yang_stmt **yp); - -clicon_hash_t *xml_hash(cxobj *x); -int xml_hash_key(cxobj *x, yang_stmt *y, cbuf *key); -int xml_hash_op(cxobj *x, void *arg); -int xml_hash_add(cxobj *x); -int xml_hash_rm_only(cxobj *x); -int xml_hash_rm_entry(cxobj *x); +int xml_sort(cxobj *x0, void *arg); #ifdef XML_COMPAT /* See CHANGELOG */ /* MANUAL CHANGE: xml_new(name, parent) --> xml_new(name, parent, NULL) */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 1a1ded10..1cff0413 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -65,7 +65,6 @@ 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/clixon/clixon_xml_sort.h b/lib/clixon/clixon_xml_sort.h new file mode 100644 index 00000000..56dcf29b --- /dev/null +++ b/lib/clixon/clixon_xml_sort.h @@ -0,0 +1,52 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * XML sort and earch functions when used with YANG + */ +#ifndef _CLIXON_XML_SORT_H +#define _CLIXON_XML_SORT_H + +/* Sort and binary search of XML children + * Experimental + */ +extern int xml_child_sort; + +/* + * Prototypes + */ +int xml_child_spec(char *name, cxobj *xp, yang_spec *yspec, yang_stmt **yp); +cxobj *xml_search(cxobj *x, char *name, int yangi, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval); +cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval); +int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc); + +#endif /* _CLIXON_XML_SORT_H */ diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 1fdf88e0..682c0a25 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -155,10 +155,17 @@ struct yang_stmt{ char *ys_argument; /* String / argument depending on keyword */ int ys_flags; /* Flags according to YANG_FLAG_* above */ - cg_var *ys_cv; /* cligen variable. The following stmts have cvs:: - leaf, leaf-list, mandatory, fraction-digits */ + cg_var *ys_cv; /* cligen variable. See ys_populate() + Following stmts have cv:s: + leaf: for default value + leaf-list, + config: boolean true or false + mandatory: boolean true or false + fraction-digits for fraction-digits */ cvec *ys_cvec; /* List of stmt-specific variables - Y_RANGE: range_min, range_max */ + Y_RANGE: range_min, range_max + Y_LIST: vector of keys + */ yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ }; @@ -208,7 +215,7 @@ yang_stmt *yang_find(yang_node *yn, int keyword, char *argument); yang_stmt *yang_find_datanode(yang_node *yn, char *argument); yang_stmt *yang_find_schemanode(yang_node *yn, char *argument); yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, int schemanode); - +int yang_order(yang_stmt *y); int yang_print(FILE *f, yang_node *yn); int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal); int yang_parse(clicon_handle h, const char *yang_dir, diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 67b1a086..e6fdf2b2 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -64,7 +64,7 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \ clixon_string.c clixon_handle.c \ - clixon_xml.c clixon_xml_map.c clixon_file.c \ + clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \ clixon_json.c clixon_yang.c clixon_yang_type.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index c79ab5a8..43981877 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -382,6 +382,11 @@ clicon_options_main(clicon_handle h) /* Read configfile */ if (clicon_option_readfile_xml(copt, configfile, yspec) < 0) goto done; + /* Specific option handling */ + if (clicon_option_bool(h, "CLICON_XML_SORT") == 1) + xml_child_sort = 1; + else + xml_child_sort = 0; } else { #ifdef CONFIG_COMPAT @@ -409,6 +414,7 @@ clicon_options_main(clicon_handle h) /*! Check if a clicon option has a value * @param[in] h clicon_handle * @param[in] name option name + * @retval */ int clicon_option_exists(clicon_handle h, diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index e12eabdd..106c8344 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -82,21 +82,37 @@ /*! xml tree node, with name, type, parent, children, etc * Note that this is a private type not visible from externally, use * access functions. + * A word on ordering of x_children: + * If there is no yang specification, xml children are ordered as they are entered. + * If there is a yang specification (and the appropriate functions are called) the + * xml children are ordered as follows: + * 1) After yang specification order. + * 2) list and leaf-list are sorted alphabetically unless ordered-by user. + * Example: + * container c{ + * leaf a; + * leaf-list x; + * } + * then regardless in which order the xml is entered, it will be sorted as follows: + * + * + * a + * b + * */ struct xml{ char *x_name; /* name of node */ char *x_namespace; /* namespace, if any */ struct xml *x_up; /* parent node in hierarchy if any */ struct xml **x_childvec; /* vector of children nodes */ - int x_childvec_len; /* length of vector */ + int x_childvec_len;/* length of vector */ enum cxobj_type x_type; /* type of node: element, attribute, body */ char *x_value; /* attribute and body nodes have values */ int _x_vector_i; /* internal use: xml_child_each */ int x_flags; /* Flags according to XML_FLAG_* above */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ - cg_var *x_cv; /* If body this contains the typed value */ - clicon_hash_t *x_hash; /* Hash of children */ + cg_var *x_cv; /* If body this contains the typed value */ }; /* Mapping between xml type <--> string */ @@ -108,10 +124,6 @@ static const map_str2int xsmap[] = { {NULL, -1} }; -/* Hash for XML trees list entries - * Experimental XXX DOES NOT WORK - */ -int xml_child_hash = 0; /*! Translate from xml type in enum form to string keyword * @param[in] type Xml type @@ -475,11 +487,11 @@ xml_child_append(cxobj *x, return 0; } -/*! Set a a childvec to a speciufic size, fill with children after +/*! Set a a childvec to a specific size, fill with children after * @code * xml_childvec_set(x, 2); - * xml_child_i(x, 0) = xc0; - * xml_child_i(x, 1) = xc1; + * xml_child_i_set(x, 0, xc0) + * xml_child_i_set(x, 1, xc1); * @endcode */ int @@ -502,9 +514,9 @@ xml_childvec_get(cxobj *x) /*! Create new xml node given a name and parent. Free with xml_free(). * - * @param[in] name Name of new - * @param[in] xp The parent where the new xml node should be inserted - * @param[in] spec Yang stmt or NULL. + * @param[in] name Name of XML node + * @param[in] xp The parent where the new xml node will be appended + * @param[in] spec Yang statement of this XML or NULL. * @retval xml Created xml object if successful. Free with xml_free() * @retval NULL Error and clicon_err() called * @code @@ -536,8 +548,6 @@ xml_new(char *name, if (xp && xml_child_append(xp, x) < 0) return NULL; x->x_spec = spec; /* Can be NULL */ - if (xml_child_hash && spec && xml_hash_add(x) < 0) - return NULL; return x; } @@ -655,8 +665,6 @@ xml_purge(cxobj *xc) int i; cxobj *xp; - if (xml_child_hash) - xml_hash_rm_entry(xc); if ((xp = xml_parent(xc)) != NULL){ /* Find child order i in parent*/ for (i=0; ix_childvec[i] = NULL; } } - if (x->x_hash) - hash_free(x->x_hash); if (x->x_childvec) free(x->x_childvec); free(x); @@ -1830,276 +1836,93 @@ xml_operation2str(enum operation_type op) } } -/*! Given an XML object and a child name, return yang stmt of child - * If no xml parent, find root yang stmt matching name - * @param[in] name Name of child - * @param[in] xp XML parent, can be NULL. - * @param[in] yspec Yang specification (top level) - * @param[out] yresult Pointer to yang stmt of result, or NULL, if not found +/*! Help function to qsort for sorting entries in xml child vector + * @param[in] arg1 + * @param[in] arg2 + * @retval 0 If equal + * @retval <0 if arg1 is less than arg2 + * @retval >0 if arg1 is greater than arg2 + * @note must be in clixon_xml.c since it uses internal (hidden) struct xml */ -int -xml_child_spec(char *name, - cxobj *xp, - yang_spec *yspec, - yang_stmt **yresult) -{ - yang_stmt *y; /* result yang node */ - yang_stmt *yparent; /* parent yang */ - - if (xp && (yparent = xml_spec(xp)) != NULL) - y = yang_find_datanode((yang_node*)yparent, name); - else if (yspec) - y = yang_find_topnode(yspec, name, 0); /* still NULL for config */ - else - y = NULL; - *yresult = y; - return 0; -} - -/*! Return yang hash - * Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml. - */ -clicon_hash_t * -xml_hash(cxobj *x) -{ - return x->x_hash; -} - static int -xml_hash_init(cxobj *x) +xml_cmp(const void* arg1, + const void* arg2) { - if ((x->x_hash = hash_init()) < 0) - return -1; - return 0; -} - -/*! XML remove hash only. Not in parent. - * @param[in] x XML object - * eg same as xml_hash_op(x, -1) - */ -int -xml_hash_rm_only(cxobj *x) -{ - if (x && x->x_hash){ - hash_free(x->x_hash); - x->x_hash = NULL; - } - return 0; -} - -/* Compute hash key for xml entry - * @param[in] x - * @param[in] y - * @param[out] key - * key: yangtype+x1name - * LEAFLIST: b0 - * LIST: b2vec+b0 -> x0c - */ -int -xml_hash_key(cxobj *x, - yang_stmt *y, - cbuf *key) -{ - int retval = -1; - yang_stmt *ykey; - cvec *cvk = NULL; /* vector of index keys */ - cg_var *cvi; - char *keyname; - char *b; - char *str; - - switch (y->ys_keyword){ - case Y_CONTAINER: str = "c"; break; - case Y_LEAF: str = "e"; break; - case Y_LEAF_LIST: str = "l"; break; - case Y_LIST: str = "i"; break; - default: - str = "xx"; break; - break; - } - cprintf(key, "%s%s", str, xml_name(x)); - switch (y->ys_keyword){ - case Y_LEAF_LIST: /* Match with name and value */ - if ((b = xml_body(x)) == NULL){ - cbuf_reset(key); - goto ok; - } - cprintf(key, "%s", xml_body(x)); - break; - case Y_LIST: /* Match with key values */ - if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", - __FUNCTION__, y->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; - cvi = NULL; - while ((cvi = cvec_each(cvk, cvi)) != NULL){ - keyname = cv_string_get(cvi); - if ((b = xml_find_body(x, keyname)) == NULL){ - cbuf_reset(key); - goto ok; - } - cprintf(key, "/%s", b); - } - break; - default: - break; - } - ok: - retval = 0; - done: - if (cvk) - cvec_free(cvk); - return retval; -} - -/*! XML hash add. Create hash and add key/value to parent - * - * @param[in] arg add flag. If 1, else if 0 remove. - * Typically called for a whole tree. - */ -int -xml_hash_op(cxobj *x, - void *arg) -{ - int add = (intptr_t)arg; -#if 1 - if (add) - return xml_hash_add(x); - else - return xml_hash_rm_entry(x); -#else + struct xml *x1 = *(struct xml**)arg1; + struct xml *x2 = *(struct xml**)arg2; + yang_stmt *y1; + yang_stmt *y2; + int yi1; + int yi2; + cvec *cvk = NULL; /* vector of index keys */ + cg_var *cvi; + int equal = 0; + char *b1; + char *b2; + char *keyname; - int retval = -1; - cxobj *xp; - clicon_hash_t *ph; - yang_stmt *y; - cbuf *key = NULL; /* cligen buffer hash key */ - - - if (xml_hash(x)==NULL){ - if (add) - xml_hash_init(x); + if (x1 == NULL){ + if (x2 == NULL) + return 0; + else + return -1; } - else if (!add) - xml_hash_rm_only(x); - if ((xp = xml_parent(x)) == NULL) - goto ok; - if ((ph = xml_hash(xp))==NULL) - goto ok; - if ((y = xml_spec(x)) == NULL) - goto ok; - if ((key = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; + else if (x2 == NULL) + return 1; + y1 = xml_spec(x1); + y2 = xml_spec(x2); + if (y1==NULL || y2==NULL) + return 0; /* just ignore */ + if (y1 != y2){ + yi1 = yang_order(y1); + yi2 = yang_order(y2); + if ((equal = yi1-yi2) != 0) + return equal; } - if (xml_hash_key(x, y, key) < 0) - goto done; - if (cbuf_len(key)){ - // fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x); - if (add){ - if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL) + /* Now y1=y2, same Yang spec, can only be list or leaf-list, + * sort according to key + */ + if (yang_find((yang_node*)y1, Y_ORDERED_BY, "user") != NULL) + return 0; /* Ordered by user: maintain existing order */ + switch (y1->ys_keyword){ + case Y_LEAF_LIST: /* Match with name and value */ + equal = strcmp(xml_body(x1), xml_body(x2)); + break; + case Y_LIST: /* Match with key values + * Use Y_LIST cache (see struct yang_stmt) + */ + cvk = y1->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + cvi = NULL; + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + keyname = cv_string_get(cvi); + b1 = xml_find_body(x1, keyname); + b2 = xml_find_body(x2, keyname); + if ((equal = strcmp(b1,b2)) != 0) goto done; } - else - if (hash_del(ph, cbuf_get(key)) < 0) - goto done; + equal = 0; + break; + default: + break; } - ok: - retval = 0; done: - if (key) - cbuf_free(key); - return retval; -#endif + return equal; } -/*! XML hash add. Create hash and add key/value to parent - * - * @param[in] x XML object - * eg same as xml_hash_op(x, 1) +/*! Sort children of an XML node + * Assume populated by yang spec. + * @param[in] x0 XML node + * @param[in] arg Dummy so it can be called by xml_apply() + * @note must be in clixon_xml.c since it uses internal (hidden) struct xml */ int -xml_hash_add(cxobj *x) +xml_sort(cxobj *x, + void *arg) { - int retval = -1; - cxobj *xp; - clicon_hash_t *ph; - yang_stmt *y; - yang_stmt *yp; - cbuf *key = NULL; /* cligen buffer hash key */ - - if ((ph = xml_hash(x))==NULL){ - xml_hash_init(x); - ph = xml_hash(x); - } - if ((xp = xml_parent(x)) == NULL) - goto ok; - yp = xml_spec(xp); - if (yp && yp->ys_keyword != Y_LIST) - goto ok; - if ((y = xml_spec(x)) == NULL) - goto ok; - if ((key = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (xml_hash_key(x, y, key) < 0) - goto done; - if (cbuf_len(key)){ - if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL) - goto done; - } - ok: - retval = 0; - done: - if (key) - cbuf_free(key); - return retval; + qsort(x->x_childvec, x->x_childvec_len, sizeof(struct xml*), xml_cmp); + return 0; } -/*! XML hash rm. Create hash and add key/value to parent - * - * @param[in] x XML object - * eg same as xml_hash_op(x, 0) - */ -int -xml_hash_rm_entry(cxobj *x) -{ - int retval = -1; - cxobj *xp; - clicon_hash_t *ph; - yang_stmt *y; - cbuf *key = NULL; /* cligen buffer hash key */ - - if (xml_hash(x)!=NULL) - xml_hash_rm_only(x); - if ((xp = xml_parent(x)) == NULL) - goto ok; - if ((ph = xml_hash(xp))==NULL) - goto ok; - if ((y = xml_spec(x)) == NULL) - goto ok; - if ((key = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (xml_hash_key(x, y, key) < 0) - goto done; - if (cbuf_len(key)){ - if (hash_del(ph, cbuf_get(key)) < 0) - goto done; - } - ok: - retval = 0; - done: - if (key) - cbuf_free(key); - return retval; -} /* * Turn this on to get a xml parse and pretty print test program diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 7bda04b9..5d91ce2c 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -85,6 +85,7 @@ #include "clixon_xsl.h" #include "clixon_log.h" #include "clixon_err.h" +#include "clixon_xml_sort.h" #include "clixon_xml_map.h" /* Something to do with reverse engineering of junos syntax? */ @@ -590,136 +591,6 @@ cvec2xml_1(cvec *cvv, return retval; } -/*! Given child tree x1c, find matching child in base tree x0 - * param[in] x0 Base tree node - * param[in] x1c Modification tree child - * param[in] yc Yang spec of tree child - * param[out] x0cp Matching base tree child (if any) - * @note XXX: room for optimization? on 1K calls we have 1M body calls and - 500K xml_child_each/cvec_each calls. - The outer loop is large for large lists - The inner loop is small - Major time in xml_find_body() - Can one do a binary search in the x0 list? -*/ -int -match_base_child(cxobj *x0, - cxobj *x1c, - cxobj **x0cp, - yang_stmt *yc) -{ - int retval = -1; - char *x1cname; - cxobj *x0c = NULL; /* x0 child */ - cvec *cvk = NULL; /* vector of index keys */ - cg_var *cvi; - char *b0; - char *b1; - yang_stmt *ykey; - char *keyname; - int equal; - char **b1vec = NULL; - int i; - cxobj **p; - cbuf *key = NULL; /* cligen buffer hash key */ - size_t vlen; - - if (xml_child_hash){ - *x0cp = NULL; /* return value */ - if (xml_hash(x0) == NULL) - goto nohash; - if ((key = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done1; - } - if (xml_hash_key(x1c, yc, key) < 0) - goto done; - x0c = NULL; - if (cbuf_len(key)) - if ((p = hash_value(xml_hash(x0), cbuf_get(key), &vlen)) != NULL){ - assert(vlen == sizeof(x0c)); - x0c = *p; - } - // fprintf(stderr, "%s get %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x0c); - *x0cp = x0c; - retval = 0; - done1: - if (key) - cbuf_free(key); - return retval; - } - nohash: - *x0cp = NULL; /* return value */ - x1cname = xml_name(x1c); - switch (yc->ys_keyword){ - case Y_CONTAINER: /* Equal regardless */ - case Y_LEAF: /* Equal regardless */ - x0c = xml_find(x0, x1cname); - break; - case Y_LEAF_LIST: /* Match with name and value */ - if ((b1 = xml_body(x1c)) == NULL) - goto ok; - x0c = xml_find_body_obj(x0, x1cname, b1); - break; - case Y_LIST: /* Match with key values */ - if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", - __FUNCTION__, yc->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; - cvi = NULL; i = 0; - while ((cvi = cvec_each(cvk, cvi)) != NULL) - i++; - if ((b1vec = calloc(i, sizeof(b1))) == NULL){ - clicon_err(OE_UNIX, errno, "calloc"); - goto done; - } - cvi = NULL; i = 0; - while ((cvi = cvec_each(cvk, cvi)) != NULL) { - keyname = cv_string_get(cvi); - if ((b1 = xml_find_body(x1c, keyname)) == NULL) - goto ok; /* not found */ - b1vec[i++] = b1; - } - /* Iterate over x0 tree to (1) find a child that matches name - (2) that have keys that matches */ - x0c = NULL; - while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){ - equal = 0; - if (strcmp(xml_name(x0c), x1cname)) - continue; - /* Must be inner loop */ - cvi = NULL; i = 0; - while ((cvi = cvec_each(cvk, cvi)) != NULL) { - b1 = b1vec[i++]; - equal = 0; - keyname = cv_string_get(cvi); - if ((b0 = xml_find_body(x0c, keyname)) == NULL) - break; /* error case */ - if (strcmp(b0, b1)) - break; /* stop as soon as inequal key found */ - equal=1; /* reaches here for all keynames, x0c is found. */ - } - if (equal) /* x0c and x1c equal, otherwise look for other */ - break; - } /* while x0c */ - break; - default: - break; - } - ok: - *x0cp = x0c; - retval = 0; - done: - if (b1vec) - free(b1vec); - if (cvk) - cvec_free(cvk); - return retval; -} /*! Find next yang node, either start from yang_spec or some yang-node * @param[in] y Node spec or sny yang-node @@ -895,7 +766,6 @@ yang2api_path_fmt_1(yang_stmt *ys, cbuf *cb) { yang_node *yp; /* parent */ - yang_stmt *ykey; int i; cvec *cvk = NULL; /* vector of index keys */ int retval = -1; @@ -926,14 +796,7 @@ yang2api_path_fmt_1(yang_stmt *ys, switch (ys->ys_keyword){ case Y_LIST: - if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", - __FUNCTION__, ys->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; + cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ if (cvec_len(cvk)) cprintf(cb, "="); /* Iterate over individual keys */ @@ -951,8 +814,6 @@ yang2api_path_fmt_1(yang_stmt *ys, } /* switch */ retval = 0; done: - if (cvk) - cvec_free(cvk); return retval; } @@ -1370,13 +1231,16 @@ xml_order(cxobj *xt, goto done; } j0 = 0; - /* Go through xml children and ensure they are same order as yspec children */ + /* Go through yang node's children and ensure that the + * xml children follow this order. + * Do not order the list or leaf-list children (have same name). + */ for (i=0; iys_len; i++){ yc = y->ys_stmt[i]; if (!yang_datanode(yc)) continue; yname = yc->ys_argument; - /* First go thru xml children with same name */ + /* First go thru xml children with same name in rest of list */ for (; j0ys_argument); - goto done; - } - clicon_debug(1, "ykey:%s", ykey->ys_argument); - - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; + cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ cvi = NULL; /* Iterate over individual yang keys */ cprintf(xpath, "/%s", name); @@ -1647,7 +1500,6 @@ api_path2xml_vec(char **vec, char *name; char *restval = NULL; char *restval_enc; - yang_stmt *ykey; cxobj *xn = NULL; /* new */ cxobj *xb; /* body */ cvec *cvk = NULL; /* vector of index keys */ @@ -1709,15 +1561,7 @@ api_path2xml_vec(char **vec, goto done; break; case Y_LIST: - /* Get the yang list key */ - if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){ - clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", - __FUNCTION__, y->ys_argument); - goto done; - } - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; + cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ if (valvec){ free(valvec); valvec = NULL; @@ -1754,10 +1598,6 @@ api_path2xml_vec(char **vec, if (xml_value_set(xb, val2) <0) goto done; } - if (cvk){ - cvec_free(cvk); - cvk = NULL; - } break; default: /* eg Y_CONTAINER, Y_LEAF */ if ((x = xml_new(name, x0, y)) == NULL) diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index f4eef6d4..5c7cdbd0 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -75,6 +75,7 @@ #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_xml.h" +#include "clixon_xml_sort.h" #include "clixon_xml_parse.h" void diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c new file mode 100644 index 00000000..1ac1197c --- /dev/null +++ b/lib/src/clixon_xml_sort.c @@ -0,0 +1,476 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * XML search functions when used with YANG + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clixon */ +#include "clixon_err.h" +#include "clixon_log.h" +#include "clixon_string.h" + +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_xml_sort.h" + +/* + * Variables + */ + +/* Sort and binary search of XML children + * Experimental + */ +int xml_child_sort = 1; + + +/*! Given an XML object and a child name, return yang stmt of child + * If no xml parent, find root yang stmt matching name + * @param[in] name Name of child + * @param[in] xp XML parent, can be NULL. + * @param[in] yspec Yang specification (top level) + * @param[out] yresult Pointer to yang stmt of result, or NULL, if not found + */ +int +xml_child_spec(char *name, + cxobj *xp, + yang_spec *yspec, + yang_stmt **yresult) +{ + yang_stmt *y; /* result yang node */ + yang_stmt *yparent; /* parent yang */ + + if (xp && (yparent = xml_spec(xp)) != NULL) + y = yang_find_datanode((yang_node*)yparent, name); + else if (yspec) + y = yang_find_topnode(yspec, name, 0); /* still NULL for config */ + else + y = NULL; + *yresult = y; + return 0; +} + +/*! + * @param[in] yangi Yang order + * @param[in] keynr Length of keyvec/keyval vector when applicable + * @param[in] keyvec Array of of yang key identifiers + * @param[in] keyval Array of of yang key values + * @param[out] userorder If set, this yang order is user ordered, linear search + * @retval 0 If equal (or userorder set) + * @retval <0 if arg1 is less than arg2 + * @retval >0 if arg1 is greater than arg2 + */ +static int +xml_cmp1(cxobj *x, + yang_stmt *y, + char *name, + enum rfc_6020 keyword, + int keynr, + char **keyvec, + char **keyval, + int *userorder) +{ + char *b; + int i; + char *keyname; + char *key; + + /* Check if same yang spec (order in yang stmt list) */ + switch (keyword){ + case Y_CONTAINER: /* Match with name */ + case Y_LEAF: /* Match with name */ + return strcmp(name, xml_name(x)); + break; + case Y_LEAF_LIST: /* Match with name and value */ + if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL) + *userorder=1; + b=xml_body(x); + return strcmp(keyval[0], b); + break; + case Y_LIST: /* Match with array of key values */ + if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL) + *userorder=1; + for (i=0; i=0; i--){ /* Then decrement */ + xc = xml_child_i(x0, i); + y = xml_spec(xc); + if (yangi!=yang_order(y)) + break; + if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, NULL) == 0) + return xc; + } + return NULL; /* Not found */ +} + +/*! + * @param[in] yangi Yang order + * @param[in] keynr Length of keyvec/keyval vector when applicable + * @param[in] keyvec Array of of yang key identifiers + * @param[in] keyval Array of of yang key values + * @param[in] low Lower bound of childvec search interval + * @param[in] upper Lower bound of childvec search interval + */ +static cxobj * +xml_search1(cxobj *x0, + char *name, + int yangi, + enum rfc_6020 keyword, + int keynr, + char **keyvec, + char **keyval, + int low, + int upper) +{ + int mid; + int cmp; + cxobj *xc; + yang_stmt *y; + int userorder= 0; + + if (upper < low) + return NULL; /* not found */ + mid = (low + upper) / 2; + if (mid >= xml_child_nr(x0)) /* beyond range */ + return NULL; + xc = xml_child_i(x0, mid); + assert(y = xml_spec(xc)); + if ((cmp = yangi-yang_order(y)) == 0){ + cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, &userorder); + if (userorder && cmp) /* Look inside this yangi order */ + return xml_search_userorder(x0, y, name, yangi, mid, keyword, keynr, keyvec, keyval); + } + if (cmp == 0) + return xc; + else if (cmp < 0) + return xml_search1(x0, name, yangi, keyword, + keynr, keyvec, keyval, low, mid-1); + else + return xml_search1(x0, name, yangi, keyword, + keynr, keyvec, keyval, mid+1, upper); + return NULL; +} + +/*! + * @param[in] yangi yang child order + * @param[in] keynr Length of keyvec/keyval vector when applicable + * @param[in] keyvec Array of of yang key identifiers + * @param[in] keyval Array of of yang key values + */ +cxobj * +xml_search(cxobj *x0, + char *name, + int yangi, + enum rfc_6020 keyword, + int keynr, + char **keyvec, + char **keyval) +{ + return xml_search1(x0, name, yangi, keyword, keynr, keyvec, keyval, + 0, xml_child_nr(x0)); +} + +/*! Find matching xml child given name and optional key values + * container: x0, y->keyword, name + * list: x0, y->keyword, y->key, name + * + * The function needs a vector of key values (or single for LEAF_LIST). + * What format? + * 1) argc/argv:: "val1","val2" <<== + * 2) cv-list? + * 3) va-list? + * + * yc - LIST (interface) - + * ^ + * | + * x0-->x0c-->(name=interface)+->x(name=name)->xb(value="eth0") <==this is + * | + * v + * x1c->name (interface) + * x1c->x(name=name)->xb(value="eth0") + * + * CONTAINER:name + * LEAF: name + * LEAFLIST: name/body... #b0 + * LIST: name/key0/key1... #b2vec+b0 -> x0c + + * eth0 + * eth1 + * eth2 + * @param[in] x0 XML node. Find child of this node. + * @param[in] keyword Yang keyword. Relevant: container, list, leaf, leaf_list + * @param[in] keynr Length of keyvec/keyval vector when applicable + * @param[in] keyvec Array of of yang key identifiers + * @param[in] keyval Array of of yang key values + * @param[out] xp Return value on success, pointer to XML child node + * @note If keyword is: + * - list, keyvec and keyval should be an array with keynr length + * - leaf_list, keyval should be 1 and keyval should contain one element + * - otherwise, keyval should be 0 and keyval and keyvec should be both NULL. + */ +cxobj * +xml_match(cxobj *x0, + char *name, + enum rfc_6020 keyword, + int keynr, + char **keyvec, + char **keyval) +{ + char *key; + char *keyname; + char *b0; + cxobj *x = NULL; + int equal; + int i; + + x = NULL; + switch (keyword){ + case Y_CONTAINER: /* Match with name */ + case Y_LEAF: /* Match with name */ + if (keynr != 0){ + clicon_err(OE_XML, EINVAL, "Expected no key argument to CONTAINER or LEAF"); + goto ok; + } + x = xml_find(x0, name); + break; + case Y_LEAF_LIST: /* Match with name and value */ + if (keynr != 1) + goto ok; + x = xml_find_body_obj(x0, name, keyval[0]); + break; + case Y_LIST: /* Match with array of key values */ + i = 0; + while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL){ + equal = 0; + if (strcmp(xml_name(x), name)) + continue; + /* Must be inner loop */ + for (i=0; iys_keyword){ + case Y_CONTAINER: /* Equal regardless */ + case Y_LEAF: /* Equal regardless */ + break; + case Y_LEAF_LIST: /* Match with name and value */ + keynr = 1; + if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){ + clicon_err(OE_UNIX, errno, "calloc"); + goto done; + } + if ((keyval[0] = xml_body(x1c)) == NULL) + goto ok; + break; + case Y_LIST: /* Match with key values */ + cvk = yc->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */ + /* Count number of key indexes */ + cvi = NULL; keynr = 0; + while ((cvi = cvec_each(cvk, cvi)) != NULL) + keynr++; + if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){ + clicon_err(OE_UNIX, errno, "calloc"); + goto done; + } + if ((keyvec = calloc(keynr+1, sizeof(char*))) == NULL){ + clicon_err(OE_UNIX, errno, "calloc"); + goto done; + } + cvi = NULL; i = 0; + while ((cvi = cvec_each(cvk, cvi)) != NULL) { + keyname = cv_string_get(cvi); + keyvec[i] = keyname; + if ((b1 = xml_find_body(x1c, keyname)) == NULL) + goto ok; /* not found */ + keyval[i++] = b1; + } + break; + default: + break; + } + /* Get match */ + { + yang_node *y0; + int yorder; + + if ((y0 = yc->ys_parent) == NULL) + goto done; + /* XXX: No we cant do this. on uppermost layer it can look like this: + * config + * ximport-----ymod = ietf + * interfaces--ymod = example + * Which means yang order can be different for different children in the + * same childvec. + */ + if (xml_child_sort==0) + *x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval); + else{ +#if 1 + if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){ + yorder = yang_order(yc); + *x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval); + } + else{ + clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__); + *x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval); + } +#else + cxobj *xx; + + + + *x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval); + if (xml_child_nr(x0) && xml_spec(xml_child_i(x0,0))!=NULL){ + xx = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval); + if (xx!=*x0cp){ + clicon_log(LOG_WARNING, "%s mismatch", __FUNCTION__); + fprintf(stderr, "mismatch\n"); + xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval); + assert(0); + } + } + else + clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__); +#endif + } + } + ok: + retval = 0; + done: + if (keyval) + free(keyval); + if (keyvec) + free(keyvec); + return retval; +} + diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 145cae44..7170b36a 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -373,7 +373,7 @@ yn_each(yang_node *yn, * * @param[in] yn Yang node, current context node. * @param[in] keyword if 0 match any keyword - * @param[in] argument String compare w wrgument. if NULL, match any. + * @param[in] argument String compare w argument. if NULL, match any. * This however means that if you actually want to match only a yang-stmt with * argument==NULL you cannot, but I have not seen any such examples. * @see yang_find_datanode @@ -384,8 +384,8 @@ yang_find(yang_node *yn, char *argument) { yang_stmt *ys = NULL; - int i; - int match = 0; + int i; + int match = 0; for (i=0; iyn_len; i++){ ys = yn->yn_stmt[i]; @@ -558,6 +558,54 @@ yang_find_myprefix(yang_stmt *ys) return prefix; } +/*! Find matching y in yp:s children, return 0 and index or -1 if not found. + * @retval 0 not found + * @retval 1 found + */ +static int +order1(yang_node *yp, + yang_stmt *y, + int *index) +{ + yang_stmt *ys; + int i; + + for (i=0; iyn_len; i++){ + ys = yp->yn_stmt[i]; + if (!yang_datanode(ys)) + continue; + if (ys==y) + return 1; + (*index)++; + } + return 0; +} + +/*! Return order of yang statement y in parents child vector + * @retval i Order of child with specified argument + * @retval -1 Not found + */ +int +yang_order(yang_stmt *y) +{ + yang_node *yp; + yang_node *ypp; + yang_node *yn; + int i; + int j=0; + + yp = y->ys_parent; + if (yp->yn_keyword == Y_MODULE ||yp->yn_keyword == Y_SUBMODULE){ + ypp = yp->yn_parent; + for (i=0; iyn_len; i++){ + yn = (yang_node*)ypp->yn_stmt[i]; + if (order1(yn, y, &j) == 1) + return j; + } + } + order1(yp, y, &j); + return j; +} /*! Reset flag in complete tree, arg contains flag */ static int @@ -880,6 +928,20 @@ ys_populate_leaf(yang_stmt *ys, return retval; } +static int +ys_populate_list(yang_stmt *ys, + void *arg) +{ + yang_stmt *ykey; + + if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL) + return 0; + cvec_free(ys->ys_cvec); + if ((ys->ys_cvec = yang_arg2cvec(ykey, " ")) == NULL) + return -1; + return 0; +} + /*! Populate range and length statements * * Create cvec variables "range_min" and "range_max". Assume parent is type. @@ -1061,6 +1123,10 @@ ys_populate(yang_stmt *ys, if (ys_populate_leaf(ys, arg) < 0) goto done; break; + case Y_LIST: + if (ys_populate_list(ys, arg) < 0) + goto done; + break; case Y_RANGE: case Y_LENGTH: if (ys_populate_range(ys, arg) < 0) diff --git a/test/lib.sh b/test/lib.sh index c9b8ac1f..467fccd8 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -2,7 +2,6 @@ testnr=0 testname= -clixon_cf=/usr/local/etc/routing.xml # error and exit, arg is optional extra errmsg err(){ echo "Error in Test$testnr [$testname]:" diff --git a/test/test_cli.sh b/test/test_cli.sh index c208d4de..d5cbe3f3 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -9,7 +9,7 @@ # include err() and new() functions . ./lib.sh -clixon_cf="-f /usr/local/etc/routing.xml" +cfg=/usr/local/etc/routing.xml # For memcheck #clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" @@ -17,81 +17,81 @@ clixon_cli=clixon_cli # kill old backend (if any) new "kill old backend" -sudo clixon_backend -z $clixon_cf +sudo clixon_backend -z -f $cfg if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init $clixon_cf +sudo clixon_backend -s init -f $cfg if [ $? -ne 0 ]; then err fi new "cli tests" new "cli configure top" -expectfn "$clixon_cli -1 $clixon_cf set interfaces" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces" "^$" new "cli show configuration top (no presence)" -expectfn "$clixon_cli -1 $clixon_cf show conf cli" "^$" +expectfn "$clixon_cli -1 -f $cfg show conf cli" "^$" new "cli configure delete top" -expectfn "$clixon_cli -1 $clixon_cf delete interfaces" "^$" +expectfn "$clixon_cli -1 -f $cfg delete interfaces" "^$" new "cli show configuration delete top" -expectfn "$clixon_cli -1 $clixon_cf show conf cli" "^$" +expectfn "$clixon_cli -1 -f $cfg show conf cli" "^$" new "cli configure" -expectfn "$clixon_cli -1 $clixon_cf set interfaces interface eth/0/0" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" "^$" new "cli show configuration" -expectfn "$clixon_cli -1 $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" +expectfn "$clixon_cli -1 -f $cfg show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" new "cli failed validate" -expectfn "$clixon_cli -1 $clixon_cf -l o validate" "Missing mandatory variable" +expectfn "$clixon_cli -1 -f $cfg -l o validate" "Missing mandatory variable" new "cli configure more" -expectfn "$clixon_cli -1 $clixon_cf set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" "^$" -expectfn "$clixon_cli -1 $clixon_cf set interfaces interface eth/0/0 description mydesc" "^$" -expectfn "$clixon_cli -1 $clixon_cf set interfaces interface eth/0/0 type bgp" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description mydesc" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type bgp" "^$" new "cli show xpath description" -expectfn "$clixon_cli -1 $clixon_cf -l o show xpath /interfaces/interface/description" "mydesc" +expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" "mydesc" new "cli delete description" -expectfn "$clixon_cli -1 $clixon_cf -l o delete interfaces interface eth/0/0 description mydesc" +expectfn "$clixon_cli -1 -f $cfg -l o delete interfaces interface eth/0/0 description mydesc" new "cli show xpath no description" -expectfn "$clixon_cli -1 $clixon_cf -l o show xpath /interfaces/interface/description" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" "^$" new "cli copy interface" -expectfn "$clixon_cli -1 $clixon_cf copy interface eth/0/0 to eth99" "^$" +expectfn "$clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99" "^$" new "cli success validate" -expectfn "$clixon_cli -1 $clixon_cf -l o validate" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o validate" "^$" new "cli commit" -expectfn "$clixon_cli -1 $clixon_cf -l o commit" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o commit" "^$" new "cli save" -expectfn "$clixon_cli -1 $clixon_cf -l o save /tmp/foo" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o save /tmp/foo" "^$" new "cli delete all" -expectfn "$clixon_cli -1 $clixon_cf -l o delete all" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o delete all" "^$" new "cli load" -expectfn "$clixon_cli -1 $clixon_cf -l o load /tmp/foo" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o load /tmp/foo" "^$" new "cli check load" -expectfn "$clixon_cli -1 $clixon_cf -l o show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" +expectfn "$clixon_cli -1 -f $cfg -l o show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" new "cli debug" -expectfn "$clixon_cli -1 $clixon_cf -l o debug level 1" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o debug level 1" "^$" # How to test this? -expectfn "$clixon_cli -1 $clixon_cf -l o debug level 0" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" "^$" new "cli rpc" -expectfn "$clixon_cli -1 $clixon_cf -l o rpc ipv4" "^" +expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" "^" new "Kill backend" # Check if still alive @@ -100,7 +100,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -z $clixon_cf +sudo clixon_backend -z -f $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 546400cb..e4986695 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -3,13 +3,15 @@ # include err() and new() functions . ./lib.sh +cfg=/usr/local/etc/routing.xml +fyang=/tmp/leafref.yang # For memcheck # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" clixon_netconf=clixon_netconf clixon_cli=clixon_cli -cat < /tmp/leafref.yang +cat < $fyang module example{ import ietf-ip { prefix ip; @@ -48,70 +50,70 @@ EOF # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf -y /tmp/leafref.yang +sudo clixon_backend -zf $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init -f $clixon_cf -y /tmp/leafref.yang +sudo clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "leafref base config" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" " +expecteof "$clixon_netconf -qf $cfg -y $fyang" " 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' +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^eth0' new "leafref base commit" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "leafref add wrong ref" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "eth3
10.0.4.6
]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth3
10.0.4.6
]]>]]>" "^]]>]]>$" new "leafref validate" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "]]>]]>" "^missing-attribute" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^missing-attribute" new "leafref discard-changes" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "leafref add correct absref" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "eth0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^]]>]]>$" new "leafref add correct relref" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "eth0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^]]>]]>$" # XXX add address new "leafref validate (ok)" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" new "leafref delete leaf" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "eth0]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^" new "leafref validate (should fail)" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "]]>]]>" "^missing-attribute" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^missing-attribute" new "leafref discard-changes" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" - +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +exit new "cli leafref lo" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set default-address absname lo" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set default-address absname lo" "^$" new "cli leafref validate" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o validate" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o validate" "^$" new "cli sender" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender a" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" "^$" new "cli sender template" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender b template a" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" "^$" new "Kill backend" # Check if still alive @@ -120,7 +122,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 0f9bef6d..12d55b5f 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -3,137 +3,137 @@ # include err() and new() functions . ./lib.sh -clixon_cf="-f /usr/local/etc/routing.xml" +cfg=/usr/local/etc/routing.xml # For memcheck #clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" clixon_netconf=clixon_netconf -echo "clixon_backend -z $clixon_cf" +echo "clixon_backend -zf $cfg" # kill old backend (if any) new "kill old backend" -sudo clixon_backend -z $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init $clixon_cf +sudo clixon_backend -s init -f $cfg if [ $? -ne 0 ]; then err fi new "netconf tests" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none which should not change anything" -expecteof "$clixon_netconf -q $clixon_cf" "noneeth/0/0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "noneeth/0/0]]>]]>" "^]]>]]>$" new "Check nothing added" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none and create which should add eth/0/0" -expecteof "$clixon_netconf -q $clixon_cf" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" new "Check eth/0/0 added using xpath" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' "^eth/0/0ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' "^eth/0/0ethtrue]]>]]>$" new "Re-create same eth/0/0 which should generate error" -expecteof "$clixon_netconf -q $clixon_cf" 'eth/0/0ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg" 'eth/0/0ethnone ]]>]]>' "^" new "Delete eth/0/0 using none config" -expecteof "$clixon_netconf -q $clixon_cf" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" new "Check deleted eth/0/0 (non-presence container)" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^]]>]]>$' new "Re-Delete eth/0/0 using none should generate error" -expecteof "$clixon_netconf -q $clixon_cf" 'eth/0/0ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg" 'eth/0/0ethnone ]]>]]>' "^" new "netconf edit config" -expecteof "$clixon_netconf -q $clixon_cf" "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" new "netconf get config xpath" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' "^eth1true]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' "^eth1true]]>]]>$" new "netconf get config xpath parent" -expecteof "$clixon_netconf -q $clixon_cf" ']]>]]>' "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" ']]>]]>' "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" new "netconf validate missing type" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf get empty config2" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf edit extra xml" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf edit config eth1" -expecteof "$clixon_netconf -q $clixon_cf" "eth1eth]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "eth1eth]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf edit config replace XXX is merge?" -expecteof "$clixon_netconf -q $clixon_cf" "eth2ethmerge]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "eth2ethmerge]]>]]>" "^]]>]]>$" new "netconf get replaced config" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^eth1ethtrueeth2ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^eth1ethtrueeth2ethtrue]]>]]>$" new "netconf discard-changes" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf edit state operation should fail" -expecteof "$clixon_netconf -q $clixon_cf" "eth1eth]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg" "eth1eth]]>]]>" "^invalid-value" new "netconf get state operation" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^eth0eth42]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^eth0eth42]]>]]>$" new "netconf lock/unlock" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" new "netconf lock/lock" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf lock" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "close-session" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "kill-session" -expecteof "$clixon_netconf -q $clixon_cf" "44]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "44]]>]]>" "^]]>]]>$" new "copy startup" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf get startup" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^eth1ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^eth1ethtrue]]>]]>$" new "netconf delete startup" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf check empty startup" -expecteof "$clixon_netconf -q $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf rpc" -expecteof "$clixon_netconf -q $clixon_cf" "ipv4ipv4]]>]]>" "^ipv4" +expecteof "$clixon_netconf -qf $cfg" "ipv4ipv4]]>]]>" "^ipv4" new "netconf rpc w/o namespace" -expecteof "$clixon_netconf -q $clixon_cf" "ipv4ipv4]]>]]>" "^ipv4" +expecteof "$clixon_netconf -qf $cfg" "ipv4ipv4]]>]]>" "^ipv4" new "netconf subscription" -expectwait "$clixon_netconf -q $clixon_cf" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 +expectwait "$clixon_netconf -qf $cfg" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 new "Kill backend" # Check if still alive @@ -142,7 +142,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -z $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_order.sh b/test/test_order.sh new file mode 100755 index 00000000..0e07f47c --- /dev/null +++ b/test/test_order.sh @@ -0,0 +1,177 @@ +#!/bin/bash +# Order test. test ordered-by user and ordered-by system. +# For each leaf and leaf-lists, there are two lists, +# one ordered-by user and one ordered by system. +# The ordered-by user MUST be the order it is entered. +# No test of ordered-by system is done yet +# (we may want to sort them alphabetically for better performance). + +# include err() and new() functions +. ./lib.sh +cfg=/tmp/conf_yang.xml +fyang=/tmp/order.yang + +# For memcheck +# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" +clixon_netconf=clixon_netconf +clixon_cli=clixon_cli +dbdir=/tmp/order + +new "Set up $dbdir" +rm -rf $dbdir +mkdir $dbdir + +cat < $cfg + + /tmp/conf_yang.xml + /usr/local/share/routing/yang + example + /usr/local/lib/routing/clispec + /usr/local/lib/routing/cli + routing + /usr/local/var/routing/routing.sock + /usr/local/var/routing/routing.pidfile + 1 + $dbdir + /usr/local/lib/xmldb/text.so + true + +EOF + +cat < $fyang +module example{ + container c{ + leaf d{ + type string; + } + } + leaf l{ + type string; + } + leaf-list y0 { + ordered-by user; + type string; + } + leaf-list y1 { + ordered-by system; + type string; + } + list y2 { + ordered-by user; + key "k"; + leaf k { + type string; + } + leaf a { + type string; + } + } + list y3 { + ordered-by system; + key "k"; + leaf k { + type string; + } + leaf a { + type string; + } + } +} +EOF + +cat < $dbdir/running_db + + d + d + d
bar + dbar + b + b + hej + c + c + abar + abar + hopp + a + a + cbar + cbar + bbar + bbar + +EOF + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +new "start backend" +# start new backend +sudo clixon_backend -s running -f $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +# Check as file +new "verify running from start, should be: l,c,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if XML_SORT set to false" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^abar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^abar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^bbar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^bbar]]>]]>$" + +new "delete candidate" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" + +# LEAF_LISTS + +new "add two entries to leaf-list user order" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "cb]]>]]>" "^]]>]]>$" + +new "add one entry to leaf-list user order" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "a]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "add one entry to leaf-list user order after commit" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "0]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "verify leaf-list user order in running (as entered)" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^cba0]]>]]>$" + +# LISTS + +new "add two entries to list user order" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "cbarbfoo]]>]]>" "^]]>]]>$" + +new "add one entry to list user order" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "afie]]>]]>" "^]]>]]>$" + +new "verify list user order (as entered)" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^cbarbfooafie]]>]]>$" + +pid=`pgrep clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +#sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi diff --git a/test/test_perf.sh b/test/test_perf.sh index 57559981..a6a9087d 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -14,13 +14,14 @@ else echo "Usage: $0 [ []]" exit 1 fi - +cfg=/tmp/scaling-conf.xml fyang=/tmp/scaling.yang fconfig=/tmp/config + # include err() and new() functions . ./lib.sh -clixon_cf=/tmp/scaling-conf.xml + # For memcheck # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" @@ -46,9 +47,9 @@ module ietf-ip{ } EOF -cat < $clixon_cf +cat < $cfg - $clixon_cf + $cfg $fyang ietf-ip /usr/local/var/routing/routing.sock @@ -61,14 +62,14 @@ EOF # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf -y $fyang +sudo clixon_backend -zf $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init -f $clixon_cf -y $fyang +sudo clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi @@ -81,43 +82,43 @@ done echo "]]>]]>" >> $fconfig # Just for manual dbg -echo "$clixon_netconf -qf $clixon_cf -y $fyang" +echo "$clixon_netconf -qf $cfg -y $fyang" new "netconf edit large config" -expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" +expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^]]>]]>$" -#echo ']]>]]>' | $clixon_netconf -qf $clixon_cf -y $fyang +#echo ']]>]]>' | $clixon_netconf -qf $cfg -y $fyang new "netconf edit large config again" -expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" +expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^]]>]]>$" -#echo ']]>]]>' | $clixon_netconf -qf $clixon_cf -y $fyang +#echo ']]>]]>' | $clixon_netconf -qf $cfg -y $fyang rm $fconfig new "netconf commit large config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf commit same config again" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" - +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +exit new "netconf add one small config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "xy]]>]]>" "^]]>]]>$" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "xy]]>]]>" "^]]>]]>$" new "netconf add $req small config" time -p for (( i=0; i<$req; i++ )); do rnd=$(( ( RANDOM % $number ) )) echo "$rnd$rnd]]>]]>" -done | $clixon_netconf -qf $clixon_cf -y $fyang > /dev/null +done | $clixon_netconf -qf $cfg -y $fyang > /dev/null new "netconf get large config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^0011" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^0011" new "netconf get $req small config" time -p for (( i=0; i<$req; i++ )); do rnd=$(( ( RANDOM % $number ) )) echo "]]>]]>" -done | $clixon_netconf -qf $clixon_cf -y $fyang > /dev/null +done | $clixon_netconf -qf $cfg -y $fyang > /dev/null new "generate large leaf-list config" echo -n "replace" > $fconfig @@ -127,21 +128,21 @@ done echo "]]>]]>" >> $fconfig new "netconf replace large list-leaf config" -expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^]]>]]>$" +expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^]]>]]>$" rm $fconfig new "netconf commit large leaf-list config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf add small leaf-list config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "x]]>]]>" "^]]>]]>$" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "x]]>]]>" "^]]>]]>$" new "netconf commit small leaf-list config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf get large leaf-list config" -expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "]]>]]>" "^01" +expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^01" new "Kill backend" # Check if still alive @@ -150,7 +151,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index a8fc45d0..b63109d0 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -4,18 +4,19 @@ # include err() and new() functions . ./lib.sh +cfg=/usr/local/etc/routing.xml # This is a fixed 'state' implemented in routing_backend. It is always there state='{"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}' # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend" -sudo clixon_backend -s init -f $clixon_cf +sudo clixon_backend -s init -f $cfg if [ $? -ne 0 ]; then err fi @@ -109,7 +110,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_startup.sh b/test/test_startup.sh index 99cf1afa..1793b89c 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -8,12 +8,36 @@ # include err() and new() functions . ./lib.sh +cfg=/tmp/conf_startup.xml # For memcheck # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" clixon_netconf=clixon_netconf clixon_cli=clixon_cli +cat < $cfg + + $cfg + /usr/local/share/routing/yang + example + routing + /usr/local/lib/routing/backend + /usr/local/lib/routing/netconf + /usr/local/lib/routing/restconf + /usr/local/lib/routing/cli + /usr/local/lib/routing/clispec + /usr/local/var/routing/routing.sock + /usr/local/var/routing/routing.pidfile + 1 + /usr/local/var/routing + /usr/local/lib/xmldb/text.so + 0 + init + true + + +EOF + run(){ mode=$1 expect=$2 @@ -42,7 +66,7 @@ EOF EOF sudo mv /tmp/db /usr/local/var/routing/startup_db - cat < /tmp/config + cat < /tmp/config @@ -55,20 +79,20 @@ EOF # kill old backend (if any) new "kill old backend" - sudo clixon_backend -zf $clixon_cf + sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend" # start new backend - sudo clixon_backend -f $clixon_cf -s $mode -c /tmp/config + sudo clixon_backend -f $cfg -s $mode -c /tmp/config if [ $? -ne 0 ]; then err fi new "Check $mode" - expecteof "$clixon_netconf -qf $clixon_cf" ']]>]]>' "^$expect]]>]]>$" + expecteof "$clixon_netconf -qf $cfg" ']]>]]>' "^$expect]]>]]>$" new "Kill backend" # Check if still alive @@ -77,7 +101,7 @@ EOF err "backend already dead" fi # kill backend - sudo clixon_backend -zf $clixon_cf + sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi @@ -85,6 +109,6 @@ EOF run init '' run none 'runethtrue' -run running 'runethtruelolocaltrueextraethtrue' -run startup 'startupethtruelolocaltrueextraethtrue' +run running 'extraethtruelolocaltruerunethtrue' +run startup 'extraethtruelolocaltruestartupethtrue' diff --git a/test/test_type.sh b/test/test_type.sh index 988dedc6..3c01e271 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -4,13 +4,15 @@ # include err() and new() functions . ./lib.sh +cfg=/usr/local/etc/routing.xml +fyang=/tmp/type.yang # For memcheck #clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" clixon_cli=clixon_cli clixon_netconf=clixon_netconf -cat < /tmp/type.yang +cat < $fyang module example{ typedef ab { type string { @@ -63,55 +65,55 @@ EOF # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init -f $clixon_cf -y /tmp/type.yang +sudo clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "cli set ab" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set list a.b.a.b" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.a.b" "^$" new "cli set cd" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set list c.d.c.d" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list c.d.c.d" "^$" new "cli set ef" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set list e.f.e.f" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list e.f.e.f" "^$" new "cli set ab fail" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set list a&b&a&b" "^CLI syntax error" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a&b&a&b" "^CLI syntax error" new "cli set ad fail" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set list a.b.c.d" "^CLI syntax error" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.c.d" "^CLI syntax error" new "cli validate" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang -l o validate" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o validate" "^$" new "cli commit" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang -l o commit" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o commit" "^$" new "netconf validate ok" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf set ab wrong" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "a.b&c.d]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "a.b&c.d]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "cli enum value" -expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set status down" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set status down" "^$" new "Kill backend" # Check if still alive @@ -120,7 +122,7 @@ if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/test/test_yang.sh b/test/test_yang.sh index 68123b6a..858954d4 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -3,16 +3,17 @@ # include err() and new() functions . ./lib.sh -clixon_cf=/tmp/conf_yang.xml +cfg=/tmp/conf_yang.xml +fyang=/tmp/test.yang # For memcheck # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" clixon_netconf=clixon_netconf clixon_cli=clixon_cli -cat < /tmp/conf_yang.xml +cat < $cfg - /tmp/conf_yang.xml + $cfg /usr/local/share/routing/yang example /usr/local/lib/routing/clispec @@ -26,7 +27,7 @@ cat < /tmp/conf_yang.xml EOF -cat < /tmp/test.yang +cat < $fyang module example{ container x { list y { @@ -80,74 +81,73 @@ EOF # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf -y /tmp/test.yang +sudo clixon_backend -zf $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "start backend" # start new backend -sudo clixon_backend -s init -f $clixon_cf -y /tmp/test.yang +sudo clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi new "netconf edit config" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "125]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "125]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" # text empty type in running new "netconf commit 2nd" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf get config xpath" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^125]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^125]]>]]>$" new "netconf edit leaf-list" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "hejhopp]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "hejhopp]]>]]>" "^]]>]]>$" new "netconf get leaf-list" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^hejhopp]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhopp]]>]]>$" new "netconf get leaf-list path" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^hejhopp]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhopp]]>]]>$" new "netconf get (should be some)" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^125]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^125]]>]]>$" new "cli set leaf-list" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test.yang set x f e foo" "" +expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" "" new "cli show leaf-list" -expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test.yang show xpath /x/f/e" "foo" +expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" "foo" new "netconf set state data (not allowed)" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "42]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "42]]>]]>" "^invalid-value" new "netconf set presence and not present" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf get presence only" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" ']]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^]]>]]>$" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "netconf anyxml" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf validate anyxml" -expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" -new "Kill backend" # Check if still alive pid=`pgrep clixon_backend` if [ -z "$pid" ]; then err "backend already dead" fi # kill backend -sudo clixon_backend -zf $clixon_cf +#sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi diff --git a/yang/Makefile.in b/yang/Makefile.in index 11b1ae2f..ba27919d 100644 --- a/yang/Makefile.in +++ b/yang/Makefile.in @@ -38,7 +38,7 @@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ -YANGSPECS = clixon-config@2017-12-03.yang +YANGSPECS = clixon-config@2017-12-27.yang YANGSPECS += ietf-netconf@2011-06-01.yang YANGSPECS += ietf-inet-types@2013-07-15.yang diff --git a/yang/clixon-config@2017-12-27.yang b/yang/clixon-config@2017-12-27.yang new file mode 100644 index 00000000..50fb9981 --- /dev/null +++ b/yang/clixon-config@2017-12-27.yang @@ -0,0 +1,266 @@ +module clixon-config { + + prefix cc; + + organization + "Clicon / Clixon"; + + contact + "Olof Hagsand "; + + description + "Clixon configuration file + ***** BEGIN LICENSE BLOCK ***** + Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON + + Licensed under the Apache License, Version 2.0 (the \"License\"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an \"AS IS\" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the \"GPL\"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK *****"; + + revision 2017-12-27 { + description + "Added xml sort option for 1.4.0"; + } + typedef startup_mode{ + description + "Which method to boot/start clicon backend. + The methods differ in how they reach a running state + Which source database to commit from, if any."; + type enumeration{ + enum none{ + description + "Do not touch running state + Typically after crash when running state and db are synched"; + } + enum init{ + description + "Initialize running state. + Start with a completely clean running state"; + } + enum running{ + description + "Commit running db configuration into running state + After reboot if a persistent running db exists"; + } + enum startup{ + description + "Commit startup configuration into running state + After reboot when no persistent running db exists"; + } + } + } + container config { + leaf CLICON_CONFIGFILE{ + type string; + description + "Location of configuration-file for default values (this file)"; + } + leaf CLICON_YANG_DIR { + type string; + mandatory true; + description + "Location of YANG module and submodule files."; + } + leaf CLICON_YANG_MODULE_MAIN { + type string; + default "clicon"; + description + "Option used to construct initial yang file: + [@]"; + } + leaf CLICON_YANG_MODULE_REVISION { + type string; + description + "Option used to construct initial yang file: + [@]"; + } + leaf CLICON_BACKEND_DIR { + type string; + description + "Location of backend .so plugins. Load all .so + plugins in this dir as backend plugins"; + } + leaf CLICON_NETCONF_DIR { + type string; + description "Location of netconf (frontend) .so plugins"; + } + leaf CLICON_RESTCONF_DIR { + type string; + description + "Location of restconf (frontend) .so plugins. Load all .so + plugins in this dir as restconf code plugins"; + } + leaf CLICON_RESTCONF_PATH { + type string; + default "/www-data/fastcgi_restconf.sock"; + description + "FastCGI unix socket. Should be specified in webserver + Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock"; + } + leaf CLICON_CLI_DIR { + type string; + description + "Location of cli frontend .so plugins. Load all .so + plugins in this dir as CLI object plugins"; + } + leaf CLICON_CLISPEC_DIR { + type string; + description + "Location of frontend .cli cligen spec files. Load all .cli + files in this dir as CLI specification files"; + } + leaf CLICON_CLISPEC_FILE { + type string; + description "Specific frontend .cli cligen spec file."; + } + leaf CLICON_CLI_MODE { + type string; + default "base"; + description + "Startup CLI mode. This should match a CLICON_MODE set in + one of the clispec files"; + } + leaf CLICON_CLI_GENMODEL { + type int32; + default 1; + description + "Generate code for CLI completion of existing db symbols. + Example: Add name=\"myspec\" in datamodel spec and reference + as @myspec"; + } + leaf CLICON_CLI_GENMODEL_COMPLETION { + type int32; + default 1; + description "Generate code for CLI completion of existing db symbols"; + } + leaf CLICON_CLI_GENMODEL_TYPE { + type string; + default "VARS"; + description "How to generate and show CLI syntax: VARS|ALL"; + } + leaf CLICON_CLI_VARONLY { + type int32; + default 1; + description + "Dont include keys in cvec in cli vars callbacks, + ie a & k in 'a k ' ignored"; + } + leaf CLICON_CLI_LINESCROLLING { + type int32; + default 1; + description + "Set to 0 if you want CLI to wrap to next line. + Set to 1 if you want CLI to scroll sideways when approaching + right margin"; + } + leaf CLICON_SOCK_FAMILY { + type string; + default "UNIX"; + description + "Address family for communicating with clixon_backend + (UNIX|IPv4|IPv6)"; + } + leaf CLICON_SOCK { + type string; + mandatory true; + description + "If family above is AF_UNIX: Unix socket for communicating + with clixon_backend. If family is AF_INET: IPv4 address"; + } + leaf CLICON_SOCK_PORT { + type int32; + default 4535; + description + "Inet socket port for communicating with clixon_backend + (only IPv4|IPv6)"; + } + leaf CLICON_SOCK_GROUP { + type string; + default "clicon"; + description "Group membership to access clixon_backend unix socket"; + } + leaf CLICON_BACKEND_PIDFILE { + type string; + mandatory true; + description "Process-id file of backend daemon"; + } + leaf CLICON_AUTOCOMMIT { + type int32; + default 0; + description + "Set if all configuration changes are committed automatically + on every edit change. Explicit commit commands unnecessary"; + } + leaf CLICON_MASTER_PLUGIN { + type string; + default "master"; + description + "Name of master plugin (both frontend and backend). + Master plugin has special callbacks for frontends. + See clicon user manual for more info. (Obsolete?)"; + } + leaf CLICON_XMLDB_DIR { + type string; + mandatory true; + description + "Directory where \"running\", \"candidate\" and \"startup\" are placed"; + } + leaf CLICON_XMLDB_PLUGIN { + type string; + mandatory true; + description + "XMLDB datastore plugin filename + (see datastore/ and clixon_xml_db.[ch])"; + } + leaf CLICON_XMLDB_CACHE { + type boolean; + default true; + description + "XMLDB datastore cache. + If set, XML candidate/running parsed tree is stored in memory + If not set, candidate/running is always accessed via disk."; + } + leaf CLICON_XML_SORT { + type boolean; + default true; + description + "If set, sort XML lists and leaf-lists alphabetically and uses binary + search. Unless ordered-by user is used. + Only works for Yang specified XML. + If not set, all lists accessed via linear search."; + } + leaf CLICON_USE_STARTUP_CONFIG { + type int32; + default 0; + description + "Enabled uses \"startup\" configuration on boot. It is called + startup_db and exists in XMLDB_DIR. + NOTE: Obsolete with 1.3.3 and CLICON_STARTUP_MODE"; + } + leaf CLICON_STARTUP_MODE { + type startup_mode; + description "Which method to boot/start clicon backend"; + } + } +}