Optimized search performance for large lists by sorting and binary search

This commit is contained in:
Olof hagsand 2017-12-27 11:34:47 +01:00
parent b743b0a080
commit 4b92dbdc10
28 changed files with 1405 additions and 701 deletions

View file

@ -2,6 +2,15 @@
## 3.4.0 (Upcoming) ## 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. * 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. * Fixed bug that deletes running on startup if backup started with -m running.

View file

@ -547,7 +547,6 @@ yang2cli_list(clicon_handle h,
{ {
yang_stmt *yc; yang_stmt *yc;
yang_stmt *yd; yang_stmt *yd;
yang_stmt *ykey;
yang_stmt *yleaf; yang_stmt *yleaf;
int i; int i;
cg_var *cvi; cg_var *cvi;
@ -568,13 +567,7 @@ yang2cli_list(clicon_handle h,
cprintf(cbuf, "(\"%s\")", helptext); cprintf(cbuf, "(\"%s\")", helptext);
} }
/* Loop over all key variables */ /* Loop over all key variables */
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
clicon_err(OE_XML, 0, "List statement \"%s\" has no key", ys->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL; cvi = NULL;
/* Iterate over individual keys */ /* Iterate over individual keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
@ -614,8 +607,6 @@ yang2cli_list(clicon_handle h,
done: done:
if (helptext) if (helptext)
free(helptext); free(helptext);
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }

View file

@ -201,14 +201,7 @@ append_listkeys(cbuf *ckey,
char *bodyenc; char *bodyenc;
int i=0; int i=0;
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, ys->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL; cvi = NULL;
/* Iterate over individual keys */ /* Iterate over individual keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
@ -230,8 +223,6 @@ append_listkeys(cbuf *ckey,
} }
retval = 0; retval = 0;
done: done:
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }

View file

@ -276,7 +276,6 @@ text_setopt(xmldb_handle xh,
return retval; return retval;
} }
/*! Ensure that xt only has a single sub-element and that is "config" /*! Ensure that xt only has a single sub-element and that is "config"
*/ */
static int static int
@ -446,6 +445,9 @@ text_get(xmldb_handle xh,
if (singleconfigroot(xt, &xt) < 0) if (singleconfigroot(xt, &xt) < 0)
goto done; 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 */ } /* xt == NULL */
/* Here xt looks like: <config>...</config> */ /* Here xt looks like: <config>...</config> */
@ -507,7 +509,10 @@ text_get(xmldb_handle xh,
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done; goto done;
/* Order XML children according to YANG */ /* 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; goto done;
if (debug>1) if (debug>1)
clicon_xml2file(stderr, xt, 0, 1); clicon_xml2file(stderr, xt, 0, 1);
@ -600,10 +605,6 @@ text_modify(cxobj *x0,
if (xml_value_set(x0b, x1bstr) < 0) if (xml_value_set(x0b, x1bstr) < 0)
goto done; goto done;
} }
if (xml_child_hash &&
xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
break; break;
case OP_DELETE: case OP_DELETE:
if (x0==NULL){ if (x0==NULL){
@ -652,11 +653,7 @@ text_modify(cxobj *x0,
goto done; goto done;
if (op==OP_NONE) if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */ xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
} }
if (xml_child_hash &&
xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
/* First pass: mark existing children in base */ /* First pass: mark existing children in base */
/* Loop through children of the modification tree */ /* Loop through children of the modification tree */
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){ if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
@ -826,6 +823,7 @@ text_put(xmldb_handle xh,
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
char *dbfile = NULL; char *dbfile = NULL;
int fd = -1; int fd = -1;
FILE *f = NULL;
cbuf *cb = NULL; cbuf *cb = NULL;
yang_spec *yspec; yang_spec *yspec;
cxobj *x0 = NULL; cxobj *x0 = NULL;
@ -880,17 +878,14 @@ text_put(xmldb_handle xh,
} }
/* Add yang specification backpointer to all XML nodes */ /* 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) if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
/* Add hash */ if (xml_child_sort && xml_apply0(x1, CX_ELMNT, xml_sort, NULL) < 0)
#if 0
if (xml_child_hash &&
xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done; 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) if (text_modify_top(x0, x1, yspec, op) < 0)
goto done; goto done;
@ -907,6 +902,8 @@ text_put(xmldb_handle xh,
/* Remove (prune) nodes that are marked (non-presence containers w/o children) */ /* Remove (prune) nodes that are marked (non-presence containers w/o children) */
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0) if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
goto done; 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 */ /* Write back to datastore cache if first time */
if (xmltree_cache){ if (xmltree_cache){
@ -926,42 +923,20 @@ text_put(xmldb_handle xh,
goto done; goto done;
} }
} }
#if 1 if (fd != -1){
if (fd != -1)
close(fd); close(fd);
{ fd = -1;
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);
} }
#else if ((f = fopen(dbfile, "w")) == NULL){
// output: clicon_err(OE_CFG, errno, "Creating file %s", dbfile);
/* Print out top-level xml tree after modification to file */
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
} }
if (clicon_xml2cbuf(cb, x0, 0, 1) < 0) if (xml_print(f, x0) < 0)
goto done; 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; retval = 0;
done: done:
if (f != NULL)
fclose(f);
if (dbfile) if (dbfile)
free(dbfile); free(dbfile);
if (fd != -1) if (fd != -1)
@ -1020,7 +995,6 @@ text_copy(xmldb_handle xh,
hash_add(th->th_dbs, to, &de0, sizeof(de0)); hash_add(th->th_dbs, to, &de0, sizeof(de0));
} }
} }
} }
if (text_db2file(th, from, &fromfile) < 0) if (text_db2file(th, from, &fromfile) < 0)
goto done; goto done;
@ -1175,7 +1149,8 @@ text_delete(xmldb_handle xh,
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
struct db_element *de = NULL; struct db_element *de = NULL;
cxobj *xt = NULL; cxobj *xt = NULL;
struct stat sb;
if (xmltree_cache){ if (xmltree_cache){
if ((de = hash_value(th->th_dbs, db, NULL)) != NULL){ if ((de = hash_value(th->th_dbs, db, NULL)) != NULL){
if ((xt = de->de_xml) != NULL){ if ((xt = de->de_xml) != NULL){
@ -1186,10 +1161,11 @@ text_delete(xmldb_handle xh,
} }
if (text_db2file(th, db, &filename) < 0) if (text_db2file(th, db, &filename) < 0)
goto done; goto done;
if (unlink(filename) < 0){ if (lstat(filename, &sb) == 0)
clicon_err(OE_DB, errno, "unlink %s", filename); if (unlink(filename) < 0){
goto done; clicon_err(OE_DB, errno, "unlink %s", filename);
} goto done;
}
retval = 0; retval = 0;
done: done:
if (filename) if (filename)
@ -1303,7 +1279,8 @@ usage(char *argv0)
} }
int int
main(int argc, char **argv) main(int argc,
char **argv)
{ {
cxobj *xt; cxobj *xt;
cxobj *xn; cxobj *xn;

View file

@ -76,6 +76,7 @@
#include <clixon/clixon_string.h> #include <clixon/clixon_string.h>
#include <clixon/clixon_file.h> #include <clixon/clixon_file.h>
#include <clixon/clixon_xml.h> #include <clixon/clixon_xml.h>
#include <clixon/clixon_xml_sort.h>
#include <clixon/clixon_proto.h> #include <clixon/clixon_proto.h>
#include <clixon/clixon_proto_client.h> #include <clixon/clixon_proto_client.h>
#include <clixon/clixon_plugin.h> #include <clixon/clixon_plugin.h>

View file

@ -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_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */ #define XML_FLAG_NONE 0x10 /* Node is added as NONE */
/* Hash for XML trees list entries /* Sort and binary search of XML children
* Experimental * Experimental
*/ */
extern int xml_child_hash; extern int xml_child_sort;
/* /*
* Prototypes * 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_body_uint32(cxobj *xb, uint32_t *val);
int xml_operation(char *opstr, enum operation_type *op); int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op); char *xml_operation2str(enum operation_type op);
int xml_child_spec(char *name, cxobj *xp, yang_spec *yspec, yang_stmt **yp); int xml_sort(cxobj *x0, void *arg);
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);
#ifdef XML_COMPAT /* See CHANGELOG */ #ifdef XML_COMPAT /* See CHANGELOG */
/* MANUAL CHANGE: xml_new(name, parent) --> xml_new(name, parent, NULL) */ /* MANUAL CHANGE: xml_new(name, parent) --> xml_new(name, parent, NULL) */

View file

@ -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_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
int schemanode, cxobj **xpathp, yang_node **ypathp); int schemanode, cxobj **xpathp, yang_node **ypathp);
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec); int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
int yang_enum_int_value(cxobj *node, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -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 */

View file

@ -155,10 +155,17 @@ struct yang_stmt{
char *ys_argument; /* String / argument depending on keyword */ char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */ int ys_flags; /* Flags according to YANG_FLAG_* above */
cg_var *ys_cv; /* cligen variable. The following stmts have cvs:: cg_var *ys_cv; /* cligen variable. See ys_populate()
leaf, leaf-list, mandatory, fraction-digits */ 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 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 */ 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_datanode(yang_node *yn, char *argument);
yang_stmt *yang_find_schemanode(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); 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(FILE *f, yang_node *yn);
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal); int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
int yang_parse(clicon_handle h, const char *yang_dir, int yang_parse(clicon_handle h, const char *yang_dir,

View file

@ -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 \ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_handle.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_json.c clixon_yang.c clixon_yang_type.c \
clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \ clixon_proto.c clixon_proto_client.c \

View file

@ -382,6 +382,11 @@ clicon_options_main(clicon_handle h)
/* Read configfile */ /* Read configfile */
if (clicon_option_readfile_xml(copt, configfile, yspec) < 0) if (clicon_option_readfile_xml(copt, configfile, yspec) < 0)
goto done; goto done;
/* Specific option handling */
if (clicon_option_bool(h, "CLICON_XML_SORT") == 1)
xml_child_sort = 1;
else
xml_child_sort = 0;
} }
else { else {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
@ -409,6 +414,7 @@ clicon_options_main(clicon_handle h)
/*! Check if a clicon option has a value /*! Check if a clicon option has a value
* @param[in] h clicon_handle * @param[in] h clicon_handle
* @param[in] name option name * @param[in] name option name
* @retval
*/ */
int int
clicon_option_exists(clicon_handle h, clicon_option_exists(clicon_handle h,

View file

@ -82,21 +82,37 @@
/*! xml tree node, with name, type, parent, children, etc /*! xml tree node, with name, type, parent, children, etc
* Note that this is a private type not visible from externally, use * Note that this is a private type not visible from externally, use
* access functions. * access functions.
* A word on ordering of x_children:
* If there is no yang specification, xml children are ordered as they are entered.
* If there is a yang specification (and the appropriate functions are called) the
* xml children are ordered as follows:
* 1) After yang specification order.
* 2) list and leaf-list are sorted alphabetically unless ordered-by user.
* Example:
* container c{
* leaf a;
* leaf-list x;
* }
* then regardless in which order the xml is entered, it will be sorted as follows:
* <c>
* <a/>
* <x>a</<x>
* <x>b</<x>
* </c>
*/ */
struct xml{ struct xml{
char *x_name; /* name of node */ char *x_name; /* name of node */
char *x_namespace; /* namespace, if any */ char *x_namespace; /* namespace, if any */
struct xml *x_up; /* parent node in hierarchy if any */ struct xml *x_up; /* parent node in hierarchy if any */
struct xml **x_childvec; /* vector of children nodes */ 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 */ enum cxobj_type x_type; /* type of node: element, attribute, body */
char *x_value; /* attribute and body nodes have values */ char *x_value; /* attribute and body nodes have values */
int _x_vector_i; /* internal use: xml_child_each */ int _x_vector_i; /* internal use: xml_child_each */
int x_flags; /* Flags according to XML_FLAG_* above */ int x_flags; /* Flags according to XML_FLAG_* above */
yang_stmt *x_spec; /* Pointer to specification, eg yang, by yang_stmt *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */ reference, dont free */
cg_var *x_cv; /* If body this contains the typed value */ cg_var *x_cv; /* If body this contains the typed value */
clicon_hash_t *x_hash; /* Hash of children */
}; };
/* Mapping between xml type <--> string */ /* Mapping between xml type <--> string */
@ -108,10 +124,6 @@ static const map_str2int xsmap[] = {
{NULL, -1} {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 /*! Translate from xml type in enum form to string keyword
* @param[in] type Xml type * @param[in] type Xml type
@ -475,11 +487,11 @@ xml_child_append(cxobj *x,
return 0; 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 * @code
* xml_childvec_set(x, 2); * xml_childvec_set(x, 2);
* xml_child_i(x, 0) = xc0; * xml_child_i_set(x, 0, xc0)
* xml_child_i(x, 1) = xc1; * xml_child_i_set(x, 1, xc1);
* @endcode * @endcode
*/ */
int int
@ -502,9 +514,9 @@ xml_childvec_get(cxobj *x)
/*! Create new xml node given a name and parent. Free with xml_free(). /*! Create new xml node given a name and parent. Free with xml_free().
* *
* @param[in] name Name of new * @param[in] name Name of XML node
* @param[in] xp The parent where the new xml node should be inserted * @param[in] xp The parent where the new xml node will be appended
* @param[in] spec Yang stmt or NULL. * @param[in] spec Yang statement of this XML or NULL.
* @retval xml Created xml object if successful. Free with xml_free() * @retval xml Created xml object if successful. Free with xml_free()
* @retval NULL Error and clicon_err() called * @retval NULL Error and clicon_err() called
* @code * @code
@ -536,8 +548,6 @@ xml_new(char *name,
if (xp && xml_child_append(xp, x) < 0) if (xp && xml_child_append(xp, x) < 0)
return NULL; return NULL;
x->x_spec = spec; /* Can be NULL */ x->x_spec = spec; /* Can be NULL */
if (xml_child_hash && spec && xml_hash_add(x) < 0)
return NULL;
return x; return x;
} }
@ -655,8 +665,6 @@ xml_purge(cxobj *xc)
int i; int i;
cxobj *xp; cxobj *xp;
if (xml_child_hash)
xml_hash_rm_entry(xc);
if ((xp = xml_parent(xc)) != NULL){ if ((xp = xml_parent(xc)) != NULL){
/* Find child order i in parent*/ /* Find child order i in parent*/
for (i=0; i<xml_child_nr(xp); i++) for (i=0; i<xml_child_nr(xp); i++)
@ -928,8 +936,6 @@ xml_free(cxobj *x)
x->x_childvec[i] = NULL; x->x_childvec[i] = NULL;
} }
} }
if (x->x_hash)
hash_free(x->x_hash);
if (x->x_childvec) if (x->x_childvec)
free(x->x_childvec); free(x->x_childvec);
free(x); 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 /*! Help function to qsort for sorting entries in xml child vector
* If no xml parent, find root yang stmt matching name * @param[in] arg1
* @param[in] name Name of child * @param[in] arg2
* @param[in] xp XML parent, can be NULL. * @retval 0 If equal
* @param[in] yspec Yang specification (top level) * @retval <0 if arg1 is less than arg2
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found * @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 static int
xml_hash_init(cxobj *x) xml_cmp(const void* arg1,
const void* arg2)
{ {
if ((x->x_hash = hash_init()) < 0) struct xml *x1 = *(struct xml**)arg1;
return -1; struct xml *x2 = *(struct xml**)arg2;
return 0; yang_stmt *y1;
} yang_stmt *y2;
int yi1;
/*! XML remove hash only. Not in parent. int yi2;
* @param[in] x XML object cvec *cvk = NULL; /* vector of index keys */
* eg same as xml_hash_op(x, -1) cg_var *cvi;
*/ int equal = 0;
int char *b1;
xml_hash_rm_only(cxobj *x) char *b2;
{ char *keyname;
if (x && x->x_hash){
hash_free(x->x_hash);
x->x_hash = NULL;
}
return 0;
}
/* Compute hash key for xml entry
* @param[in] x
* @param[in] y
* @param[out] key
* key: yangtype+x1name
* LEAFLIST: b0
* LIST: b2vec+b0 -> x0c
*/
int
xml_hash_key(cxobj *x,
yang_stmt *y,
cbuf *key)
{
int retval = -1;
yang_stmt *ykey;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
char *b;
char *str;
switch (y->ys_keyword){
case Y_CONTAINER: str = "c"; break;
case Y_LEAF: str = "e"; break;
case Y_LEAF_LIST: str = "l"; break;
case Y_LIST: str = "i"; break;
default:
str = "xx"; break;
break;
}
cprintf(key, "%s%s", str, xml_name(x));
switch (y->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
if ((b = xml_body(x)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "%s", xml_body(x));
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
keyname = cv_string_get(cvi);
if ((b = xml_find_body(x, keyname)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "/%s", b);
}
break;
default:
break;
}
ok:
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! XML hash add. Create hash and add key/value to parent
*
* @param[in] arg add flag. If 1, else if 0 remove.
* Typically called for a whole tree.
*/
int
xml_hash_op(cxobj *x,
void *arg)
{
int add = (intptr_t)arg;
#if 1
if (add)
return xml_hash_add(x);
else
return xml_hash_rm_entry(x);
#else
int retval = -1; if (x1 == NULL){
cxobj *xp; if (x2 == NULL)
clicon_hash_t *ph; return 0;
yang_stmt *y; else
cbuf *key = NULL; /* cligen buffer hash key */ return -1;
if (xml_hash(x)==NULL){
if (add)
xml_hash_init(x);
} }
else if (!add) else if (x2 == NULL)
xml_hash_rm_only(x); return 1;
if ((xp = xml_parent(x)) == NULL) y1 = xml_spec(x1);
goto ok; y2 = xml_spec(x2);
if ((ph = xml_hash(xp))==NULL) if (y1==NULL || y2==NULL)
goto ok; return 0; /* just ignore */
if ((y = xml_spec(x)) == NULL) if (y1 != y2){
goto ok; yi1 = yang_order(y1);
if ((key = cbuf_new()) == NULL){ yi2 = yang_order(y2);
clicon_err(OE_XML, errno, "cbuf_new"); if ((equal = yi1-yi2) != 0)
goto done; return equal;
} }
if (xml_hash_key(x, y, key) < 0) /* Now y1=y2, same Yang spec, can only be list or leaf-list,
goto done; * sort according to key
if (cbuf_len(key)){ */
// fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x); if (yang_find((yang_node*)y1, Y_ORDERED_BY, "user") != NULL)
if (add){ return 0; /* Ordered by user: maintain existing order */
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL) 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; goto done;
} }
else equal = 0;
if (hash_del(ph, cbuf_get(key)) < 0) break;
goto done; default:
break;
} }
ok:
retval = 0;
done: done:
if (key) return equal;
cbuf_free(key);
return retval;
#endif
} }
/*! XML hash add. Create hash and add key/value to parent /*! Sort children of an XML node
* * Assume populated by yang spec.
* @param[in] x XML object * @param[in] x0 XML node
* eg same as xml_hash_op(x, 1) * @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 int
xml_hash_add(cxobj *x) xml_sort(cxobj *x,
void *arg)
{ {
int retval = -1; qsort(x->x_childvec, x->x_childvec_len, sizeof(struct xml*), xml_cmp);
cxobj *xp; return 0;
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;
} }
/*! 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 * Turn this on to get a xml parse and pretty print test program

View file

@ -85,6 +85,7 @@
#include "clixon_xsl.h" #include "clixon_xsl.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
/* Something to do with reverse engineering of junos syntax? */ /* Something to do with reverse engineering of junos syntax? */
@ -590,136 +591,6 @@ cvec2xml_1(cvec *cvv,
return retval; return retval;
} }
/*! Given child tree x1c, find matching child in base tree x0
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
500K xml_child_each/cvec_each calls.
The outer loop is large for large lists
The inner loop is small
Major time in xml_find_body()
Can one do a binary search in the x0 list?
*/
int
match_base_child(cxobj *x0,
cxobj *x1c,
cxobj **x0cp,
yang_stmt *yc)
{
int retval = -1;
char *x1cname;
cxobj *x0c = NULL; /* x0 child */
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *keyname;
int equal;
char **b1vec = NULL;
int i;
cxobj **p;
cbuf *key = NULL; /* cligen buffer hash key */
size_t vlen;
if (xml_child_hash){
*x0cp = NULL; /* return value */
if (xml_hash(x0) == NULL)
goto nohash;
if ((key = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done1;
}
if (xml_hash_key(x1c, yc, key) < 0)
goto done;
x0c = NULL;
if (cbuf_len(key))
if ((p = hash_value(xml_hash(x0), cbuf_get(key), &vlen)) != NULL){
assert(vlen == sizeof(x0c));
x0c = *p;
}
// fprintf(stderr, "%s get %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x0c);
*x0cp = x0c;
retval = 0;
done1:
if (key)
cbuf_free(key);
return retval;
}
nohash:
*x0cp = NULL; /* return value */
x1cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
x0c = xml_find(x0, x1cname);
break;
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1c)) == NULL)
goto ok;
x0c = xml_find_body_obj(x0, x1cname, b1);
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL)
i++;
if ((b1vec = calloc(i, sizeof(b1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
goto ok; /* not found */
b1vec[i++] = b1;
}
/* Iterate over x0 tree to (1) find a child that matches name
(2) that have keys that matches */
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
equal = 0;
if (strcmp(xml_name(x0c), x1cname))
continue;
/* Must be inner loop */
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
b1 = b1vec[i++];
equal = 0;
keyname = cv_string_get(cvi);
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break; /* stop as soon as inequal key found */
equal=1; /* reaches here for all keynames, x0c is found. */
}
if (equal) /* x0c and x1c equal, otherwise look for other */
break;
} /* while x0c */
break;
default:
break;
}
ok:
*x0cp = x0c;
retval = 0;
done:
if (b1vec)
free(b1vec);
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Find next yang node, either start from yang_spec or some yang-node /*! Find next yang node, either start from yang_spec or some yang-node
* @param[in] y Node spec or sny yang-node * @param[in] y Node spec or sny yang-node
@ -895,7 +766,6 @@ yang2api_path_fmt_1(yang_stmt *ys,
cbuf *cb) cbuf *cb)
{ {
yang_node *yp; /* parent */ yang_node *yp; /* parent */
yang_stmt *ykey;
int i; int i;
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
int retval = -1; int retval = -1;
@ -926,14 +796,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
switch (ys->ys_keyword){ switch (ys->ys_keyword){
case Y_LIST: case Y_LIST:
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, ys->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
if (cvec_len(cvk)) if (cvec_len(cvk))
cprintf(cb, "="); cprintf(cb, "=");
/* Iterate over individual keys */ /* Iterate over individual keys */
@ -951,8 +814,6 @@ yang2api_path_fmt_1(yang_stmt *ys,
} /* switch */ } /* switch */
retval = 0; retval = 0;
done: done:
if (cvk)
cvec_free(cvk);
return retval; return retval;
} }
@ -1370,13 +1231,16 @@ xml_order(cxobj *xt,
goto done; goto done;
} }
j0 = 0; j0 = 0;
/* Go through xml children and ensure they are same order as yspec children */ /* Go through yang node's children and ensure that the
* xml children follow this order.
* Do not order the list or leaf-list children (have same name).
*/
for (i=0; i<y->ys_len; i++){ for (i=0; i<y->ys_len; i++){
yc = y->ys_stmt[i]; yc = y->ys_stmt[i];
if (!yang_datanode(yc)) if (!yang_datanode(yc))
continue; continue;
yname = yc->ys_argument; yname = yc->ys_argument;
/* First go thru xml children with same name */ /* First go thru xml children with same name in rest of list */
for (; j0<xml_child_nr(xt); j0++){ for (; j0<xml_child_nr(xt); j0++){
xc = xml_child_i(xt, j0); xc = xml_child_i(xt, j0);
if (xml_type(xc) != CX_ELMNT) if (xml_type(xc) != CX_ELMNT)
@ -1528,7 +1392,6 @@ api_path2xpath_cvv(yang_spec *yspec,
yang_stmt *y = NULL; yang_stmt *y = NULL;
char *val; char *val;
char *v; char *v;
yang_stmt *ykey;
cg_var *cvi; cg_var *cvi;
for (i=offset; i<cvec_len(cvv); i++){ for (i=offset; i<cvec_len(cvv); i++){
@ -1559,17 +1422,7 @@ api_path2xpath_cvv(yang_spec *yspec,
*v = '\0'; *v = '\0';
v++; v++;
} }
/* Find keys */ cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_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;
}
clicon_debug(1, "ykey:%s", ykey->ys_argument);
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL; cvi = NULL;
/* Iterate over individual yang keys */ /* Iterate over individual yang keys */
cprintf(xpath, "/%s", name); cprintf(xpath, "/%s", name);
@ -1647,7 +1500,6 @@ api_path2xml_vec(char **vec,
char *name; char *name;
char *restval = NULL; char *restval = NULL;
char *restval_enc; char *restval_enc;
yang_stmt *ykey;
cxobj *xn = NULL; /* new */ cxobj *xn = NULL; /* new */
cxobj *xb; /* body */ cxobj *xb; /* body */
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
@ -1709,15 +1561,7 @@ api_path2xml_vec(char **vec,
goto done; goto done;
break; break;
case Y_LIST: case Y_LIST:
/* Get the yang list key */ cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
if (valvec){ if (valvec){
free(valvec); free(valvec);
valvec = NULL; valvec = NULL;
@ -1754,10 +1598,6 @@ api_path2xml_vec(char **vec,
if (xml_value_set(xb, val2) <0) if (xml_value_set(xb, val2) <0)
goto done; goto done;
} }
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
break; break;
default: /* eg Y_CONTAINER, Y_LEAF */ default: /* eg Y_CONTAINER, Y_LEAF */
if ((x = xml_new(name, x0, y)) == NULL) if ((x = xml_new(name, x0, y)) == NULL)

View file

@ -75,6 +75,7 @@
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"
void void

476
lib/src/clixon_xml_sort.c Normal file
View file

@ -0,0 +1,476 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* XML search functions when used with YANG
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
/*
* Variables
*/
/* Sort and binary search of XML children
* Experimental
*/
int xml_child_sort = 1;
/*! Given an XML object and a child name, return yang stmt of child
* If no xml parent, find root yang stmt matching name
* @param[in] name Name of child
* @param[in] xp XML parent, can be NULL.
* @param[in] yspec Yang specification (top level)
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
*/
int
xml_child_spec(char *name,
cxobj *xp,
yang_spec *yspec,
yang_stmt **yresult)
{
yang_stmt *y; /* result yang node */
yang_stmt *yparent; /* parent yang */
if (xp && (yparent = xml_spec(xp)) != NULL)
y = yang_find_datanode((yang_node*)yparent, name);
else if (yspec)
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
else
y = NULL;
*yresult = y;
return 0;
}
/*!
* @param[in] yangi Yang order
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
* @param[out] userorder If set, this yang order is user ordered, linear search
* @retval 0 If equal (or userorder set)
* @retval <0 if arg1 is less than arg2
* @retval >0 if arg1 is greater than arg2
*/
static int
xml_cmp1(cxobj *x,
yang_stmt *y,
char *name,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
int *userorder)
{
char *b;
int i;
char *keyname;
char *key;
/* Check if same yang spec (order in yang stmt list) */
switch (keyword){
case Y_CONTAINER: /* Match with name */
case Y_LEAF: /* Match with name */
return strcmp(name, xml_name(x));
break;
case Y_LEAF_LIST: /* Match with name and value */
if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL)
*userorder=1;
b=xml_body(x);
return strcmp(keyval[0], b);
break;
case Y_LIST: /* Match with array of key values */
if (userorder && yang_find((yang_node*)y, Y_ORDERED_BY, "user") != NULL)
*userorder=1;
for (i=0; i<keynr; i++){
keyname = keyvec[i];
key = keyval[i];
if ((b = xml_find_body(x, keyname)) == NULL)
break; /* error case */
return strcmp(key, b);
}
return 0;
break;
default:
break;
}
return 0; /* should not reach here */
}
static cxobj *
xml_search_userorder(cxobj *x0,
yang_stmt *y,
char *name,
int yangi,
int mid,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval)
{
int i;
cxobj *xc;
for (i=mid+1; i<xml_child_nr(x0); i++){ /* First increment */
xc = xml_child_i(x0, i);
y = xml_spec(xc);
if (yangi!=yang_order(y))
break;
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, NULL) == 0)
return xc;
}
for (i=mid-1; i>=0; i--){ /* Then decrement */
xc = xml_child_i(x0, i);
y = xml_spec(xc);
if (yangi!=yang_order(y))
break;
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, NULL) == 0)
return xc;
}
return NULL; /* Not found */
}
/*!
* @param[in] yangi Yang order
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
* @param[in] low Lower bound of childvec search interval
* @param[in] upper Lower bound of childvec search interval
*/
static cxobj *
xml_search1(cxobj *x0,
char *name,
int yangi,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
int low,
int upper)
{
int mid;
int cmp;
cxobj *xc;
yang_stmt *y;
int userorder= 0;
if (upper < low)
return NULL; /* not found */
mid = (low + upper) / 2;
if (mid >= xml_child_nr(x0)) /* beyond range */
return NULL;
xc = xml_child_i(x0, mid);
assert(y = xml_spec(xc));
if ((cmp = yangi-yang_order(y)) == 0){
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, &userorder);
if (userorder && cmp) /* Look inside this yangi order */
return xml_search_userorder(x0, y, name, yangi, mid, keyword, keynr, keyvec, keyval);
}
if (cmp == 0)
return xc;
else if (cmp < 0)
return xml_search1(x0, name, yangi, keyword,
keynr, keyvec, keyval, low, mid-1);
else
return xml_search1(x0, name, yangi, keyword,
keynr, keyvec, keyval, mid+1, upper);
return NULL;
}
/*!
* @param[in] yangi yang child order
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
*/
cxobj *
xml_search(cxobj *x0,
char *name,
int yangi,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval)
{
return xml_search1(x0, name, yangi, keyword, keynr, keyvec, keyval,
0, xml_child_nr(x0));
}
/*! Find matching xml child given name and optional key values
* container: x0, y->keyword, name
* list: x0, y->keyword, y->key, name
*
* The function needs a vector of key values (or single for LEAF_LIST).
* What format?
* 1) argc/argv:: "val1","val2" <<==
* 2) cv-list?
* 3) va-list?
*
* yc - LIST (interface) -
* ^
* |
* x0-->x0c-->(name=interface)+->x(name=name)->xb(value="eth0") <==this is
* |
* v
* x1c->name (interface)
* x1c->x(name=name)->xb(value="eth0")
*
* CONTAINER:name
* LEAF: name
* LEAFLIST: name/body... #b0
* LIST: name/key0/key1... #b2vec+b0 -> x0c
* <interface><name>eth0</name></interface>
* <interface><name>eth1</name></interface>
* <interface><name>eth2</name></interface>
* @param[in] x0 XML node. Find child of this node.
* @param[in] keyword Yang keyword. Relevant: container, list, leaf, leaf_list
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
* @param[out] xp Return value on success, pointer to XML child node
* @note If keyword is:
* - list, keyvec and keyval should be an array with keynr length
* - leaf_list, keyval should be 1 and keyval should contain one element
* - otherwise, keyval should be 0 and keyval and keyvec should be both NULL.
*/
cxobj *
xml_match(cxobj *x0,
char *name,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval)
{
char *key;
char *keyname;
char *b0;
cxobj *x = NULL;
int equal;
int i;
x = NULL;
switch (keyword){
case Y_CONTAINER: /* Match with name */
case Y_LEAF: /* Match with name */
if (keynr != 0){
clicon_err(OE_XML, EINVAL, "Expected no key argument to CONTAINER or LEAF");
goto ok;
}
x = xml_find(x0, name);
break;
case Y_LEAF_LIST: /* Match with name and value */
if (keynr != 1)
goto ok;
x = xml_find_body_obj(x0, name, keyval[0]);
break;
case Y_LIST: /* Match with array of key values */
i = 0;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL){
equal = 0;
if (strcmp(xml_name(x), name))
continue;
/* Must be inner loop */
for (i=0; i<keynr; i++){
keyname = keyvec[i];
key = keyval[i];
equal = 0;
if ((b0 = xml_find_body(x, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, key))
break; /* stop as soon as inequal key found */
equal=1; /* reaches here for all keynames, x is found. */
}
if (equal) /* x matches, oyherwise look for other */
break;
} /* while x */
break;
default:
break;
}
ok:
return x;
}
/*! Given child tree x1c, find matching child in base tree x0
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
500K xml_child_each/cvec_each calls.
The outer loop is large for large lists
The inner loop is small
Major time in xml_find_body()
Can one do a binary search in the x0 list?
*/
int
match_base_child(cxobj *x0,
cxobj *x1c,
cxobj **x0cp,
yang_stmt *yc)
{
int retval = -1;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b1;
char *keyname;
char **keyval = NULL;
char **keyvec = NULL;
char keynr = 0;
int i;
*x0cp = NULL; /* return value */
switch (yc->ys_keyword){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
break;
case Y_LEAF_LIST: /* Match with name and value */
keynr = 1;
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((keyval[0] = xml_body(x1c)) == NULL)
goto ok;
break;
case Y_LIST: /* Match with key values */
cvk = yc->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
/* Count number of key indexes */
cvi = NULL; keynr = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL)
keynr++;
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((keyvec = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
keyvec[i] = keyname;
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
goto ok; /* not found */
keyval[i++] = b1;
}
break;
default:
break;
}
/* Get match */
{
yang_node *y0;
int yorder;
if ((y0 = yc->ys_parent) == NULL)
goto done;
/* XXX: No we cant do this. on uppermost layer it can look like this:
* config
* ximport-----ymod = ietf
* interfaces--ymod = example
* Which means yang order can be different for different children in the
* same childvec.
*/
if (xml_child_sort==0)
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
else{
#if 1
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
yorder = yang_order(yc);
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
}
else{
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
}
#else
cxobj *xx;
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
if (xml_child_nr(x0) && xml_spec(xml_child_i(x0,0))!=NULL){
xx = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
if (xx!=*x0cp){
clicon_log(LOG_WARNING, "%s mismatch", __FUNCTION__);
fprintf(stderr, "mismatch\n");
xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
assert(0);
}
}
else
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
#endif
}
}
ok:
retval = 0;
done:
if (keyval)
free(keyval);
if (keyvec)
free(keyvec);
return retval;
}

View file

@ -373,7 +373,7 @@ yn_each(yang_node *yn,
* *
* @param[in] yn Yang node, current context node. * @param[in] yn Yang node, current context node.
* @param[in] keyword if 0 match any keyword * @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 * 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. * argument==NULL you cannot, but I have not seen any such examples.
* @see yang_find_datanode * @see yang_find_datanode
@ -384,8 +384,8 @@ yang_find(yang_node *yn,
char *argument) char *argument)
{ {
yang_stmt *ys = NULL; yang_stmt *ys = NULL;
int i; int i;
int match = 0; int match = 0;
for (i=0; i<yn->yn_len; i++){ for (i=0; i<yn->yn_len; i++){
ys = yn->yn_stmt[i]; ys = yn->yn_stmt[i];
@ -558,6 +558,54 @@ yang_find_myprefix(yang_stmt *ys)
return prefix; return prefix;
} }
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
* @retval 0 not found
* @retval 1 found
*/
static int
order1(yang_node *yp,
yang_stmt *y,
int *index)
{
yang_stmt *ys;
int i;
for (i=0; i<yp->yn_len; i++){
ys = yp->yn_stmt[i];
if (!yang_datanode(ys))
continue;
if (ys==y)
return 1;
(*index)++;
}
return 0;
}
/*! Return order of yang statement y in parents child vector
* @retval i Order of child with specified argument
* @retval -1 Not found
*/
int
yang_order(yang_stmt *y)
{
yang_node *yp;
yang_node *ypp;
yang_node *yn;
int i;
int j=0;
yp = y->ys_parent;
if (yp->yn_keyword == Y_MODULE ||yp->yn_keyword == Y_SUBMODULE){
ypp = yp->yn_parent;
for (i=0; i<ypp->yn_len; i++){
yn = (yang_node*)ypp->yn_stmt[i];
if (order1(yn, y, &j) == 1)
return j;
}
}
order1(yp, y, &j);
return j;
}
/*! Reset flag in complete tree, arg contains flag */ /*! Reset flag in complete tree, arg contains flag */
static int static int
@ -880,6 +928,20 @@ ys_populate_leaf(yang_stmt *ys,
return retval; 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 /*! Populate range and length statements
* *
* Create cvec variables "range_min" and "range_max". Assume parent is type. * 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) if (ys_populate_leaf(ys, arg) < 0)
goto done; goto done;
break; break;
case Y_LIST:
if (ys_populate_list(ys, arg) < 0)
goto done;
break;
case Y_RANGE: case Y_RANGE:
case Y_LENGTH: case Y_LENGTH:
if (ys_populate_range(ys, arg) < 0) if (ys_populate_range(ys, arg) < 0)

View file

@ -2,7 +2,6 @@
testnr=0 testnr=0
testname= testname=
clixon_cf=/usr/local/etc/routing.xml
# error and exit, arg is optional extra errmsg # error and exit, arg is optional extra errmsg
err(){ err(){
echo "Error in Test$testnr [$testname]:" echo "Error in Test$testnr [$testname]:"

View file

@ -9,7 +9,7 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
clixon_cf="-f /usr/local/etc/routing.xml" cfg=/usr/local/etc/routing.xml
# For memcheck # For memcheck
#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" #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) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -z $clixon_cf sudo clixon_backend -z -f $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -s init $clixon_cf sudo clixon_backend -s init -f $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "cli tests" new "cli tests"
new "cli configure top" 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)" 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" 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" 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" 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" 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" 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" 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 -f $cfg 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 -f $cfg 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 type bgp" "^$"
new "cli show xpath description" new "cli show xpath description"
expectfn "$clixon_cli -1 $clixon_cf -l o show xpath /interfaces/interface/description" "<description>mydesc</description>" expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" "<description>mydesc</description>"
new "cli delete description" 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" 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" 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" 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" new "cli commit"
expectfn "$clixon_cli -1 $clixon_cf -l o commit" "^$" expectfn "$clixon_cli -1 -f $cfg -l o commit" "^$"
new "cli save" 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" 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" 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" 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" 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? # 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" new "cli rpc"
expectfn "$clixon_cli -1 $clixon_cf -l o rpc ipv4" "^<rpc-reply>" expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" "^<rpc-reply>"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive
@ -100,7 +100,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -z $clixon_cf sudo clixon_backend -z -f $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -3,13 +3,15 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
cfg=/usr/local/etc/routing.xml
fyang=/tmp/leafref.yang
# For memcheck # For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
clixon_cli=clixon_cli clixon_cli=clixon_cli
cat <<EOF > /tmp/leafref.yang cat <<EOF > $fyang
module example{ module example{
import ietf-ip { import ietf-ip {
prefix ip; prefix ip;
@ -48,70 +50,70 @@ EOF
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" 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 if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new 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 if [ $? -ne 0 ]; then
err err
fi fi
new "leafref base config" new "leafref base config"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces> expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><interfaces>
<interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface> <interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface>
<interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface> <interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface>
</interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" </interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref get config" new "leafref get config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>' expecteof "$clixon_netconf -qf $cfg" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'
new "leafref base commit" new "leafref base commit"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref add wrong ref" new "leafref add wrong ref"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref validate" new "leafref validate"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>"
new "leafref discard-changes" new "leafref discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref add correct absref" new "leafref add correct absref"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth0</absname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth0</absname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref add correct relref" new "leafref add correct relref"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><default-address><relname>eth0</relname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><default-address><relname>eth0</relname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# XXX add address # XXX add address
new "leafref validate (ok)" new "leafref validate (ok)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
new "leafref delete leaf" new "leafref delete leaf"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
new "leafref validate (should fail)" new "leafref validate (should fail)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>"
new "leafref discard-changes" new "leafref discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
exit
new "cli leafref lo" 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" 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" 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" 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" new "Kill backend"
# Check if still alive # Check if still alive
@ -120,7 +122,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -3,137 +3,137 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
clixon_cf="-f /usr/local/etc/routing.xml" cfg=/usr/local/etc/routing.xml
# For memcheck # For memcheck
#clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" #clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
echo "clixon_backend -z $clixon_cf" echo "clixon_backend -zf $cfg"
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -z $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -s init $clixon_cf sudo clixon_backend -s init -f $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "netconf tests" new "netconf tests"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Add subtree eth/0/0 using none which should not change anything" new "Add subtree eth/0/0 using none which should not change anything"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check nothing added" new "Check nothing added"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Add subtree eth/0/0 using none and create which should add eth/0/0" new "Add subtree eth/0/0 using none and create which should add eth/0/0"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check eth/0/0 added using xpath" new "Check eth/0/0 added using xpath"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth/0/0]"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth/0/0]"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
new "Re-create same eth/0/0 which should generate error" new "Re-create same eth/0/0 which should generate error"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $cfg" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "Delete eth/0/0 using none config" new "Delete eth/0/0 using none config"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check deleted eth/0/0 (non-presence container)" new "Check deleted eth/0/0 (non-presence container)"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Re-Delete eth/0/0 using none should generate error" new "Re-Delete eth/0/0 using none should generate error"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $cfg" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "netconf edit config" new "netconf edit config"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get config xpath" new "netconf get config xpath"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
new "netconf get config xpath parent" new "netconf get config xpath parent"
expecteof "$clixon_netconf -q $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled/../.."/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled/../.."/></get-config></rpc>]]>]]>' "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></data></rpc-reply>]]>]]>$"
new "netconf validate missing type" new "netconf validate missing type"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $cfg" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get empty config2" new "netconf get empty config2"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
new "netconf edit extra xml" new "netconf edit extra xml"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><extra/></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><target><candidate/></target><config><interfaces><extra/></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config eth1" new "netconf edit config eth1"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf validate" new "netconf validate"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "netconf commit"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config replace XXX is merge?" new "netconf edit config replace XXX is merge?"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get replaced config" new "netconf get replaced config"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit state operation should fail" new "netconf edit state operation should fail"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>" expecteof "$clixon_netconf -qf $cfg" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>"
new "netconf get state operation" new "netconf get state operation"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><get><filter type=\"xpath\" select=\"/interfaces-state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><get><filter type=\"xpath\" select=\"/interfaces-state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data></rpc-reply>]]>]]>$"
new "netconf lock/unlock" new "netconf lock/unlock"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock/lock" new "netconf lock/lock"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock" new "netconf lock"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "close-session" new "close-session"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><close-session/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><close-session/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "kill-session" new "kill-session"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "copy startup" new "copy startup"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get startup" new "netconf get startup"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
new "netconf delete startup" new "netconf delete startup"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf check empty startup" new "netconf check empty startup"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
new "netconf rpc" new "netconf rpc"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><rt:fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></rt:fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>" expecteof "$clixon_netconf -qf $cfg" "<rpc><rt:fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></rt:fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
new "netconf rpc w/o namespace" new "netconf rpc w/o namespace"
expecteof "$clixon_netconf -q $clixon_cf" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>" expecteof "$clixon_netconf -qf $cfg" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
new "netconf subscription" new "netconf subscription"
expectwait "$clixon_netconf -q $clixon_cf" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30 expectwait "$clixon_netconf -qf $cfg" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive
@ -142,7 +142,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -z $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

177
test/test_order.sh Executable file
View file

@ -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 <<EOF > $cfg
<config>
<CLICON_CONFIGFILE>/tmp/conf_yang.xml</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/routing/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>routing</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/routing/routing.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>$dbdir</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_XML_SORT>true</CLICON_XML_SORT>
</config>
EOF
cat <<EOF > $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 <<EOF > $dbdir/running_db
<config>
<y0>d</y0>
<y1>d</y1>
<y2><k>d</k><a>bar</a></y2>
<y3><k>d</k><a>bar</a></y3>
<y0>b</y0>
<y1>b</y1>
<c><d>hej</d></c>
<y0>c</y0>
<y1>c</y1>
<y2><k>a</k><a>bar</a></y2>
<y3><k>a</k><a>bar</a></y3>
<l>hopp</l>
<y0>a</y0>
<y1>a</y1>
<y2><k>c</k><a>bar</a></y2>
<y3><k>c</k><a>bar</a></y3>
<y2><k>b</k><a>bar</a></y2>
<y3><k>b</k><a>bar</a></y3>
</config>
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" "<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><c><d>hej</d></c><l>hopp</l><y0>d</y0><y0>b</y0><y0>c</y0><y0>a</y0><y1>a</y1><y1>b</y1><y1>c</y1><y1>d</y1><y2><k>d</k><a>bar</a></y2><y2><k>a</k><a>bar</a></y2><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>bar</a></y2><y3><k>a</k><a>bar</a></y3><y3><k>b</k><a>bar</a></y3><y3><k>c</k><a>bar</a></y3><y3><k>d</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
new "get each ordered-by user leaf-list"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k=a]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>a</k><a>bar</a></y2></data></rpc-reply>]]>]]>$"
new "get each ordered-by user leaf-list"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k=a]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>a</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
new "get each ordered-by user leaf-list"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k=b]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>b</k><a>bar</a></y2></data></rpc-reply>]]>]]>$"
new "get each ordered-by user leaf-list"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k=b]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
new "delete candidate"
expecteof "$clixon_netconf -qf $cfg" "<rpc><delete-config><target><candidate/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# LEAF_LISTS
new "add two entries to leaf-list user order"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><y0>c</y0><y0>b</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "add one entry to leaf-list user order"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><y0>a</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "add one entry to leaf-list user order after commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><y0>0</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "verify leaf-list user order in running (as entered)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y0\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y0>c</y0><y0>b</y0><y0>a</y0><y0>0</y0></data></rpc-reply>]]>]]>$"
# LISTS
new "add two entries to list user order"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "add one entry to list user order"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><y2><k>a</k><a>fie</a></y2></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "verify list user order (as entered)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/y2\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2><y2><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$"
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

View file

@ -14,13 +14,14 @@ else
echo "Usage: $0 [<number> [<requests>]]" echo "Usage: $0 [<number> [<requests>]]"
exit 1 exit 1
fi fi
cfg=/tmp/scaling-conf.xml
fyang=/tmp/scaling.yang fyang=/tmp/scaling.yang
fconfig=/tmp/config fconfig=/tmp/config
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
clixon_cf=/tmp/scaling-conf.xml
# For memcheck # For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
@ -46,9 +47,9 @@ module ietf-ip{
} }
EOF EOF
cat <<EOF > $clixon_cf cat <<EOF > $cfg
<config> <config>
<CLICON_CONFIGFILE>$clixon_cf</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>$fyang</CLICON_YANG_DIR> <CLICON_YANG_DIR>$fyang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>ietf-ip</CLICON_YANG_MODULE_MAIN> <CLICON_YANG_MODULE_MAIN>ietf-ip</CLICON_YANG_MODULE_MAIN>
<CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK> <CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK>
@ -61,14 +62,14 @@ EOF
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -zf $clixon_cf -y $fyang sudo clixon_backend -zf $cfg -y $fyang
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new 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 if [ $? -ne 0 ]; then
err err
fi fi
@ -81,43 +82,43 @@ done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
# Just for manual dbg # Just for manual dbg
echo "$clixon_netconf -qf $clixon_cf -y $fyang" echo "$clixon_netconf -qf $cfg -y $fyang"
new "netconf edit large config" new "netconf edit large config"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
#echo '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' | $clixon_netconf -qf $clixon_cf -y $fyang #echo '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' | $clixon_netconf -qf $cfg -y $fyang
new "netconf edit large config again" new "netconf edit large config again"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
#echo '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' | $clixon_netconf -qf $clixon_cf -y $fyang #echo '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' | $clixon_netconf -qf $cfg -y $fyang
rm $fconfig rm $fconfig
new "netconf commit large config" new "netconf commit large config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit same config again" new "netconf commit same config again"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
exit
new "netconf add one small config" new "netconf add one small config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add $req small config" new "netconf add $req small config"
time -p for (( i=0; i<$req; i++ )); do time -p for (( i=0; i<$req; i++ )); do
rnd=$(( ( RANDOM % $number ) )) rnd=$(( ( RANDOM % $number ) ))
echo "<rpc><edit-config><target><candidate/></target><config><x><y><a>$rnd</a><b>$rnd</b></y></x></config></edit-config></rpc>]]>]]>" echo "<rpc><edit-config><target><candidate/></target><config><x><y><a>$rnd</a><b>$rnd</b></y></x></config></edit-config></rpc>]]>]]>"
done | $clixon_netconf -qf $clixon_cf -y $fyang > /dev/null done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
new "netconf get large config" new "netconf get large config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>"
new "netconf get $req small config" new "netconf get $req small config"
time -p for (( i=0; i<$req; i++ )); do time -p for (( i=0; i<$req; i++ )); do
rnd=$(( ( RANDOM % $number ) )) rnd=$(( ( RANDOM % $number ) ))
echo "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=$rnd][b=$rnd]\" /></get-config></rpc>]]>]]>" echo "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=$rnd][b=$rnd]\" /></get-config></rpc>]]>]]>"
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" new "generate large leaf-list config"
echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x>" > $fconfig echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x>" > $fconfig
@ -127,21 +128,21 @@ done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
new "netconf replace large list-leaf config" new "netconf replace large list-leaf config"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof_file "time -p $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
rm $fconfig rm $fconfig
new "netconf commit large leaf-list config" new "netconf commit large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add small leaf-list config" new "netconf add small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><c>x</c></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><c>x</c></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit small leaf-list config" new "netconf commit small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get large leaf-list config" new "netconf get large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><c>0</c><c>1</c>" expecteof "time -p $clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><c>0</c><c>1</c>"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive
@ -150,7 +151,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -4,18 +4,19 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
cfg=/usr/local/etc/routing.xml
# This is a fixed 'state' implemented in routing_backend. It is always there # This is a fixed 'state' implemented in routing_backend. It is always there
state='{"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}' state='{"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}'
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
sudo clixon_backend -s init -f $clixon_cf sudo clixon_backend -s init -f $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
@ -109,7 +110,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -8,12 +8,36 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
cfg=/tmp/conf_startup.xml
# For memcheck # For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
clixon_cli=clixon_cli clixon_cli=clixon_cli
cat <<EOF > $cfg
<config>
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLI_MODE>routing</CLICON_CLI_MODE>
<CLICON_BACKEND_DIR>/usr/local/lib/routing/backend</CLICON_BACKEND_DIR>
<CLICON_NETCONF_DIR>/usr/local/lib/routing/netconf</CLICON_NETCONF_DIR>
<CLICON_RESTCONF_DIR>/usr/local/lib/routing/restconf</CLICON_RESTCONF_DIR>
<CLICON_CLI_DIR>/usr/local/lib/routing/cli</CLICON_CLI_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR>
<CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/routing/routing.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/routing</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
<CLICON_XML_SORT>true</CLICON_XML_SORT>
</config>
EOF
run(){ run(){
mode=$1 mode=$1
expect=$2 expect=$2
@ -42,7 +66,7 @@ EOF
EOF EOF
sudo mv /tmp/db /usr/local/var/routing/startup_db sudo mv /tmp/db /usr/local/var/routing/startup_db
cat <<EOF > /tmp/config cat <<EOF > /tmp/config
<config> <config>
<interfaces> <interfaces>
<interface> <interface>
@ -55,20 +79,20 @@ EOF
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new 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 if [ $? -ne 0 ]; then
err err
fi fi
new "Check $mode" new "Check $mode"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply>$expect</rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply>$expect</rpc-reply>]]>]]>$"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive
@ -77,7 +101,7 @@ EOF
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi
@ -85,6 +109,6 @@ EOF
run init '<data/>' run init '<data/>'
run none '<data><interfaces><interface><name>run</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>' run none '<data><interfaces><interface><name>run</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'
run running '<data><interfaces><interface><name>run</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>' run running '<data><interfaces><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>run</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'
run startup '<data><interfaces><interface><name>startup</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>' run startup '<data><interfaces><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>startup</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'

View file

@ -4,13 +4,15 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
cfg=/usr/local/etc/routing.xml
fyang=/tmp/type.yang
# For memcheck # For memcheck
#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" #clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli"
clixon_cli=clixon_cli clixon_cli=clixon_cli
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
cat <<EOF > /tmp/type.yang cat <<EOF > $fyang
module example{ module example{
typedef ab { typedef ab {
type string { type string {
@ -63,55 +65,55 @@ EOF
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" new "kill old backend"
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new 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 if [ $? -ne 0 ]; then
err err
fi fi
new "cli set ab" 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" 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" 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" 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" 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" 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" 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" new "netconf validate ok"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf set ab wrong" new "netconf set ab wrong"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><edit-config><target><candidate/></target><config><list><ip>a.b&c.d</ip></list></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><list><ip>a.b&c.d</ip></list></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf validate" new "netconf validate"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "cli enum value" 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" new "Kill backend"
# Check if still alive # Check if still alive
@ -120,7 +122,7 @@ if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -3,16 +3,17 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
clixon_cf=/tmp/conf_yang.xml cfg=/tmp/conf_yang.xml
fyang=/tmp/test.yang
# For memcheck # For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
clixon_cli=clixon_cli clixon_cli=clixon_cli
cat <<EOF > /tmp/conf_yang.xml cat <<EOF > $cfg
<config> <config>
<CLICON_CONFIGFILE>/tmp/conf_yang.xml</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR> <CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN> <CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR> <CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR>
@ -26,7 +27,7 @@ cat <<EOF > /tmp/conf_yang.xml
</config> </config>
EOF EOF
cat <<EOF > /tmp/test.yang cat <<EOF > $fyang
module example{ module example{
container x { container x {
list y { list y {
@ -80,74 +81,73 @@ EOF
# kill old backend (if any) # kill old backend (if any)
new "kill old backend" 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 if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
# start new 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 if [ $? -ne 0 ]; then
err err
fi fi
new "netconf edit config" new "netconf edit config"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit" new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# text empty type in running # text empty type in running
new "netconf commit 2nd" new "netconf commit 2nd"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get config xpath" new "netconf get config xpath"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c></y></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c></y></x></data></rpc-reply>]]>]]>$"
new "netconf edit leaf-list" new "netconf edit leaf-list"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><edit-config><target><candidate/></target><config><x><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get leaf-list" new "netconf get leaf-list"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f/e\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f/e\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
new "netconf get leaf-list path" new "netconf get leaf-list path"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
new "netconf get (should be some)" new "netconf get (should be some)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></data></rpc-reply>]]>]]>$"
new "cli set leaf-list" 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" new "cli show leaf-list"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test.yang show xpath /x/f/e" "<e>foo</e>" expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" "<e>foo</e>"
new "netconf set state data (not allowed)" new "netconf set state data (not allowed)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value"
new "netconf set presence and not present" new "netconf set presence and not present"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get presence only" new "netconf get presence only"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/*presence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><x><presence/></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/*presence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><x><presence/></x></data></rpc-reply>]]>]]>$"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf anyxml" new "netconf anyxml"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><edit-config><target><candidate/></target><config><x><any><foo><bar a=\"nisse\"/></foo></any></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><any><foo><bar a=\"nisse\"/></foo></any></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf validate anyxml" new "netconf validate anyxml"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test.yang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Kill backend"
# Check if still alive # Check if still alive
pid=`pgrep clixon_backend` pid=`pgrep clixon_backend`
if [ -z "$pid" ]; then if [ -z "$pid" ]; then
err "backend already dead" err "backend already dead"
fi fi
# kill backend # kill backend
sudo clixon_backend -zf $clixon_cf #sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi

View file

@ -38,7 +38,7 @@ bindir = @bindir@
includedir = @includedir@ includedir = @includedir@
datarootdir = @datarootdir@ 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-netconf@2011-06-01.yang
YANGSPECS += ietf-inet-types@2013-07-15.yang YANGSPECS += ietf-inet-types@2013-07-15.yang

View file

@ -0,0 +1,266 @@
module clixon-config {
prefix cc;
organization
"Clicon / Clixon";
contact
"Olof Hagsand <olof@hagsand.se>";
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:
<module>[@<revision>]";
}
leaf CLICON_YANG_MODULE_REVISION {
type string;
description
"Option used to construct initial yang file:
<module>[@<revision>]";
}
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 <b> k <c>' 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";
}
}
}