From 697c021d49c82f1a52311041767c2ae1296d829e Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 6 May 2018 20:55:38 +0200 Subject: [PATCH 01/53] Starting 3.7.0 branch --- CHANGELOG.md | 2 ++ configure | 4 ++-- configure.ac | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 942f236f..665392fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Clixon Changelog +## 3.7.0 (Upcoming) + ## 3.6.0 (30 April 2018) ### Major changes: diff --git a/configure b/configure index da33abc1..7b2c1036 100755 --- a/configure +++ b/configure @@ -2147,9 +2147,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="3" -CLIXON_VERSION_MINOR="6" +CLIXON_VERSION_MINOR="7" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" # Fix to specific version (eg 3.5) or head (3) CLIGEN_VERSION="3" if test "$prefix" = "NONE"; then diff --git a/configure.ac b/configure.ac index 19fe9b44..f77742a5 100644 --- a/configure.ac +++ b/configure.ac @@ -43,9 +43,9 @@ AC_INIT(lib/clixon/clixon.h.in) : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="3" -CLIXON_VERSION_MINOR="6" +CLIXON_VERSION_MINOR="7" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" # Fix to specific version (eg 3.5) or head (3) CLIGEN_VERSION="3" if test "$prefix" = "NONE"; then From 4257999df549e9ce8aaea16c4562d85103410f47 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 6 May 2018 21:06:52 +0200 Subject: [PATCH 02/53] Fixed JSON unbalanced braces resultin assert --- CHANGELOG.md | 6 +++++- README.md | 2 +- lib/src/clixon_err.c | 11 ++++++----- lib/src/clixon_json.c | 3 ++- lib/src/clixon_json_parse.y | 12 ++++++++---- lib/src/clixon_log.c | 15 +++++++-------- 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 665392fd..bc1f43d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ # Clixon Changelog ## 3.7.0 (Upcoming) - +### Major changes: +### Minor changes: +### Corrected Bugs +- Fixed JSON unbalanced braces resultin assert. + ## 3.6.0 (30 April 2018) ### Major changes: diff --git a/README.md b/README.md index 44198776..7b3a05e5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Background ========== Clixon was implemented to provide an open-source generic configuration -tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, whilke clixon is a system with config-db, xml and rest interfaces. Most of the projects using clixon are for embedded network and measuring devices. But Clixon is more generic than that. +tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while clixon is a system with configuration database, xml and rest interfaces. Most of the projects using clixon are for embedded network and measuring devices. But Clixon is more generic than that. Users of clixon currently include: * [Netgate](https://www.netgate.com) diff --git a/lib/src/clixon_err.c b/lib/src/clixon_err.c index c50148e0..b6f9069d 100644 --- a/lib/src/clixon_err.c +++ b/lib/src/clixon_err.c @@ -102,7 +102,8 @@ static struct errvec EV[] = { }; static char * -clicon_strerror1(int err, struct errvec vec[]) +clicon_strerror1(int err, + struct errvec vec[]) { struct errvec *ev; @@ -142,10 +143,10 @@ clicon_err_reset(void) */ int clicon_err_fn(const char *fn, - const int line, - int category, - int suberr, - char *reason, ...) + const int line, + int category, + int suberr, + char *reason, ...) { va_list args; int len; diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index b14592cc..d498a202 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -792,7 +792,8 @@ usage(char *argv0) } int -main(int argc, char **argv) +main(int argc, + char **argv) { cxobj *xt; cxobj *xc; diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y index 55fb5f5a..1cf6a583 100644 --- a/lib/src/clixon_json_parse.y +++ b/lib/src/clixon_json_parse.y @@ -101,7 +101,7 @@ object. /* typecast macro */ #define _JY ((struct clicon_json_yacc_arg *)_jy) -#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;} +#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;} /* add _yy to error paramaters */ #define YY_(msgid) msgid @@ -139,7 +139,8 @@ extern int clixon_json_parseget_lineno (void); */ void -clixon_json_parseerror(void *_jy, char *s) +clixon_json_parseerror(void *_jy, + char *s) { clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'", _JY->jy_name, @@ -192,7 +193,10 @@ json_current_clone(struct clicon_json_yacc_arg *jy) { cxobj *xn; - assert(xn = jy->jy_current); + if (jy->jy_current == NULL){ + return -1; + } + xn = jy->jy_current; json_current_pop(jy); if (jy->jy_current) json_current_new(jy, xml_name(xn)); @@ -258,7 +262,7 @@ array : '[' ']' ; valuelist : value - | valuelist { json_current_clone(_JY);} ',' value + | valuelist { if (json_current_clone(_JY)< 0) _YYERROR("stack?");} ',' value ; /* quoted string */ diff --git a/lib/src/clixon_log.c b/lib/src/clixon_log.c index e55bcba0..21d24bcc 100644 --- a/lib/src/clixon_log.c +++ b/lib/src/clixon_log.c @@ -157,13 +157,12 @@ slogtime(void) /*! Make a logging call to syslog (or stderr). - * - * This is the _only_ place the actual syslog (or stderr) logging is made in clicon,.. - * @note syslog makes itw own filtering, but if log to stderr we do it here - * @see clicon_debug() * * @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER * @param[in] msg Message to print as argv. + * This is the _only_ place the actual syslog (or stderr) logging is made in clicon,.. + * @note syslog makes itw own filtering, but if log to stderr we do it here + * @see clicon_debug */ int clicon_log_str(int level, @@ -214,13 +213,13 @@ clicon_log_str(int level, /*! Make a logging call to syslog using variable arg syntax. * - * See also clicon_log_init() and clicon_log_str() - * + * @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. This + * is OR:d with facility == LOG_USER + * @param[in] format Message to print as argv. * @code clicon_log(LOG_NOTICE, "%s: dump to dtd not supported", __PROGRAM__); * @endcode - * @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER - * @param[in] format Message to print as argv. + * @see cicon_log_init and clicon_log_str */ int clicon_log(int level, From 82351afde39087a9c35620f692faf351df042b02 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Tue, 8 May 2018 21:53:46 +0200 Subject: [PATCH 03/53] header docs --- lib/src/clixon_yang.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index f15057a1..93074e32 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -255,6 +255,13 @@ yn_realloc(yang_node *yn) } /*! Copy yang statement recursively from old to new + * @param[in] ynew New empty (but created) yang statement (to) + * @param[in] yold Old existing yang statement (from) + * @code + * yang_stmt *new = ys_new(Y_LEAF); + * if (ys_cp(new, old) < 0) + * err; + * @endcode */ int ys_cp(yang_stmt *ynew, @@ -304,8 +311,10 @@ ys_cp(yang_stmt *ynew, return retval; } -/*! Create a new yang node and copy the contents recursively from the original. - * +/*! Create a new yang node and copy the contents recursively from the original. * + * @param[in] old Old existing yang statement (from) + * @retval NULL Error + * @retval new New created yang statement * This may involve duplicating strings, etc. * The new yang tree needs to be freed by ys_free(). * The parent of new is NULL, it needs to be explicityl inserted somewhere @@ -328,7 +337,6 @@ ys_dup(yang_stmt *old) return new; } - /*! Insert yang statement as child of a parent yang_statement, last in list * * Also add parent to child as up-pointer From 20a677cb6c17eba63d9d98026ebc95da7679e144 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sat, 12 May 2018 19:00:46 +0200 Subject: [PATCH 04/53] code cleanup and doc --- apps/backend/backend_client.c | 2 +- apps/cli/cli_common.c | 32 ++++++++++++++++++++++++++++++++ apps/cli/clixon_cli_api.h | 2 ++ lib/src/clixon_plugin.c | 4 ++-- lib/src/clixon_xml_map.c | 3 ++- lib/src/clixon_xsl.c | 4 ++-- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index fc619640..469f7652 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1202,7 +1202,7 @@ from_client_msg(clicon_handle h, } reply: if (cbuf_len(cbret) == 0) - if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) + if (netconf_operation_failed(cbret, "application", clicon_errno?clicon_err_reason:"unknown")< 0) goto done; clicon_debug(1, "%s cbret:%s", __FUNCTION__, cbuf_get(cbret)); /* XXX problem here is that cbret has not been parsed so may contain diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 4979f412..a80c722d 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -427,6 +427,38 @@ int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv) return cli_debug_backend(h, vars, argv); } +/*! Set debug level on restconf daemon + * @param[in] h Clicon handle + * @param[in] vars If variable "level" exists, its integer value is used + * @param[in] arg Else use the integer value of argument + * @note The level is either what is specified in arg as int argument. + * _or_ if a 'level' variable is present in vars use that value instead. + */ +int +cli_debug_restconf(clicon_handle h, + cvec *vars, + cvec *argv) +{ + int retval = -1; + cg_var *cv; + int level; + + if ((cv = cvec_find_var(vars, "level")) == NULL){ + if (cvec_len(argv) != 1){ + clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__); + goto done; + } + cv = cvec_i(argv, 0); + } + level = cv_int32_get(cv); + /* restconf daemon */ + if (0) /* XXX notyet */ + retval = clicon_rpc_debug(h, level); + done: + return retval; +} + + /*! Set syntax mode */ int diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 9c0ececf..3946cfdc 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -89,6 +89,8 @@ int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv); int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv); int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv); +int cli_debug_restconf(clicon_handle h, cvec *vars, cvec *argv); + int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv); int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv); diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index c8ce70e7..a0f2a4d3 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -126,7 +126,7 @@ clixon_plugin_each_revert(clicon_handle h, int nr) { int i; - clixon_plugin *cp; + clixon_plugin *cp = NULL; clixon_plugin *cpnext = NULL; if (cpprev == NULL) @@ -466,7 +466,7 @@ rpc_callback_delete_all(void) * @param[in] h clicon handle * @param[in] xn Sub-tree (under xorig) at child of rpc: . * @param[out] xret Return XML, error or OK - * @param[in] arg Domain-speific arg (eg client_entry) + * @param[in] arg Domain-speific arg (eg client_entry) * * @retval -1 Error * @retval 0 OK, not found handler. diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 1be9d750..60ea29a6 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -394,6 +394,7 @@ xml_yang_validate_all(cxobj *xt, * @param[out] cvv CLIgen variable vector. Should be freed by cvec_free() * @retval 0 Everything OK, cvv allocated and set * @retval -1 Something wrong, clicon_err() called to set error. No cvv returned + * @note cvv Should be freed by cvec_free() after use. * 'Not recursive' means that only one level of XML bodies is translated to cvec:s. * If range is wriong (eg 1000 for uint8) a warning is logged, the value is * skipped, and continues. @@ -426,7 +427,7 @@ xml2cvec(cxobj *xt, char *name; xc = NULL; - /* Tried to allocate whole cvv here,but some cg_vars may be invalid */ + /* Tried to allocate whole cvv here, but some cg_vars may be invalid */ if ((cvv = cvec_new(0)) == NULL){ clicon_err(OE_UNIX, errno, "cvec_new"); goto err; diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 22be2b63..cef4bcfb 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -976,10 +976,10 @@ xpath_first(cxobj *xcur, * } * @endcode * - * Note that the returned pointer points into the original tree so should not be freed + * @note The returned pointer points into the original tree so should not be freed * after use. * @see also xpath, xpath_vec. - * NOTE: uses a static variable: consider replacing with xpath_vec() instead + * @note uses a static variable: consider replacing with xpath_vec() instead */ cxobj * xpath_each(cxobj *xcur, From 353f2e603878b63ee89d6a08c6fdbda5c90cf369 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Tue, 15 May 2018 22:24:44 +0200 Subject: [PATCH 05/53] added merge of json trees --- lib/src/clixon_json.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index d498a202..930dbb6f 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -691,8 +691,9 @@ int json_parse_str(char *str, cxobj **xt) { - if ((*xt = xml_new("top", NULL, NULL)) == NULL) - return -1; + if (*xt == NULL) + if ((*xt = xml_new("top", NULL, NULL)) == NULL) + return -1; return json_parse(str, "", *xt); } From a14c8c97783645e632fd26c1925a96d052a56295 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Fri, 18 May 2018 22:17:09 +0000 Subject: [PATCH 06/53] unit test --- lib/src/clixon_xsl.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 22be2b63..88327238 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -1145,11 +1145,11 @@ xpath_vec_flag(cxobj *xcur, /* * Turn this on to get an xpath test program * Usage: xpath [] - * read xml from input + * read xpath on first line and xml on rest of lines from input * Example compile: gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen * Example run: - echo "" | xpath "a" +echo "a\n" | xpath */ #if 0 /* Test program */ @@ -1165,22 +1165,47 @@ int main(int argc, char **argv) { int i; - cxobj **xv + cxobj **xv; cxobj *x; cxobj *xn; size_t xlen = 0; - - if (argc != 2){ + int c; + int len; + char *buf = NULL; + + if (argc != 1){ usage(argv[0]); return 0; } - if (xml_parse_file(0, &x, "") < 0){ + /* Read xpath */ + len = 1024; /* any number is fine */ + if ((buf = malloc(len)) == NULL){ + perror("pt_file malloc"); + return -1; + } + memset(buf, 0, len); + while (1){ /* read the whole file */ + if ((c = fgetc(stdin)) == EOF) + return -1; + if (c == '\n') + break; + if (len==i){ + if ((buf = realloc(buf, 2*len)) == NULL){ + fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); + return -1; + } + memset(buf+len, 0, len); + len *= 2; + } + buf[i++] = (char)(c&0xff); + } + if (xml_parse_file(0, "", NULL, &x) < 0){ fprintf(stderr, "parsing 2\n"); return -1; } printf("\n"); - if (xpath_vec(x, argv[1], &xv, &xlen) < 0) + if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0) return -1; if (xv){ for (i=0; i Date: Sat, 19 May 2018 00:27:53 +0200 Subject: [PATCH 07/53] unit test pgm --- lib/src/clixon_xsl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 80dfb534..34f32b9c 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -1151,7 +1151,7 @@ xpath_vec_flag(cxobj *xcur, * Example run: echo "a\n" | xpath */ -#if 0 /* Test program */ +#if 1 /* Test program */ static int @@ -1164,14 +1164,14 @@ usage(char *argv0) int main(int argc, char **argv) { - int i; + int i; cxobj **xv; cxobj *x; cxobj *xn; - size_t xlen = 0; - int c; - int len; - char *buf = NULL; + size_t xlen = 0; + int c; + int len; + char *buf = NULL; if (argc != 1){ usage(argv[0]); @@ -1184,6 +1184,7 @@ main(int argc, char **argv) return -1; } memset(buf, 0, len); + i = 0; while (1){ /* read the whole file */ if ((c = fgetc(stdin)) == EOF) return -1; @@ -1199,6 +1200,7 @@ main(int argc, char **argv) } buf[i++] = (char)(c&0xff); } + x = NULL; if (xml_parse_file(0, "", NULL, &x) < 0){ fprintf(stderr, "parsing 2\n"); return -1; From 0622f6aafe6e866ae5bf8b98d7d2a50fc9327442 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sun, 27 May 2018 18:31:19 +0000 Subject: [PATCH 08/53] xml/xsl test programs --- lib/src/clixon_xml.c | 4 ++-- lib/src/clixon_xsl.c | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 15e25d1a..c23d8d37 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1883,7 +1883,7 @@ usage(char *argv0) int main(int argc, char **argv) { - cxobj *xt; + cxobj *xt = NULL; cxobj *xc; cbuf *cb = cbuf_new(); @@ -1891,7 +1891,7 @@ main(int argc, char **argv) usage(argv[0]); return 0; } - if (xml_parse_file(0, "", NULL,&xt) < 0){ + if (xml_parse_file(0, "", NULL, &xt) < 0){ fprintf(stderr, "parsing 2\n"); return -1; } diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 34f32b9c..6b40ec3e 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -94,6 +94,7 @@ in #include #include #include +#include /* cligen */ #include @@ -1151,19 +1152,20 @@ xpath_vec_flag(cxobj *xcur, * Example run: echo "a\n" | xpath */ -#if 1 /* Test program */ +#if 0 /* Test program */ static int usage(char *argv0) { - fprintf(stderr, "usage:%s .\n\tInput on stdin\n", argv0); + fprintf(stderr, "usage:%s .\n\tInput on stdin\n", argv0); exit(0); } int main(int argc, char **argv) { + int retval = -1; int i; cxobj **xv; cxobj *x; @@ -1172,11 +1174,18 @@ main(int argc, char **argv) int c; int len; char *buf = NULL; + char *filename; + int fd; - if (argc != 1){ + if (argc != 2){ usage(argv[0]); - return 0; + goto done; } + filename = argv[1]; + if ((fd = open(filename, O_RDONLY)) < 0){ + clicon_err(OE_UNIX, errno, "open(%s)", filename); + goto done; + } /* Read xpath */ len = 1024; /* any number is fine */ if ((buf = malloc(len)) == NULL){ @@ -1201,10 +1210,11 @@ main(int argc, char **argv) buf[i++] = (char)(c&0xff); } x = NULL; - if (xml_parse_file(0, "", NULL, &x) < 0){ - fprintf(stderr, "parsing 2\n"); + if (xml_parse_file(fd, "", NULL, &x) < 0){ + fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); return -1; } + close (fd); printf("\n"); if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0) @@ -1219,8 +1229,9 @@ main(int argc, char **argv) } if (x) xml_free(x); - - return 0; + retval = 0; + done: + return retval; } #endif /* Test program */ From cfe47020699cd01566a687a13569d876c4618eab Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 28 May 2018 22:50:22 +0200 Subject: [PATCH 09/53] * Added cligen variable translation. * See FAQ and example --- CHANGELOG.md | 4 +++- apps/cli/cli_plugin.c | 3 +++ doc/FAQ.md | 30 ++++++++++++++++++++++++++++++ example/example.yang | 6 ++++++ example/example_cli.c | 18 ++++++++++++++++++ example/example_cli.cli | 5 +++++ 6 files changed, 65 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1f43d1..caec7492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ ## 3.7.0 (Upcoming) ### Major changes: ### Minor changes: +* Added cligen variable translation. + * See FAQ and example ### Corrected Bugs -- Fixed JSON unbalanced braces resultin assert. +* Fixed JSON unbalanced braces resultin assert. ## 3.6.0 (30 April 2018) diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 04fc027c..369f1691 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -277,6 +277,9 @@ cli_load_syntax(clicon_handle h, } if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0) goto done; + /* Variable translation functions */ + if (cligen_translate_str2fn(pt, (translate_str2fn_t*)clixon_str2fn, handle) < 0) + goto done; /* Make sure we have a syntax mode specified */ if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */ diff --git a/doc/FAQ.md b/doc/FAQ.md index 949f94ce..2ac6dab8 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -344,3 +344,33 @@ To authenticate, the callback needs to return the value 1 and supply a username. See [../apps/example/example_restconf.c] plugin_credentials() for an example of HTTP basic auth. + +## How do I write a CLI translator function + +The CLI can perform variable translation. This is useful if you want to +prcess the input, such as hashing, encrypting or in other way +translate the input. + +Yang example: +``` +list translate{ + leaf value{ + type string; + } +} +``` + +CLI specification: +``` +translate value (),cli_set("/translate/value"); +``` + +If you run this example using the `incstr()` function which increments the characters in the input, you get this result: +``` +cli> translate value HAL +cli> show configuration +translate { + value IBM; +} +``` +You can perform translation on any type, not only strings. \ No newline at end of file diff --git a/example/example.yang b/example/example.yang index f9159228..ad8e1367 100644 --- a/example/example.yang +++ b/example/example.yang @@ -11,6 +11,12 @@ module example { } description "Example code that includes ietf-ip and ietf-routing"; + /* Translation function example - See also example_cli */ + list translate{ + leaf value{ + type string; + } + } rpc client-rpc { description "Example local client-side RPC that is processed by the the netconf/restconf and not sent to the backend. diff --git a/example/example_cli.c b/example/example_cli.c index b8a91fa3..fef13c35 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -137,3 +137,21 @@ clixon_plugin_init(clicon_handle h) return &api; } + +/*! Translate function from an original value to a new. + * In this case, assume string and increment characters, eg HAL->IBM + */ +int +incstr(cligen_handle h, + cg_var *cv) +{ + char *str; + int i; + + if (cv_type_get(cv) != CGV_STRING) + return 0; + str = cv_string_get(cv); + for (i=0; i),cli_set("/translate/value"); + + + # Note, when switching to PT, change datamodel to only @datamodel set @datamodel:example, cli_set(); merge @datamodel:example, cli_merge(); From 5bc39a9bc079b6c145b6a29307fa70e782e7244b Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 29 May 2018 22:27:41 +0200 Subject: [PATCH 10/53] from 3.6.1: * https://github.com/clicon/clixon/issues/23 clixon_cli failing with error * The example included a reference to nacm yang file which did not exist and was not used * Added clixon-config@2018-04-30.yang --- CHANGELOG.md | 7 + configure | 109 +++++++++ configure.ac | 6 + example/example.yang | 3 - test/test_auth.sh | 2 +- yang/Makefile.in | 2 +- yang/clixon-config@2018-02-12.yang | 67 +----- yang/clixon-config@2018-04-30.yang | 353 +++++++++++++++++++++++++++++ 8 files changed, 486 insertions(+), 63 deletions(-) create mode 100644 yang/clixon-config@2018-04-30.yang diff --git a/CHANGELOG.md b/CHANGELOG.md index caec7492..e18c817e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ ### Corrected Bugs * Fixed JSON unbalanced braces resultin assert. +## 3.6.1 (29 May 2018) + +### Corrected Bugs +* https://github.com/clicon/clixon/issues/23 clixon_cli failing with error + * The example included a reference to nacm yang file which did not exist and was not used +* Added clixon-config@2018-04-30.yang + ## 3.6.0 (30 April 2018) ### Major changes: diff --git a/configure b/configure index 7b2c1036..6667152b 100755 --- a/configure +++ b/configure @@ -640,6 +640,11 @@ SH_SUFFIX AR_SUFFIX OBJ_SUFFIX INSTALLFLAGS +INSTALL_LIB +INSTALL +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM CPPFLAGS INCLUDES LDFLAGS @@ -2339,6 +2344,104 @@ test -n "$target_alias" && +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + + + + @@ -4787,6 +4890,7 @@ gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' +INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF @@ -5369,6 +5473,10 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix # CONFIG_FILE # + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 @@ -5422,6 +5530,7 @@ s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ diff --git a/configure.ac b/configure.ac index f77742a5..978cb921 100644 --- a/configure.ac +++ b/configure.ac @@ -73,11 +73,17 @@ AC_SUBST(CLIGEN_PREFIX) AC_MSG_RESULT(CLIXON version is ${CLIXON_VERSION}) AC_CANONICAL_TARGET + AC_SUBST(CC) AC_SUBST(CFLAGS) AC_SUBST(LDFLAGS) AC_SUBST(INCLUDES) AC_SUBST(CPPFLAGS) +AC_PROG_INSTALL +AC_SUBST(INSTALL) +AC_SUBST(INSTALL_LIB) +AC_SUBST(INSTALL_DATA) +AC_SUBST(INSTALL_PROGRAM) AC_SUBST(INSTALLFLAGS) AC_SUBST(LIBS) AC_SUBST(OBJ_SUFFIX) diff --git a/example/example.yang b/example/example.yang index ad8e1367..f4921978 100644 --- a/example/example.yang +++ b/example/example.yang @@ -6,9 +6,6 @@ module example { import ietf-routing { prefix rt; } - import ietf-netconf-acm { - prefix nacm; - } description "Example code that includes ietf-ip and ietf-routing"; /* Translation function example - See also example_cli */ diff --git a/test/test_auth.sh b/test/test_auth.sh index 225a3ba7..447fb4bf 100755 --- a/test/test_auth.sh +++ b/test/test_auth.sh @@ -14,7 +14,7 @@ fyangerr=$dir/err.yang cat < $cfg $cfg - /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/restconf diff --git a/yang/Makefile.in b/yang/Makefile.in index 965c0655..66b2621f 100644 --- a/yang/Makefile.in +++ b/yang/Makefile.in @@ -38,7 +38,7 @@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ -YANGSPECS = clixon-config@2018-02-12.yang +YANGSPECS = clixon-config@2018-04-30.yang YANGSPECS += ietf-netconf@2011-06-01.yang YANGSPECS += ietf-netconf-acm@2018-02-14.yang YANGSPECS += ietf-inet-types@2013-07-15.yang diff --git a/yang/clixon-config@2018-02-12.yang b/yang/clixon-config@2018-02-12.yang index c629a3c7..883a002f 100644 --- a/yang/clixon-config@2018-02-12.yang +++ b/yang/clixon-config@2018-02-12.yang @@ -82,40 +82,6 @@ module clixon-config { } } } - typedef cli_genmodel_type{ - description - "How to generate CLI from YANG model, - eg list a{ key x; leaf x; leaf y;}"; - type enumeration{ - enum NONE{ - description "No extra keywords: a "; - } - enum VARS{ - description "Keywords on non-key variables: a y "; - } - enum ALL{ - description "Keywords on all variables: a x y "; - } - } - } - typedef nacm_mode{ - description - "Mode of RFC8341 Network Configuration Access Control Model. - It is unclear from the RFC whether NACM rules are internal - in a configuration (ie embedded in regular config) or external/OOB - in s separate, specific NACM-config"; - type enumeration{ - enum disabled{ - description "NACM is disabled"; - } - enum internal{ - description "NACM is enabled and available in the regular config"; - } - enum external{ - description "NACM is enabled and available in a separate config"; - } - } - } container config { leaf CLICON_CONFIGFILE{ type string; @@ -147,12 +113,6 @@ module clixon-config { "Location of backend .so plugins. Load all .so plugins in this dir as backend plugins"; } - leaf CLICON_BACKEND_REGEXP { - type string; - description - "Regexp of matching backend plugins in CLICON_BACKEND_DIR"; - default "(.so)$"; - } leaf CLICON_NETCONF_DIR { type string; description "Location of netconf (frontend) .so plugins"; @@ -220,7 +180,7 @@ module clixon-config { description "Generate code for CLI completion of existing db symbols"; } leaf CLICON_CLI_GENMODEL_TYPE { - type cli_genmodel_type; + type string; default "VARS"; description "How to generate and show CLI syntax: VARS|ALL"; } @@ -277,6 +237,14 @@ module clixon-config { "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; @@ -332,22 +300,5 @@ module clixon-config { type startup_mode; description "Which method to boot/start clicon backend"; } - leaf CLICON_TRANSACTION_MOD { - type boolean; - default false; - description "If set, modifications in validation and commit - callbacks are written back into the datastore"; - } - leaf CLICON_NACM_MODE { - type nacm_mode; - default disabled; - description "RFC8341 network access configuration control model - (NACM) mode: disabled, in regular (internal) config - or separate external file given by CLICON_NACM_FILE"; - } - leaf CLICON_NACM_FILE { - type string; - description "RFC8341 NACM external configuration file"; - } } } diff --git a/yang/clixon-config@2018-04-30.yang b/yang/clixon-config@2018-04-30.yang new file mode 100644 index 00000000..c629a3c7 --- /dev/null +++ b/yang/clixon-config@2018-04-30.yang @@ -0,0 +1,353 @@ +module clixon-config { + + prefix cc; + + organization + "Clicon / Clixon"; + + contact + "Olof Hagsand "; + + description + "Clixon configuration file + ***** BEGIN LICENSE BLOCK ***** + Copyright (C) 2009-2018 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 2018-02-12 { + description + "Added pretty print for datastore"; + } + 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"; + } + } + } + typedef xmldb_format{ + description + "Format of TEXT xml database format."; + type enumeration{ + enum xml{ + description "Save and load xmldb as XML"; + } + enum json{ + description "Save and load xmldb as JSON"; + } + } + } + typedef cli_genmodel_type{ + description + "How to generate CLI from YANG model, + eg list a{ key x; leaf x; leaf y;}"; + type enumeration{ + enum NONE{ + description "No extra keywords: a "; + } + enum VARS{ + description "Keywords on non-key variables: a y "; + } + enum ALL{ + description "Keywords on all variables: a x y "; + } + } + } + typedef nacm_mode{ + description + "Mode of RFC8341 Network Configuration Access Control Model. + It is unclear from the RFC whether NACM rules are internal + in a configuration (ie embedded in regular config) or external/OOB + in s separate, specific NACM-config"; + type enumeration{ + enum disabled{ + description "NACM is disabled"; + } + enum internal{ + description "NACM is enabled and available in the regular config"; + } + enum external{ + description "NACM is enabled and available in a separate config"; + } + } + } + container config { + leaf CLICON_CONFIGFILE{ + type string; + description + "Location of configuration-file for default values (this file)"; + } + leaf CLICON_YANG_DIR { + type string; + mandatory true; + description + "Location of YANG module and submodule files."; + } + leaf CLICON_YANG_MODULE_MAIN { + type string; + default "clicon"; + description + "Option used to construct initial yang file: + [@]"; + } + leaf CLICON_YANG_MODULE_REVISION { + type string; + description + "Option used to construct initial yang file: + [@]"; + } + leaf CLICON_BACKEND_DIR { + type string; + description + "Location of backend .so plugins. Load all .so + plugins in this dir as backend plugins"; + } + leaf CLICON_BACKEND_REGEXP { + type string; + description + "Regexp of matching backend plugins in CLICON_BACKEND_DIR"; + default "(.so)$"; + } + 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_RESTCONF_PRETTY { + type boolean; + default true; + description + "Restconf return value pretty print. + Restconf clients may add HTTP header: + Accept: application/yang-data+json, or + Accept: application/yang-data+xml + to get return value in XML or JSON. + RFC 8040 examples print XML and JSON in pretty-printed form. + Setting this value to false makes restconf return not pretty-printed + which may be desirable for performance or tests"; + } + 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 cli_genmodel_type; + default "VARS"; + description "How to generate and show CLI syntax: VARS|ALL"; + } + leaf CLICON_CLI_VARONLY { + type int32; + default 1; + description + "Dont include keys in cvec in cli vars callbacks, + ie a & k in 'a k ' ignored"; + } + leaf CLICON_CLI_LINESCROLLING { + type int32; + default 1; + description + "Set to 0 if you want CLI to wrap to next line. + Set to 1 if you want CLI to scroll sideways when approaching + right margin"; + } + leaf CLICON_SOCK_FAMILY { + type string; + default "UNIX"; + description + "Address family for communicating with clixon_backend + (UNIX|IPv4|IPv6)"; + } + leaf CLICON_SOCK { + type string; + mandatory true; + description + "If family above is AF_UNIX: Unix socket for communicating + with clixon_backend. If family is AF_INET: IPv4 address"; + } + leaf CLICON_SOCK_PORT { + type int32; + default 4535; + description + "Inet socket port for communicating with clixon_backend + (only IPv4|IPv6)"; + } + leaf CLICON_SOCK_GROUP { + type string; + default "clicon"; + description "Group membership to access clixon_backend unix socket"; + } + leaf CLICON_BACKEND_PIDFILE { + type string; + mandatory true; + description "Process-id file of backend daemon"; + } + leaf CLICON_AUTOCOMMIT { + type int32; + default 0; + description + "Set if all configuration changes are committed automatically + on every edit change. Explicit commit commands unnecessary"; + } + leaf CLICON_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_XMLDB_FORMAT { + type xmldb_format; + default xml; + description "XMLDB datastore format."; + } + leaf CLICON_XMLDB_PRETTY { + type boolean; + default true; + description + "XMLDB datastore pretty print. + If set, insert spaces and line-feeds making the XML/JSON human + readable. If not set, make the XML/JSON more compact."; + } + 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"; + } + leaf CLICON_TRANSACTION_MOD { + type boolean; + default false; + description "If set, modifications in validation and commit + callbacks are written back into the datastore"; + } + leaf CLICON_NACM_MODE { + type nacm_mode; + default disabled; + description "RFC8341 network access configuration control model + (NACM) mode: disabled, in regular (internal) config + or separate external file given by CLICON_NACM_FILE"; + } + leaf CLICON_NACM_FILE { + type string; + description "RFC8341 NACM external configuration file"; + } + } +} From ea13727e9739ad7c4f05bed21886b55335174fc2 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 30 May 2018 22:38:12 +0200 Subject: [PATCH 11/53] * Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. * Added --enable-debug. --- CHANGELOG.md | 26 +++++- apps/cli/cli_common.c | 183 +++++++++++++++++++++++--------------- apps/cli/cli_show.c | 20 +++-- apps/cli/clixon_cli_api.h | 51 +++++++---- configure | 42 ++++++--- configure.ac | 24 +++-- doc/FAQ.md | 2 +- include/clixon_custom.h | 7 +- lib/src/Makefile.in | 6 +- 9 files changed, 232 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e18c817e..0480c96c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,32 @@ ## 3.7.0 (Upcoming) ### Major changes: ### Minor changes: +* Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. + * Replace functions as follows in CLI SPEC files: + * cli_setv --> cli_set + * cli_mergev --> cli_merge + * cli_delv --> cli_del + * cli_debug_cliv --> cli_debug_cli + * cli_debug_backendv --> cli_debug_backend + * cli_set_modev --> cli_set_mode + * cli_start_shellv --> cli_start_shell + * cli_quitv --> cli_quit + * cli_commitv --> cli_commit + * cli_validatev --> cli_validate + * compare_dbsv --> compare_dbs + * load_config_filev --> load_config_file + * save_config_filev --> save_config_file + * delete_allv --> delete_all + * discard_changesv --> discard_changes + * cli_notifyv --> cli_notify + * show_yangv --> show_yang + * show_confv_xpath --> show_conf_xpath + +* Added --enable-debug. * Added cligen variable translation. * See FAQ and example ### Corrected Bugs -* Fixed JSON unbalanced braces resultin assert. +* Fixed JSON unbalanced braces resulting in assert. ## 3.6.1 (29 May 2018) @@ -460,7 +482,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed: Instead use the rpc calls in clixon_proto_client.[ch] In clients (eg cli/netconf) replace xmldb_get() in client code with clicon_rpc_get_config(). - pIf you use the vector arguments of xmldb_get(), replace as follows: + If you use the vector arguments of xmldb_get(), replace as follows: xmldb_get(h, db, api_path, &xt, &xvec, &xlen); with clicon_rpc_get_config(h, dbstr, api_path, &xt); diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index a80c722d..c0a18f4c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -184,7 +184,7 @@ cli_signal_flush(clicon_handle h) cli_signal_block (h); } -/*! Modify xml database from a callback using xml key format strings +/*! Modify xml datastore from a callback using xml key format strings * @param[in] h Clicon handle * @param[in] cvv Vector of cli string and instantiated variables * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" @@ -283,8 +283,15 @@ cli_dbxml(clicon_handle h, return retval; } +/*! Set datastore xml entry + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" + */ int -cli_set(clicon_handle h, cvec *cvv, cvec *argv) +cli_set(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = 1; @@ -294,13 +301,16 @@ cli_set(clicon_handle h, cvec *cvv, cvec *argv) done: return retval; } -int cli_setv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_set(h, vars, argv); -} +/*! Merge datastore xml entry + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" + */ int -cli_merge(clicon_handle h, cvec *cvv, cvec *argv) +cli_merge(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; @@ -310,13 +320,16 @@ cli_merge(clicon_handle h, cvec *cvv, cvec *argv) done: return retval; } -int cli_mergev(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_merge(h, vars, argv); -} +/*! Create datastore xml entry + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" + */ int -cli_create(clicon_handle h, cvec *cvv, cvec *argv) +cli_create(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; @@ -326,11 +339,16 @@ cli_create(clicon_handle h, cvec *cvv, cvec *argv) done: return retval; } -/*! +/*! Remove datastore xml entry + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" * @see cli_del */ int -cli_remove(clicon_handle h, cvec *cvv, cvec *argv) +cli_remove(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; @@ -341,8 +359,15 @@ cli_remove(clicon_handle h, cvec *cvv, cvec *argv) return retval; } +/*! Delete datastore xml + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" + */ int -cli_del(clicon_handle h, cvec *cvv, cvec *argv) +cli_del(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; @@ -352,11 +377,6 @@ cli_del(clicon_handle h, cvec *cvv, cvec *argv) done: return retval; } -int cli_delv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_del(h, vars, argv); -} - /*! Set debug level on CLI client (not backend daemon) * @param[in] h Clicon handle @@ -388,10 +408,6 @@ cli_debug_cli(clicon_handle h, done: return retval; } -int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_debug_cli(h, vars, argv); -} /*! Set debug level on backend daemon (not CLI) * @param[in] h Clicon handle @@ -422,10 +438,6 @@ cli_debug_backend(clicon_handle h, done: return retval; } -int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_debug_backend(h, vars, argv); -} /*! Set debug level on restconf daemon * @param[in] h Clicon handle @@ -479,10 +491,6 @@ cli_set_mode(clicon_handle h, done: return retval; } -int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_set_mode(h, vars, argv); -} /*! Start bash from cli callback * XXX Application specific?? @@ -540,10 +548,6 @@ cli_start_shell(clicon_handle h, return 0; } -int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_start_shell(h, vars, argv); -} /*! Generic quit callback */ @@ -555,10 +559,6 @@ cli_quit(clicon_handle h, cligen_exiting_set(cli_cligen(h), 1); return 0; } -int cli_quitv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_quit(h, vars, argv); -} /*! Generic commit callback * @param[in] argv No arguments expected @@ -576,10 +576,6 @@ cli_commit(clicon_handle h, done: return retval; } -int cli_commitv(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_commit(h, vars, argv); -} /*! Generic validate callback */ @@ -596,10 +592,6 @@ cli_validate(clicon_handle h, done: return retval; } -int cli_validatev(clicon_handle h, cvec *vars, cvec *argv) -{ - return cli_validate(h, vars, argv); -} /*! Compare two dbs using XML. Write to file and run diff */ @@ -709,10 +701,6 @@ compare_dbs(clicon_handle h, return retval; } -int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv) -{ - return compare_dbs(h, vars, argv); -} /*! Load a configuration file to candidate database * Utility function used by cligen spec file @@ -724,7 +712,7 @@ int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv) * @note file is assumed to have a dummy top-tag, eg * @code * # cligen spec - * load file , load_config_filev("name2","merge"); + * load file , load_config_file("name2","merge"); * @endcode * @see save_config_file */ @@ -804,10 +792,6 @@ load_config_file(clicon_handle h, close(fd); return ret; } -int load_config_filev(clicon_handle h, cvec *vars, cvec *argv) -{ - return load_config_file(h, vars, argv); -} /*! Copy database to local file * Utility function used by cligen spec file @@ -889,10 +873,6 @@ save_config_file(clicon_handle h, fclose(f); return retval; } -int save_config_filev(clicon_handle h, cvec *vars, cvec *argv) -{ - return save_config_file(h, vars, argv); -} /*! Delete all elements in a database * Utility function used by cligen spec file @@ -922,10 +902,6 @@ delete_all(clicon_handle h, done: return retval; } -int delete_allv(clicon_handle h, cvec *vars, cvec *argv) -{ - return delete_all(h, vars, argv); -} /*! Discard all changes in candidate and replace with running */ @@ -935,12 +911,8 @@ discard_changes(clicon_handle h, cvec *argv) { return clicon_rpc_discard_changes(h); -} -int discard_changesv(clicon_handle h, cvec *vars, cvec *argv) -{ - return discard_changes(h, vars, argv); -} +} /*! Copy from one database to another, eg running->startup * @param[in] argv a string: " " Copy from db1 to db2 */ @@ -1024,7 +996,7 @@ cli_notification_cb(int s, * and is XXX * Example code: Start logging of mystream and show logs as xml * @code - * cmd("comment"), cli_notifyv("mystream","1","xml"); + * cmd("comment"), cli_notify("mystream","1","xml"); * @endcode * XXX: format is a memory leak */ @@ -1062,10 +1034,79 @@ cli_notify(clicon_handle h, done: return retval; } + +/* Backward compatible Set if you want to enable "v" cli callback functions, + * such as cli_setv() + * This was obsoleted in 3.7. + * @see include/clixon_custom.h + */ +#ifdef COMPAT_CLIV +int cli_setv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_set(h, vars, argv); +} +int cli_mergev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_merge(h, vars, argv); +} +int cli_delv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_del(h, vars, argv); +} +int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_debug_cli(h, vars, argv); +} +int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_debug_backend(h, vars, argv); +} +int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_set_mode(h, vars, argv); +} +int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_start_shell(h, vars, argv); +} +int cli_quitv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_quit(h, vars, argv); +} +int cli_commitv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_commit(h, vars, argv); +} +int cli_validatev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_validate(h, vars, argv); + +} +int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv) +{ + return compare_dbs(h, vars, argv); +} +int load_config_filev(clicon_handle h, cvec *vars, cvec *argv) +{ + return load_config_file(h, vars, argv); +} +int save_config_filev(clicon_handle h, cvec *vars, cvec *argv) +{ + return save_config_file(h, vars, argv); +} +int delete_allv(clicon_handle h, cvec *vars, cvec *argv) +{ + return delete_all(h, vars, argv); +} +int discard_changesv(clicon_handle h, cvec *vars, cvec *argv) +{ + return discard_changes(h, vars, argv); +} int cli_notifyv(clicon_handle h, cvec *vars, cvec *argv) { return cli_notify(h, vars, argv); } +#endif /* COMPAT_CLIV */ /*! Lock database * diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index f4753f3c..ed92d5de 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -409,10 +409,6 @@ show_yang(clicon_handle h, yang_print(stdout, yn); return 0; } -int show_yangv(clicon_handle h, cvec *vars, cvec *argv) -{ - return show_yang(h, vars, argv); -} /*! Generic show configuration CLIGEN callback * Utility function used by cligen spec file @@ -598,13 +594,19 @@ done: return retval; } -int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv) -{ - return show_conf_xpath(h, vars, argv); -} - int cli_show_version(clicon_handle h, cvec *vars, cvec *argv) { cli_output(stdout, "%s\n", CLIXON_VERSION_STRING); return 0; } + +#ifdef COMPAT_CLIV +int show_yangv(clicon_handle h, cvec *vars, cvec *argv) +{ + return show_yang(h, vars, argv); +} +int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv) +{ + return show_conf_xpath(h, vars, argv); +} +#endif /* COMPAT_CLIV */ diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 3946cfdc..713ea521 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -72,57 +72,51 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo int cli_set(clicon_handle h, cvec *vars, cvec *argv); -int cli_setv(clicon_handle h, cvec *vars, cvec *argv); int cli_merge(clicon_handle h, cvec *vars, cvec *argv); -int cli_mergev(clicon_handle h, cvec *vars, cvec *argv); int cli_create(clicon_handle h, cvec *vars, cvec *argv); int cli_remove(clicon_handle h, cvec *vars, cvec *argv); int cli_del(clicon_handle h, cvec *vars, cvec *argv); -int cli_delv(clicon_handle h, cvec *vars, cvec *argv); int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv); -int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv); + int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv); -int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv); + int cli_debug_restconf(clicon_handle h, cvec *vars, cvec *argv); int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv); -int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv); + int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv); -int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv); + int cli_quit(clicon_handle h, cvec *vars, cvec *argv); -int cli_quitv(clicon_handle h, cvec *vars, cvec *argv); + int cli_commit(clicon_handle h, cvec *vars, cvec *argv); -int cli_commitv(clicon_handle h, cvec *vars, cvec *argv); int cli_validate(clicon_handle h, cvec *vars, cvec *argv); -int cli_validatev(clicon_handle h, cvec *vars, cvec *argv); + int compare_dbs(clicon_handle h, cvec *vars, cvec *argv); -int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv); int load_config_file(clicon_handle h, cvec *vars, cvec *argv); -int load_config_filev(clicon_handle h, cvec *vars, cvec *argv); int save_config_file(clicon_handle h, cvec *vars, cvec *argv); -int save_config_filev(clicon_handle h, cvec *vars, cvec *argv); + int delete_all(clicon_handle h, cvec *vars, cvec *argv); -int delete_allv(clicon_handle h, cvec *vars, cvec *argv); + int discard_changes(clicon_handle h, cvec *vars, cvec *argv); -int discard_changesv(clicon_handle h, cvec *vars, cvec *argv); + int cli_notify(clicon_handle h, cvec *cvv, cvec *argv); -int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv); + int db_copy(clicon_handle h, cvec *cvv, cvec *argv); @@ -141,11 +135,32 @@ int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv, /* cli_show.c: CLIgen new vector arg callbacks */ int show_yang(clicon_handle h, cvec *vars, cvec *argv); -int show_yangv(clicon_handle h, cvec *vars, cvec *argv); + int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv); -int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv); + int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv); +#ifdef COMPAT_CLIV +int cli_setv(clicon_handle h, cvec *vars, cvec *argv); +int cli_mergev(clicon_handle h, cvec *vars, cvec *argv); +int cli_delv(clicon_handle h, cvec *vars, cvec *argv); +int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv); +int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv); +int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv); +int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv); +int cli_quitv(clicon_handle h, cvec *vars, cvec *argv); +int cli_commitv(clicon_handle h, cvec *vars, cvec *argv); +int cli_validatev(clicon_handle h, cvec *vars, cvec *argv); +int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv); +int load_config_filev(clicon_handle h, cvec *vars, cvec *argv); +int save_config_filev(clicon_handle h, cvec *vars, cvec *argv); +int delete_allv(clicon_handle h, cvec *vars, cvec *argv); +int discard_changesv(clicon_handle h, cvec *vars, cvec *argv); +int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv); +int show_yangv(clicon_handle h, cvec *vars, cvec *argv); +int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv); +#endif /* COMPAT_CLIV */ + #endif /* _CLIXON_CLI_API_H_ */ diff --git a/configure b/configure index 6667152b..cdc7a30b 100755 --- a/configure +++ b/configure @@ -634,13 +634,8 @@ EXEEXT ac_ct_CC with_restconf RANLIB -AR -EXE_SUFFIX SH_SUFFIX -AR_SUFFIX -OBJ_SUFFIX INSTALLFLAGS -INSTALL_LIB INSTALL INSTALL_DATA INSTALL_SCRIPT @@ -710,6 +705,7 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking +enable_debug with_cligen with_restconf with_configfile @@ -1344,6 +1340,12 @@ if test -n "$ac_init_help"; then cat <<\_ACEOF +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug Build with debug symbols, default: no + Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) @@ -2444,11 +2446,6 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - - - - - # If yes, compile apps/restconf # ac_ext=c @@ -3382,6 +3379,27 @@ CPPFLAGS="-DHAVE_CONFIG_H ${CPPFLAGS}" { $as_echo "$as_me:${as_lineno-$LINENO}: result: compiler is $CC" >&5 $as_echo "compiler is $CC" >&6; } +# Debug flag +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; + if test "$enableval" = no; then + ac_enable_debug=no + else + ac_enable_debug=yes + fi + +else + ac_enable_debug=no +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug is $ac_enable_debug" >&5 +$as_echo "debug is $ac_enable_debug" >&6; } +if test "$ac_enable_debug" = "yes"; then + CFLAGS="-g -Wall" + INSTALLFLAGS="" +fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS is $CPPFLAGS" >&5 $as_echo "CPPFLAGS is $CPPFLAGS" >&6; } @@ -3643,11 +3661,7 @@ _ACEOF fi -EXE_SUFFIX="" -OBJ_SUFFIX=".o" -AR_SUFFIX=".a" SH_SUFFIX=".so" -AR="ar" # This is for cligen diff --git a/configure.ac b/configure.ac index 978cb921..433291fb 100644 --- a/configure.ac +++ b/configure.ac @@ -81,16 +81,11 @@ AC_SUBST(INCLUDES) AC_SUBST(CPPFLAGS) AC_PROG_INSTALL AC_SUBST(INSTALL) -AC_SUBST(INSTALL_LIB) AC_SUBST(INSTALL_DATA) AC_SUBST(INSTALL_PROGRAM) AC_SUBST(INSTALLFLAGS) AC_SUBST(LIBS) -AC_SUBST(OBJ_SUFFIX) -AC_SUBST(AR_SUFFIX) AC_SUBST(SH_SUFFIX) -AC_SUBST(EXE_SUFFIX) -AC_SUBST(AR) AC_SUBST(RANLIB) AC_SUBST(with_restconf) # If yes, compile apps/restconf # @@ -100,6 +95,21 @@ AC_PROG_CPP CPPFLAGS="-DHAVE_CONFIG_H ${CPPFLAGS}" AC_MSG_RESULT(compiler is $CC) +# Debug flag +AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[Build with debug symbols, default: no]),[ + if test "$enableval" = no; then + ac_enable_debug=no + else + ac_enable_debug=yes + fi + ], + [ ac_enable_debug=no]) + +AC_MSG_RESULT(debug is $ac_enable_debug) +if test "$ac_enable_debug" = "yes"; then + CFLAGS="-g -Wall" + INSTALLFLAGS="" +fi AC_MSG_RESULT(CPPFLAGS is $CPPFLAGS) AC_MSG_RESULT(CFLAGS is $CFLAGS) @@ -119,11 +129,7 @@ if test "$prefix" = "NONE"; then fi AC_CHECK_LIB(m, main) -EXE_SUFFIX="" -OBJ_SUFFIX=".o" -AR_SUFFIX=".a" SH_SUFFIX=".so" -AR="ar" # This is for cligen AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] ) diff --git a/doc/FAQ.md b/doc/FAQ.md index 2ac6dab8..09459599 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -342,7 +342,7 @@ plugin_credentials(clicon_handle h, To authenticate, the callback needs to return the value 1 and supply a username. -See [../apps/example/example_restconf.c] plugin_credentials() for +See [../apps/example/example_restconf.c] example_restconf_credentials() for an example of HTTP basic auth. ## How do I write a CLI translator function diff --git a/include/clixon_custom.h b/include/clixon_custom.h index eaad25ac..92b12204 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -43,6 +43,7 @@ int strverscmp (__const char *__s1, __const char *__s2); #endif - - - +/* Set if you want to enable "v" cli callback functions, such as cli_setv() + * This was obsoleted in 3.7 + */ +#undef COMPAT_CLIV diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index b26efee5..3360934e 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -52,6 +52,8 @@ CLIXON_MINOR = @CLIXON_VERSION_MINOR@ VPATH = @srcdir@ CC = @CC@ CFLAGS = -fPIC @CFLAGS@ +INSTALL = @INSTALL@ +INSTALL_LIB = @INSTALL@ INSTALLFLAGS = @INSTALLFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @@ -182,8 +184,8 @@ install: install-lib install-include: install-lib: $(MYLIB) - install -m 0755 -d $(DESTDIR)$(libdir) - install -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir) + $(INSTALL) -m 0755 -d $(DESTDIR)$(libdir) + $(INSTALL_LIB) -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir) ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclixon.so.3 ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclixon.so From 7e4e1d6debbeefdc4a5d2957befb1fb449f69ab2 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 3 Jun 2018 15:36:05 +0200 Subject: [PATCH 12/53] * Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 * Previous support did no validation of values. * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities --- CHANGELOG.md | 8 + README.md | 3 +- apps/cli/cli_generate.c | 58 +- apps/cli/cli_main.c | 19 +- apps/cli/cli_plugin.c | 22 +- example/Makefile.in | 1 + example/example.yang | 14 + example/example_backend.c | 4 +- example/example_cli.cli | 2 +- example/iana-if-type@2014-05-08.yang | 1506 ++++++++++++++++++++++++++ lib/clixon/clixon_plugin.h | 5 +- lib/clixon/clixon_yang.h | 2 + lib/src/clixon_options.c | 1 + lib/src/clixon_xml_map.c | 80 +- lib/src/clixon_yang.c | 76 +- lib/src/clixon_yang_type.c | 17 +- test/lib.sh | 24 +- test/test_cli.sh | 52 +- test/test_datastore.sh | 60 +- test/test_identity.sh | 188 ++++ test/test_leafref.sh | 22 +- test/test_netconf.sh | 30 +- test/test_restconf.sh | 54 +- test/test_restconf2.sh | 43 +- test/test_startup.sh | 12 +- test/test_type.sh | 16 +- test/test_yang.sh | 8 +- 27 files changed, 2124 insertions(+), 203 deletions(-) create mode 100644 example/iana-if-type@2014-05-08.yang create mode 100755 test/test_identity.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 0480c96c..46c0ee11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ## 3.7.0 (Upcoming) ### Major changes: +* Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 + * Previous support did no validation of values. + * Validation of types and CLI expansion + * Example extended with inclusion of iana-if-type RFC 7224 interface identities + ### Minor changes: +* Added yang identityref runtime validation * Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. * Replace functions as follows in CLI SPEC files: * cli_setv --> cli_set @@ -27,7 +33,9 @@ * Added --enable-debug. * Added cligen variable translation. * See FAQ and example + ### Corrected Bugs +* Added cli returna value also for single commands (eg -1) * Fixed JSON unbalanced braces resulting in assert. ## 3.6.1 (29 May 2018) diff --git a/README.md b/README.md index 7b3a05e5..bf4d82c5 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,9 @@ also manages an XML datastore. Clixon mainly follows [YANG 1.0 RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) with some exceptions: - conformance: feature, if-feature, deviation -- identity, base, identityref - list features: min/max-elements, unique +- when, must, action statements +- notifications The aim is also to cover new features in YANG 1.1 [YANG RFC 7950](https://www.rfc-editor.org/rfc/rfc7950.txt) diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 67fa9cec..d4a2aca9 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -204,20 +204,49 @@ yang2cli_var_sub(clicon_handle h, } type = ytype?ytype->ys_argument:NULL; cvtypestr = cv_type2str(cvtype); + if (strcmp(type, "identityref") == 0) + cprintf(cb, "("); cprintf(cb, "<%s:%s", ys->ys_argument, cvtypestr); /* enumeration special case completion */ - if (type && (strcmp(type, "enumeration") == 0 || strcmp(type, "bits") == 0)){ - cprintf(cb, " choice:"); - i = 0; - while ((yi = yn_each((yang_node*)ytype, yi)) != NULL){ - if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT) - continue; - if (i) - cprintf(cb, "|"); - cprintf(cb, "%s", yi->ys_argument); - i++; + if (type){ + if (strcmp(type, "enumeration") == 0 || strcmp(type, "bits") == 0){ + cprintf(cb, " choice:"); + i = 0; + while ((yi = yn_each((yang_node*)ytype, yi)) != NULL){ + if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT) + continue; + if (i) + cprintf(cb, "|"); + cprintf(cb, "%s", yi->ys_argument); + i++; + } + } + else if (strcmp(type, "identityref") == 0){ + yang_stmt *ybaseref; + yang_stmt *ybaseid; + cg_var *cv = NULL; + char *name; + char *id; + /* Add a wildchar string first -let validate take it for default prefix */ + cprintf(cb, ">"); + if (helptext) + cprintf(cb, "(\"%s\")", helptext); + cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr); + if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) != NULL && + (ybaseid = yang_find_identity(ys, ybaseref->ys_argument)) != NULL){ + i = 0; + while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){ + if (i++) + cprintf(cb, "|"); + name = strdup(cv_name_get(cv)); + if ((id=strchr(name, ':')) != NULL) + *id = '\0'; + cprintf(cb, "%s:%s", name, id+1); + } + } } } + if (options & YANG_OPTIONS_FRACTION_DIGITS) cprintf(cb, " fraction-digits:%u", fraction_digits); if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){ @@ -259,10 +288,12 @@ yang2cli_var_sub(clicon_handle h, } if (options & YANG_OPTIONS_PATTERN) cprintf(cb, " regexp:\"%s\"", pattern); - cprintf(cb, ">"); if (helptext) cprintf(cb, "(\"%s\")", helptext); + if (strcmp(type, "identityref") == 0) + cprintf(cb, ")"); + retval = 0; done: return retval; @@ -385,6 +416,7 @@ yang2cli_var(clicon_handle h, enum cv_type cvtype; int options = 0; int completionp; + char *type; if (yang_type_get(ys, &origtype, &yrestype, &options, &mincv, &maxcv, &pattern, &fraction_digits) < 0) @@ -413,11 +445,11 @@ yang2cli_var(clicon_handle h, cprintf(cb, ")"); } else{ - char *type; type = yrestype?yrestype->ys_argument:NULL; if (type) completionp = clicon_cli_genmodel_completion(h) && - strcmp(type, "enumeration") != 0 && + strcmp(type, "enumeration") != 0 && + strcmp(type, "identityref") != 0 && strcmp(type, "bits") != 0; else completionp = clicon_cli_genmodel_completion(h); diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index f1d9678e..2e269016 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -108,6 +108,8 @@ cli_signal_init (clicon_handle h) /*! Interactive CLI command loop * @param[in] h CLICON handle + * @retval 0 + * @retval -1 * @see cligen_loop */ static int @@ -124,7 +126,6 @@ cli_interactive(clicon_handle h) new_mode = cli_syntax_mode(h); if ((cmd = clicon_cliread(h)) == NULL) { cligen_exiting_set(cli_cligen(h), 1); /* EOF */ - retval = -1; goto done; } if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0) @@ -229,6 +230,7 @@ usage(char *argv0, clicon_handle h) int main(int argc, char **argv) { + int retval = -1; char c; int once; char *tmp; @@ -476,11 +478,19 @@ main(int argc, char **argv) if (restarg != NULL && strlen(restarg)){ char *mode = cli_syntax_mode(h); int result; - clicon_parse(h, restarg, &mode, &result); + + /* */ + if (clicon_parse(h, restarg, &mode, &result) != 1){ + goto done; + } + if (result < 0) + goto done; } /* Go into event-loop unless -1 command-line */ if (!once) - cli_interactive(h); + retval = cli_interactive(h); + else + retval = 0; done: if (treename) free(treename); @@ -491,6 +501,5 @@ main(int argc, char **argv) clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid()); if (h) cli_terminate(h); - - return 0; + return retval; } diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 369f1691..193e6df3 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -481,6 +481,7 @@ clicon_eval(clicon_handle h, * @param[out] result -2 On eof (shouldnt happen) * -1 On parse error * >=0 Number of matches + * @retval 0 XXX How does this relate to result??? */ int clicon_parse(clicon_handle h, @@ -490,7 +491,7 @@ clicon_parse(clicon_handle h, { char *modename; char *modename0; - int res = -1; + int retval = -1; int r; cli_syntax_t *stx = NULL; cli_syntaxmode_t *smode; @@ -511,10 +512,11 @@ clicon_parse(clicon_handle h, else { if ((smode = syntax_mode_find(stx, modename, 0)) == NULL) { cli_output(f, "Can't find syntax mode '%s'\n", modename); - return -1; + goto done; } } - while(smode) { + if (smode) + while(1) { modename0 = NULL; if ((pt = cligen_tree_active_get(cli_cligen(h))) != NULL) modename0 = pt->pt_name; @@ -530,24 +532,24 @@ clicon_parse(clicon_handle h, clicon_err(OE_UNIX, errno, "cvec_new"); goto done;; } - res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv); - if (res != CG_MATCH) + retval = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv); + if (retval != CG_MATCH) pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */ if (modename0){ cligen_tree_active_set(cli_cligen(h), modename0); modename0 = NULL; } - switch (res) { + switch (retval) { case CG_EOF: /* eof */ case CG_ERROR: cli_output(f, "CLI parse error: %s\n", cmd); goto done; case CG_NOMATCH: /* no match */ - smode = NULL; /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", cmd, cli_nomatch(h));*/ cli_output(f, "CLI syntax error: \"%s\": %s\n", cmd, cli_nomatch(h)); + goto done; break; case CG_MATCH: if (strcmp(modename, *modenamep)){ /* Command in different mode */ @@ -565,12 +567,12 @@ clicon_parse(clicon_handle h, cli_output(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd); goto done; break; - } - } + } /* switch retval */ + } /* while smode */ done: if (cvv) cvec_free(cvv); - return res; + return retval; } /*! Read command from CLIgen's cliread() using current syntax mode. diff --git a/example/Makefile.in b/example/Makefile.in index 0be6a8aa..ac5f73d6 100644 --- a/example/Makefile.in +++ b/example/Makefile.in @@ -69,6 +69,7 @@ YANGSPECS += ietf-routing@2014-10-26.yang YANGSPECS += ietf-ipv4-unicast-routing@2014-10-26.yang YANGSPECS += ietf-ipv6-unicast-routing@2014-10-26.yang YANGSPECS += ietf-ipsec@2016-03-09.yang +YANGSPECS += iana-if-type@2014-05-08.yang # Backend plugin BE_SRC = $(APPNAME)_backend.c diff --git a/example/example.yang b/example/example.yang index f4921978..fbb1c738 100644 --- a/example/example.yang +++ b/example/example.yang @@ -1,14 +1,28 @@ module example { prefix ex; + import ietf-interfaces { + prefix if; + } import ietf-ip { prefix ip; } import ietf-routing { prefix rt; } + import iana-if-type { + prefix ianaift; + } description "Example code that includes ietf-ip and ietf-routing"; + /* Example interface type for tests, local callbacks, etc */ + identity eth { + base if:interface-type; + } + identity loopback { + base if:interface-type; + } /* Translation function example - See also example_cli */ + list translate{ leaf value{ type string; diff --git a/example/example_backend.c b/example/example_backend.c index 495ad14f..a3dfe832 100644 --- a/example/example_backend.c +++ b/example/example_backend.c @@ -190,7 +190,7 @@ plugin_statedata(clicon_handle h, /* Example of (static) statedata, real code would poll state */ if (xml_parse_string("" "eth0" - "eth" + "ex:eth" "42" "", NULL, &xstate) < 0) goto done; @@ -221,7 +221,7 @@ plugin_reset(clicon_handle h, cxobj *xt = NULL; if (xml_parse_string("" - "lolocal" + "loex:loopback" "", NULL, &xt) < 0) goto done; /* Replace parent w fiorst child */ diff --git a/example/example_cli.cli b/example/example_cli.cli index a941eee2..657f884b 100644 --- a/example/example_cli.cli +++ b/example/example_cli.cli @@ -55,7 +55,7 @@ load("Load configuration from XML file") ("Filename (local file merge("Merge file with existent candidate"), load_config_file("filename", "merge"); } example("This is a comment") ("Just a random number"), mycallback("myarg"); -rpc("fib-route rpc") ("routing instance"), fib_route_rpc("myarg"); +rpc("ex:fib-route rpc") ("routing instance"), fib_route_rpc("myarg"); notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text"); no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml"); lock,cli_lock("candidate"); diff --git a/example/iana-if-type@2014-05-08.yang b/example/iana-if-type@2014-05-08.yang new file mode 100644 index 00000000..da5b65b7 --- /dev/null +++ b/example/iana-if-type@2014-05-08.yang @@ -0,0 +1,1506 @@ + module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + import ietf-interfaces { + prefix if; + } + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + "; + description + "This YANG module defines YANG identities for IANA-registered + interface types. + + This YANG module is maintained by IANA and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Requests for new values should be made to IANA via + email (iana@iana.org). + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC 7224; + see the RFC itself for full legal notices."; + reference + "IANA 'ifType definitions' registry. + "; + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7224: IANA Interface Type YANG Module"; + } + + identity iana-interface-type { + base if:interface-type; + description + "This identity is used as a base for all interface types + defined in the 'ifType definitions' registry."; + } + identity other { + base iana-interface-type; + } + identity regular1822 { + base iana-interface-type; + } + identity hdh1822 { + base iana-interface-type; + } + identity ddnX25 { + base iana-interface-type; + } + identity rfc877x25 { + base iana-interface-type; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + identity ethernetCsmacd { + base iana-interface-type; + description + "For all Ethernet-like interfaces, regardless of speed, + as per RFC 3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88023Csmacd { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88024TokenBus { + base iana-interface-type; + } + identity iso88025TokenRing { + base iana-interface-type; + } + identity iso88026Man { + base iana-interface-type; + } + identity starLan { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity proteon10Mbit { + base iana-interface-type; + } + identity proteon80Mbit { + base iana-interface-type; + } + identity hyperchannel { + base iana-interface-type; + } + identity fddi { + base iana-interface-type; + reference + "RFC 1512 - FDDI Management Information Base"; + } + identity lapb { + base iana-interface-type; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + identity sdlc { + base iana-interface-type; + } + identity ds1 { + base iana-interface-type; + description + "DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + identity e1 { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + identity basicISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity primaryISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity propPointToPointSerial { + base iana-interface-type; + description + "Proprietary serial."; + } + identity ppp { + base iana-interface-type; + } + identity softwareLoopback { + base iana-interface-type; + } + identity eon { + base iana-interface-type; + description + "CLNP over IP."; + } + identity ethernet3Mbit { + base iana-interface-type; + } + identity nsip { + base iana-interface-type; + description + "XNS over IP."; + } + identity slip { + base iana-interface-type; + description + "Generic SLIP."; + } + identity ultra { + base iana-interface-type; + description + "Ultra Technologies."; + } + identity ds3 { + base iana-interface-type; + description + "DS3-MIB."; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + identity sip { + base iana-interface-type; + description + "SMDS, coffee."; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + identity frameRelay { + base iana-interface-type; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + identity rs232 { + base iana-interface-type; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + identity para { + base iana-interface-type; + description + "Parallel-port."; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + identity arcnet { + base iana-interface-type; + description + "ARCnet."; + } + identity arcnetPlus { + base iana-interface-type; + description + "ARCnet Plus."; + } + identity atm { + base iana-interface-type; + description + "ATM cells."; + } + identity miox25 { + base iana-interface-type; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + identity sonet { + base iana-interface-type; + description + "SONET or SDH."; + } + identity x25ple { + base iana-interface-type; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + identity iso88022llc { + base iana-interface-type; + } + identity localTalk { + base iana-interface-type; + } + identity smdsDxi { + base iana-interface-type; + } + identity frameRelayService { + base iana-interface-type; + description + "FRNETSERV-MIB."; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + identity v35 { + base iana-interface-type; + } + identity hssi { + base iana-interface-type; + } + identity hippi { + base iana-interface-type; + } + identity modem { + base iana-interface-type; + description + "Generic modem."; + } + identity aal5 { + base iana-interface-type; + description + "AAL5 over ATM."; + } + identity sonetPath { + base iana-interface-type; + } + identity sonetVT { + base iana-interface-type; + } + identity smdsIcip { + base iana-interface-type; + description + "SMDS InterCarrier Interface."; + } + identity propVirtual { + base iana-interface-type; + description + "Proprietary virtual/internal."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity propMultiplexor { + base iana-interface-type; + description + "Proprietary multiplexing."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity ieee80212 { + base iana-interface-type; + description + "100BaseVG."; + } + identity fibreChannel { + base iana-interface-type; + description + "Fibre Channel."; + } + identity hippiInterface { + base iana-interface-type; + description + "HIPPI interfaces."; + } + identity frameRelayInterconnect { + base iana-interface-type; + status obsolete; + description + "Obsolete; use either + frameRelay(32) or frameRelayService(44)."; + } + identity aflane8023 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.3."; + } + identity aflane8025 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.5."; + } + identity cctEmul { + base iana-interface-type; + description + "ATM Emulated circuit."; + } + identity fastEther { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity isdn { + base iana-interface-type; + description + "ISDN and X.25."; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + identity v11 { + base iana-interface-type; + description + "CCITT V.11/X.21."; + } + identity v36 { + base iana-interface-type; + description + "CCITT V.36."; + } + identity g703at64k { + base iana-interface-type; + description + "CCITT G703 at 64Kbps."; + } + identity g703at2mb { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + } + identity qllc { + base iana-interface-type; + description + "SNA QLLC."; + } + identity fastEtherFX { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity channel { + base iana-interface-type; + description + "Channel."; + } + identity ieee80211 { + base iana-interface-type; + description + "Radio spread spectrum."; + } + identity ibm370parChan { + base iana-interface-type; + description + "IBM System 360/370 OEMI Channel."; + } + identity escon { + base iana-interface-type; + description + "IBM Enterprise Systems Connection."; + } + identity dlsw { + base iana-interface-type; + description + "Data Link Switching."; + } + identity isdns { + base iana-interface-type; + description + "ISDN S/T interface."; + } + identity isdnu { + base iana-interface-type; + description + "ISDN U interface."; + } + identity lapd { + base iana-interface-type; + description + "Link Access Protocol D."; + } + identity ipSwitch { + base iana-interface-type; + description + "IP Switching Objects."; + } + identity rsrb { + base iana-interface-type; + description + "Remote Source Route Bridging."; + } + identity atmLogical { + base iana-interface-type; + description + "ATM Logical Port."; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + identity ds0 { + base iana-interface-type; + description + "Digital Signal Level 0."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity ds0Bundle { + base iana-interface-type; + description + "Group of ds0s on the same ds1."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity bsc { + base iana-interface-type; + description + "Bisynchronous Protocol."; + } + identity async { + base iana-interface-type; + description + "Asynchronous Protocol."; + } + identity cnr { + base iana-interface-type; + description + "Combat Net Radio."; + } + identity iso88025Dtr { + base iana-interface-type; + description + "ISO 802.5r DTR."; + } + identity eplrs { + base iana-interface-type; + description + "Ext Pos Loc Report Sys."; + } + identity arap { + base iana-interface-type; + description + "Appletalk Remote Access Protocol."; + } + identity propCnls { + base iana-interface-type; + description + "Proprietary Connectionless Protocol."; + } + identity hostPad { + base iana-interface-type; + description + "CCITT-ITU X.29 PAD Protocol."; + } + identity termPad { + base iana-interface-type; + description + "CCITT-ITU X.3 PAD Facility."; + } + identity frameRelayMPI { + base iana-interface-type; + description + "Multiproto Interconnect over FR."; + } + identity x213 { + base iana-interface-type; + description + "CCITT-ITU X213."; + } + identity adsl { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop."; + } + identity radsl { + base iana-interface-type; + description + "Rate-Adapt. Digital Subscriber Loop."; + } + identity sdsl { + base iana-interface-type; + description + "Symmetric Digital Subscriber Loop."; + } + identity vdsl { + base iana-interface-type; + description + "Very H-Speed Digital Subscrib. Loop."; + } + identity iso88025CRFPInt { + base iana-interface-type; + description + "ISO 802.5 CRFP."; + } + identity myrinet { + base iana-interface-type; + description + "Myricom Myrinet."; + } + identity voiceEM { + base iana-interface-type; + description + "Voice recEive and transMit."; + } + identity voiceFXO { + base iana-interface-type; + description + "Voice Foreign Exchange Office."; + } + identity voiceFXS { + base iana-interface-type; + description + "Voice Foreign Exchange Station."; + } + identity voiceEncap { + base iana-interface-type; + description + "Voice encapsulation."; + } + identity voiceOverIp { + base iana-interface-type; + description + "Voice over IP encapsulation."; + } + identity atmDxi { + base iana-interface-type; + description + "ATM DXI."; + } + identity atmFuni { + base iana-interface-type; + description + "ATM FUNI."; + } + identity atmIma { + base iana-interface-type; + description + "ATM IMA."; + } + identity pppMultilinkBundle { + base iana-interface-type; + description + "PPP Multilink Bundle."; + } + identity ipOverCdlc { + base iana-interface-type; + description + "IBM ipOverCdlc."; + } + identity ipOverClaw { + base iana-interface-type; + description + "IBM Common Link Access to Workstn."; + } + identity stackToStack { + base iana-interface-type; + description + "IBM stackToStack."; + } + identity virtualIpAddress { + base iana-interface-type; + description + "IBM VIPA."; + } + identity mpc { + base iana-interface-type; + description + "IBM multi-protocol channel support."; + } + identity ipOverAtm { + base iana-interface-type; + description + "IBM ipOverAtm."; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + identity iso88025Fiber { + base iana-interface-type; + description + "ISO 802.5j Fiber Token Ring."; + } + identity tdlc { + base iana-interface-type; + description + "IBM twinaxial data link control."; + } + identity gigabitEthernet { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity hdlc { + base iana-interface-type; + description + "HDLC."; + } + identity lapf { + base iana-interface-type; + description + "LAP F."; + } + identity v37 { + base iana-interface-type; + description + "V.37."; + } + identity x25mlp { + base iana-interface-type; + description + "Multi-Link Protocol."; + } + identity x25huntGroup { + base iana-interface-type; + description + "X25 Hunt Group."; + } + identity transpHdlc { + base iana-interface-type; + description + "Transp HDLC."; + } + identity interleave { + base iana-interface-type; + description + "Interleave channel."; + } + identity fast { + base iana-interface-type; + description + "Fast channel."; + } + identity ip { + base iana-interface-type; + description + "IP (for APPN HPR in IP networks)."; + } + identity docsCableMaclayer { + base iana-interface-type; + description + "CATV Mac Layer."; + } + identity docsCableDownstream { + base iana-interface-type; + description + "CATV Downstream interface."; + } + identity docsCableUpstream { + base iana-interface-type; + description + "CATV Upstream interface."; + } + identity a12MppSwitch { + base iana-interface-type; + description + "Avalon Parallel Processor."; + } + identity tunnel { + base iana-interface-type; + description + "Encapsulation interface."; + } + identity coffee { + base iana-interface-type; + description + "Coffee pot."; + reference + "RFC 2325 - Coffee MIB"; + } + identity ces { + base iana-interface-type; + description + "Circuit Emulation Service."; + } + identity atmSubInterface { + base iana-interface-type; + description + "ATM Sub Interface."; + } + identity l2vlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using 802.1Q."; + } + identity l3ipvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IP."; + } + identity l3ipxvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IPX."; + } + identity digitalPowerline { + base iana-interface-type; + description + "IP over Power Lines."; + } + identity mediaMailOverIp { + base iana-interface-type; + description + "Multimedia Mail over IP."; + } + identity dtm { + base iana-interface-type; + description + "Dynamic synchronous Transfer Mode."; + } + identity dcn { + base iana-interface-type; + description + "Data Communications Network."; + } + identity ipForward { + base iana-interface-type; + description + "IP Forwarding Interface."; + } + identity msdsl { + base iana-interface-type; + description + "Multi-rate Symmetric DSL."; + } + identity ieee1394 { + base iana-interface-type; + description + "IEEE1394 High Performance Serial Bus."; + } + identity if-gsn { + base iana-interface-type; + description + "HIPPI-6400."; + } + identity dvbRccMacLayer { + base iana-interface-type; + description + "DVB-RCC MAC Layer."; + } + identity dvbRccDownstream { + base iana-interface-type; + description + "DVB-RCC Downstream Channel."; + } + identity dvbRccUpstream { + base iana-interface-type; + description + "DVB-RCC Upstream Channel."; + } + identity atmVirtual { + base iana-interface-type; + description + "ATM Virtual Interface."; + } + identity mplsTunnel { + base iana-interface-type; + description + "MPLS Tunnel Virtual Interface."; + } + identity srp { + base iana-interface-type; + description + "Spatial Reuse Protocol."; + } + identity voiceOverAtm { + base iana-interface-type; + description + "Voice over ATM."; + } + identity voiceOverFrameRelay { + base iana-interface-type; + description + "Voice Over Frame Relay."; + } + identity idsl { + base iana-interface-type; + description + "Digital Subscriber Loop over ISDN."; + } + identity compositeLink { + base iana-interface-type; + description + "Avici Composite Link Interface."; + } + identity ss7SigLink { + base iana-interface-type; + description + "SS7 Signaling Link."; + } + identity propWirelessP2P { + base iana-interface-type; + description + "Prop. P2P wireless interface."; + } + identity frForward { + base iana-interface-type; + description + "Frame Forward Interface."; + } + identity rfc1483 { + base iana-interface-type; + description + "Multiprotocol over ATM AAL5."; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + identity usb { + base iana-interface-type; + description + "USB Interface."; + } + identity ieee8023adLag { + base iana-interface-type; + description + "IEEE 802.3ad Link Aggregate."; + } + identity bgppolicyaccounting { + base iana-interface-type; + description + "BGP Policy Accounting."; + } + identity frf16MfrBundle { + base iana-interface-type; + description + "FRF.16 Multilink Frame Relay."; + } + identity h323Gatekeeper { + base iana-interface-type; + description + "H323 Gatekeeper."; + } + identity h323Proxy { + base iana-interface-type; + description + "H323 Voice and Video Proxy."; + } + identity mpls { + base iana-interface-type; + description + "MPLS."; + } + identity mfSigLink { + base iana-interface-type; + description + "Multi-frequency signaling link."; + } + identity hdsl2 { + base iana-interface-type; + description + "High Bit-Rate DSL - 2nd generation."; + } + identity shdsl { + base iana-interface-type; + description + "Multirate HDSL2."; + } + identity ds1FDL { + base iana-interface-type; + description + "Facility Data Link (4Kbps) on a DS1."; + } + identity pos { + base iana-interface-type; + description + "Packet over SONET/SDH Interface."; + } + identity dvbAsiIn { + base iana-interface-type; + description + "DVB-ASI Input."; + } + identity dvbAsiOut { + base iana-interface-type; + description + "DVB-ASI Output."; + } + identity plc { + base iana-interface-type; + description + "Power Line Communications."; + } + identity nfas { + base iana-interface-type; + description + "Non-Facility Associated Signaling."; + } + identity tr008 { + base iana-interface-type; + description + "TR008."; + } + identity gr303RDT { + base iana-interface-type; + description + "Remote Digital Terminal."; + } + identity gr303IDT { + base iana-interface-type; + description + "Integrated Digital Terminal."; + } + identity isup { + base iana-interface-type; + description + "ISUP."; + } + identity propDocsWirelessMaclayer { + base iana-interface-type; + description + "Cisco proprietary Maclayer."; + } + identity propDocsWirelessDownstream { + base iana-interface-type; + description + "Cisco proprietary Downstream."; + } + identity propDocsWirelessUpstream { + base iana-interface-type; + description + "Cisco proprietary Upstream."; + } + identity hiperlan2 { + base iana-interface-type; + description + "HIPERLAN Type 2 Radio Interface."; + } + identity propBWAp2Mp { + base iana-interface-type; + description + "PropBroadbandWirelessAccesspt2Multipt (use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated, and ieee80216WMAN(237) should be used + instead)."; + } + identity sonetOverheadChannel { + base iana-interface-type; + description + "SONET Overhead Channel."; + } + identity digitalWrapperOverheadChannel { + base iana-interface-type; + description + "Digital Wrapper."; + } + identity aal2 { + base iana-interface-type; + description + "ATM adaptation layer 2."; + } + identity radioMAC { + base iana-interface-type; + description + "MAC layer over radio links."; + } + identity atmRadio { + base iana-interface-type; + description + "ATM over radio links."; + } + identity imt { + base iana-interface-type; + description + "Inter-Machine Trunks."; + } + identity mvl { + base iana-interface-type; + description + "Multiple Virtual Lines DSL."; + } + identity reachDSL { + base iana-interface-type; + description + "Long Reach DSL."; + } + identity frDlciEndPt { + base iana-interface-type; + description + "Frame Relay DLCI End Point."; + } + identity atmVciEndPt { + base iana-interface-type; + description + "ATM VCI End Point."; + } + identity opticalChannel { + base iana-interface-type; + description + "Optical Channel."; + } + identity opticalTransport { + base iana-interface-type; + description + "Optical Transport."; + } + identity propAtm { + base iana-interface-type; + description + "Proprietary ATM."; + } + identity voiceOverCable { + base iana-interface-type; + description + "Voice Over Cable Interface."; + } + identity infiniband { + base iana-interface-type; + description + "Infiniband."; + } + identity teLink { + base iana-interface-type; + description + "TE Link."; + } + identity q2931 { + base iana-interface-type; + description + "Q.2931."; + } + identity virtualTg { + base iana-interface-type; + description + "Virtual Trunk Group."; + } + identity sipTg { + base iana-interface-type; + description + "SIP Trunk Group."; + } + identity sipSig { + base iana-interface-type; + description + "SIP Signaling."; + } + identity docsCableUpstreamChannel { + base iana-interface-type; + description + "CATV Upstream Channel."; + } + identity econet { + base iana-interface-type; + description + "Acorn Econet."; + } + identity pon155 { + base iana-interface-type; + description + "FSAN 155Mb Symetrical PON interface."; + } + identity pon622 { + base iana-interface-type; + description + "FSAN 622Mb Symetrical PON interface."; + } + identity bridge { + base iana-interface-type; + description + "Transparent bridge interface."; + } + identity linegroup { + base iana-interface-type; + description + "Interface common to multiple lines."; + } + identity voiceEMFGD { + base iana-interface-type; + description + "Voice E&M Feature Group D."; + } + identity voiceFGDEANA { + base iana-interface-type; + description + "Voice FGD Exchange Access North American."; + } + identity voiceDID { + base iana-interface-type; + description + "Voice Direct Inward Dialing."; + } + identity mpegTransport { + base iana-interface-type; + description + "MPEG transport interface."; + } + identity sixToFour { + base iana-interface-type; + status deprecated; + description + "6to4 interface (DEPRECATED)."; + reference + "RFC 4087 - IP Tunnel MIB"; + } + identity gtp { + base iana-interface-type; + description + "GTP (GPRS Tunneling Protocol)."; + } + identity pdnEtherLoop1 { + base iana-interface-type; + description + "Paradyne EtherLoop 1."; + } + identity pdnEtherLoop2 { + base iana-interface-type; + description + "Paradyne EtherLoop 2."; + } + identity opticalChannelGroup { + base iana-interface-type; + description + "Optical Channel Group."; + } + identity homepna { + base iana-interface-type; + description + "HomePNA ITU-T G.989."; + } + identity gfp { + base iana-interface-type; + description + "Generic Framing Procedure (GFP)."; + } + identity ciscoISLvlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using Cisco ISL."; + } + identity actelisMetaLOOP { + base iana-interface-type; + description + "Acteleis proprietary MetaLOOP High Speed Link."; + } + identity fcipLink { + base iana-interface-type; + description + "FCIP Link."; + } + identity rpr { + base iana-interface-type; + description + "Resilient Packet Ring Interface Type."; + } + identity qam { + base iana-interface-type; + description + "RF Qam Interface."; + } + identity lmp { + base iana-interface-type; + description + "Link Management Protocol."; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + identity cblVectaStar { + base iana-interface-type; + description + "Cambridge Broadband Networks Limited VectaStar."; + } + identity docsCableMCmtsDownstream { + base iana-interface-type; + description + "CATV Modular CMTS Downstream Interface."; + } + identity adsl2 { + base iana-interface-type; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)."; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + identity macSecControlledIF { + base iana-interface-type; + description + "MACSecControlled."; + } + identity macSecUncontrolledIF { + base iana-interface-type; + description + "MACSecUncontrolled."; + } + identity aviciOpticalEther { + base iana-interface-type; + description + "Avici Optical Ethernet Aggregate."; + } + identity atmbond { + base iana-interface-type; + description + "atmbond."; + } + identity voiceFGDOS { + base iana-interface-type; + description + "Voice FGD Operator Services."; + } + identity mocaVersion1 { + base iana-interface-type; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA."; + } + identity ieee80216WMAN { + base iana-interface-type; + description + "IEEE 802.16 WMAN interface."; + } + identity adsl2plus { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop Version 2 - + Version 2 Plus and all variants."; + } + identity dvbRcsMacLayer { + base iana-interface-type; + description + "DVB-RCS MAC Layer."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbTdm { + base iana-interface-type; + description + "DVB Satellite TDM."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbRcsTdma { + base iana-interface-type; + description + "DVB-RCS TDMA."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity x86Laps { + base iana-interface-type; + description + "LAPS based on ITU-T X.86/Y.1323."; + } + identity wwanPP { + base iana-interface-type; + description + "3GPP WWAN."; + } + identity wwanPP2 { + base iana-interface-type; + description + "3GPP2 WWAN."; + } + identity voiceEBS { + base iana-interface-type; + description + "Voice P-phone EBS physical interface."; + } + identity ifPwType { + base iana-interface-type; + description + "Pseudowire interface type."; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)"; + } + identity ilan { + base iana-interface-type; + description + "Internal LAN on a bridge per IEEE 802.1ap."; + } + identity pip { + base iana-interface-type; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB."; + } + identity aluELP { + base iana-interface-type; + description + "Alcatel-Lucent Ethernet Link Protection."; + } + identity gpon { + base iana-interface-type; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948."; + } + identity vdsl2 { + base iana-interface-type; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)."; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + identity capwapDot11Profile { + base iana-interface-type; + description + "WLAN Profile Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapDot11Bss { + base iana-interface-type; + description + "WLAN BSS Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapWtpVirtualRadio { + base iana-interface-type; + description + "WTP Virtual Radio Interface."; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + identity bits { + base iana-interface-type; + description + "bitsport."; + } + identity docsCableUpstreamRfPort { + base iana-interface-type; + description + "DOCSIS CATV Upstream RF Port."; + } + identity cableDownstreamRfPort { + base iana-interface-type; + description + "CATV downstream RF Port."; + } + identity vmwareVirtualNic { + base iana-interface-type; + description + "VMware Virtual Network Interface."; + } + identity ieee802154 { + base iana-interface-type; + description + "IEEE 802.15.4 WPAN interface."; + reference + "IEEE 802.15.4-2006"; + } + identity otnOdu { + base iana-interface-type; + description + "OTN Optical Data Unit."; + } + identity otnOtu { + base iana-interface-type; + description + "OTN Optical channel Transport Unit."; + } + identity ifVfiType { + base iana-interface-type; + description + "VPLS Forwarding Instance Interface Type."; + } + identity g9981 { + base iana-interface-type; + description + "G.998.1 bonded interface."; + } + identity g9982 { + base iana-interface-type; + description + "G.998.2 bonded interface."; + } + identity g9983 { + base iana-interface-type; + description + "G.998.3 bonded interface."; + } + identity aluEpon { + base iana-interface-type; + description + "Ethernet Passive Optical Networks (E-PON)."; + } + identity aluEponOnu { + base iana-interface-type; + description + "EPON Optical Network Unit."; + } + identity aluEponPhysicalUni { + base iana-interface-type; + description + "EPON physical User to Network interface."; + } + identity aluEponLogicalLink { + base iana-interface-type; + description + "The emulation of a point-to-point link over the EPON + layer."; + } + identity aluGponOnu { + base iana-interface-type; + description + "GPON Optical Network Unit."; + reference + "ITU-T G.984.2"; + } + identity aluGponPhysicalUni { + base iana-interface-type; + description + "GPON physical User to Network interface."; + reference + "ITU-T G.984.2"; + } + identity vmwareNicTeam { + base iana-interface-type; + description + "VMware NIC Team."; + } + } diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index f7aa7174..cd340458 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -91,8 +91,9 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */ /* Plugin authorization. Set username option (or not) * @param[in] Clicon handle * @param[in] void*, eg Fastcgihandle request restconf - * @retval 0 if credentials OK - * @retval -1 credentials not OK + * @retval -1 Fatal error + * @retval 0 Credential not OK + * @retval 1 Credential OK */ typedef int (plgauth_t)(clicon_handle, void *); diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index c77d64aa..806baac7 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -202,6 +202,7 @@ struct yang_stmt{ cvec *ys_cvec; /* List of stmt-specific variables Y_RANGE: range_min, range_max Y_LIST: vector of keys + Y_TYPE & identity: store all derived types */ yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ }; @@ -252,6 +253,7 @@ yang_stmt *yang_find(yang_node *yn, int keyword, char *argument); yang_stmt *yang_find_datanode(yang_node *yn, char *argument); yang_stmt *yang_find_schemanode(yang_node *yn, char *argument); yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, yang_class class); +char *yang_find_myprefix(yang_stmt *ys); int yang_order(yang_stmt *y); int yang_print(FILE *f, yang_node *yn); int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal); diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 45c642e0..4f7cc68a 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -186,6 +186,7 @@ clicon_option_readfile_xml(clicon_hash_t *copt, /*! Initialize option values * * Set default options, Read config-file, Check that all values are set. + * Read clixon system config files * @param[in] h clicon handle */ int diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 60ea29a6..6960d6a7 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -226,7 +226,7 @@ xml2cli(FILE *f, return retval; } -/*! Validate an xml node of type leafref, ensure the value is one of that path's reference +/*! Validate xml node of type leafref, ensure the value is one of that path's reference * @param[in] xt XML leaf node of type leafref * @param[in] ytype Yang type statement belonging to the XML node */ @@ -270,6 +270,70 @@ validate_leafref(cxobj *xt, return retval; } +/*! Validate xml node of type identityref, ensure value is a defined identity + * Check if a given node has value derived from base identity. This is + * a run-time check necessary when validating eg netconf. + * Valid values for an identityref are any identities derived from all + * the identityref's base identities. + * Example: + * b0 --> b1 --> b2 (b1 & b2 are derived) + * identityref b2 + * base b0; + * This function does: derived_from(b2, b0); + * @param[in] xt XML leaf node of type identityref + * @param[in] ys Yang spec of leaf + * @param[in] ytype Yang type field of type identityref + * @see ys_populate_identity where the derived types are set + * @see RFC7950 Sec 9.10.2: + + */ +static int +validate_identityref(cxobj *xt, + yang_stmt *ys, + yang_stmt *ytype) +{ + int retval = -1; + char *node; + yang_stmt *ybaseref; /* This is the type's base reference */ + yang_stmt *ybaseid; + char *prefix = NULL; + cbuf *cb = NULL; + + /* Get idref value. Then see if this value is derived from ytype. + * Always add default prefix because derived identifiers are stored with + * prefixes in the base identifiers derived-list. + */ + if ((node = xml_body(xt)) == NULL) + return 0; + if (strchr(node, ':') == NULL){ + prefix = yang_find_myprefix(ys); + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + cprintf(cb, "%s:%s", prefix, node); + node = cbuf_get(cb); + } + /* This is the type's base reference */ + if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){ + clicon_err(OE_DB, 0, "Identityref validation failed, no base"); + goto done; + } + /* This is the actual base identity */ + if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){ + clicon_err(OE_DB, 0, "Identityref validation failed, no base identity"); + goto done; + } + /* Here check if node is in the derived node list of the base identity */ + if (cvec_find(ybaseid->ys_cvec, node) == NULL){ + clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument); + goto done; + } + retval = 0; + done: + return retval; +} + /*! Validate a single XML node with yang specification for added entry * 1. Check if mandatory leafs present as subs. * 2. Check leaf values, eg int ranges and string regexps. @@ -374,10 +438,16 @@ xml_yang_validate_all(cxobj *xt, /* Special case if leaf is leafref, then first check against current xml tree */ - if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL && - strcmp(ytype->ys_argument, "leafref") == 0) - if (validate_leafref(xt, ytype) < 0) - goto done; + if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ + if (strcmp(ytype->ys_argument, "leafref") == 0){ + if (validate_leafref(xt, ytype) < 0) + goto done; + } + else if (strcmp(ytype->ys_argument, "identityref") == 0){ + if (validate_identityref(xt, ys, ytype) < 0) + goto done; + } + } break; default: break; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 93074e32..cd9a567d 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -817,8 +817,6 @@ yarg_prefix(yang_stmt *ys) return prefix; } - - /*! Given a yang statement and a prefix, return yang module to that prefix * Note, not the other module but the proxy import statement only * @param[in] ys A yang statement @@ -1168,6 +1166,8 @@ ys_populate_range(yang_stmt *ys, /*! Sanity check yang type statement * XXX: Replace with generic parent/child type-check + * @param[in] ys The yang statement (type) to populate. + * @ */ static int ys_populate_type(yang_stmt *ys, @@ -1199,28 +1199,82 @@ ys_populate_type(yang_stmt *ys, return retval; } -/*! Sanity check yang type statement +/*! Sanity check yang identity statement recursively + * + * Find base identities if any and add this identity to derived list. + * Do this recursively + * @param[in] ys The yang identity to populate. + * @param[in] arg If set contains a derived identifier + * @see validate_identityref which in runtime validates actual valoues */ static int ys_populate_identity(yang_stmt *ys, void *arg) { int retval = -1; - yang_stmt *ybase; + yang_stmt *yc = NULL; + yang_stmt *ybaseid; + cg_var *cv; + char *derid; + char *baseid; + char *prefix = NULL; + cbuf *cb = NULL; + char *idref = (char*)arg; + char *p; - if ((ybase = yang_find((yang_node*)ys, Y_BASE, NULL)) == NULL) - return 0; - if ((yang_find_identity(ys, ybase->ys_argument)) == NULL){ - clicon_err(OE_YANG, 0, "Identity %s not found (base type of %s)", - ybase->ys_argument, ys->ys_argument); - goto done; + if (idref == NULL){ + /* Create derived identity through prefix:id if not recursively called*/ + derid = ys->ys_argument; /* derived id */ + if ((prefix = yarg_prefix(ys)) == NULL){ + if ((p = yang_find_myprefix(ys)) != NULL) + prefix = strdup(yang_find_myprefix(ys)); + } + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + if (prefix) + cprintf(cb, "%s:%s", prefix, derid); + else + cprintf(cb, "%s", derid); + idref = cbuf_get(cb); + } + /* Iterate through all base statements and check the base identity exists + * AND populate the base identity recursively + */ + while ((yc = yn_each((yang_node*)ys, yc)) != NULL) { + if (yc->ys_keyword != Y_BASE) + continue; + baseid = yc->ys_argument; + if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){ + clicon_err(OE_YANG, 0, "No such identity: %s", baseid); + goto done; + } + // continue; /* root identity */ + /* Check if derived id is already in base identifier */ + if (cvec_find(ybaseid->ys_cvec, idref) != NULL) + continue; + /* Add derived id to ybaseid */ + if ((cv = cv_new(CGV_STRING)) == NULL){ + clicon_err(OE_UNIX, errno, "cv_new"); + goto done; + } + /* add prefix */ + cv_name_set(cv, idref); + cvec_append_var(ybaseid->ys_cvec, cv); + /* Transitive to the root */ + if (ys_populate_identity(ybaseid, idref) < 0) + goto done; } retval = 0; done: + if (prefix) + free(prefix); + if (cb) + cbuf_free(cb); return retval; } - /*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree. * * We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 1d7f57d4..f54af474 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -380,7 +380,7 @@ clicon_type2cv(char *origtype, (rmax && (i) > cv_##type##_get(rmax))) -/*! +/*! Validate CLIgen variable * @retval -1 Error (fatal), with errno set to indicate error * @retval 0 Validation not OK, malloced reason is returned. Free reason with free() * @retval 1 Validation OK @@ -764,9 +764,9 @@ ys_typedef_up(yang_stmt *ys) return (yang_stmt*)ys; } -/*! Return yang-stmt of identity +/*! Find identity yang-stmt This is a sanity check of base identity of identity-ref and for identity - statements. + statements when parsing. Return true if node is identityref and is derived from identity_name The derived-from() function returns true if the (first) node (in @@ -779,12 +779,17 @@ ys_typedef_up(yang_stmt *ys) identityref's base identity. 1. (base) identity must exist (be found). This is a sanity check of the specification and also necessary for identity statements. + (This is what is dine here) 2. Check if a given node has value derived from base identity. This is a run-time check necessary when validating eg netconf. + (This is validation) 3. Find all valid derived identities from a identityref base identity. - This is for cli generation. - Så vad är det denna function ska göra? Svar: 1 -*/ + (This is for cli generation) + * @param[in] ys Yang spec of id statement + * @param[in] identity Identity string -check if it exists + * @retval 0 OK + * @see validate_identityref for (2) above + */ yang_stmt * yang_find_identity(yang_stmt *ys, char *identity) diff --git a/test/lib.sh b/test/lib.sh index 061730f6..f82907a0 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -55,21 +55,28 @@ new2(){ >&2 echo -n "Test$testnr [$1]" } -# clixon tester. First arg is command and second is expected outcome +# clixon command tester. +# Arguments: +# - command, +# - expected command return value (0 if OK) +# - expected stdout outcome, +# - expected2 stdout outcome, expectfn(){ cmd=$1 - expect=$2 + retval=$2 + expect="$3" - if [ $# = 3 ]; then - expect2=$3 + if [ $# = 4 ]; then + expect2=$4 else expect2= fi ret=$($cmd) - -# if [ $? -ne 0 ]; then -# err "wrong args" -# fi + if [ $? -ne $retval ]; then + echo -e "\e[31m\nError in Test$testnr [$testname]:" + echo -e "\e[0m:" + exit -1 + fi # Match if both are empty string if [ -z "$ret" -a -z "$expect" ]; then return @@ -79,7 +86,6 @@ expectfn(){ fi # grep extended grep match=`echo $ret | grep -EZo "$expect"` - if [ -z "$match" ]; then err "$expect" "$ret" fi diff --git a/test/test_cli.sh b/test/test_cli.sh index ec71a707..ee524339 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -18,6 +18,7 @@ cat < $cfg $cfg /usr/local/share/$APPNAME/yang $APPNAME + /usr/local/lib/$APPNAME/backend /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME @@ -43,74 +44,73 @@ fi new "cli tests" new "cli configure top" -expectfn "$clixon_cli -1 -f $cfg set interfaces" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces" 0 "^$" new "cli show configuration top (no presence)" -expectfn "$clixon_cli -1 -f $cfg show conf cli" "^$" +expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$" new "cli configure delete top" -expectfn "$clixon_cli -1 -f $cfg delete interfaces" "^$" +expectfn "$clixon_cli -1 -f $cfg delete interfaces" 0 "^$" new "cli show configuration delete top" -expectfn "$clixon_cli -1 -f $cfg show conf cli" "^$" +expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$" new "cli configure" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" 0 "^$" new "cli show configuration" -expectfn "$clixon_cli -1 -f $cfg show conf cli" "^interfaces interface eth/0/0 enabled true" +expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^interfaces interface eth/0/0 enabled true" new "cli configure using encoded chars data <&" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description \"foo<&bar\"" "" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description \"foo<&bar\"" 0 "" new "cli configure using encoded chars name <&" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type eth" "" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 "" new "cli failed validate" -expectfn "$clixon_cli -1 -f $cfg -l o validate" "Missing mandatory variable" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "Missing mandatory variable" new "cli configure more" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" "^$" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description mydesc" "^$" -expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type bgp" "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description mydesc" 0 "^$" +expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type ex:eth" 0 "^$" new "cli show xpath description" -expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" "mydesc" +expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" 0 "mydesc" new "cli delete description" -expectfn "$clixon_cli -1 -f $cfg -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" 0 "" new "cli show xpath no description" -expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" 0 "^$" new "cli copy interface" -expectfn "$clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99" "^$" +expectfn "$clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99" 0 "^$" new "cli success validate" -expectfn "$clixon_cli -1 -f $cfg -l o validate" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$" new "cli commit" -expectfn "$clixon_cli -1 -f $cfg -l o commit" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o commit" 0 "^$" new "cli save" -expectfn "$clixon_cli -1 -f $cfg -l o save /tmp/foo" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o save /tmp/foo" 0 "^$" new "cli delete all" -expectfn "$clixon_cli -1 -f $cfg -l o delete all" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o delete all" 0 "^$" new "cli load" -expectfn "$clixon_cli -1 -f $cfg -l o load /tmp/foo" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o load /tmp/foo" 0 "^$" new "cli check load" -expectfn "$clixon_cli -1 -f $cfg -l o show conf cli" "^interfaces interface name eth/0/0 type bgp -interfaces interface eth/0/0 ipv4 enabled true" +expectfn "$clixon_cli -1 -f $cfg -l o show conf cli" 0 "interfaces interface eth/0/0 ipv4 enabled true" new "cli debug" -expectfn "$clixon_cli -1 -f $cfg -l o debug level 1" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o debug level 1" 0 "^$" # How to test this? -expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" "^$" +expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" 0 "^$" new "cli rpc" -expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" "^" +expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 "ipv4" "2.3.4.5" new "Kill backend" # Check if still alive diff --git a/test/test_datastore.sh b/test/test_datastore.sh index b0714b4d..d9155b0e 100755 --- a/test/test_datastore.sh +++ b/test/test_datastore.sh @@ -57,99 +57,99 @@ run(){ conf="-d candidate -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip" new "datastore $name init" - expectfn "$datastore $conf init" "" + expectfn "$datastore $conf init" 0 "" # Whole tree operations new "datastore $name put all replace" - expectfn "$datastore $conf put replace $db" "" + expectfn "$datastore $conf put replace $db" 0 "" new "datastore $name get" - expectfn "$datastore $conf get /" "^$db$" + expectfn "$datastore $conf get /" 0 "^$db$" new "datastore $name put all remove" - expectfn "$datastore $conf put remove " + expectfn "$datastore $conf put remove " 0 "" new "datastore $name get" - expectfn "$datastore $conf get /" "^$" + expectfn "$datastore $conf get /" 0 "^$" new "datastore $name put all merge" - expectfn "$datastore $conf put merge $db" "" + expectfn "$datastore $conf put merge $db" 0 "" new "datastore $name get" - expectfn "$datastore $conf get /" "^$db$" + expectfn "$datastore $conf get /" 0 "^$db$" new "datastore $name put all delete" - expectfn "$datastore $conf put remove " + expectfn "$datastore $conf put remove " 0 "" new "datastore $name get" - expectfn "$datastore $conf get /" "^$" + expectfn "$datastore $conf get /" 0 "^$" new "datastore $name put all create" - expectfn "$datastore $conf put create $db" "" + expectfn "$datastore $conf put create $db" 0 "" new "datastore $name get" - expectfn "$datastore $conf get /" "^$db$" + expectfn "$datastore $conf get /" 0 "^$db$" new "datastore $name put top create" - expectfn "$datastore $conf put create " "" # error + expectfn "$datastore $conf put create " 0 "" # error # Single key operations # leaf new "datastore $name put all delete" - expectfn "$datastore $conf delete" "" + expectfn "$datastore $conf delete" 0 "" new "datastore $name init" - expectfn "$datastore $conf init" "" + expectfn "$datastore $conf init" 0 "" new "datastore $name create leaf" - expectfn "$datastore $conf put create 13newentry" + expectfn "$datastore $conf put create 13newentry" 0 "" new "datastore $name create leaf" - expectfn "$datastore $conf put create 13newentry" + expectfn "$datastore $conf put create 13newentry" 0 "" new "datastore $name delete leaf" - expectfn "$datastore $conf put delete 13" + expectfn "$datastore $conf put delete 13" 0 "" new "datastore $name replace leaf" - expectfn "$datastore $conf put create 13newentry" + expectfn "$datastore $conf put create 13newentry" 0 "" new "datastore $name remove leaf" - expectfn "$datastore $conf put remove " + expectfn "$datastore $conf put remove " 0 "" new "datastore $name remove leaf" - expectfn "$datastore $conf put remove 13" + expectfn "$datastore $conf put remove 13" 0 "" new "datastore $name delete leaf" - expectfn "$datastore $conf put delete " + expectfn "$datastore $conf put delete " 0 "" new "datastore $name merge leaf" - expectfn "$datastore $conf put merge nalle" + expectfn "$datastore $conf put merge nalle" 0 "" new "datastore $name replace leaf" - expectfn "$datastore $conf put replace nalle" + expectfn "$datastore $conf put replace nalle" 0 "" new "datastore $name merge leaf" - expectfn "$datastore $conf put merge 13newentry" + expectfn "$datastore $conf put merge 13newentry" 0 "" new "datastore $name replace leaf" - expectfn "$datastore $conf put replace 13newentry" + expectfn "$datastore $conf put replace 13newentry" 0 "" new "datastore $name create leaf" - expectfn "$datastore $conf put create aaa" + expectfn "$datastore $conf put create aaa" 0 "" new "datastore $name create leaf" - expectfn "$datastore $conf put create 13newentry" + expectfn "$datastore $conf put create 13newentry" 0 "" new "datastore other db init" - expectfn "$datastore -d kalle -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip init" + expectfn "$datastore -d kalle -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip init" 0 "" new "datastore other db copy" - expectfn "$datastore $conf copy kalle" "" + expectfn "$datastore $conf copy kalle" 0 "" diff $mydir/kalle_db $mydir/candidate_db new "datastore lock" - expectfn "$datastore $conf lock 756" "" + expectfn "$datastore $conf lock 756" 0 "" #leaf-list diff --git a/test/test_identity.sh b/test/test_identity.sh new file mode 100755 index 00000000..b828fc1f --- /dev/null +++ b/test/test_identity.sh @@ -0,0 +1,188 @@ +#!/bin/bash +# Identity and identityref tests +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/example-my-crypto.yang + +cat < $cfg + + $cfg + $dir + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/backend + example_backend.so$ + /usr/local/lib/$APPNAME/netconf + /usr/local/lib/$APPNAME/restconf + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + +EOF + +# Example from RFC7950 Sec 7.18 and 9.10 +# with two changes: the leaf statement is in the original module and +# a transitive dependent identifier (foo) +cat < $dir/example-crypto-base.yang + module example-crypto-base { + yang-version 1.1; + namespace "urn:example:crypto-base"; + prefix "crypto"; + + identity crypto-alg { + description + "Base identity from which all crypto algorithms + are derived."; + } + identity symmetric-key { + description + "Base identity used to identify symmetric-key crypto + algorithms."; + } + identity public-key { + description + "Base identity used to identify public-key crypto + algorithms."; + } + } + +EOF + +cat < $dir/example-des.yang + module example-des { + yang-version 1.1; + namespace "urn:example:des"; + prefix "des"; + import "example-crypto-base" { + prefix "crypto"; + } + identity des { + base "crypto:crypto-alg"; + base "crypto:symmetric-key"; + description "DES crypto algorithm."; + } + identity des3 { + base "crypto:crypto-alg"; + base "crypto:symmetric-key"; + description "Triple DES crypto algorithm."; + } + } +EOF + +cat < $fyang + module example { + yang-version 1.1; + namespace "urn:example:my-crypto"; + prefix mc; + import "example-crypto-base" { + prefix "crypto"; + } + import "example-des" { + prefix "des"; + } + identity aes { + base "crypto:crypto-alg"; + } + identity foo { + description "transitive dependent identifier"; + base "des:des"; + } + leaf crypto { + description "Value can be any transitively derived from crypto-alg"; + type identityref { + base "crypto:crypto-alg"; + } + } + container aes-parameters { + when "../crypto = 'mc:aes'"; + } + } +EOF + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err +fi +new "start backend -s init -f $cfg -y $fyang" +# start new backend +sudo clixon_backend -s init -f $cfg -y $fyang # -D 1 +if [ $? -ne 0 ]; then + err +fi + +new "Set crypto to aes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "aes]]>]]>" "^]]>]]>$" + +new "netconf validate " +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "Set crypto to mc:aes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "mc:aes]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "Set crypto to des:des3" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "des:des3]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "Set crypto to mc:foo" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "mc:foo]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +#new "Set crypto to x:des3" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + +new "Set crypto to foo:bar" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "foo:bar]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^operation-failedapplicationerrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$" + +new "cli set crypto to mc:aes" +expectfn "$clixon_cli -1 -f $cfg -l o set crypto mc:aes" 0 "^$" + +new "cli validate" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$" + +new "cli set crypto to aes" +expectfn "$clixon_cli -1 -f $cfg -l o set crypto aes" 0 "^$" + +new "cli validate" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$" + +new "cli set crypto to des:des3" +expectfn "$clixon_cli -1 -f $cfg -l o set crypto des:des3" 0 "^$" + +new "cli validate" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$" + +new "Kill backend" +# Check if still alive +pid=`pgrep clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 1b7c2990..f18263d4 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -24,9 +24,19 @@ EOF cat < $fyang module example{ + prefix ex; + import ietf-interfaces { + prefix if; + } import ietf-ip { prefix ip; } + identity eth { + base if:interface-type; + } + identity lo { + base if:interface-type; + } container default-address { leaf absname { type leafref { @@ -75,8 +85,8 @@ fi new "leafref base config" expecteof "$clixon_netconf -qf $cfg -y $fyang" " -eth0 eth
192.0.2.1
192.0.2.2
-lolo
127.0.0.1
+eth0ex:eth
192.0.2.1
192.0.2.2
+loex:lo
127.0.0.1
]]>]]>" "^]]>]]>$" new "leafref get config" @@ -115,16 +125,16 @@ new "leafref discard-changes" expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" new "cli leafref lo" -expectfn "$clixon_cli -1f $cfg -y $fyang -l o set default-address absname lo" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set default-address absname lo" 0 "^$" new "cli leafref validate" -expectfn "$clixon_cli -1f $cfg -y $fyang -l o validate" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o validate" 0 "^$" new "cli sender" -expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" 0 "^$" new "cli sender template" -expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" "^$" +expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" 0 "^$" new "Kill backend" # Check if still alive diff --git a/test/test_netconf.sh b/test/test_netconf.sh index b6fc58ac..800dcdcb 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -30,6 +30,9 @@ EOF cat < $fyang module example{ prefix ex; + import ietf-interfaces { + prefix if; + } import ietf-ip { prefix ip; } @@ -42,6 +45,9 @@ module example{ } rpc empty { } + identity eth { + base if:interface-type; + } rpc client-rpc { description "Example local client-side RPC that is processed by the the netconf/restconf and not sent to the backend. @@ -84,22 +90,22 @@ new "Check nothing added" expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none and create which should add eth/0/0" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" new "Check eth/0/0 added using xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^eth/0/0ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^eth/0/0ex:ethtrue]]>]]>$" new "Re-create same eth/0/0 which should generate error" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^" new "Delete eth/0/0 using none config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" new "Check deleted eth/0/0 (non-presence container)" expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '^]]>]]>$' new "Re-Delete eth/0/0 using none should generate error" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^" new "netconf edit config" expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" @@ -126,7 +132,7 @@ new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf edit config eth1" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1eth]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1ex:eth]]>]]>" "^]]>]]>$" new "netconf validate" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" @@ -135,26 +141,26 @@ new "netconf commit" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf edit config merge" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth2ethmerge]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth2ex:ethmerge]]>]]>" "^]]>]]>$" new "netconf edit ampersand encoding(<&): name:'eth&' type:'t<>'" expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth& t< > ]]>]]>" "^]]>]]>$" new "netconf get replaced config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth& t< > trueeth1ethtrueeth2ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth& t< > trueeth1ex:ethtrueeth2ex:ethtrue]]>]]>$" new "cli show configuration eth& - encoding tests" -expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" "interfaces interface eth& type t<> +expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" 0 "interfaces interface eth& type t<> interfaces interface eth& enabled true" new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf edit state operation should fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1eth]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1ex:eth]]>]]>" "^invalid-value" new "netconf get state operation" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth0eth42]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth0ex:eth42]]>]]>$" new "netconf lock/unlock" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" @@ -175,7 +181,7 @@ new "copy startup" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "netconf get startup" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth1ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth1ex:ethtrue]]>]]>$" new "netconf delete startup" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 344b521f..d1ff4ade 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -31,6 +31,9 @@ EOF cat < $fyang module example{ prefix ex; + import ietf-interfaces { + prefix if; + } import ietf-ip { prefix ip; } @@ -41,6 +44,9 @@ module example{ prefix "inet"; revision-date "2013-07-15"; } + identity eth { + base if:interface-type; + } rpc empty { } rpc input { @@ -68,7 +74,7 @@ module example{ EOF # This is a fixed 'state' implemented in routing_backend. It is assumed to be always there -state='{"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}' +state='{"interfaces-state": {"interface": \[{"name": "eth0","type": "ex:eth","if-index": 42}\]}}' # kill old backend (if any) new "kill old backend" @@ -130,113 +136,113 @@ if [ -z "$match" ]; then fi new "restconf options. RFC 8040 4.1" -expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE" +expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" 0 "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE" new "restconf head. RFC 8040 4.2" -expectfn "curl -s -I http://localhost/restconf/data" "HTTP/1.1 200 OK" +expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK" #Content-Type: application/yang-data+json" new2 "restconf empty rpc" expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" "" new2 "restconf get empty config + state json" -expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}} +expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new "restconf get empty config + state xml" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data) -expect="eth0eth42" +expect="eth0ex:eth42" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new2 "restconf get data/interfaces-state/interface=eth0 json" -expecteq "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0)" '{"interface": [{"name": "eth0","type": "eth","if-index": 42}]} +expecteq "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0)" '{"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]} ' new "restconf get state operation eth0 xml" # Cant get shell macros to work, inline matching from lib.sh ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/interfaces-state/interface=eth0) -expect="eth0eth42" +expect="eth0ex:eth42" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new2 "restconf get state operation eth0 type json" -expecteq "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0/type)" '{"type": "eth"} +expecteq "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0/type)" '{"type": "ex:eth"} ' new "restconf get state operation eth0 type xml" # Cant get shell macros to work, inline matching from lib.sh ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/interfaces-state/interface=eth0/type) -expect="eth" +expect="ex:eth" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new2 "restconf GET datastore" -expecteq "$(curl -s -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}} +expecteq "$(curl -s -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' # Exact match new "restconf Add subtree to datastore using POST" -expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' 'HTTP/1.1 200 OK' +expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK' new "restconf Re-add subtree which should give error" -expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' +expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' # XXX Cant get this to work -#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' +#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' new "restconf Check interfaces eth/0/0 added" -expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}} +expectfn "curl -s -G http://localhost/restconf/data" 0 '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "ex:eth","if-index": 42}\]}} ' new2 "restconf delete interfaces" expecteq $(curl -s -X DELETE http://localhost/restconf/data/interfaces) "" new "restconf Check empty config" -expectfn "curl -sG http://localhost/restconf/data" "$state" +expectfn "curl -sG http://localhost/restconf/data" 0 "$state" new "restconf Add interfaces subtree eth/0/0 using POST" -expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces' "" +expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/interfaces' 0 "" # XXX cant get this to work -#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"eth","enabled":true}}' http://localhost/restconf/data/interfaces)" "" +#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" "" new2 "restconf Check eth/0/0 added" -expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}} +expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new2 "restconf Re-post eth/0/0 which should generate error" -expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "Add leaf description using POST" expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" new "Add nothing using POST" -expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:' +expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:' new2 "restconf Check description added" -expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}} +expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new "restconf delete eth/0/0" expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" new "Check deleted eth/0/0" -expectfn 'curl -s -G http://localhost/restconf/data' $state +expectfn 'curl -s -G http://localhost/restconf/data' 0 $state new2 "restconf Re-Delete eth/0/0 using none should generate error" expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-missing","error-type": "application","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} ' new "restconf Add subtree eth/0/0 using PUT" -expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" +expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" new2 "restconf get subtree" -expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}} +expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new2 "restconf rpc using POST json" diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index df401e24..26cf56d7 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -66,22 +66,22 @@ sleep 1 new "restconf tests" new "restconf POST initial tree" -expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' "" +expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 "" new "restconf GET datastore" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' new "restconf GET interface" -expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" '{"interface": \[{"name": "local0","type": "regular"}\]}' +expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" 0 '{"interface": \[{"name": "local0","type": "regular"}\]}' new "restconf GET if-type" -expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" '{"type": "regular"}' +expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" 0 '{"type": "regular"}' new "restconf POST interface without mandatory type" -expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' '"error-message": "Missing mandatory variable: type"' +expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' 0 '"error-message": "Missing mandatory variable: type"' new "restconf POST interface" -expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' "" +expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 "" new2 "restconf POST again" expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/cont1)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' @@ -90,51 +90,50 @@ new2 "restconf POST from top" expecteq "$(curl -s -X POST -d '{"cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "restconf DELETE" -expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' "" +expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 "" new "restconf GET null datastore" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": null}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": null}' new "restconf POST initial tree" -expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' "" +expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 "" new "restconf GET initial tree" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' new "restconf DELETE whole datastore" -expectfn 'curl -s -X DELETE http://localhost/restconf/data' "" +expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 "" new "restconf GET null datastore" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": null}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": null}' new "restconf PUT initial datastore" -expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' "" +expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 "" new "restconf GET datastore" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}' new "restconf PUT replace datastore" -expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' "" +expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 "" new "restconf GET replaced datastore" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"cont2": {"name": "foo"}}}' - +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont2": {"name": "foo"}}}' new "restconf PUT initial datastore again" -expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' "" +expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 "" new "restconf PUT change interface" -expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' "" +expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' 0 "" new "restconf GET datastore atm" -expectfn "curl -s -X GET http://localhost/restconf/data" '{"data": {"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}}' +expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}}' new "restconf PUT add interface" -expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' "" +expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 "" new "restconf PUT change key error" -expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "api-path keys do not match data keys"}}}}' +expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "api-path keys do not match data keys"}}}}' new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf diff --git a/test/test_startup.sh b/test/test_startup.sh index d7b5d6a8..7c3208f0 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -43,7 +43,7 @@ run(){ run - eth + ex:eth
@@ -55,7 +55,7 @@ EOF startup - eth + ex:eth
@@ -67,7 +67,7 @@ EOF extra - eth + ex:eth
@@ -103,8 +103,8 @@ EOF } run init '' -run none 'runethtrue' -run running 'extraethtruelolocaltruerunethtrue' -run startup 'extraethtruelolocaltruestartupethtrue' +run none 'runex:ethtrue' +run running 'extraex:ethtrueloex:loopbacktruerunex:ethtrue' +run startup 'extraex:ethtrueloex:loopbacktruestartupex:ethtrue' rm -rf $dir diff --git a/test/test_type.sh b/test/test_type.sh index 7788ee02..cae1227a 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -139,25 +139,25 @@ if [ $? -ne 0 ]; then fi new "cli set ab" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.a.b" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.a.b" 0 "^$" new "cli set cd" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list c.d.c.d" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list c.d.c.d" 0 "^$" new "cli set ef" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list e.f.e.f" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list e.f.e.f" 0 "^$" new "cli set ab fail" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a&b&a&b" "^CLI syntax error" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a&b&a&b" 255 "^CLI syntax error" new "cli set ad fail" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.c.d" "^CLI syntax error" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set list a.b.c.d" 255 "^CLI syntax error" new "cli validate" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o validate" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o validate" 0 "^$" new "cli commit" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o commit" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o commit" 0 "^$" new "netconf validate ok" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" @@ -175,7 +175,7 @@ new "netconf commit" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" new "cli enum value" -expectfn "$clixon_cli -1f $cfg -l o -y $fyang set status down" "^$" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang set status down" 0 "^$" new "Kill backend" # Check if still alive diff --git a/test/test_yang.sh b/test/test_yang.sh index 52d4a70f..86c56b3b 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -111,11 +111,11 @@ if [ $? -ne 0 ]; then fi new "cli defined extension" -expectfn "$clixon_cli -1f $cfg -y $fyang show version" "3." +expectfn "$clixon_cli -1f $cfg -y $fyang show version" 0 "3." new "cli not defined extension" # This text yields an error, but the test cannot detect the error message yet -#expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" "Yang error: Extension ex:not-defined not found" +#expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found" new "netconf edit config" expecteof "$clixon_netconf -qf $cfg -y $fyang" "125one]]>]]>" "^]]>]]>$" @@ -143,10 +143,10 @@ new "netconf get (should be some)" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^125one]]>]]>$" new "cli set leaf-list" -expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" "" +expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 "" new "cli show leaf-list" -expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" "foo" +expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "foo" new "netconf set state data (not allowed)" expecteof "$clixon_netconf -qf $cfg -y $fyang" "42]]>]]>" "^invalid-value" From 39538461c217597ad4e47299413b0b41516330a5 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 3 Jun 2018 20:18:43 +0200 Subject: [PATCH 13/53] dont exit cli when expand gets auth denied from backend --- CHANGELOG.md | 2 +- apps/cli/cli_main.c | 4 ++-- apps/cli/cli_plugin.c | 38 +++++++++++++++++++++----------------- apps/cli/cli_show.c | 2 +- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c0ee11..9faaded1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Previous support did no validation of values. * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities - + * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: * Added yang identityref runtime validation * Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 2e269016..4e5b2048 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -119,7 +119,7 @@ cli_interactive(clicon_handle h) int res; char *cmd; char *new_mode; - int result; + int eval; /* Loop through all commands */ while(!cligen_exiting(cli_cligen(h))) { @@ -128,7 +128,7 @@ cli_interactive(clicon_handle h) cligen_exiting_set(cli_cligen(h), 1); /* EOF */ goto done; } - if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0) + if ((res = clicon_parse(h, cmd, &new_mode, &eval)) < 0) goto done; } retval = 0; diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 193e6df3..92caf7fd 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -444,6 +444,8 @@ cli_handler_err(FILE *f) /*! Evaluate a matched command * @param[in] h Clicon handle * @param[in] cmd The command string + * @retval int If there is a callback, the return value of the callback is returned, + * @retval 0 otherwise */ int clicon_eval(clicon_handle h, @@ -451,10 +453,12 @@ clicon_eval(clicon_handle h, cg_obj *match_obj, cvec *cvv) { + int retval = 0; + cli_output_reset(); if (!cligen_exiting(cli_cligen(h))) { clicon_err_reset(); - if (cligen_eval(cli_cligen(h), match_obj, cvv) < 0) { + if ((retval = cligen_eval(cli_cligen(h), match_obj, cvv)) < 0) { #if 0 /* This is removed since we get two error messages on failure. But maybe only sometime? Both a real log when clicon_err is called, and the here again. @@ -463,11 +467,10 @@ clicon_eval(clicon_handle h, #endif } } - return 0; + return retval; } - -/*! Given a command string, parse and evaluate. +/*! Given a command string, parse and if match single command, eval it. * Parse and evaluate the string according to * the syntax parse tree of the syntax mode specified by *mode. * If there is no match in the tree for the command, the parse hook @@ -478,20 +481,25 @@ clicon_eval(clicon_handle h, * @param[in] h Clicon handle * @param[in] cmd Command string * @param[in,out] modenamep Pointer to the mode string pointer - * @param[out] result -2 On eof (shouldnt happen) + * @param[out] evalres Evaluation result if retval=1 + * -2 On eof (shouldnt happen) * -1 On parse error * >=0 Number of matches - * @retval 0 XXX How does this relate to result??? + * @retval -2 Eof CG_EOF + * @retval -1 Error CG_ERROR + * @retval 0 No match CG_NOMATCH + * @retval 1 Exactly one match CG_MATCH + * @retval 2+ Multiple matches */ int clicon_parse(clicon_handle h, char *cmd, char **modenamep, - int *result) + int *evalres) { + int retval = -1; char *modename; char *modename0; - int retval = -1; int r; cli_syntax_t *stx = NULL; cli_syntaxmode_t *smode; @@ -515,8 +523,7 @@ clicon_parse(clicon_handle h, goto done; } } - if (smode) - while(1) { + if (smode){ modename0 = NULL; if ((pt = cligen_tree_active_get(cli_cligen(h))) != NULL) modename0 = pt->pt_name; @@ -543,13 +550,12 @@ clicon_parse(clicon_handle h, case CG_EOF: /* eof */ case CG_ERROR: cli_output(f, "CLI parse error: %s\n", cmd); - goto done; + break; case CG_NOMATCH: /* no match */ /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", cmd, cli_nomatch(h));*/ cli_output(f, "CLI syntax error: \"%s\": %s\n", cmd, cli_nomatch(h)); - goto done; break; case CG_MATCH: if (strcmp(modename, *modenamep)){ /* Command in different mode */ @@ -559,16 +565,14 @@ clicon_parse(clicon_handle h, if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0) cli_handler_err(stdout); pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */ - if (result) - *result = r; - goto done; + if (evalres) + *evalres = r; break; default: cli_output(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd); - goto done; break; } /* switch retval */ - } /* while smode */ + } done: if (cvv) cvec_free(cvv); diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index ed92d5de..94a4c7c4 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -161,7 +161,7 @@ expand_dbvar(void *h, goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); - goto done; + goto ok; } xcur = xt; /* default top-of-tree */ xpathcur = xpath; From 849e46191efa6d404546c46d595dfbe9515a161e Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 5 Jun 2018 09:42:11 +0200 Subject: [PATCH 14/53] * Added xmlns validation * for eg --- CHANGELOG.md | 2 + lib/clixon/clixon_xml.h | 2 +- lib/src/clixon_plugin.c | 2 +- lib/src/clixon_xml.c | 77 +++++++++++++++++++++++++++++--- lib/src/clixon_xml_parse.y | 91 ++++++++++++++++++++------------------ test/test_cli.sh | 2 +- 6 files changed, 124 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9faaded1..f10731fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added xmlns validation + * for eg * Added yang identityref runtime validation * Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. * Replace functions as follows in CLI SPEC files: diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 6a0513ec..f22d30f2 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -69,7 +69,7 @@ typedef struct xml cxobj; /* struct defined in clicon_xml.c */ * @retval 1 Abort, dont continue with others * @retval 2 Locally, just abort this subtree, continue with others */ -typedef int (xml_applyfn_t)(cxobj *yn, void *arg); +typedef int (xml_applyfn_t)(cxobj *x, void *arg); /* * xml_flag() flags: diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index a0f2a4d3..2e1ffdfb 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -430,7 +430,7 @@ rpc_callback_register(clicon_handle h, clicon_err(OE_DB, errno, "malloc: %s", strerror(errno)); goto done; } - memset (rc, 0, sizeof (*rc)); + memset(rc, 0, sizeof(*rc)); rc->rc_callback = cb; rc->rc_arg = arg; rc->rc_tag = strdup(tag); /* XXX strdup memleak */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index c23d8d37..edc11643 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -109,7 +109,7 @@ struct xml{ enum cxobj_type x_type; /* type of node: element, attribute, body */ char *x_value; /* attribute and body nodes have values */ int _x_vector_i; /* internal use: xml_child_each */ - int x_flags; /* Flags according to XML_FLAG_* above */ + int x_flags; /* Flags according to XML_FLAG_* */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ cg_var *x_cv; /* If body this contains the typed value */ @@ -181,7 +181,7 @@ xml_namespace(cxobj *xn) return xn->x_namespace; } -/*! Set name of xnode, name is copied +/*! Set name space of xnode, namespace is copied * @param[in] xn xml node * @param[in] namespace new namespace, null-terminated string, copied by function * @retval -1 on error with clicon-err set @@ -204,6 +204,71 @@ xml_namespace_set(cxobj *xn, return 0; } +/*! See if xmlns:= exists, if so return + * + * @param[in] xn XML node + * @param[in] nsn Namespace name + * @retval URI return associated URI if found + * @retval NULL No namespace name binding found for nsn + */ +static char * +xmlns_check(cxobj *xn, + char *nsn) +{ + cxobj *x = NULL; + char *xns; + + while ((x = xml_child_each(xn, x, -1)) != NULL) + if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 && + strcmp(xml_name(x), nsn) == 0) + return xml_value(x); + return NULL; +} + +/*! Check namespace of xml node by searhing recursively among ancestors + * @param[in] xn xml node + * @param[in] namespace check validity of namespace + * @retval 0 Found / validated or no yang spec + * @retval -1 Not found + * @note This function is grossly inefficient + */ +static int +xml_namespace_check(cxobj *xn, + void *arg) +{ + cxobj *xp = NULL; + char *nsn; + char *n; + yang_stmt *ys = xml_spec(xn); + + /* No namespace name - comply */ + if ((nsn = xml_namespace(xn)) == NULL) + return 0; + /* Check if NSN defined in same node */ + if (xmlns_check(xn, nsn) != NULL) + return 0; + /* Check if NSN defined in some ancestor */ + while ((xp = xml_parent(xn)) != NULL) { + if (xmlns_check(xp, nsn) != NULL) + return 0; + xn = xp; + } + if (ys == NULL) + return 0; /* If no yang spec */ + else{ + /* Check if my namespace */ + if ((n = yang_find_myprefix(ys)) != NULL && strcmp(nsn,n)==0) + return 0; + /* Check if any imported module */ + if (yang_find_module_by_prefix(ys, nsn) != NULL) + return 0; + } + /* Not found, error */ + clicon_err(OE_XML, ENOENT, "Namespace name %s in %s:%s not found", + nsn, nsn, xml_name(xn)); + return -1; +} + /*! Get parent of xnode * @param[in] xn xml node * @retval parent xml node @@ -869,7 +934,6 @@ xml_find_value(cxobj *xt, * Explaining picture: * xt --> x --> bx (x_type=CX_BODY) * x_name=name return x_value - */ char * xml_find_body(cxobj *xt, @@ -1247,6 +1311,9 @@ _xml_parse(const char *str, goto done; if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */ goto done; + /* Verify namespaces after parsing */ + if (xml_apply0(xt, CX_ELMNT, xml_namespace_check, NULL) < 0) + goto done; /* Sort the complete tree after parsing */ if (yspec){ if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) @@ -1658,7 +1725,6 @@ xml_apply0(cxobj *xn, return retval; } - /*! Apply a function call recursively on all ancestors * Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for * each object found. The function is called with the xml node and an @@ -1861,7 +1927,6 @@ xml_operation2str(enum operation_type op) } } - /* * Turn this on to get a xml parse and pretty print test program * Usage: xpath @@ -1892,7 +1957,7 @@ main(int argc, char **argv) return 0; } if (xml_parse_file(0, "
", NULL, &xt) < 0){ - fprintf(stderr, "parsing 2\n"); + fprintf(stderr, "xml parse error %s\n", clicon_err_reason); return -1; } xc = NULL; diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index ab9d5193..8a80ff8d 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -43,12 +43,11 @@ %token NAME CHARDATA %token VER ENC -%token BSLASH ESLASH +%token BSLASH ESLASH %token BTEXT ETEXT %token BCOMMENT ECOMMENT - -%type attvalue attqname +%type attvalue %lex-param {void *_ya} /* Add this argument to parse() and lex() function */ %parse-param {void *_ya} @@ -117,7 +116,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, char *ver) { if(strcmp(ver, "1.0")){ - clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0\n", ver); + clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0", ver); free(ver); return -1; } @@ -125,15 +124,41 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, return 0; } -/*! Parse Qualified name +/*! Parse Qualified name --> Unprefixed name * @param[in] ya XML parser yacc handler struct * @param[in] prefix Prefix, namespace, or NULL * @param[in] localpart Name */ static int -xml_parse_qname(struct xml_parse_yacc_arg *ya, - char *prefix, - char *name) +xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya, + char *name) +{ + int retval = -1; + cxobj *x; + yang_stmt *y = NULL; /* yang node */ + cxobj *xp; /* xml parent */ + + xp = ya->ya_xparent; + if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0) + goto done; + if ((x = xml_new(name, xp, y)) == NULL) + goto done; + ya->ya_xelement = x; + retval = 0; + done: + free(name); + return retval; +} + +/*! Parse Qualified name -> PrefixedName + * @param[in] ya XML parser yacc handler struct + * @param[in] prefix Prefix, namespace, or NULL + * @param[in] localpart Name + */ +static int +xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya, + char *prefix, + char *name) { int retval = -1; cxobj *x; @@ -197,7 +222,7 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, goto done; } if (xml_namespace(x)!=NULL){ - clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s\n", + clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s", xml_namespace(x), xml_name(x), name); goto done; } @@ -235,7 +260,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, cxobj *xc; if (strcmp(xml_name(x), name)){ - clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", + clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", xml_namespace(x), xml_name(x), namespace, @@ -244,7 +269,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, } if (xml_namespace(x)==NULL || strcmp(xml_namespace(x), namespace)){ - clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", + clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", xml_namespace(x), xml_name(x), namespace, @@ -276,44 +301,29 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, static int xml_parse_attr(struct xml_parse_yacc_arg *ya, - char *qname, + char *prefix, + char *name, char *attval) { int retval = -1; cxobj *xa; - if ((xa = xml_new(qname, ya->ya_xelement, NULL)) == NULL) + if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); + if (prefix && xml_namespace_set(xa, prefix) < 0) + goto done; if (xml_value_set(xa, attval) < 0) goto done; retval = 0; done: - free(qname); + free(name); + if (prefix) + free(prefix); free(attval); return retval; } -/*! Parse Attribue Qualified name, Just transform prefix:name into a new string - * - */ -static char* -xml_merge_attqname(struct xml_parse_yacc_arg *ya, - char *prefix, - char *name) -{ - char *str; - int len = strlen(prefix)+strlen(name)+2; - - if ((str=malloc(len)) == NULL) - return NULL; - snprintf(str, len, "%s:%s", prefix, name); - free(prefix); - free(name); - return str; -} - - %} %% @@ -344,9 +354,9 @@ element : '<' qname attrs element1 { clicon_debug(3, "element -> < qname attrs element1"); } ; -qname : NAME { if (xml_parse_qname(_YA, NULL, $1) < 0) YYABORT; +qname : NAME { if (xml_parse_unprefixed_name(_YA, $1) < 0) YYABORT; clicon_debug(3, "qname -> NAME %s", $1);} - | NAME ':' NAME { if (xml_parse_qname(_YA, $1, $3) < 0) YYABORT; + | NAME ':' NAME { if (xml_parse_prefixed_name(_YA, $1, $3) < 0) YYABORT; clicon_debug(3, "qname -> NAME : NAME");} ; @@ -385,15 +395,10 @@ attrs : attrs attr | ; -attr : attqname '=' attvalue { if (xml_parse_attr(_YA, $1, $3) < 0) YYABORT; } +attr : NAME '=' attvalue { if (xml_parse_attr(_YA, NULL, $1, $3) < 0) YYABORT; } + | NAME ':' NAME '=' attvalue { if (xml_parse_attr(_YA, $1, $3, $5) < 0) YYABORT; } ; -attqname : NAME {$$ = $1;} - | NAME ':' NAME - { if (($$ = xml_merge_attqname(_YA, $1, $3)) == NULL) YYABORT; } - ; - - attvalue : '\"' CHARDATA '\"' { $$=$2; /* $2 must be consumed */} | '\"' '\"' { $$=strdup(""); /* $2 must be consumed */} ; diff --git a/test/test_cli.sh b/test/test_cli.sh index ee524339..8b3857ff 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -67,7 +67,7 @@ new "cli configure using encoded chars name <&" expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 "" new "cli failed validate" -expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "Missing mandatory variable" +expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Missing mandatory variable" new "cli configure more" expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$" From 0ad6703663b25d3edd836c3976540cdf9795a891 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 5 Jun 2018 16:43:43 +0200 Subject: [PATCH 15/53] xmlns sanity check --- CHANGELOG.md | 4 ++++ example/example_cli.cli | 19 ++++++++++++------- lib/clixon/clixon_xml.h | 3 +++ lib/src/clixon_xml.c | 5 ++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f10731fe..e9edfa55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,10 @@ * Added cli returna value also for single commands (eg -1) * Fixed JSON unbalanced braces resulting in assert. +### Known issues +* Namespace name relabeling is not supported. + * Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlfns is not supported, such as: x:des3 + ## 3.6.1 (29 May 2018) ### Corrected Bugs diff --git a/example/example_cli.cli b/example/example_cli.cli index 657f884b..8853aed2 100644 --- a/example/example_cli.cli +++ b/example/example_cli.cli @@ -6,8 +6,6 @@ CLICON_PLUGIN="example_cli"; # Translate variable "value" by incrementing its characters translate value (),cli_set("/translate/value"); - - # Note, when switching to PT, change datamodel to only @datamodel set @datamodel:example, cli_set(); merge @datamodel:example, cli_merge(); @@ -41,11 +39,18 @@ show("Show a particular state of the system"){ text("Show comparison in text"), compare_dbs((int32)1); } configuration("Show configuration"), cli_show_config("candidate", "text", "/");{ - xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/"); - netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/"); - text("Show configuration as text"), cli_show_config("candidate","text","/"); - cli("Show configuration as cli commands"), cli_show_config("candidate", "cli", "/"); - json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/"); + xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{ + @datamodel:example, cli_show_auto("candidate", "text"); + } + netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{ + @datamodel:example, cli_show_auto("candidate", "netconf"); + } + text("Show configuration as text"), cli_show_config("candidate","text","/");{ + @datamodel:example, cli_show_auto("candidate", "text"); + } + json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/");{ + @datamodel:example, cli_show_auto("candidate", "json"); + } } } diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index f22d30f2..34b0265a 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -80,6 +80,9 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg); #define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */ #define XML_FLAG_NONE 0x10 /* Node is added as NONE */ +/* Full xmlns validation check is made only if XML has associated YANG spec */ +#define XMLNS_YANG_ONLY 1 + /* Sort and binary search of XML children * Experimental */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index edc11643..63be3008 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -253,9 +253,12 @@ xml_namespace_check(cxobj *xn, return 0; xn = xp; } +#ifdef XMLNS_YANG_ONLY if (ys == NULL) return 0; /* If no yang spec */ - else{ + else +#endif + { /* Check if my namespace */ if ((n = yang_find_myprefix(ys)) != NULL && strcmp(nsn,n)==0) return 0; From c5991c98441de03c167045f9cc9953cffa4ea3b3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 5 Jun 2018 16:45:43 +0200 Subject: [PATCH 16/53] * Added a generated CLI show command that works on the generated parse tree with auto completion. * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); * The example contains a more elaborate example. * Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24 --- CHANGELOG.md | 4 ++ apps/cli/cli_generate.c | 1 + apps/cli/cli_show.c | 88 ++++++++++++++++++++++++++++++++++++++- apps/cli/clixon_cli_api.h | 2 + example/example_cli.cli | 5 ++- 5 files changed, 98 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9edfa55..c73fdc88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added a generated CLI show command that works on the generated parse tree with auto completion. + * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); + * The example contains a more elaborate example. + * Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24 * Added xmlns validation * for eg * Added yang identityref runtime validation diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index d4a2aca9..24c52c2f 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -141,6 +141,7 @@ cli_expand_var_generate(clicon_handle h, * @param[in] ys yang_stmt of the node at hand * @param[in] cb0 The string where the result format string is inserted. * @see cli_dbxml This is where the xmlkeyfmt string is used + * @see pt_callback_reference in CLIgen where the actual callback overwrites the template */ static int cli_callback_generate(clicon_handle h, diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 94a4c7c4..52821f7a 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -417,7 +417,7 @@ show_yang(clicon_handle h, * @param[in] argv String vector: [] * Format of argv: * "running"|"candidate"|"startup" - * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) + * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) * xpath expression, that may contain one %, eg "/sender[name=%s]" * optional name of variable in cvv. If set, xpath must have a '%s' * @code @@ -600,6 +600,92 @@ int cli_show_version(clicon_handle h, cvec *vars, cvec *argv) return 0; } +/*! Generic show configuration CLIGEN callback using generated CLI syntax + * Format of argv: + * Generated API PATH + * "running"|"candidate"|"startup" + * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) + */ +int +cli_show_auto(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = 1; + yang_spec *yspec; + char *api_path_fmt; /* xml key format */ + // char *api_path = NULL; /* xml key */ + char *db; + char *xpath; + char *formatstr; + enum format_enum format = FORMAT_XML; + cxobj *xt = NULL; + cxobj *xp; + cxobj *xerr; + enum genmodel_type gt; + + if (cvec_len(argv) != 3){ + clicon_err(OE_PLUGIN, 0, "%s: Usage: * . (*) generated.", __FUNCTION__); + goto done; + } + /* First argv argument: API_path format */ + api_path_fmt = cv_string_get(cvec_i(argv, 0)); + /* Second argv argument: Database */ + db = cv_string_get(cvec_i(argv, 1)); + /* Third format: output format */ + formatstr = cv_string_get(cvec_i(argv, 2)); + if ((format = format_str2int(formatstr)) < 0){ + clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr); + goto done; + } + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_FATAL, 0, "No DB_SPEC"); + goto done; + } + + // if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) + // goto done; + if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0) + goto done; + /* Get configuration from database */ + if (clicon_rpc_get_config(h, db, xpath, &xt) < 0) + goto done; + if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + clicon_rpc_generate_error("Get configuration", xerr); + goto done; + } + if ((xp = xpath_first(xt, xpath)) != NULL) + /* Print configuration according to format */ + switch (format){ + case FORMAT_XML: + clicon_xml2file(stdout, xp, 0, 1); + break; + case FORMAT_JSON: + xml2json(stdout, xp, 1); + break; + case FORMAT_TEXT: + xml2txt(stdout, xp, 0); /* tree-formed text */ + break; + case FORMAT_CLI: + if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) + goto done; + xml2cli(stdout, xp, NULL, gt); /* cli syntax */ + break; + case FORMAT_NETCONF: + fprintf(stdout, "\n"); + clicon_xml2file(stdout, xp, 2, 1); + fprintf(stdout, "]]>]]>\n"); + break; + default: /* see cli_show_config() */ + break; + } + retval = 0; + done: + if (xt) + xml_free(xt); + return retval; +} + #ifdef COMPAT_CLIV int show_yangv(clicon_handle h, cvec *vars, cvec *argv) { diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 713ea521..c31645ba 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -142,6 +142,8 @@ int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv); int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv); +int cli_show_auto(clicon_handle h, cvec *cvv, cvec *argv); + #ifdef COMPAT_CLIV int cli_setv(clicon_handle h, cvec *vars, cvec *argv); int cli_mergev(clicon_handle h, cvec *vars, cvec *argv); diff --git a/example/example_cli.cli b/example/example_cli.cli index 8853aed2..1040f834 100644 --- a/example/example_cli.cli +++ b/example/example_cli.cli @@ -42,13 +42,16 @@ show("Show a particular state of the system"){ xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{ @datamodel:example, cli_show_auto("candidate", "text"); } + cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/");{ + @datamodel:example, cli_show_auto("candidate", "cli"); + } netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{ @datamodel:example, cli_show_auto("candidate", "netconf"); } text("Show configuration as text"), cli_show_config("candidate","text","/");{ @datamodel:example, cli_show_auto("candidate", "text"); } - json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/");{ + json("Show configuration as JSON"), cli_show_config("candidate", "json", "/");{ @datamodel:example, cli_show_auto("candidate", "json"); } } From de69b253dc6955210c5d8d95b3d3d6d5f0fceb62 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Thu, 7 Jun 2018 21:10:01 +0200 Subject: [PATCH 17/53] check null ptr --- apps/cli/cli_generate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 24c52c2f..f387cdf7 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -205,7 +205,7 @@ yang2cli_var_sub(clicon_handle h, } type = ytype?ytype->ys_argument:NULL; cvtypestr = cv_type2str(cvtype); - if (strcmp(type, "identityref") == 0) + if (type && strcmp(type, "identityref") == 0) cprintf(cb, "("); cprintf(cb, "<%s:%s", ys->ys_argument, cvtypestr); /* enumeration special case completion */ @@ -292,7 +292,7 @@ yang2cli_var_sub(clicon_handle h, cprintf(cb, ">"); if (helptext) cprintf(cb, "(\"%s\")", helptext); - if (strcmp(type, "identityref") == 0) + if (type && strcmp(type, "identityref") == 0) cprintf(cb, ")"); retval = 0; From 5cabc11bfb91a47a66c35d56b7fa81eb6d93ac87 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 7 Jun 2018 21:34:52 +0200 Subject: [PATCH 18/53] * Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests --- CHANGELOG.md | 1 + apps/backend/backend_client.c | 6 ++--- apps/backend/backend_main.c | 21 +++++++-------- apps/backend/backend_plugin.c | 2 ++ apps/cli/cli_main.c | 38 +++++++++++++++++---------- apps/netconf/netconf_main.c | 11 ++++++-- example/example_backend.c | 16 ++++++------ example/example_cli.c | 9 ++++++- include/clixon_custom.h | 8 ++++++ lib/clixon/clixon_xml.h | 2 -- lib/src/clixon_proto_client.c | 3 +++ lib/src/clixon_xml_map.c | 2 +- test/test_auth.sh | 2 +- test/test_auth_ext.sh | 48 ++++++++++++++++++++++++++++++++--- test/test_identity.sh | 11 ++++++-- 15 files changed, 132 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c73fdc88..e2ee0eae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests * Added a generated CLI show command that works on the generated parse tree with auto completion. * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); * The example contains a more elaborate example. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 469f7652..d721b471 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -803,7 +803,7 @@ from_client_debug(clicon_handle h, return retval; } -/*! Match nacm access operations according to RFC8321 3.4.4. +/*! Match nacm access operations according to RFC8341 3.4.4. * Incoming RPC Message Validation Step 7 (c) * The rule's "access-operations" leaf has the "exec" bit set or * has the special value "*". @@ -832,7 +832,7 @@ nacm_match_access(char *access_operations, * @retval 0 Matching rule AND Not access and cbret set * @retval 1 Matchung rule AND Access * @retval 2 No matching rule Goto step 10 - * From RFC8321 3.4.4. Incoming RPC Message Validation + * From RFC8341 3.4.4. Incoming RPC Message Validation +---------+-----------------+---------------------+-----------------+ | Method | Resource class | NETCONF operation | Access | | | | | operation | @@ -911,7 +911,7 @@ nacm_match_rule(clicon_handle h, * @retval -1 Error * @retval 0 Not access and cbret set * @retval 1 Access - * From RFC8321 3.4.4. Incoming RPC Message Validation + * From RFC8341 3.4.4. Incoming RPC Message Validation */ static int nacm_access(clicon_handle h, diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 9babb24e..db331b7e 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -117,7 +117,8 @@ backend_sig_term(int arg) /*! usage */ static void -usage(char *argv0, clicon_handle h) +usage(clicon_handle h, + char *argv0) { char *plgdir = clicon_backend_dir(h); char *confsock = clicon_sock(h); @@ -581,11 +582,11 @@ main(int argc, break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &debug) != 1) - usage(argv[0], h); + usage(h, argv[0]); break; case 'f': /* config file */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; } @@ -602,7 +603,7 @@ main(int argc, /* Find and read configfile */ if (clicon_options_main(h) < 0){ if (help) - usage(argv[0], h); + usage(h, argv[0]); return -1; } /* External NACM file? */ @@ -621,12 +622,12 @@ main(int argc, break; /* see above */ case 'd': /* Plugin directory */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg); break; case 'b': /* XMLDB database directory */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_XMLDB_DIR", optarg); break; case 'F' : /* foreground */ @@ -640,7 +641,7 @@ main(int argc, break; case 'u': /* config unix domain path / ip address */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_SOCK", optarg); break; case 'P': /* pidfile */ @@ -650,7 +651,7 @@ main(int argc, clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg); if (clicon_startup_mode(h) < 0){ fprintf(stderr, "Invalid startup mode: %s\n", optarg); - usage(argv[0], h); + usage(h, argv[0]); } break; case 'c': /* Load application config */ @@ -668,7 +669,7 @@ main(int argc, break; } default: - usage(argv[0], h); + usage(h, argv[0]); break; } @@ -677,7 +678,7 @@ main(int argc, /* Defer: Wait to the last minute to print help message */ if (help) - usage(argv[0], h); + usage(h, argv[0]); /* Check pid-file, if zap kil the old daemon, else return here */ if ((pidfile = clicon_backend_pidfile(h)) == NULL){ diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index e69c21fa..4d7e9eaa 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -157,6 +157,8 @@ clixon_plugin_statedata(clicon_handle h, if (reason){ while ((xc = xml_child_i(*xtop, 0)) != NULL) xml_purge(xc); + clicon_log(LOG_NOTICE, "%s: Plugin '%s' state callback failed", + __FUNCTION__, cp->cp_name); if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) goto done; goto ok; diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 4e5b2048..685546f7 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -71,7 +71,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:" +#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:U:" /*! terminate cli application */ static int @@ -194,7 +194,8 @@ dump_configfile_xml_fn(FILE *fout, } static void -usage(char *argv0, clicon_handle h) +usage(clicon_handle h, + char *argv0) { char *confsock = clicon_sock(h); char *plgdir = clicon_cli_dir(h); @@ -217,7 +218,8 @@ usage(char *argv0, clicon_handle h) "\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n" "\t-l \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n" "\t-y \tOverride yang spec file (dont include .yang suffix)\n" - "\t-c \tSpecify cli spec file.\n", + "\t-c \tSpecify cli spec file.\n" + "\t-U \tOver-ride unix user with a pseudo user for NACM.\n", argv0, confsock ? confsock : "none", plgdir ? plgdir : "none" @@ -256,7 +258,9 @@ main(int argc, char **argv) /* Initiate CLICON handle */ if ((h = cli_handle_init()) == NULL) goto done; - /* Set username to clicon handle. Use in all communication to backend */ + /* Set username to clicon handle. Use in all communication to backend + * Note, can be overridden by -U + */ if ((pw = getpwuid(getuid())) == NULL){ clicon_err(OE_UNIX, errno, "getpwuid"); goto done; @@ -283,11 +287,11 @@ main(int argc, char **argv) break; case 'D' : /* debug */ if (sscanf(optarg, "%d", &debug) != 1) - usage(argv[0], h); + usage(h, argv[0]); break; case 'f': /* config file */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; case 'x': /* dump config file as xml (migration from .conf file)*/ @@ -305,9 +309,9 @@ main(int argc, char **argv) logdst = CLICON_LOG_STDOUT; break; default: - usage(argv[0], h); + usage(h, argv[0]); } - break; + break; } /* * Logs, error and debug to stderr or syslog, set debug level @@ -327,7 +331,7 @@ main(int argc, char **argv) /* Find and read configfile */ if (clicon_options_main(h) < 0){ if (help) - usage(argv[0], h); + usage(h, argv[0]); return -1; } @@ -352,17 +356,17 @@ main(int argc, char **argv) break; case 'u': /* config unix domain path/ ip host */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_SOCK", optarg); break; case 'd': /* Plugin directory: overrides configfile */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CLI_DIR", optarg); break; case 'm': /* CLI syntax mode */ if (!strlen(optarg)) - usage(argv[0], h); + usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CLI_MODE", optarg); break; case 'q' : /* Quiet mode */ @@ -385,8 +389,14 @@ main(int argc, char **argv) clicon_option_str_set(h, "CLICON_CLISPEC_FILE", optarg); break; } + case 'U': /* Clixon 'pseudo' user */ + if (!strlen(optarg)) + usage(h, argv[0]); + if (clicon_username_set(h, optarg) < 0) + goto done; + break; default: - usage(argv[0], h); + usage(h, argv[0]); break; } } @@ -395,7 +405,7 @@ main(int argc, char **argv) /* Defer: Wait to the last minute to print help message */ if (help) - usage(argv[0], h); + usage(h, argv[0]); /* Setup signal handlers */ cli_signal_init(h); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 20fd8cdf..708b49f5 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -71,7 +71,7 @@ #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ -#define NETCONF_OPTS "hDqf:d:Sy:" +#define NETCONF_OPTS "hDqf:d:Sy:U:" /*! Process incoming packet * @param[in] h Clicon handle @@ -292,7 +292,8 @@ usage(clicon_handle h, "\t-f \tConfiguration file (mandatory)\n" "\t-d \tSpecify netconf plugin directory dir (default: %s)\n" "\t-S\t\tLog on syslog\n" - "\t-y \tOverride yang spec file (dont include .yang suffix)\n", + "\t-y \tOverride yang spec file (dont include .yang suffix)\n" + "\t-U \tOver-ride unix user with a pseudo user for NACM.\n", argv0, clicon_netconf_dir(h) ); @@ -379,6 +380,12 @@ main(int argc, clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); break; } + case 'U': /* Clixon 'pseudo' user */ + if (!strlen(optarg)) + usage(h, argv[0]); + if (clicon_username_set(h, optarg) < 0) + goto done; + break; default: usage(h, argv[0]); break; diff --git a/example/example_backend.c b/example/example_backend.c index a3dfe832..4db69f71 100644 --- a/example/example_backend.c +++ b/example/example_backend.c @@ -180,7 +180,7 @@ empty(clicon_handle h, /* Clicon handle */ * Real code would poll state */ int -plugin_statedata(clicon_handle h, +example_statedata(clicon_handle h, char *xpath, cxobj *xstate) { @@ -214,8 +214,8 @@ plugin_statedata(clicon_handle h, * @note This assumes example yang with interfaces/interface */ int -plugin_reset(clicon_handle h, - const char *db) +example_reset(clicon_handle h, + const char *db) { int retval = -1; cxobj *xt = NULL; @@ -250,7 +250,7 @@ plugin_reset(clicon_handle h, * can be processed with the standard getopt(3). */ int -plugin_start(clicon_handle h, +example_start(clicon_handle h, int argc, char **argv) { @@ -261,11 +261,11 @@ clixon_plugin_api *clixon_plugin_init(clicon_handle h); static clixon_plugin_api api = { "example", /* name */ - clixon_plugin_init, /* init */ - plugin_start, /* start */ + clixon_plugin_init, /* init - must be called clixon_plugin_init */ + example_start, /* start */ NULL, /* exit */ - .ca_reset=plugin_reset, /* reset */ - .ca_statedata=plugin_statedata, /* statedata */ + .ca_reset=example_reset, /* reset */ + .ca_statedata=example_statedata, /* statedata */ .ca_trans_begin=NULL, /* trans begin */ .ca_trans_validate=transaction_validate,/* trans validate */ .ca_trans_complete=NULL, /* trans complete */ diff --git a/example/example_cli.c b/example/example_cli.c index fef13c35..ed5ebb20 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -90,17 +90,24 @@ fib_route_rpc(clicon_handle h, cxobj *xtop = NULL; cxobj *xrpc; cxobj *xret = NULL; + cxobj *xerr; /* User supplied variable in CLI command */ instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */ /* Create XML for fib-route netconf RPC */ - if (xml_parse_va(&xtop, NULL, "%s", cv_string_get(instance)) < 0) + if (xml_parse_va(&xtop, NULL, "%s", + clicon_username_get(h), + cv_string_get(instance)) < 0) goto done; /* Skip top-level */ xrpc = xml_child_i(xtop, 0); /* Send to backend */ if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; + if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){ + clicon_rpc_generate_error("Get configuration", xerr); + goto done; + } /* Print result */ xml_print(stdout, xml_child_i(xret, 0)); retval = 0; diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 92b12204..d95ce399 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -47,3 +47,11 @@ int strverscmp (__const char *__s1, __const char *__s2); * This was obsoleted in 3.7 */ #undef COMPAT_CLIV + +/* Set if you want to assert that all rpc messages have set username + */ +#undef RPC_USERNAME_ASSERT + +/* Full xmlns validation check is made only if XML has associated YANG spec +*/ +#define XMLNS_YANG_ONLY 1 diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 34b0265a..2199629b 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -80,8 +80,6 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg); #define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */ #define XML_FLAG_NONE 0x10 /* Node is added as NONE */ -/* Full xmlns validation check is made only if XML has associated YANG spec */ -#define XMLNS_YANG_ONLY 1 /* Sort and binary search of XML children * Experimental diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 7c1bec04..7e255208 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -91,6 +91,9 @@ clicon_rpc_msg(clicon_handle h, cxobj *xret = NULL; yang_spec *yspec; +#ifdef RPC_USERNAME_ASSERT + assert(strstr(msg->op_body, "username")!=NULL); /* XXX */ +#endif clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body); if ((sock = clicon_sock(h)) == NULL){ clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set"); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 6960d6a7..cac926d9 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1779,7 +1779,7 @@ xml_merge1(cxobj *x0, * @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x1 xml tree which modifies base * @param[in] yspec Yang spec - * @param[out] reason If retval=0 a malloced string. Needs to be freed by caller + * @param[out] reason If retval=0, reason is set. Malloced. Needs to be freed by caller * @retval 0 OK. If reason is set, Yang error * @retval -1 Error * @note both x0 and x1 need to be top-level trees diff --git a/test/test_auth.sh b/test/test_auth.sh index 447fb4bf..a99e0242 100755 --- a/test/test_auth.sh +++ b/test/test_auth.sh @@ -1,6 +1,6 @@ #!/bin/bash # Authentication and authorization and IETF NACM -# See RFC 8321 A.2 +# See RFC 8341 A.2 # But replaced ietf-netconf-monitoring with * APPNAME=example diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh index 373fca1f..a6162fe1 100755 --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -1,7 +1,7 @@ #!/bin/bash # Authentication and authorization and IETF NACM # External NACM file -# See RFC 8321 A.2 +# See RFC 8341 A.2 # But replaced ietf-netconf-monitoring with * APPNAME=example @@ -16,9 +16,11 @@ nacmfile=$dir/nacmfile cat < $cfg $cfg - /usr/local/share/$APPNAME/yang + /usr/local/share/clixon $fyang /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/backend + example_backend.so$ /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli $APPNAME @@ -36,6 +38,12 @@ EOF cat < $fyang module $APPNAME{ prefix ex; + import ietf-interfaces { + prefix if; + } + import iana-if-type { + prefix ianaift; + } container authentication { description "Example code for enabling www basic auth and some example users"; @@ -61,6 +69,21 @@ module $APPNAME{ type int32; description "something to edit"; } + container interfaces-state { + config false; + list interface{ + key "name"; + leaf name{ + type string; + } + leaf type{ + type string; + } + leaf if-index { + type int32; + } + } + } } EOF @@ -75,7 +98,6 @@ cat < $nacmfile admin admin adm1 - olof limited @@ -167,7 +189,7 @@ new "restconf DELETE whole datastore" expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" "" new2 "auth get" -expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": null} +expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} ' new "Set x to 0" @@ -203,6 +225,24 @@ expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf new2 "guest edit nacm" expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +new "cli show conf as admin" +expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang show conf" 0 "^x 1;$" + +new "cli show conf as limited" +expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang show conf" 0 "^x 1;$" + +new "cli show conf as guest" +expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang show conf" 255 "CLI command error" + +new "cli rpc as admin" +expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang rpc ipv4" 0 "2.3.4.5" + +new "cli rpc as limited" +expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang rpc ipv4" 0 "access-denied" + +new "cli rpc as guest" +expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang rpc ipv4" 0 "access-denied" + new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf diff --git a/test/test_identity.sh b/test/test_identity.sh index b828fc1f..2d01fa01 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -143,12 +143,19 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" -#new "Set crypto to x:des3" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" +new "Set crypto to des:des3 using xmlns" +expecteof "$clixon_netconf -qf $cfg -y $fyang" "des:des3]]>]]>" "^]]>]]>$" new "netconf validate" expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +# XXX this is not supported +#new "Set crypto to x:des3 using xmlns" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" + +#new "netconf validate" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" + new "Set crypto to foo:bar" expecteof "$clixon_netconf -qf $cfg -y $fyang" "foo:bar]]>]]>" "^]]>]]>$" From 8f9a38d2aa8a7a549b7e733e7fea94d1d787ab1c Mon Sep 17 00:00:00 2001 From: Dave Cornejo Date: Thu, 7 Jun 2018 13:26:18 -1000 Subject: [PATCH 19/53] add handling of CDATA to XML parser --- lib/src/clixon_xml_parse.l | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l index e0c39884..770f6288 100644 --- a/lib/src/clixon_xml_parse.l +++ b/lib/src/clixon_xml_parse.l @@ -77,6 +77,7 @@ int clixon_xml_parsewrap(void) %x START %s STATEA %s AMPERSAND +%s CDATA %s CMNT %s STR %s TEXTDECL @@ -106,7 +107,9 @@ int clixon_xml_parsewrap(void) \< { BEGIN(START); return *clixon_xml_parsetext; } & { _YA->ya_lex_state =STATEA;BEGIN(AMPERSAND);} \n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);} +"ya_lex_state = STATEA; clixon_xml_parselval.string = yytext; return CHARDATA;} . { clixon_xml_parselval.string = yytext; return CHARDATA; /*XXX:optimize*/} + /* @see xml_chardata_encode */ "amp; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "&"; return CHARDATA;} "lt; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "<"; return CHARDATA;} @@ -114,6 +117,8 @@ int clixon_xml_parsewrap(void) "apos; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;} "aquot; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;} +. { clixon_xml_parselval.string = yytext; return CHARDATA;} +"]]>" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = yytext; return CHARDATA;} "-->" { BEGIN(START); return ECOMMENT; } \n _YA->ya_linenum++; From e2ee6e67573e2c3ae3aee3f8a27ae3bdc61ffecf Mon Sep 17 00:00:00 2001 From: Dave Cornejo Date: Thu, 7 Jun 2018 17:56:07 -1000 Subject: [PATCH 20/53] handle newlines in CDATA properly --- lib/src/clixon_xml_parse.l | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l index 770f6288..029baa8f 100644 --- a/lib/src/clixon_xml_parse.l +++ b/lib/src/clixon_xml_parse.l @@ -118,6 +118,7 @@ int clixon_xml_parsewrap(void) "aquot; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;} . { clixon_xml_parselval.string = yytext; return CHARDATA;} +\n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);} "]]>" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = yytext; return CHARDATA;} "-->" { BEGIN(START); return ECOMMENT; } From ff5f93ac1e753e6a7cddae5748cd0fcae484ddca Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 8 Jun 2018 13:23:43 +0200 Subject: [PATCH 21/53] tag all netconf msg with username --- apps/netconf/netconf_main.c | 6 ++++++ apps/netconf/netconf_rpc.c | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 708b49f5..620b3e76 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -134,6 +134,12 @@ process_incoming_packet(clicon_handle h, if ((cbret = cbuf_new()) != NULL){ if ((xc = xml_child_i(xret,0))!=NULL){ xa=NULL; + /* Copy message-id attribute from incoming to reply. + * RFC 6241: + * If additional attributes are present in an element, a NETCONF + * peer MUST return them unmodified in the element. This + * includes any "xmlns" attributes. + */ while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){ if ((xa2 = xml_dup(xa)) ==NULL) goto done; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 83484335..80eebfe2 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -964,12 +964,26 @@ netconf_rpc_dispatch(clicon_handle h, int retval = -1; cxobj *xe; yang_spec *yspec = NULL; + char *username; + cxobj *xa; /* Check incoming RPC against system / netconf RPC:s */ if ((yspec = clicon_netconf_yang(h)) == NULL){ clicon_err(OE_YANG, ENOENT, "No netconf yang spec"); goto done; } + /* Tag username on all incoming requests in case they are forwarded as internal messages + * This may be unecesary since not all are forwarded. + * It may even be wrong if something else is done with the incoming message? + */ + if ((username = clicon_username_get(h)) != NULL){ + if ((xa = xml_new("username", xn, NULL)) == NULL) + goto done; + xml_type_set(xa, CX_ATTR); + if (xml_value_set(xa, username) < 0) + goto done; + } + xe = NULL; while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) { if (strcmp(xml_name(xe), "get-config") == 0){ @@ -1047,5 +1061,8 @@ netconf_rpc_dispatch(clicon_handle h, } retval = 0; done: + /* Username attribute added at top - otherwise it is returned to sender */ + if ((xa = xml_find(xn, "username")) != NULL) + xml_purge(xa); return retval; } From dacd2fe3a03a4866ab0bbe2d8c422678af3439b3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 8 Jun 2018 15:54:43 +0200 Subject: [PATCH 22/53] replace obsolete cvec_find_var with cvec_find --- apps/cli/cli_common.c | 16 ++++++++-------- apps/cli/cli_show.c | 4 ++-- example/example_cli.c | 2 +- test/test_auth_ext.sh | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index c0a18f4c..a3fb9ad6 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -394,7 +394,7 @@ cli_debug_cli(clicon_handle h, cg_var *cv; int level; - if ((cv = cvec_find_var(vars, "level")) == NULL){ + if ((cv = cvec_find(vars, "level")) == NULL){ if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__); goto done; @@ -425,7 +425,7 @@ cli_debug_backend(clicon_handle h, cg_var *cv; int level; - if ((cv = cvec_find_var(vars, "level")) == NULL){ + if ((cv = cvec_find(vars, "level")) == NULL){ if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__); goto done; @@ -455,7 +455,7 @@ cli_debug_restconf(clicon_handle h, cg_var *cv; int level; - if ((cv = cvec_find_var(vars, "level")) == NULL){ + if ((cv = cvec_find(vars, "level")) == NULL){ if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__); goto done; @@ -750,7 +750,7 @@ load_config_file(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such op: %s, expected merge or replace", opstr); goto done; } - if ((cv = cvec_find_var(cvv, varstr)) == NULL){ + if ((cv = cvec_find(cvv, varstr)) == NULL){ clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } @@ -838,7 +838,7 @@ save_config_file(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); goto done; } - if ((cv = cvec_find_var(cvv, varstr)) == NULL){ + if ((cv = cvec_find(cvv, varstr)) == NULL){ clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } @@ -1230,7 +1230,7 @@ cli_copy_config(clicon_handle h, tovar = cv_string_get(cvec_i(argv, 4)); /* Get from variable -> cv -> from name */ - if ((fromcv = cvec_find_var(cvv, fromvar)) == NULL){ + if ((fromcv = cvec_find(cvv, fromvar)) == NULL){ clicon_err(OE_PLUGIN, 0, "fromvar '%s' not found in cligen var list", fromvar); goto done; } @@ -1261,7 +1261,7 @@ cli_copy_config(clicon_handle h, } /* Get to variable -> cv -> to name */ - if ((tocv = cvec_find_var(cvv, tovar)) == NULL){ + if ((tocv = cvec_find(cvv, tovar)) == NULL){ clicon_err(OE_PLUGIN, 0, "tovar '%s' not found in cligen var list", tovar); goto done; } @@ -1310,7 +1310,7 @@ cli_debug(clicon_handle h, cg_var *cv; int level; - if ((cv = cvec_find_var(vars, "level")) == NULL) + if ((cv = cvec_find(vars, "level")) == NULL) cv = arg; level = cv_int32_get(cv); /* cli */ diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 52821f7a..e7a855f1 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -477,7 +477,7 @@ cli_show_config(clicon_handle h, clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'"); goto done; } - if ((cvattr = cvec_find_var(cvv, attr)) == NULL){ + if ((cvattr = cvec_find(cvv, attr)) == NULL){ clicon_err(OE_PLUGIN, 0, "attr '%s' not found in cligen var list", attr); goto done; } @@ -572,7 +572,7 @@ show_conf_xpath(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such db name: %s", str); goto done; } - cv = cvec_find_var(cvv, "xpath"); + cv = cvec_find(cvv, "xpath"); xpath = cv_string_get(cv); if (clicon_rpc_get_config(h, str, xpath, &xt) < 0) goto done; diff --git a/example/example_cli.c b/example/example_cli.c index ed5ebb20..35aa6b25 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -104,7 +104,7 @@ fib_route_rpc(clicon_handle h, /* Send to backend */ if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh index a6162fe1..43c0aa88 100755 --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -226,22 +226,22 @@ new2 "guest edit nacm" expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new "cli show conf as admin" -expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang show conf" 0 "^x 1;$" +expectfn "$clixon_cli -1 -U adm1 -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" new "cli show conf as limited" -expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang show conf" 0 "^x 1;$" +expectfn "$clixon_cli -1 -U wilma -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" new "cli show conf as guest" -expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang show conf" 255 "CLI command error" +expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang show conf" 255 "protocol access-denied" new "cli rpc as admin" -expectfn "$clixon_cli -1 -U adm1 -f $cfg -y $fyang rpc ipv4" 0 "2.3.4.5" +expectfn "$clixon_cli -1 -U adm1 -l o -f $cfg -y $fyang rpc ipv4" 0 "2.3.4.5" new "cli rpc as limited" -expectfn "$clixon_cli -1 -U wilma -f $cfg -y $fyang rpc ipv4" 0 "access-denied" +expectfn "$clixon_cli -1 -U wilma -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol access-denied default deny" new "cli rpc as guest" -expectfn "$clixon_cli -1 -U guest -f $cfg -y $fyang rpc ipv4" 0 "access-denied" +expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol access-denied access denied" new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf From a576951e57b06c02f3dd820df5864df2dc32f42c Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 10 Jun 2018 10:32:20 +0200 Subject: [PATCH 23/53] Add validation of yang bits value --- lib/src/clixon_string.c | 12 ++++++- lib/src/clixon_yang_type.c | 65 +++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index d9892e90..1f0db0cc 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -54,7 +54,6 @@ #include "clixon_string.h" #include "clixon_err.h" - /*! Split string into a vector based on character delimiters. Using malloc * * The given string is split into a vector where the delimiter can be @@ -63,6 +62,17 @@ * The vector returned is one single memory block that must be freed * by the caller * + * @code + * char **vec = NULL; + * int nvec; + * if ((vec = clicon_strsep("/home/user/src/clixon", "/", &nvec)) == NULL) + * err; + * for (i=0; iys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT) - continue; - if (strcmp(yi->ys_argument, str) == 0){ - found++; + if (restype){ + if (strcmp(restype, "enumeration") == 0){ + found = 0; + while ((yi = yn_each((yang_node*)yrestype, yi)) != NULL){ + if (yi->ys_keyword != Y_ENUM) + continue; + if (strcmp(yi->ys_argument, str) == 0){ + found++; + break; + } + } + if (!found){ + if (reason) + *reason = cligen_reason("'%s' does not match enumeration", str); + retval = 0; break; } } - if (!found){ - if (reason) - *reason = cligen_reason("'%s' does not match enumeration", str); - retval = 0; - break; + if (strcmp(restype, "bits") == 0){ + /* The lexical representation of the bits type is a space-separated list + * of the names of the bits that are set. A zero-length string thus + * represents a value where no bits are set. + */ + if ((vec = clicon_strsep(str, " \t", &nvec)) == NULL) + goto done; + for (i=0; iys_keyword != Y_BIT) + continue; + if (strcmp(yi->ys_argument, v) == 0){ + found++; + break; + } + } + if (!found){ + if (reason) + *reason = cligen_reason("'%s' does not match enumeration", v); + retval = 0; + break; + } + } } } if ((options & YANG_OPTIONS_LENGTH) != 0){ @@ -571,9 +604,11 @@ cv_validate1(cg_var *cv, case CGV_EMPTY: /* XXX */ break; } - if (reason && *reason) assert(retval == 0); /* validation failed with error reason */ + done: + if (vec) + free(vec); return retval; } @@ -728,7 +763,7 @@ ys_cv_validate(cg_var *cv, } else if ((retval = cv_validate1(cv, cvtype, options, range_min, range_max, pattern, - yrestype, restype, reason)) < 0) + yrestype, restype, reason)) < 0) goto done; done: if (cvt) From 5e587b3a01300a3440cd333954e6f55884ed579b Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 10 Jun 2018 10:41:06 +0200 Subject: [PATCH 24/53] Validation of yang bits type space-separated list value --- CHANGELOG.md | 1 + apps/backend/backend_client.c | 4 +++- test/test_type.sh | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ee0eae..f23c555e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Validation of yang bits type space-separated list value * Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests * Added a generated CLI show command that works on the generated parse tree with auto completion. * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index d721b471..e917f75d 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -809,6 +809,7 @@ from_client_debug(clicon_handle h, * has the special value "*". * @retval 0 No match * @retval 1 Match + * XXX access_operations is bit-fields */ static int nacm_match_access(char *access_operations, @@ -818,7 +819,7 @@ nacm_match_access(char *access_operations, return 0; if (strcmp(access_operations,"*")==0) return 1; - if (strstr(mode, access_operations)!=NULL) + if (strstr(access_operations, mode)!=NULL) return 1; return 0; } @@ -874,6 +875,7 @@ nacm_match_rule(clicon_handle h, module_name = xml_find_body(xrule, "module-name"); rpc_name = xml_find_body(xrule, "rpc-name"); + /* XXX access_operations can be a set of bits */ access_operations = xml_find_body(xrule, "access-operations"); action = xml_find_body(xrule, "action"); clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__, diff --git a/test/test_type.sh b/test/test_type.sh index cae1227a..2658fe8a 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -122,6 +122,17 @@ module example{ range "min..max"; } }*/ + typedef mybits { + description "Test adding several bits"; + type bits { + bit create; + bit read; + bit write; + } + } + leaf mbits{ + type mybits; + } } EOF @@ -177,6 +188,19 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^create read]]>]]>" "^]]>]]>$" + +new "cli bits validate" +expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$" + new "Kill backend" # Check if still alive pid=`pgrep clixon_backend` From 9eff8794584888257c5d5c3701a5cd59f9d39da6 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 13 Jun 2018 22:55:46 +0200 Subject: [PATCH 25/53] CDATA Encode and decode (parsing) support --- CHANGELOG.md | 1 + apps/cli/cli_generate.c | 2 ++ lib/src/clixon_string.c | 56 +++++++++++++++++++++++++++++--------- lib/src/clixon_xml_parse.l | 5 ++-- lib/src/clixon_yang.c | 6 +++- test/test_netconf.sh | 7 +++++ 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 898854f7..2e7b8d51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Minor changes: * CDATA xml support (patch by David Cornejo, Netgate) + * Encode and decode (parsing) support * Validation of yang bits type space-separated list value * Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests * Added a generated CLI show command that works on the generated parse tree with auto completion. diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index f387cdf7..dc1cbca3 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -243,6 +243,8 @@ yang2cli_var_sub(clicon_handle h, if ((id=strchr(name, ':')) != NULL) *id = '\0'; cprintf(cb, "%s:%s", name, id+1); + if (name) + free(name); } } } diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 1f0db0cc..2386d561 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -297,31 +297,56 @@ xml_chardata_encode(char *str, int l; int len; int i, j; + int cdata; /* when set, skip encoding */ - len = 0; + /* First compute length (do nothing) */ + len = 0; cdata = 0; for (i=0; i': - len += strlen("> "); - break; - default: + if (cdata){ + if (strncmp(&str[i], "]]>", strlen("]]>")) == 0) + cdata = 0; len++; } + else + switch (str[i]){ + case '&': + len += strlen("& "); + break; + case '<': + if (strncmp(&str[i], "': + len += strlen("> "); + break; + default: + len++; + } } len++; /* trailing \0 */ + /* We know length, allocate encoding buffer */ if ((esc = malloc(len)) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); goto done; } memset(esc, 0, len); - j = 0; + + /* Same code again, but now actually encode into output buffer */ + j = 0; cdata = 0; for (i=0; i", strlen("]]>")) == 0){ + cdata = 0; + esc[j++] = str[i++]; + esc[j++] = str[i++]; + } + esc[j++] = str[i]; + } + else switch (str[i]){ case '&': if ((l=snprintf(&esc[j], 7, "& ")) < 0){ @@ -331,6 +356,11 @@ xml_chardata_encode(char *str, j += l; break; case '<': + if (strncmp(&str[i], """ clicon constants */ static const map_str2int ykmap[] = { @@ -158,7 +163,7 @@ yspec_new(void) yang_spec *yspec; if ((yspec = malloc(sizeof(*yspec))) == NULL){ - clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__); + clicon_err(OE_YANG, errno, "malloc"); return NULL; } memset(yspec, 0, sizeof(*yspec)); @@ -176,7 +181,7 @@ ys_new(enum rfc_6020 keyw) yang_stmt *ys; if ((ys = malloc(sizeof(*ys))) == NULL){ - clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__); + clicon_err(OE_YANG, errno, "malloc"); return NULL; } memset(ys, 0, sizeof(*ys)); @@ -184,7 +189,7 @@ ys_new(enum rfc_6020 keyw) /* The cvec contains stmt-specific variables. Only few stmts need variables so the cvec could be lazily created to save some heap and cycles. */ if ((ys->ys_cvec = cvec_new(0)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cvec_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cvec_new"); return NULL; } return ys; @@ -247,7 +252,7 @@ yn_realloc(yang_node *yn) yn->yn_len++; if ((yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){ - clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__); + clicon_err(OE_YANG, errno, "realloc"); return -1; } yn->yn_stmt[yn->yn_len - 1] = NULL; /* init field */ @@ -276,22 +281,22 @@ ys_cp(yang_stmt *ynew, ynew->ys_parent = NULL; if (yold->ys_stmt) if ((ynew->ys_stmt = calloc(yold->ys_len, sizeof(yang_stmt *))) == NULL){ - clicon_err(OE_YANG, errno, "%s: calloc", __FUNCTION__); + clicon_err(OE_YANG, errno, "calloc"); goto done; } if (yold->ys_argument) if ((ynew->ys_argument = strdup(yold->ys_argument)) == NULL){ - clicon_err(OE_YANG, errno, "%s: strdup", __FUNCTION__); + clicon_err(OE_YANG, errno, "strdup"); goto done; } if (yold->ys_cv) if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cv_dup", __FUNCTION__); + clicon_err(OE_YANG, errno, "cv_dup"); goto done; } if (yold->ys_cvec) if ((ynew->ys_cvec = cvec_dup(yold->ys_cvec)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cvec_dup", __FUNCTION__); + clicon_err(OE_YANG, errno, "cvec_dup"); goto done; } if (yold->ys_typecache){ @@ -902,7 +907,7 @@ yang_print(FILE *f, cbuf *cb = NULL; if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cbuf_new"); goto done; } if (yang_print_cbuf(cb, yn, 0) < 0) @@ -998,14 +1003,14 @@ ys_populate_leaf(yang_stmt *ys, goto done; /* 2. Create the CV using cvtype and name it */ if ((cv = cv_new(cvtype)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cv_new"); goto done; } if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */ cv_dec64_n_set(cv, fraction_digits); if (cv_name_set(cv, ys->ys_argument) == NULL){ - clicon_err(OE_YANG, errno, "%s: cv_new_set", __FUNCTION__); + clicon_err(OE_YANG, errno, "cv_new_set"); goto done; } /* 3. Check if default value. Here we parse the string in the default-stmt @@ -1083,7 +1088,7 @@ ys_populate_range(yang_stmt *ys, yparent = ys->ys_parent; /* Find parent: type */ if (yparent->yn_keyword != Y_TYPE){ - clicon_err(OE_YANG, 0, "%s: parent should be type", __FUNCTION__); + clicon_err(OE_YANG, 0, "parent should be type"); goto done; } if (yang_type_resolve(ys, (yang_stmt*)yparent, &yrestype, @@ -1103,7 +1108,8 @@ ys_populate_range(yang_stmt *ys, } if ((maxstr = strstr(minstr, "..")) != NULL){ if (strlen(maxstr) < 2){ - clicon_err(OE_YANG, 0, "range statement: %s not on the form: .."); + clicon_err(OE_YANG, 0, "range statement: %s not on the form: ..", + ys->ys_argument); goto done; } minstr[maxstr-minstr] = '\0'; @@ -1499,7 +1505,7 @@ yang_expand(yang_node *yn) size = (yn->yn_len - i - 1)*sizeof(struct yang_stmt *); yn->yn_len += glen - 1; if (glen && (yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){ - clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__); + clicon_err(OE_YANG, errno, "realloc"); return -1; } /* Then move all existing elements up from i+1 (not uses-stmt) */ @@ -1540,10 +1546,10 @@ yang_expand(yang_node *yn) * Syntax parsing. A string is input and a syntax-tree is returned (or error). * A variable record is also returned containing a list of (global) variable values. * (cloned from cligen) - * @param h CLICON handle - * @param str String of yang statements - * @param name Log string, typically filename - * @param ysp Yang specification. Should ave been created by caller using yspec_new + * @param[in] h CLICON handle + * @param[in] str String of yang statements + * @param[in] name Log string, typically filename + * @param[in] ysp Yang specification. Should ave been created by caller using yspec_new * @retval ymod Top-level yang (sub)module * @retval NULL Error encountered * Calling order: @@ -1554,15 +1560,17 @@ yang_expand(yang_node *yn) * clixon_yang_parseparse # Actual yang parsing using yacc */ static yang_stmt * -yang_parse_str(clicon_handle h, - char *str, +yang_parse_str(char *str, const char *name, /* just for errs */ yang_spec *yspec) { struct clicon_yang_yacc_arg yy = {0,}; yang_stmt *ymod = NULL; - yy.yy_handle = h; + if (yspec == NULL){ + clicon_err(OE_YANG, 0, "Yang parse need top level yang spec"); + goto done; + } yy.yy_name = (char*)name; yy.yy_linenum = 1; yy.yy_parse_string = str; @@ -1595,15 +1603,66 @@ yang_parse_str(clicon_handle h, return ymod; /* top-level (sub)module */ } -/*! Read an opened file into a string and call yang string parsing +/*! Parse yang spec from an open file descriptor + * @param[in] fd File descriptor containing the YANG file as ASCII characters + * @param[in] name For debug, eg filename + * @param[in] ysp Yang specification. Should ave been created by caller using yspec_new + * @retval ymod Top-level yang (sub)module + * @retval NULL Error + */ +yang_stmt * +yang_parse_file(int fd, + const char *name, + yang_spec *ysp) +{ + char *buf = NULL; + int i; + int c; + int len; + yang_stmt *ymod = NULL; + int ret; + + len = BUFLEN; /* any number is fine */ + if ((buf = malloc(len)) == NULL){ + perror("pt_file malloc"); + return NULL; + } + memset(buf, 0, len); + i = 0; /* position in buf */ + while (1){ /* read the whole file */ + if ((ret = read(fd, &c, 1)) < 0){ + clicon_err(OE_XML, errno, "read"); + break; + } + if (ret == 0) + break; /* eof */ + if (len==i){ + if ((buf = realloc(buf, 2*len)) == NULL){ + clicon_err(OE_XML, errno, "realloc"); + goto done; + } + memset(buf+len, 0, len); + len *= 2; + } + buf[i++] = (char)(c&0xff); + } /* read a line */ + if ((ymod = yang_parse_str(buf, name, ysp)) < 0) + goto done; + done: + if (buf) + free(buf); + return ymod; /* top-level (sub)module */ +} + +/*! Open a file, read into a string and invoke yang parsing * * Similar to clicon_yang_str(), just read a file first * (cloned from cligen) - * @param h CLICON handle - * @param filename Name of file - * @param ysp Yang specification. Should ave been created by caller using yspec_new - * @retval ymod Top-level yang (sub)module - * @retval NULL Error encountered + * @param[in] h CLICON handle + * @param[in] filename Name of file + * @param[in] ysp Yang specification. Should ave been created by caller using yspec_new + * @retval ymod Top-level yang (sub)module + * @retval NULL Error encountered * The database symbols are inserted in alphabetical order. * Calling order: @@ -1614,57 +1673,27 @@ yang_parse_str(clicon_handle h, * clixon_yang_parseparse # Actual yang parsing using yacc */ static yang_stmt * -yang_parse_file(clicon_handle h, - const char *filename, - yang_spec *ysp - ) +yang_parse_filename(const char *filename, + yang_spec *ysp) { - char *buf = NULL; - int i; - int c; - int len; yang_stmt *ymod = NULL; - FILE *f = NULL; - struct stat st; + int fd = -1; + struct stat st; clicon_log(LOG_DEBUG, "Parsing yang file: %s", filename); if (stat(filename, &st) < 0){ clicon_err(OE_YANG, errno, "%s not found", filename); goto done; } - if ((f = fopen(filename, "r")) == NULL){ - clicon_err(OE_UNIX, errno, "fopen(%s)", filename); + if ((fd = open(filename, O_RDONLY)) < 0){ + clicon_err(OE_YANG, errno, "open(%s)", filename); goto done; } - - len = 1024; /* any number is fine */ - if ((buf = malloc(len)) == NULL){ - perror("pt_file malloc"); - return NULL; - } - memset(buf, 0, len); - - i = 0; /* position in buf */ - while (1){ /* read the whole file */ - if ((c = fgetc(f)) == EOF) - break; - if (len==i){ - if ((buf = realloc(buf, 2*len)) == NULL){ - fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); - goto done; - } - memset(buf+len, 0, len); - len *= 2; - } - buf[i++] = (char)(c&0xff); - } /* read a line */ - if ((ymod = yang_parse_str(h, buf, filename, ysp)) < 0) + if ((ymod = yang_parse_file(fd, filename, ysp)) < 0) goto done; done: - if (f) - fclose(f); - if (buf) - free(buf); + if (fd != -1) + close(fd); return ymod; /* top-level (sub)module */ } @@ -1679,8 +1708,7 @@ yang_parse_file(clicon_handle h, * @retval -1 Error */ static int -yang_parse_find_match(clicon_handle h, - const char *yang_dir, +yang_parse_find_match(const char *yang_dir, const char *module, cbuf *fbuf) { @@ -1724,7 +1752,7 @@ yang_parse_find_match(clicon_handle h, * @param[in] h CLICON handle * @param[in] yang_dir Directory where all YANG module files reside * @param[in] module Name of main YANG module. Or absolute file name. - * @param[in] revision Optional module revision date + * @param[in] revision Module revision date or NULL * @param[in] ysp Yang specification. Should have been created by caller using yspec_new * @retval ymod Top-level yang (sub)module * @retval NULL Error encountered @@ -1737,8 +1765,7 @@ yang_parse_find_match(clicon_handle h, * clixon_yang_parseparse # Actual yang parsing using yacc */ static yang_stmt * -yang_parse_recurse(clicon_handle h, - const char *yang_dir, +yang_parse_recurse(const char *yang_dir, const char *module, const char *revision, yang_spec *ysp) @@ -1752,26 +1779,26 @@ yang_parse_recurse(clicon_handle h, int nr; if (module[0] == '/'){ - if ((ymod = yang_parse_file(h, module, ysp)) == NULL) + if ((ymod = yang_parse_filename(module, ysp)) == NULL) goto done; } else { if ((fbuf = cbuf_new()) == NULL){ - clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cbuf_new"); goto done; } if (revision) cprintf(fbuf, "%s/%s@%s.yang", yang_dir, module, revision); else{ /* No specific revision, Match a yang file */ - if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0) + if ((nr = yang_parse_find_match(yang_dir, module, fbuf)) < 0) goto done; if (nr == 0){ clicon_err(OE_YANG, errno, "No matching %s yang files found (expected module name or absolute filename)", module); goto done; } } - if ((ymod = yang_parse_file(h, cbuf_get(fbuf), ysp)) == NULL) + if ((ymod = yang_parse_filename(cbuf_get(fbuf), ysp)) == NULL) goto done; } @@ -1786,7 +1813,7 @@ yang_parse_recurse(clicon_handle h, subrevision = NULL; if (yang_find((yang_node*)ysp, Y_MODULE, modname) == NULL) /* recursive call */ - if (yang_parse_recurse(h, yang_dir, modname, subrevision, ysp) == NULL){ + if (yang_parse_recurse(yang_dir, modname, subrevision, ysp) == NULL){ ymod = NULL; goto done; } @@ -1845,7 +1872,7 @@ ys_schemanode_check(yang_stmt *ys, * @param[in] h CLICON handle * @param[in] yang_dir Directory where all YANG module files reside (except mainfile) * @param[in] mainmod Name of main YANG module. Or absolute file name. - * @param[in] revision Optional main module revision date. + * @param[in] revision Main module revision date string or NULL * @param[out] ysp Yang specification. Should ave been created by caller using yspec_new * @retval 0 Everything OK * @retval -1 Error encountered @@ -1854,7 +1881,8 @@ ys_schemanode_check(yang_stmt *ys, * @note if mainmod is filename, revision is not considered. * Calling order: * yang_parse # Parse top-level yang module. Expand and populate yang tree - * yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them + * yang_parse_recurse # Parse one yang module, go through its (sub)modules, + * parse them and then recursively parse them * yang_parse_file # Read yang file into a string * yang_parse_str # Set up yacc parser and call it given a string * clixon_yang_parseparse # Actual yang parsing using yacc @@ -1870,7 +1898,7 @@ yang_parse(clicon_handle h, yang_stmt *ymod; /* Top-level yang (sub)module */ /* Step 1: parse from text to yang parse-tree. */ - if ((ymod = yang_parse_recurse(h, yang_dir, mainmodule, revision, ysp)) == NULL) + if ((ymod = yang_parse_recurse(yang_dir, mainmodule, revision, ysp)) == NULL) goto done; /* Add top module name as dbspec-name */ clicon_dbspec_name_set(h, ymod->ys_argument); @@ -2080,13 +2108,13 @@ yang_abs_schema_nodeid(yang_spec *yspec, goto done; } if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){ - clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__); + clicon_err(OE_YANG, errno, "strsep"); goto done; } /* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */ if (nvec < 2){ - clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s", - __FUNCTION__, schema_nodeid); + clicon_err(OE_YANG, 0, "NULL or truncated path: %s", + schema_nodeid); goto done; } /* split : */ @@ -2095,7 +2123,7 @@ yang_abs_schema_nodeid(yang_spec *yspec, goto ok; } if ((prefix = strdup(vec[1])) == NULL){ - clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__); + clicon_err(OE_UNIX, errno, "strdup"); goto done; } prefix[id-vec[1]] = '\0'; @@ -2183,7 +2211,7 @@ ys_parse(yang_stmt *ys, assert(ys->ys_cv == NULL); /* Cv:s are parsed in different places, difficult to separate */ if ((ys->ys_cv = cv_new(cvtype)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cv_new"); goto done; } if ((cvret = cv_parse1(ys->ys_argument, ys->ys_cv, &reason)) < 0){ /* error */ @@ -2252,7 +2280,7 @@ ys_parse_sub(yang_stmt *ys, goto done; } if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){ - clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__); + clicon_err(OE_YANG, errno, "cv_new"); goto done; } if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */ @@ -2334,9 +2362,9 @@ yang_spec_netconf(clicon_handle h) } /*! Read, parse and save application yang specification as option - * @param h clicon handle - * @param f file to print to (if printspec enabled) - * @param printspec print database (YANG) specification as read from file + * @param[in] h clicon handle + * @param[in] f file to print to (if printspec enabled) + * @param[in] printspec print database (YANG) specification as read from file */ yang_spec* yang_spec_main(clicon_handle h) @@ -2346,7 +2374,7 @@ yang_spec_main(clicon_handle h) char *yang_module; char *yang_revision; - if ((yang_dir = clicon_yang_dir(h)) == NULL){ + if ((yang_dir = clicon_yang_dir(h)) == NULL){ clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set"); goto done; } diff --git a/lib/src/clixon_yang_parse.h b/lib/src/clixon_yang_parse.h index 0b9b1ec4..baa136df 100644 --- a/lib/src/clixon_yang_parse.h +++ b/lib/src/clixon_yang_parse.h @@ -51,7 +51,6 @@ struct ys_stack{ }; struct clicon_yang_yacc_arg{ /* XXX: mostly unrelevant */ - clicon_handle yy_handle; /* cligen_handle */ char *yy_name; /* Name of syntax (for error string) */ int yy_linenum; /* Number of \n in parsed buffer */ char *yy_parse_string; /* original (copy of) parse string */ diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index eb48253f..219f9766 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -260,7 +260,7 @@ ysp_add(struct clicon_yang_yacc_arg *yy, clicon_err(OE_YANG, errno, "No stack"); goto err; } - yn = ystack->ys_node; + assert(yn = ystack->ys_node); if ((ys = ys_new(keyword)) == NULL) goto err; /* NOTE: does not make a copy of string, ie argument is 'consumed' here */ diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 73a0097c..a67500db 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #define __USE_GNU /* strverscmp */ @@ -354,7 +355,7 @@ clicon_type2cv(char *origtype, if (restype != NULL){ yang2cv_type(restype, cvtype); if (*cvtype == CGV_ERR){ - clicon_err(OE_DB, 0, "%s: \"%s\" type not translated", __FUNCTION__, restype); + clicon_err(OE_DB, 0, "\"%s\" type not translated", restype); goto done; } } @@ -365,7 +366,7 @@ clicon_type2cv(char *origtype, */ yang2cv_type(origtype, cvtype); if (*cvtype == CGV_ERR){ - clicon_err(OE_DB, 0, "%s: \"%s\": type not resolved", __FUNCTION__, origtype); + clicon_err(OE_DB, 0, "\"%s\": type not resolved", origtype); goto done; } } @@ -399,8 +400,8 @@ cv_validate1(cg_var *cv, int retval = 1; /* OK */ int retval2; yang_stmt *yi = NULL; - uint64_t u = 0; - int64_t i = 0; + unsigned int u = 0; + int i = 0; char *str; int found; char **vec = NULL; @@ -417,7 +418,7 @@ cv_validate1(cg_var *cv, i = cv_int8_get(cv); if (range_check(i, range_min, range_max, int8)){ if (reason) - *reason = cligen_reason("Number out of range: %ld", i); + *reason = cligen_reason("Number out of range: %d", i); retval = 0; break; } @@ -428,7 +429,7 @@ cv_validate1(cg_var *cv, i = cv_int16_get(cv); if (range_check(i, range_min, range_max, int16)){ if (reason) - *reason = cligen_reason("Number out of range: %ld", i); + *reason = cligen_reason("Number out of range: %d", i); retval = 0; break; } @@ -439,29 +440,31 @@ cv_validate1(cg_var *cv, i = cv_int32_get(cv); if (range_check(i, range_min, range_max, int32)){ if (reason) - *reason = cligen_reason("Number out of range: %ld", i); + *reason = cligen_reason("Number out of range: %d", i); retval = 0; break; } } break; - case CGV_INT64: + case CGV_INT64:{ + int64_t i64; if ((options & YANG_OPTIONS_RANGE) != 0){ - i = cv_int64_get(cv); + i64 = cv_int64_get(cv); if (range_check(i, range_min, range_max, int64)){ if (reason) - *reason = cligen_reason("Number out of range: %ld", i); + *reason = cligen_reason("Number out of range: %" PRId64, i64); retval = 0; break; } } break; + } case CGV_UINT8: if ((options & YANG_OPTIONS_RANGE) != 0){ u = cv_uint8_get(cv); if (range_check(u, range_min, range_max, uint8)){ if (reason) - *reason = cligen_reason("Number out of range: %lu", u); + *reason = cligen_reason("Number out of range: %u", u); retval = 0; break; } @@ -472,7 +475,7 @@ cv_validate1(cg_var *cv, u = cv_uint16_get(cv); if (range_check(u, range_min, range_max, uint16)){ if (reason) - *reason = cligen_reason("Number out of range: %lu", u); + *reason = cligen_reason("Number out of range: %u", u); retval = 0; break; } @@ -483,29 +486,31 @@ cv_validate1(cg_var *cv, u = cv_uint32_get(cv); if (range_check(u, range_min, range_max, uint32)){ if (reason) - *reason = cligen_reason("Number out of range: %lu", u); + *reason = cligen_reason("Number out of range: %u", u); retval = 0; break; } } break; - case CGV_UINT64: + case CGV_UINT64:{ + uint64_t u64; if ((options & YANG_OPTIONS_RANGE) != 0){ - u = cv_uint64_get(cv); + u64 = cv_uint64_get(cv); if (range_check(u, range_min, range_max, uint64)){ if (reason) - *reason = cligen_reason("Number out of range: %lu", u); + *reason = cligen_reason("Number out of range: %" PRIu64, u64); retval = 0; break; } } break; + } case CGV_DEC64: if ((options & YANG_OPTIONS_RANGE) != 0){ i = cv_int64_get(cv); if (range_check(i, range_min, range_max, int64)){ if (reason) - *reason = cligen_reason("Number out of range: %ld", i); + *reason = cligen_reason("Number out of range: %d", i); retval = 0; break; } @@ -565,7 +570,7 @@ cv_validate1(cg_var *cv, u = strlen(str); if (range_check(u, range_min, range_max, uint64)){ if (reason) - *reason = cligen_reason("string length out of range: %lu", u); + *reason = cligen_reason("string length out of range: %u", u); retval = 0; break; } @@ -748,8 +753,8 @@ ys_cv_validate(cg_var *cv, if (cvtype == CGV_STRING && cv_type_get(ycv) == CGV_REST) ; else { - clicon_err(OE_DB, 0, "%s: Type mismatch data:%s != yang:%s", - __FUNCTION__, cv_type2str(cvtype), cv_type2str(cv_type_get(ycv))); + clicon_err(OE_DB, 0, "Type mismatch data:%s != yang:%s", + cv_type2str(cvtype), cv_type2str(cv_type_get(ycv))); goto done; } } @@ -904,14 +909,14 @@ resolve_restrictions(yang_stmt *yrange, } /*! Recursively resolve a yang type to built-in type with optional restrictions - * @param [in] ys yang-stmt from where the current search is based - * @param [in] ytype yang-stmt object containing currently resolving type - * @param [out] yrestype resolved type. return built-in type or NULL. mandatory - * @param [out] options pointer to flags field of optional values. optional - * @param [out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE - * @param [out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE - * @param [out] pattern pointer to static string of yang string pattern. optional - * @param [out] fraction for decimal64, how many digits after period + * @param[in] ys yang-stmt from where the current search is based + * @param[in] ytype yang-stmt object containing currently resolving type + * @param[out] yrestype resolved type. return built-in type or NULL. mandatory + * @param[out] options pointer to flags field of optional values. optional + * @param[out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE + * @param[out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE + * @param[out] pattern pointer to static string of yang string pattern. optional + * @param[out] fraction for decimal64, how many digits after period * @retval 0 OK. Note yrestype may still be NULL. * @retval -1 Error, clicon_err handles errors * The setting of the options argument has the following semantics: @@ -996,7 +1001,7 @@ yang_type_resolve(yang_stmt *ys, if (rytypedef != NULL){ /* We have found a typedef */ /* Find associated type statement */ if ((rytype = yang_find((yang_node*)rytypedef, Y_TYPE, NULL)) == NULL){ - clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__); + clicon_err(OE_DB, 0, "mandatory type object is not found"); goto done; } /* recursively resolve this new type */ @@ -1034,14 +1039,14 @@ yang_type_resolve(yang_stmt *ys, * if (options & YANG_OPTIONS_PATTERN != 0) * printf("regexp: %s\n", pattern); * @endcode - * @param [in] ys yang-stmt, leaf or leaf-list - * @param [out] origtype original type may be derived or built-in - * @param [out] yrestype pointer to resolved type stmt. should be built-in or NULL - * @param [out] options pointer to flags field of optional values - * @param [out] mincv pointer to cv of min range or length. optional - * @param [out] maxcv pointer to cv of max range or length. optional - * @param [out] pattern pointer to static string of yang string pattern. optional - * @param [out] fraction for decimal64, how many digits after period + * @param[in] ys yang-stmt, leaf or leaf-list + * @param[out] origtype original type may be derived or built-in + * @param[out] yrestype pointer to resolved type stmt. should be built-in or NULL + * @param[out] options pointer to flags field of optional values + * @param[out] mincv pointer to cv of min range or length. optional + * @param[out] maxcv pointer to cv of max range or length. optional + * @param[out] pattern pointer to static string of yang string pattern. optional + * @param[out] fraction for decimal64, how many digits after period * @retval 0 OK, but note that restype==NULL means not resolved. * @retval -1 Error, clicon_err handles errors * The setting of the options argument has the following semantics: @@ -1071,7 +1076,7 @@ yang_type_get(yang_stmt *ys, *options = 0x0; /* Find mandatory type */ if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) == NULL){ - clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__); + clicon_err(OE_DB, 0, "mandatory type object is not found"); goto done; } /* XXX: here we seem to have some problems if type is union */ diff --git a/test/Jenkinsfile b/test/Jenkinsfile index 48314e84..b31de847 100644 --- a/test/Jenkinsfile +++ b/test/Jenkinsfile @@ -8,15 +8,15 @@ node { /* `make check` returns non-zero on test failures, * using `true` to allow the Pipeline to continue nonetheless */ - sh 'make clean || true' - sh './configure || true' + sh 'make clean' + sh './configure' } stage('Make') { /* `make check` returns non-zero on test failures, * using `true` to allow the Pipeline to continue nonetheless */ - sh 'make || true' + sh 'make' } stage('Testing') { diff --git a/test/lib.sh b/test/lib.sh index f82907a0..8430584b 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -36,10 +36,11 @@ err(){ if [ $# -gt 1 ]; then echo "Received: $2" fi - echo -e "\e[0m:" + echo -e "\e[0m" echo "$ret"| od -t c > $dir/clixon-ret echo "$expect"| od -t c > $dir/clixon-expect diff $dir/clixon-ret $dir/clixon-expect + exit $testnr } @@ -110,18 +111,29 @@ expecteq(){ fi } -# clixon tester. First arg is command second is stdin and -# third is expected outcome +# Pipe stdin to command +# Arguments: +# - Command +# - expected command return value (0 if OK) +# - stdin input +# - expected stdout outcome expecteof(){ cmd=$1 - input=$2 - expect=$3 + retval=$2 + input=$3 + expect=$4 # Do while read stuff ret=$($cmd<$RULES]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$RULES]]>]]>" "^]]>]]>$" new "commit it" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new2 "auth get (no user: access denied)" expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' diff --git a/test/test_identity.sh b/test/test_identity.sh index 2d01fa01..3df22c73 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -120,47 +120,47 @@ if [ $? -ne 0 ]; then fi new "Set crypto to aes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "aes]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "aes]]>]]>" "^]]>]]>$" new "netconf validate " -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "Set crypto to mc:aes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "mc:aes]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "mc:aes]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "Set crypto to des:des3" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "des:des3]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "des:des3]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "Set crypto to mc:foo" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "mc:foo]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "mc:foo]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "Set crypto to des:des3 using xmlns" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "des:des3]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "des:des3]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" # XXX this is not supported #new "Set crypto to x:des3 using xmlns" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" "x:des3]]>]]>" "^]]>]]>$" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "x:des3]]>]]>" "^]]>]]>$" #new "netconf validate" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "Set crypto to foo:bar" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "foo:bar]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo:bar]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^operation-failedapplicationerrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$" new "cli set crypto to mc:aes" expectfn "$clixon_cli -1 -f $cfg -l o set crypto mc:aes" 0 "^$" diff --git a/test/test_json.sh b/test/test_json.sh new file mode 100755 index 00000000..63543abd --- /dev/null +++ b/test/test_json.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Test: JSON parser tests +PROG=../lib/src/clixon_util_json + +# include err() and new() functions and creates $dir +. ./lib.sh + +new "json parse" +expecteof $PROG 0 '{"foo": -23}' "^-23$" + +new "json parse list" +expecteof $PROG 0 '{"a":[0,1,2,3]}' "^0123$" + +rm -rf $dir diff --git a/test/test_leafref.sh b/test/test_leafref.sh index f18263d4..f5e15dec 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -84,45 +84,45 @@ if [ $? -ne 0 ]; then fi new "leafref base config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" " +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " eth0ex:eth
192.0.2.1
192.0.2.2
loex:lo
127.0.0.1
]]>]]>" "^]]>]]>$" new "leafref get config" -expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^eth0' +expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^eth0' new "leafref base commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "leafref add wrong ref" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth3
10.0.4.6
]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth3
10.0.4.6
]]>]]>" "^]]>]]>$" new "leafref validate XXX shouldnt really be operation-failed, more work in validate code" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^operation-failed" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failed" new "leafref discard-changes" -expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new "leafref add correct absref" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0]]>]]>" "^]]>]]>$" new "leafref add correct relref" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0]]>]]>" "^]]>]]>$" # XXX add address new "leafref validate (ok)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^" new "leafref delete leaf" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth0]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0]]>]]>" "^" new "leafref validate (should fail)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^operation-failed" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failed" new "leafref discard-changes" -expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new "cli leafref lo" expectfn "$clixon_cli -1f $cfg -y $fyang -l o set default-address absname lo" 0 "^$" diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 0fbd63f5..02982e5a 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -81,132 +81,132 @@ if [ $? -ne 0 ]; then fi new "netconf get-config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none which should not change anything" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "noneeth/0/0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "noneeth/0/0]]>]]>" "^]]>]]>$" new "Check nothing added" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none and create which should add eth/0/0" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" new "Check eth/0/0 added using xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^eth/0/0ex:ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth/0/0ex:ethtrue]]>]]>$" new "Re-create same eth/0/0 which should generate error" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^" new "Delete eth/0/0 using none config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" new "Check deleted eth/0/0 (non-presence container)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '^]]>]]>$' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^]]>]]>$' new "Re-Delete eth/0/0 using none should generate error" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 'eth/0/0ex:ethnone ]]>]]>' "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^" new "netconf edit config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" new "netconf get config xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^eth1true]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth1true]]>]]>$" new "netconf get config xpath parent" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" new "netconf validate missing type" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get empty config2" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf edit extra xml" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf edit config eth1" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1ex:eth]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth1ex:eth]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf edit config merge" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth2ex:ethmerge]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth2ex:ethmerge]]>]]>" "^]]>]]>$" new "netconf edit ampersand encoding(<&): name:'eth&' type:'t<>'" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth& t< > ]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth& t< > ]]>]]>" "^]]>]]>$" new "netconf get replaced config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth& t< > trueeth1ex:ethtrueeth2ex:ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^eth& t< > trueeth1ex:ethtrueeth2ex:ethtrue]]>]]>$" new "cli show configuration eth& - encoding tests" expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" 0 "interfaces interface eth& type t<> interfaces interface eth& enabled true" new "netconf edit CDATA" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth/0/0ex:eth]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0ex:eth]]>]]>" "^]]>]]>$" #new "netconf get CDATA" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "eth/0/0true]]>]]>" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "eth/0/0true]]>]]>" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf edit state operation should fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "eth1ex:eth]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth1ex:eth]]>]]>" "^invalid-value" new "netconf get state operation" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth0ex:eth42]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^eth0ex:eth42]]>]]>$" new "netconf lock/unlock" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" new "netconf lock/lock" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf lock" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "close-session" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "kill-session" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "44]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "44]]>]]>" "^]]>]]>$" new "copy startup" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get startup" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^eth1ex:ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^eth1ex:ethtrue]]>]]>$" new "netconf delete startup" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf check empty startup" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf rpc" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "ipv4ipv4]]>]]>" "^ipv4" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4ipv4]]>]]>" "^ipv4" new "netconf rpc w/o namespace" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "ipv4ipv4]]>]]>" "^ipv4" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4ipv4]]>]]>" "^ipv4" new "netconf empty rpc" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf client-side rpc" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "example]]>]]>" "^ok]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "example]]>]]>" "^ok]]>]]>$" new "netconf subscription" expectwait "$clixon_netconf -qf $cfg -y $fyang" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 diff --git a/test/test_order.sh b/test/test_order.sh index a046d5c6..64eaace4 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -119,53 +119,53 @@ fi # Check as file new "verify running from start, should be: l,c,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if XML_SORT set to false" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" new "get each ordered-by user leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^abar]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" new "get each ordered-by user leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^abar]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" new "get each ordered-by user leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^bbar]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" new "get each ordered-by user leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^bbar]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" new "delete candidate" -expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" # LEAF_LISTS new "add two entries to leaf-list user order" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "cb]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "cb]]>]]>" "^]]>]]>$" new "add one entry to leaf-list user order" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "a]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "add one entry to leaf-list user order after commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "0]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "0]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "verify leaf-list user order in running (as entered)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^cba0]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^cba0]]>]]>$" # LISTS new "add two entries to list user order" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "cbarbfoo]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "cbarbfoo]]>]]>" "^]]>]]>$" new "add one entry to list user order" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "afie]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "afie]]>]]>" "^]]>]]>$" new "verify list user order (as entered)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^cbarbfooafie]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^cbarbfooafie]]>]]>$" pid=`pgrep clixon_backend` if [ -z "$pid" ]; then diff --git a/test/test_perf.sh b/test/test_perf.sh index 8ad933e1..16f7b19a 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -99,13 +99,13 @@ expecteof_file "time -f %e $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf commit large config again" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf add small (1 entry) config" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "xy]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "xy]]>]]>" "^]]>]]>$" new "netconf get $req small config" time -p for (( i=0; i<$req; i++ )); do @@ -132,7 +132,7 @@ time -p for (( i=0; i<$req; i++ )); do done new "netconf get large config" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^0011" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^0011" new "generate large leaf-list config" echo -n "replace" > $fconfig @@ -147,7 +147,7 @@ expecteof_file "time -f %e $clixon_netconf -qf $cfg -y $fyang" "$fconfig" "^]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf add $req small leaf-list config" time -p for (( i=0; i<$req; i++ )); do @@ -156,13 +156,13 @@ time -p for (( i=0; i<$req; i++ )); do done | $clixon_netconf -qf $cfg -y $fyang > /dev/null new "netconf add small leaf-list config" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "x]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "x]]>]]>" "^]]>]]>$" new "netconf commit small leaf-list config" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get large leaf-list config" -expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^01" +expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^01" new "Kill backend" # Check if still alive diff --git a/test/test_startup.sh b/test/test_startup.sh index 7c3208f0..d3cdcd54 100755 --- a/test/test_startup.sh +++ b/test/test_startup.sh @@ -87,7 +87,7 @@ EOF fi new "Check $mode" - expecteof "$clixon_netconf -qf $cfg" ']]>]]>' "^$expect]]>]]>$" + expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^$expect]]>]]>$" new "Kill backend" # Check if still alive diff --git a/test/test_type.sh b/test/test_type.sh index 2658fe8a..1535f3b3 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -171,19 +171,19 @@ new "cli commit" expectfn "$clixon_cli -1f $cfg -l o -y $fyang -l o commit" 0 "^$" new "netconf validate ok" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf set ab wrong" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "a.b& c.d]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a.b& c.d]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "cli enum value" expectfn "$clixon_cli -1f $cfg -l o -y $fyang set status down" 0 "^$" @@ -196,7 +196,7 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits create" 0 "^$" #expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits \"create read\"" 0 "^$" new "netconf bits two values" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "create read]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "create read]]>]]>" "^]]>]]>$" new "cli bits validate" expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$" diff --git a/test/test_xml.sh b/test/test_xml.sh new file mode 100755 index 00000000..4dd63631 --- /dev/null +++ b/test/test_xml.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Test: XML parser tests +PROG=../lib/src/clixon_util_xml + +# include err() and new() functions and creates $dir +. ./lib.sh + +new "xml parse" +expecteof $PROG 0 "" "^$" + +XML=$(cat <An example of escaped CENDs + + y" so I guess that means that z > x ]]> + + + +]]> + +]]> + +EOF +) +new "xml CDATA" +expecteof $PROG 0 "$XML" "^An example of escaped CENDs + y\" so I guess that means that z > x ]]> +]]>]]>$" + +XML=$(cat <Less than: < , greater than: > ampersand: & +EOF +) +new "xml encode <>&" +expecteof $PROG 0 "$XML" "^$XML$" + +XML=$(cat <To allow attribute values to contain both single and double quotes, the apostrophe or single-quote character ' may be represented as ' and the double-quote character as " +EOF +) +new "xml optional encode single and double quote" +expecteof $PROG 0 "$XML" "^To allow attribute values to contain both single and double quotes, the apostrophe or single-quote character ' may be represented as ' and the double-quote character as \"$" + +rm -rf $dir + diff --git a/test/test_xsl.sh b/test/test_xsl.sh new file mode 100755 index 00000000..b7e322ad --- /dev/null +++ b/test/test_xsl.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Test: XSL tests +PROG=../lib/src/clixon_util_xsl + +# include err() and new() functions and creates $dir +. ./lib.sh + +new "xsl test" +expecteof $PROG 0 "a +" "^0:$" + +rm -rf $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 86c56b3b..1e542501 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -118,29 +118,29 @@ new "cli not defined extension" #expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found" new "netconf edit config" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "125one]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "125one]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" # text empty type in running new "netconf commit 2nd" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get config xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^125one]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^125one]]>]]>$" new "netconf edit leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "hejhopp]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "hejhopp]]>]]>" "^]]>]]>$" new "netconf get leaf-list" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhopp]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" new "netconf get leaf-list path" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^hejhopp]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" new "netconf get (should be some)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^125one]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^125one]]>]]>$" new "cli set leaf-list" expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 "" @@ -148,50 +148,50 @@ expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 "" new "cli show leaf-list" expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "foo" new "netconf set state data (not allowed)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "42]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42]]>]]>" "^invalid-value" new "netconf set presence and not present" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get presence only" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new "netconf anyxml" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf validate anyxml" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf delete candidate" -expecteof "$clixon_netconf -qf $cfg" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" # Check 3-keys new "netconf add one 3-key entry" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "111one]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "111one]]>]]>" "^]]>]]>$" new "netconf check add one 3-key" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '111one]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '111one]]>]]>' new "netconf add another (with same 1st key)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "121two]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "121two]]>]]>" "^]]>]]>$" new "netconf check add another" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '111one121two]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '111one121two]]>]]>' new "netconf replace first" -expecteof "$clixon_netconf -qf $cfg -y $fyang" "111replace]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "111replace]]>]]>" "^]]>]]>$" new "netconf check replace" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '111replace121two]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '111replace121two]]>]]>' new "netconf delete first" -expecteof "$clixon_netconf -qf $cfg -y $fyang" '111]]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '111]]>]]>' "^]]>]]>$" new "netconf check delete" -expecteof "$clixon_netconf -qf $cfg -y $fyang" ']]>]]>' '121two]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '121two]]>]]>' # Check if still alive pid=`pgrep clixon_backend` diff --git a/test/test_yang_parse.sh b/test/test_yang_parse.sh new file mode 100755 index 00000000..1a80c89d --- /dev/null +++ b/test/test_yang_parse.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Test: XML parser tests +PROG=../lib/src/clixon_util_yang + +# include err() and new() functions and creates $dir +. ./lib.sh + +YANG=$(cat < Date: Sun, 17 Jun 2018 17:58:59 +0000 Subject: [PATCH 28/53] 64-bit vars --- apps/restconf/restconf_methods.c | 2 +- lib/src/clixon_event.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index b1e05378..5aeded27 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -254,7 +254,7 @@ api_data_get2(clicon_handle h, else{ if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0) goto done; - clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, xlen); + clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, (int)xlen); if (use_xml){ for (i=0; ie_next; - clicon_debug(2, "%s timeout: %s[%x]", - __FUNCTION__, e->e_string, (int)e->e_arg); + clicon_debug(2, "%s timeout: %s[%lx]", + __FUNCTION__, e->e_string, (intptr_t)e->e_arg); if ((*e->e_fn)(0, e->e_arg) < 0){ free(e); goto err; @@ -331,8 +331,8 @@ event_loop(void) break; e_next = e->e_next; if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){ - clicon_debug(2, "%s: FD_ISSET: %s[%x]", - __FUNCTION__, e->e_string, (int)e->e_arg); + clicon_debug(2, "%s: FD_ISSET: %s[%lx]", + __FUNCTION__, e->e_string, (intptr_t)e->e_arg); if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){ clicon_debug(1, "%s Error in: %s", __FUNCTION__, e->e_string); goto err; From 5a8660afb6fb61505bd43423dfcafdbf54a0cc45 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sun, 17 Jun 2018 18:02:54 +0000 Subject: [PATCH 29/53] removed void ptr debug print --- lib/src/clixon_event.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/src/clixon_event.c b/lib/src/clixon_event.c index 8065c26b..1569f69f 100644 --- a/lib/src/clixon_event.c +++ b/lib/src/clixon_event.c @@ -317,8 +317,7 @@ event_loop(void) if (n==0){ /* Timeout */ e = ee_timers; ee_timers = ee_timers->e_next; - clicon_debug(2, "%s timeout: %s[%lx]", - __FUNCTION__, e->e_string, (intptr_t)e->e_arg); + clicon_debug(2, "%s timeout: %s", __FUNCTION__, e->e_string); if ((*e->e_fn)(0, e->e_arg) < 0){ free(e); goto err; @@ -331,8 +330,7 @@ event_loop(void) break; e_next = e->e_next; if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){ - clicon_debug(2, "%s: FD_ISSET: %s[%lx]", - __FUNCTION__, e->e_string, (intptr_t)e->e_arg); + clicon_debug(2, "%s: FD_ISSET: %s", __FUNCTION__, e->e_string); if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){ clicon_debug(1, "%s Error in: %s", __FUNCTION__, e->e_string); goto err; From 5304acb086fe31b37f7e73b81cc9d36f0400e4ca Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 17 Jun 2018 23:09:36 +0200 Subject: [PATCH 30/53] remove xml x_cv; added makes to jenkinsfile --- lib/clixon/clixon_xml.h | 3 --- lib/src/clixon_xml.c | 40 ---------------------------------------- test/Jenkinsfile | 17 ++++++++++++++--- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 2199629b..59e25913 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -107,9 +107,6 @@ char *xml_value_append(cxobj *xn, char *val); enum cxobj_type xml_type(cxobj *xn); int xml_type_set(cxobj *xn, enum cxobj_type type); -cg_var *xml_cv_get(cxobj *xn); -int xml_cv_set(cxobj *xn, cg_var *cv); - int xml_child_nr(cxobj *xn); int xml_child_nr_type(cxobj *xn, enum cxobj_type type); cxobj *xml_child_i(cxobj *xn, int i); diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 8816aff2..1e80bf68 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -112,7 +112,6 @@ struct xml{ int x_flags; /* Flags according to XML_FLAG_* */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ - cg_var *x_cv; /* If body this contains the typed value */ }; /* Mapping between xml type <--> string */ @@ -413,35 +412,6 @@ xml_type_set(cxobj *xn, return old; } -/*! Get cligen variable associated with node - * @param[in] xn xml node - * @retval cv Cligen variable if set - * @retval NULL If not set, or not applicable - */ -cg_var * -xml_cv_get(cxobj *xn) -{ - if (xn->x_cv) - return xn->x_cv; - else - return NULL; -} - -/*! Set cligen variable associated with node - * @param[in] xn xml node - * @param[in] cv Cligen variable or NULL - * @retval 0 if OK - */ -int -xml_cv_set(cxobj *xn, - cg_var *cv) -{ - if (xn->x_cv) - free(xn->x_cv); - xn->x_cv = cv; - return 0; -} - /*! Get number of children * @param[in] xn xml node * @retval number of children in XML tree @@ -999,8 +969,6 @@ xml_free(cxobj *x) free(x->x_value); if (x->x_namespace) free(x->x_namespace); - if (x->x_cv) - cv_free(x->x_cv); for (i=0; ix_childvec_len; i++){ if ((xc = x->x_childvec[i]) != NULL){ xml_free(xc); @@ -1531,14 +1499,6 @@ xml_copy_one(cxobj *x0, if (xml_name(x0)) /* malloced string */ if ((xml_name_set(x1, xml_name(x0))) < 0) return -1; - if (xml_cv_get(x0)){ - if ((cv1 = cv_dup(xml_cv_get(x0))) == NULL){ - clicon_err(OE_XML, errno, "cv_dup"); - return -1; - } - if ((xml_cv_set(x1, cv1)) < 0) - return -1; - } return 0; } diff --git a/test/Jenkinsfile b/test/Jenkinsfile index b31de847..eb9f85c5 100644 --- a/test/Jenkinsfile +++ b/test/Jenkinsfile @@ -18,8 +18,19 @@ node { */ sh 'make' } - - stage('Testing') { - + stage('Make install') { + sh 'make install' } + stage('Make install-include') { + sh 'make install-include' + } + stage('Make Example') { + sh 'cd example' + sh 'make' + sh 'sudo make install' + } + stage('Testing') { + sh 'cd test' + sh './all.sh' + } } From 578a96eff7c1d0057e8358d20032b90218b8fbed Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 17 Jun 2018 23:12:39 +0200 Subject: [PATCH 31/53] Jenkinsfile sudo --- test/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Jenkinsfile b/test/Jenkinsfile index eb9f85c5..01219556 100644 --- a/test/Jenkinsfile +++ b/test/Jenkinsfile @@ -19,10 +19,10 @@ node { sh 'make' } stage('Make install') { - sh 'make install' + sh 'sudo make install' } stage('Make install-include') { - sh 'make install-include' + sh 'sudo make install-include' } stage('Make Example') { sh 'cd example' From 60ce7b12bd27be36c766d895e43ecfe17a0e0d90 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 20 Jun 2018 20:53:34 +0200 Subject: [PATCH 32/53] * Prefix of rpc was ignored * https://github.com/clicon/clixon/issues/30 --- CHANGELOG.md | 2 ++ apps/netconf/netconf_rpc.c | 18 +++++++++++++----- apps/restconf/restconf_methods.c | 9 +++++++-- lib/src/clixon_yang.c | 18 +++++++++++------- test/test_netconf.sh | 6 +++--- test/test_restconf.sh | 3 +++ 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b72a6ad..ff1a1db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ * See FAQ and example ### Corrected Bugs +* Prefix of rpc was ignored + * https://github.com/clicon/clixon/issues/30 * Added cli returna value also for single commands (eg -1) * Fixed JSON unbalanced braces resulting in assert. diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index ac341c03..da50636e 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -888,12 +888,19 @@ netconf_application_rpc(clicon_handle h, goto done; } cbuf_reset(cb); - // if (xml_namespace(xn)) - cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn)); - // else - // cprintf(cb, "/%s", xml_name(xn)); /* XXX not accepdted by below */ + if (xml_namespace(xn) == NULL){ + xml_parse_va(xret, NULL, "" + "operation-failed" + "rpc" + "error" + "%s" + "Not recognized" + "", xml_name(xn)); + goto ok; + } + cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn)); /* Find yang rpc statement, return yang rpc statement if found */ - if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0) + if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0) goto done; /* Check if found */ if (yrpc != NULL){ @@ -937,6 +944,7 @@ netconf_application_rpc(clicon_handle h, retval = 1; /* handled by callback */ goto done; } + ok: retval = 0; done: if (cb) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 5aeded27..142105d0 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -1048,8 +1048,13 @@ api_operations_post(clicon_handle h, clicon_debug(1, "%s oppath: %s", __FUNCTION__, oppath); /* Find yang rpc statement, return yang rpc statement if found */ - if (yang_abs_schema_nodeid(yspec, oppath, Y_RPC, &yrpc) < 0) - goto done; + if (yang_abs_schema_nodeid(yspec, oppath, Y_RPC, &yrpc) < 0){ + if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) + goto done; + if (api_return_err(h, r, xerr, pretty, use_xml) < 0) + goto done; + goto ok; + } if (yrpc == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) goto done; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 76a8ff98..8960fdca 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -2082,16 +2082,16 @@ schema_nodeid_vec(yang_node *yn, * Assume schema nodeid:s have prefixes, (actually the first). * @see yang_desc_schema_nodeid * @see RFC7950 6.5 - o schema node: A node in the schema tree. One of action, container, - leaf, leaf-list, list, choice, case, rpc, input, output, - notification, anydata, and anyxml. + * o schema node: A node in the schema tree. One of action, container, + * leaf, leaf-list, list, choice, case, rpc, input, output, + * notification, anydata, and anyxml. * Used in yang: deviation, top-level augment */ int -yang_abs_schema_nodeid(yang_spec *yspec, - char *schema_nodeid, +yang_abs_schema_nodeid(yang_spec *yspec, + char *schema_nodeid, enum rfc_6020 keyword, - yang_stmt **yres) + yang_stmt **yres) { int retval = -1; char **vec = NULL; @@ -2136,7 +2136,6 @@ yang_abs_schema_nodeid(yang_spec *yspec, } } if (ymod == NULL){ /* Try with topnode */ - if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){ clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id); goto done; @@ -2145,6 +2144,11 @@ yang_abs_schema_nodeid(yang_spec *yspec, clicon_err(OE_YANG, 0, "Module with id:%s:%s not found2", prefix,id); goto done; } + if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL && + strcmp(yprefix->ys_argument, prefix) != 0){ + clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id); + goto done; + } } if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0) goto done; diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 02982e5a..e0364e1a 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -199,11 +199,11 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4ipv4]]>]]>" "^ipv4" -new "netconf rpc w/o namespace" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4ipv4]]>]]>" "^ipv4" +new "netconf rpc without namespace" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4ipv4]]>]]>" "^ipv4" new "netconf empty rpc" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf client-side rpc" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "example]]>]]>" "^ok]]>]]>$" diff --git a/test/test_restconf.sh b/test/test_restconf.sh index d1ff4ade..5008ad80 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -275,6 +275,9 @@ if [ -z "$match" ]; then err "$expect" "$ret" fi +new2 "restconf rpc using wrong prefix" +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' + new "restconf local client rpc using POST xml" ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/ex:client-rpc) expect="ok" From ee946a00f5a8ae1042434a07b18a0f9bce2b2e7a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 12:44:59 +0200 Subject: [PATCH 33/53] Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. Also, example documentation corrected according to: https://github.com/clicon/clixon/issues/33 --- CHANGELOG.md | 3 ++- example/README.md | 2 +- example/example_backend_nacm.c | 7 +++++++ lib/clixon/clixon_plugin.h | 5 ++++- lib/src/clixon_plugin.c | 18 ++++++++++++------ lib/src/clixon_xml.c | 2 -- lib/src/clixon_xml_map.c | 32 ++++++++++++++++++++++++++------ 7 files changed, 52 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff1a1db6..48aecb65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: -* Dedicated xml,json,yang and xsl parser utility programs added +* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. +* Dedicated standalone xml,json,yang and xsl parser utility test programs added under lib/src/. * CDATA xml support (patch by David Cornejo, Netgate) * Encode and decode (parsing) support * Validation of yang bits type space-separated list value diff --git a/example/README.md b/example/README.md index cafd796f..e373476b 100644 --- a/example/README.md +++ b/example/README.md @@ -23,7 +23,7 @@ routing example. It contains the following files: ``` Start backend: ``` - clixon_backend -f /usr/local/etc/example.xml -I + sudo clixon_backend -f /usr/local/etc/example.xml -s init ``` Edit cli: ``` diff --git a/example/example_backend_nacm.c b/example/example_backend_nacm.c index d6ed4600..f484362e 100644 --- a/example/example_backend_nacm.c +++ b/example/example_backend_nacm.c @@ -114,6 +114,13 @@ static clixon_plugin_api api = { clixon_plugin_api * clixon_plugin_init(clicon_handle h) { + char *nacm_mode; + clicon_debug(1, "%s backend nacm", __FUNCTION__); + nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE"); + if (nacm_mode==NULL || strcmp(nacm_mode, "disabled") == 0){ + clicon_debug(1, "%s CLICON_NACM_MODE not enabled: example nacm module disabled", __FUNCTION__); + return NULL; + } return &api; } diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index cd340458..cd6038aa 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -41,7 +41,9 @@ /* * Constants */ -/* Hardcoded plugin symbol. Must exist in all plugins to kickstart */ +/* Hardcoded plugin symbol. Must exist in all plugins to kickstart + * @see clixon_plugin_init + */ #define CLIXON_PLUGIN_INIT "clixon_plugin_init" /* @@ -181,6 +183,7 @@ typedef struct clixon_plugin clixon_plugin; /*! Plugin initialization function. Must appear in all plugins * @param[in] h Clixon handle * @retval api Pointer to API struct + * @retval NULL Failure (if clixon_err() called), module disabled otherwise. * @see CLIXON_PLUGIN_INIT default symbol */ clixon_plugin_api *clixon_plugin_init(clicon_handle h); diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 3d0b80ae..2631938c 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -204,12 +205,16 @@ plugin_load_one(clicon_handle h, clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error); goto done; } + clicon_err_reset(); if ((api = initfn(h)) == NULL) { - clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); - if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ - clicon_err(OE_DB, 0, "Unknown error: %s: plugin_init does not make clicon_err call on error", - file); - goto err; + if (!clicon_errno){ /* if clicon_err() is not called then log and continue */ + clicon_log(LOG_WARNING, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); + dlclose(handle); + } + else{ + clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); + goto err; + } } /* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */ if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){ @@ -228,7 +233,8 @@ plugin_load_one(clicon_handle h, snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s", (int)strlen(name), name); - cp->cp_api = *api; + if (api) + cp->cp_api = *api; clicon_debug(1, "%s", __FUNCTION__); done: return cp; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 1e80bf68..c2393782 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1487,8 +1487,6 @@ int xml_copy_one(cxobj *x0, cxobj *x1) { - cg_var *cv1; - xml_type_set(x1, xml_type(x0)); if (xml_value(x0)){ /* malloced string */ if ((x1->x_value = strdup(x0->x_value)) == NULL){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 7e1b69e4..88aa0d6d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1715,6 +1715,7 @@ xml_merge1(cxobj *x0, cxobj *x1c; /* mod child */ char *x1bstr; /* mod body string */ yang_stmt *yc; /* yang child */ + cbuf *cbr = NULL; /* Reason buffer */ assert(x1 && xml_type(x1) == CX_ELMNT); assert(y0); @@ -1753,9 +1754,16 @@ xml_merge1(cxobj *x0, x1cname = xml_name(x1c); /* Get yang spec of the child */ if ((yc = yang_find_datanode(y0, x1cname)) == NULL){ - if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (reason){ + if ((cbr = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname); + if ((*reason = strdup(cbuf_get(cbr))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } } break; } @@ -1772,6 +1780,8 @@ xml_merge1(cxobj *x0, ok: retval = 0; done: + if (cbr) + cbuf_free(cbr); return retval; } @@ -1797,6 +1807,7 @@ xml_merge(cxobj *x0, cxobj *x0c; /* base child */ cxobj *x1c; /* mod child */ yang_stmt *yc; + cbuf *cbr = NULL; /* Reason buffer */ /* Loop through children of the modification tree */ x1c = NULL; @@ -1804,9 +1815,16 @@ xml_merge(cxobj *x0, x1cname = xml_name(x1c); /* Get yang spec of the child */ if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){ - if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (reason){ + if ((cbr = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname); + if ((*reason = strdup(cbuf_get(cbr))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } } break; } @@ -1820,6 +1838,8 @@ xml_merge(cxobj *x0, } retval = 0; /* OK */ done: + if (cbr) + cbuf_free(cbr); return retval; } From fd19640579c9903133e41f06f27e1a406e9f01a0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 13:13:11 +0200 Subject: [PATCH 34/53] Added systemd example files under example/systemd --- CHANGELOG.md | 1 + apps/restconf/README.md | 2 +- doc/FAQ.md | 9 +++++++-- example/README.md | 4 ++++ example/systemd/example.service | 13 +++++++++++++ example/systemd/example_restconf.service | 14 ++++++++++++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 example/systemd/example.service create mode 100644 example/systemd/example_restconf.service diff --git a/CHANGELOG.md b/CHANGELOG.md index 48aecb65..5448e813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: +* Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. * Dedicated standalone xml,json,yang and xsl parser utility test programs added under lib/src/. * CDATA xml support (patch by David Cornejo, Netgate) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index d6c69b89..7c5cc03e 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -2,7 +2,7 @@ ### Installation using Nginx -Define nginx config file/etc/nginx/sites-available/default +Define nginx config file: /etc/nginx/sites-available/default ``` server { ... diff --git a/doc/FAQ.md b/doc/FAQ.md index 09459599..1884a246 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -24,7 +24,7 @@ Clixon is written in C. The plugins are written in C. The CLI specification uses cligen (http://cligen.se) ## How to best understand Clixon? -Run the Clixon example, in the example directory. +Run the Clixon example, in the [example](../example) directory. ## How do you build and install Clixon (and the example)? Clixon: @@ -178,6 +178,11 @@ You may also add a default method in the configuration file: ``` +## Can I use systemd with Clixon? + +Yes. Systemd example files are provide for the backend and the +restconf daemon as part of the [example](../example/systemd). + ## How can I add extra XML? There are two ways to add extra XML to running database after start. Note that this XML is not "committed" into running. @@ -345,7 +350,7 @@ To authenticate, the callback needs to return the value 1 and supply a username. See [../apps/example/example_restconf.c] example_restconf_credentials() for an example of HTTP basic auth. -## How do I write a CLI translator function +## How do I write a CLI translator function? The CLI can perform variable translation. This is useful if you want to prcess the input, such as hashing, encrypting or in other way diff --git a/example/README.md b/example/README.md index e373476b..3622612d 100644 --- a/example/README.md +++ b/example/README.md @@ -188,6 +188,10 @@ The example contains some stubs for authorization according to [RFC8341(NACM)](h * A NACM backend plugin reporting the mandatory NACM state variables. +## Systemd files + +Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example. + ## Run as docker container (Note not updated) diff --git a/example/systemd/example.service b/example/systemd/example.service new file mode 100644 index 00000000..0bc4130d --- /dev/null +++ b/example/systemd/example.service @@ -0,0 +1,13 @@ +[Unit] +Description=Starts and stops a clixon example service on this system +Wants=example_restconf.service + +[Service] +Type=forking +User=root +RestartSec=60 +Restart=on-failure +ExecStart=/usr/local/sbin/clixon_backend -s running -f /usr/local/etc/example.xml + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/example/systemd/example_restconf.service b/example/systemd/example_restconf.service new file mode 100644 index 00000000..d47ff885 --- /dev/null +++ b/example/systemd/example_restconf.service @@ -0,0 +1,14 @@ +[Unit] +Description=Starts and stops an example clixon restconf service on this system +Wants=example.service +After=example.service + +[Service] +Type=simple +User=www-data +WorkingDirectory=/www-data +Restart=on-failure +ExecStart=/www-data/clixon_restconf -f /usr/local/etc/example.xml + +[Install] +WantedBy=multi-user.target From 719ea9339817a3f2d3fe53ea9ab1d08d44031470 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:31:39 +0200 Subject: [PATCH 35/53] Conformance to RFC-8040 operations where prefix was used instead of module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out --- CHANGELOG.md | 4 +++ apps/restconf/restconf_methods.c | 35 +++++++++++++++++------- lib/clixon/clixon_yang.h | 1 + lib/src/clixon_yang.c | 46 +++++++++++++++++++++++++++++--- test/test_restconf.sh | 26 +++++++++--------- 5 files changed, 87 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5448e813..0ec25fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 3.7.0 (Upcoming) ### Major changes: +* Conformance for RFC-8040 operations where prefix was used instead of module name. + * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 + * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 + * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. * Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 * Previous support did no validation of values. * Validation of types and CLI expansion diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 142105d0..62580cd6 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -925,8 +925,7 @@ api_operations_get(clicon_handle h, yang_spec *yspec; yang_stmt *ym; yang_stmt *yc; - yang_stmt *yprefix; - char *prefix; + char *modname; cbuf *cbx = NULL; cxobj *xt = NULL; @@ -937,15 +936,12 @@ api_operations_get(clicon_handle h, cprintf(cbx, ""); ym = NULL; while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) { - if ((yprefix = yang_find((yang_node*)ym, Y_PREFIX, NULL)) != NULL) - prefix = yprefix->ys_argument; - else - continue; + modname = ym->ys_argument; yc = NULL; while ((yc = yn_each((yang_node*)ym, yc)) != NULL) { if (yc->ys_keyword != Y_RPC) continue; - cprintf(cbx, "<%s:%s />", prefix, yc->ys_argument); + cprintf(cbx, "<%s:%s />", modname, yc->ys_argument); } } cprintf(cbx, ""); @@ -1030,6 +1026,9 @@ api_operations_post(clicon_handle h, char *username; cbuf *cbret = NULL; int ret = 0; + char *prefix = NULL; + char *id = NULL; + yang_stmt *ys = NULL; clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path); if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -1047,8 +1046,22 @@ api_operations_post(clicon_handle h, } clicon_debug(1, "%s oppath: %s", __FUNCTION__, oppath); - /* Find yang rpc statement, return yang rpc statement if found */ - if (yang_abs_schema_nodeid(yspec, oppath, Y_RPC, &yrpc) < 0){ + /* Find yang rpc statement, return yang rpc statement if found + * POST {+restconf}/operations/ + * + * The field identifies the module name and rpc identifier + * string for the desired operation. + */ + if (yang_nodeid_split(oppath+1, &prefix, &id) < 0) /* +1 skip / */ + goto done; + if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){ + if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) + goto done; + if (api_return_err(h, r, xerr, pretty, use_xml) < 0) + goto done; + goto ok; + } + if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) goto done; if (api_return_err(h, r, xerr, pretty, use_xml) < 0) @@ -1229,6 +1242,10 @@ api_operations_post(clicon_handle h, retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (prefix) + free(prefix); + if (id) + free(id); if (xdata) xml_free(xdata); if (xtop) diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 5ea7e986..18fbee6b 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -246,6 +246,7 @@ yang_stmt *yn_each(yang_node *yn, yang_stmt *ys); char *yang_key2str(int keyword); char *yarg_prefix(yang_stmt *ys); char *yarg_id(yang_stmt *ys); +int yang_nodeid_split(char *nodeid, char **prefix, char **id); yang_stmt *ys_module(yang_stmt *ys); yang_spec *ys_spec(yang_stmt *ys); yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix); diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 8960fdca..d034798b 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -611,8 +611,8 @@ yang_find_schemanode(yang_node *yn, /*! Find first matching data node in all (sub)modules in a yang spec * * @param[in] ysp Yang specification - * @param[in] argument if NULL, match any(first) argument. XXX is that really a case? - * @param[in] schemanode If set look for schema nodes, otherwise only data nodes + * @param[in] argument Name of node. If NULL match first + * @param[in] class See yang_class for class of yang nodes * A yang specification has modules as children which in turn can have * syntax-nodes as children. This function goes through all the modules to * look for nodes. Note that if a child to a module is a choice, @@ -803,7 +803,7 @@ yarg_id(yang_stmt *ys) return id; } -/* Assume argument is id on the type: <[prefix:]id>, return 'prefix' +/*! Assume argument is id on the type: <[prefix:]id>, return 'prefix' * @param[in] ys A yang statement * @retval NULL No prefix * @retval prefix Malloced string that needs to be freed by caller. @@ -822,6 +822,46 @@ yarg_prefix(yang_stmt *ys) return prefix; } + +/*! Split yang node identifier into prefix and identifer. + * @param[in] node-id + * @param[out] prefix Malloced string. May be NULL. + * @param[out] id Malloced identifier. + * @retval 0 OK + * @retval -1 Error + * @note caller need to free id and prefix after use + */ +int +yang_nodeid_split(char *nodeid, + char **prefix, + char **id) +{ + int retval = -1; + char *str; + + if ((str = strchr(nodeid, ':')) == NULL){ + if ((*id = strdup(nodeid)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + } + else{ + if ((*prefix = strdup(nodeid)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + (*prefix)[str-nodeid] = '\0'; + str++; + if ((*id = strdup(str)) == NULL){ + clicon_err(OE_YANG, errno, "strdup"); + goto done; + } + } + retval = 0; + done: + return retval; +} + /*! Given a yang statement and a prefix, return yang module to that prefix * Note, not the other module but the proxy import statement only * @param[in] ys A yang statement diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 5008ad80..f4856e81 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -92,7 +92,7 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang # -D +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang -D sleep 1 @@ -113,12 +113,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r ' new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)" -expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"ex:client-rpc": null,"rt:fib-route": null,"rt:route-count": null}} +expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:input": null,"example:output": null,"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null}} ' new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) -expect="" +expect="" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" @@ -143,7 +143,7 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK" #Content-Type: application/yang-data+json" new2 "restconf empty rpc" -expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" "" +expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/example:empty)" "" new2 "restconf get empty config + state json" expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}} @@ -246,29 +246,29 @@ expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": ' new2 "restconf rpc using POST json" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}} +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}} ' # Cant get this to work due to quoting #new2 "restconf rpc using POST wrong JSON" -#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} ' +#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} ' new2 "restconf rpc using POST json w/o mandatory element" -expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' +expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' new2 "restconf rpc non-existing rpc w/o namespace" expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' new2 "restconf rpc non-existing rpc" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ex:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' new2 "restconf rpc missing name" expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Operation name expected"}}}} ' new2 "restconf rpc missing input" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' new "restconf rpc using POST xml" -ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route) +ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route) expect="ipv42.3.4.5" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then @@ -276,10 +276,10 @@ if [ -z "$match" ]; then fi new2 "restconf rpc using wrong prefix" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang module not found"}}}} ' new "restconf local client rpc using POST xml" -ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/ex:client-rpc) +ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc) expect="ok" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then @@ -287,7 +287,7 @@ if [ -z "$match" ]; then fi # XXX cant get -H to work -#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' 'ipv42.3.4.5' +#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' 'ipv42.3.4.5' # Cant get shell macros to work, inline matching from lib.sh From d61f86c1e0b1cf07832645a3b78b22efae6e750f Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:51:51 +0200 Subject: [PATCH 36/53] restconf www-data group membership clarification --- apps/restconf/README.md | 5 +++++ doc/FAQ.md | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 7c5cc03e..06ec90bb 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -2,6 +2,11 @@ ### Installation using Nginx +Ensure www-data is member of the CLICON_SOCK_GROUP (default clicon). If not, add it: +``` + sudo usermod -a -G clicon www-data +``` + Define nginx config file: /etc/nginx/sites-available/default ``` server { diff --git a/doc/FAQ.md b/doc/FAQ.md index 1884a246..880e34dc 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -1,4 +1,4 @@ -i# Clixon FAQ +# Clixon FAQ ## What is Clixon? @@ -41,14 +41,25 @@ The example: sudo make install ``` -## Do I need to setup anything? +## Do I need to setup anything? (IMPORTANT) The config demon requires a valid group to create a server UNIX socket. Define a valid CLICON_SOCK_GROUP in the config file or via the -g option or create the group and add the user to it. The default group is 'clicon'. +Add yourself and www-data, if you intend to use restconf. + On linux: +``` sudo groupadd clicon - sudo usermod -a -G clicon user + sudo usermod -a -G clicon + sudo usermod -a -G clicon www-data +``` + +Verify: +``` +grep clicon /etc/group +clicon:x:1001:,www-data +``` ## What about reference documentation? Clixon uses Doxygen for reference documentation. From 0c5ef826d2bf32890c1410660f3a03e1f1f0ebc8 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 13 Jul 2018 16:58:19 +0200 Subject: [PATCH 37/53] clicon group in example clarification --- example/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/README.md b/example/README.md index 3622612d..be751154 100644 --- a/example/README.md +++ b/example/README.md @@ -16,7 +16,11 @@ routing example. It contains the following files: * example_netconf.c Netconf callback plugin * Makefile.in Example makefile where plugins are built and installed + ## Compile and run + +Before you start, see [preparation](../doc/FAQ.md#do-i-need-to-setup-anything-important). + ``` cd example make && sudo make install From bf728b37b8b729d5449977117736f818baa2fbd2 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 16 Jul 2018 16:13:31 +0200 Subject: [PATCH 38/53] * Full support of XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex * The previous XPATH imlementation was very restricted. * The only function implemented is the Yang extension "current()". No other functions are implemented (eg last(), count()). --- CHANGELOG.md | 7 +- apps/cli/cli_main.c | 2 +- apps/cli/cli_plugin.c | 14 +- datastore/keyvalue/clixon_chunk.c | 152 ++-- datastore/keyvalue/clixon_qdb.c | 4 +- lib/clixon/clixon.h.in | 2 + lib/clixon/clixon_string.h | 2 + lib/clixon/clixon_xpath.h | 95 +++ lib/clixon/clixon_xpath_ctx.h | 103 +++ lib/src/Makefile.in | 25 +- lib/src/clixon_hash.c | 38 +- lib/src/clixon_string.c | 16 +- lib/src/clixon_util_xpath.c | 231 ++++++ lib/src/clixon_util_xsl.c | 141 ---- lib/src/clixon_xml.c | 2 - lib/src/clixon_xml_map.c | 26 +- lib/src/clixon_xpath.c | 1183 +++++++++++++++++++++++++++++ lib/src/clixon_xpath_ctx.c | 294 +++++++ lib/src/clixon_xpath_parse.h | 101 +++ lib/src/clixon_xpath_parse.l | 174 +++++ lib/src/clixon_xpath_parse.y | 289 +++++++ lib/src/clixon_xsl.c | 269 ++----- test/lib.sh | 2 +- test/test_xpath.sh | 151 ++++ test/test_xsl.sh | 12 - test/test_yang.sh | 2 +- 26 files changed, 2856 insertions(+), 481 deletions(-) create mode 100644 lib/clixon/clixon_xpath.h create mode 100644 lib/clixon/clixon_xpath_ctx.h create mode 100644 lib/src/clixon_util_xpath.c delete mode 100644 lib/src/clixon_util_xsl.c create mode 100644 lib/src/clixon_xpath.c create mode 100644 lib/src/clixon_xpath_ctx.c create mode 100644 lib/src/clixon_xpath_parse.h create mode 100644 lib/src/clixon_xpath_parse.l create mode 100644 lib/src/clixon_xpath_parse.y create mode 100755 test/test_xpath.sh delete mode 100755 test/test_xsl.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index ff1a1db6..f1bd40ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 3.7.0 (Upcoming) ### Major changes: +* Full support of XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex + * The previous XPATH imlementation was very restricted. + * The only function implemented is the Yang extension "current()". No other functions are implemented (eg last(), count()). * Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 * Previous support did no validation of values. * Validation of types and CLI expansion @@ -9,7 +12,7 @@ * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. ### Minor changes: -* Dedicated xml,json,yang and xsl parser utility programs added +* Dedicated xml,json,yang and xpath parser utility programs added * CDATA xml support (patch by David Cornejo, Netgate) * Encode and decode (parsing) support * Validation of yang bits type space-separated list value @@ -47,7 +50,7 @@ * See FAQ and example ### Corrected Bugs -* Prefix of rpc was ignored +* Prefix of rpc was ignored (thanks Dmitri at netgate) * https://github.com/clicon/clixon/issues/30 * Added cli returna value also for single commands (eg -1) * Fixed JSON unbalanced braces resulting in assert. diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 825fbc84..bd10b8f8 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -457,7 +457,7 @@ main(int argc, char **argv) } if (!cli_syntax_mode(h)){ - fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n"); + fprintf(stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n"); goto done; } if (cligen_tree_find(cli_cligen(h), cli_syntax_mode(h)) == NULL) diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 64f94bd8..f5781f87 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -97,7 +97,7 @@ syntax_mode_find(cli_syntax_t *stx, perror("malloc"); return NULL; } - memset (m, 0, sizeof (*m)); + memset(m, 0, sizeof(*m)); strncpy(m->csm_name, mode, sizeof(m->csm_name)-1); strncpy(m->csm_prompt, CLI_DEFAULT_PROMPT, sizeof(m->csm_prompt)-1); INSQ(m, stx->stx_modes); @@ -314,7 +314,7 @@ done: * @param[in] h Clicon handle */ int -cli_syntax_load (clicon_handle h) +cli_syntax_load(clicon_handle h) { int retval = -1; char *plugin_dir = NULL; @@ -343,7 +343,7 @@ cli_syntax_load (clicon_handle h) clicon_err(OE_UNIX, errno, "malloc"); goto done; } - memset (stx, 0, sizeof (*stx)); /* Zero out all */ + memset(stx, 0, sizeof(*stx)); /* Zero out all */ cli_syntax_set(h, stx); @@ -676,9 +676,9 @@ cli_set_prompt(clicon_handle h, * @param[in] fmt Stdarg fmt string */ static int -prompt_fmt (char *prompt, - size_t plen, - char *fmt, ...) +prompt_fmt(char *prompt, + size_t plen, + char *fmt, ...) { va_list ap; char *s = fmt; @@ -698,7 +698,7 @@ prompt_fmt (char *prompt, if (*s == '%' && *++s) { switch(*s) { case 'H': /* Hostname */ - if (gethostname (hname, sizeof (hname)) != 0) + if (gethostname(hname, sizeof(hname)) != 0) strncpy(hname, "unknown", sizeof(hname)-1); cprintf(cb, "%s", hname); break; diff --git a/datastore/keyvalue/clixon_chunk.c b/datastore/keyvalue/clixon_chunk.c index cb197724..80db76da 100644 --- a/datastore/keyvalue/clixon_chunk.c +++ b/datastore/keyvalue/clixon_chunk.c @@ -92,7 +92,7 @@ chunk_initialize () chunk_pagesz = getpagesize(); - bzero (&chunk_heads, sizeof (chunk_heads)); + bzero (&chunk_heads, sizeof(chunk_heads)); for (idx = 0; idx < CHUNK_HEADS; idx++) { chunk_head_t *chead = &chunk_heads[idx]; @@ -121,7 +121,7 @@ chunk_initialize () */ chead->ch_size = (chead->ch_blksz / chead->ch_nchkperblk) - - sizeof (chunk_t); + - sizeof(chunk_t); } @@ -150,22 +150,22 @@ chunk_new_block (chunk_head_t *chead) PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (blk == MAP_FAILED) return -1; - memset ((void *)blk, 0, sizeof(*blk)); + memset((void *)blk, 0, sizeof(*blk)); /* Allocate chunk block */ blk->cb_blk = (void *) mmap(NULL, chead->ch_blksz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (blk->cb_blk == MAP_FAILED) { - munmap (blk, chead->ch_blksz); + munmap(blk, chead->ch_blksz); return -1; } - memset (blk->cb_blk, 0, chead->ch_blksz); + memset(blk->cb_blk, 0, chead->ch_blksz); /* Initialize chunk header */ blk->cb_head = chead; - INSQ (blk, chead->ch_blks); + INSQ(blk, chead->ch_blks); chead->ch_nblks++; /* Initialize chunks */ @@ -174,10 +174,10 @@ chunk_new_block (chunk_head_t *chead) cnk = (chunk_t *)c; cnk->c_blk = blk; - INSQ (cnk, chead->ch_free); + INSQ(cnk, chead->ch_free); chead->ch_nfree++; - c += (chead->ch_size + sizeof (chunk_t)); + c += (chead->ch_size + sizeof(chunk_t)); } @@ -188,7 +188,7 @@ chunk_new_block (chunk_head_t *chead) * chunk_release_block() - Unqueue a block, it's chunks and free mem */ static void -chunk_release_block (chunk_block_t *cblk) +chunk_release_block(chunk_block_t *cblk) { int idx; char *c; @@ -201,7 +201,7 @@ chunk_release_block (chunk_block_t *cblk) /* * Dequeue block */ - DELQ (cblk, chead->ch_blks, chunk_block_t *); + DELQ(cblk, chead->ch_blks, chunk_block_t *); chead->ch_nblks--; /* @@ -211,17 +211,17 @@ chunk_release_block (chunk_block_t *cblk) for (idx = 0; idx < chead->ch_nchkperblk; idx++) { cnk = (chunk_t *)c; - DELQ (cnk, chead->ch_free, chunk_t *); + DELQ(cnk, chead->ch_free, chunk_t *); chead->ch_nfree--; - c += (chead->ch_size + sizeof (chunk_t)); + c += (chead->ch_size + sizeof(chunk_t)); } /* * Free block */ - munmap ((void *)cblk->cb_blk, chead->ch_blksz); - munmap ((void *)cblk, sizeof(*cblk)); + munmap((void *)cblk->cb_blk, chead->ch_blksz); + munmap((void *)cblk, sizeof(*cblk)); } @@ -230,7 +230,7 @@ chunk_release_block (chunk_block_t *cblk) * chunk_alloc() - Map new chunk of memory */ static void * -chunk_alloc (size_t len) +chunk_alloc(size_t len) { register int idx; chunk_head_t *chead; @@ -258,21 +258,21 @@ chunk_alloc (size_t len) /* Get new block if necessary */ if (!chead->ch_nfree) - if (chunk_new_block (chead)) + if (chunk_new_block(chead)) return (void *)NULL; /* Move a free chunk to the in-use list */ cnk = chead->ch_free; - DELQ (cnk, chead->ch_free, chunk_t *); + DELQ(cnk, chead->ch_free, chunk_t *); chead->ch_nfree--; - INSQ (cnk, chead->ch_cnks); + INSQ(cnk, chead->ch_cnks); /* Add reference to the corresponding block */ cnk->c_blk->cb_ref++; #ifdef CHUNK_DIAG /* Clear diag info */ - bzero ((void *)&cnk->c_diag, sizeof (cnk->c_diag)); + bzero((void *)&cnk->c_diag, sizeof(cnk->c_diag)); #endif /* CHUNK_DIAG */ /* Return pointer to first byte after the chunk header */ @@ -285,9 +285,9 @@ chunk_alloc (size_t len) */ void * #ifdef CHUNK_DIAG -_chunk (size_t len, const char *name, const char *file, int line) +_chunk(size_t len, const char *name, const char *file, int line) #else -chunk (size_t len, const char *name) +chunk(size_t len, const char *name) #endif { int newgrp = 0; @@ -306,10 +306,10 @@ chunk (size_t len, const char *name) /* Get actual chunk */ - ptr = chunk_alloc (len); + ptr = chunk_alloc(len); if (!ptr) goto error; - cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t)); + cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t)); #ifdef CHUNK_DIAG /* Store who reuested us @@ -329,7 +329,7 @@ chunk (size_t len, const char *name) if (chunk_grp) { tmp = chunk_grp; do { - if (!strcmp (tmp->cg_name, name)) { + if (!strcmp(tmp->cg_name, name)) { grp = tmp; break; } @@ -343,26 +343,26 @@ chunk (size_t len, const char *name) */ if ( !grp ) { - grp = (chunk_group_t *) chunk_alloc (sizeof (chunk_group_t)); + grp = (chunk_group_t *) chunk_alloc(sizeof(chunk_group_t)); if (!grp) goto error; - bzero (grp, sizeof (chunk_group_t)); + bzero(grp, sizeof(chunk_group_t)); - grp->cg_name = (char *) chunk_alloc (strlen (name) + 1); + grp->cg_name = (char *) chunk_alloc(strlen(name) + 1); if (!grp->cg_name) goto error; - bcopy (name, grp->cg_name, strlen(name)+1); + bcopy(name, grp->cg_name, strlen(name)+1); newgrp = 1; } /* Get new entry. */ - ent = (chunk_grpent_t *) chunk_alloc (sizeof (chunk_grpent_t)); + ent = (chunk_grpent_t *) chunk_alloc(sizeof(chunk_grpent_t)); if (!ent) goto error; - bzero (ent, sizeof (chunk_grpent_t)); + bzero(ent, sizeof(chunk_grpent_t)); /* Now put everything together */ @@ -371,22 +371,22 @@ chunk (size_t len, const char *name) ent->ce_cnk = cnk; ent->ce_grp = grp; - INSQ (ent, grp->cg_ent); + INSQ(ent, grp->cg_ent); if (newgrp) - INSQ (grp, chunk_grp); + INSQ(grp, chunk_grp); return (ptr); error: if (grp && newgrp) { if (grp->cg_name) - unchunk (grp->cg_name); - unchunk (grp); + unchunk(grp->cg_name); + unchunk(grp); } if (ent) - unchunk (ent); + unchunk(ent); if (ptr) - unchunk (ptr); + unchunk(ptr); return (void *) NULL; } @@ -397,9 +397,9 @@ chunk (size_t len, const char *name) */ void * #ifdef CHUNK_DIAG -_rechunk (void *ptr, size_t len, const char *name, const char *file, int line) +_rechunk(void *ptr, size_t len, const char *name, const char *file, int line) #else -rechunk (void *ptr, size_t len, const char *name) +rechunk(void *ptr, size_t len, const char *name) #endif { int idx; @@ -414,22 +414,22 @@ rechunk (void *ptr, size_t len, const char *name) */ if (!ptr) { #ifdef CHUNK_DIAG - return _chunk (len, name, file, line); + return _chunk(len, name, file, line); #else - return chunk (len, name); + return chunk(len, name); #endif } /* Zero length, free chunk */ if (len == 0) { - unchunk (ptr); + unchunk(ptr); return (void *) NULL; } /* Rewind pointer to beginning of chunk header */ - cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t)); + cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t)); chead = cnk->c_blk->cb_head; /* Find sufficient sized block head @@ -451,22 +451,22 @@ rechunk (void *ptr, size_t len, const char *name) /* Get new chunk */ #ifdef CHUNK_DIAG - new = _chunk (len, name, file, line); + new = _chunk(len, name, file, line); #else - new = chunk (len, name); + new = chunk(len, name); #endif if (!new) return (void *) NULL; - newcnk = (chunk_t *) (((char *)new) - sizeof (chunk_t)); + newcnk = (chunk_t *) (((char *)new) - sizeof(chunk_t)); newchead = newcnk->c_blk->cb_head; /* Copy contents to new chunk */ - bcopy (ptr, new, MIN(newchead->ch_size, chead->ch_size)); + bcopy(ptr, new, MIN(newchead->ch_size, chead->ch_size)); /* Free old chunk */ - unchunk (ptr); + unchunk(ptr); return (new); @@ -476,7 +476,7 @@ rechunk (void *ptr, size_t len, const char *name) * unchunk() - Release chunk */ void -unchunk (void *ptr) +unchunk(void *ptr) { chunk_t *cnk; chunk_head_t *chead; @@ -489,14 +489,14 @@ unchunk (void *ptr) /* Rewind pointer to beginning of chunk header */ - cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t)); + cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t)); cblk = cnk->c_blk; chead = cblk->cb_head; /* Move chunk back to free list */ - DELQ (cnk, chead->ch_cnks, chunk_t *); - INSQ (cnk, chead->ch_free); + DELQ(cnk, chead->ch_cnks, chunk_t *); + INSQ(cnk, chead->ch_free); chead->ch_nfree++; /* If chunk is grouped, remove from group. @@ -504,13 +504,13 @@ unchunk (void *ptr) ent = cnk->c_grpent; if (ent) { grp = ent->ce_grp; - DELQ (ent, grp->cg_ent, chunk_grpent_t *); - unchunk (ent); + DELQ(ent, grp->cg_ent, chunk_grpent_t *); + unchunk(ent); cnk->c_grpent = NULL; /* Group empty? */ if (!dont_unchunk_group && !grp->cg_ent) { - DELQ (grp, chunk_grp, chunk_group_t *); + DELQ(grp, chunk_grp, chunk_group_t *); unchunk(grp->cg_name); unchunk(grp); } @@ -528,7 +528,7 @@ unchunk (void *ptr) * unchunk_group() - Release all group chunks. */ void -unchunk_group (const char *name) +unchunk_group(const char *name) { chunk_group_t *tmp; chunk_group_t *grp = NULL; @@ -542,7 +542,7 @@ unchunk_group (const char *name) if (chunk_grp) { tmp = chunk_grp; do { - if (!strcmp (tmp->cg_name, name)) { + if (!strcmp(tmp->cg_name, name)) { grp = tmp; break; } @@ -560,16 +560,16 @@ unchunk_group (const char *name) dont_unchunk_group = 1; while (grp->cg_ent) { cnk = grp->cg_ent->ce_cnk; - unchunk ((chunk_t *)(((char *)cnk) + sizeof (chunk_t))); + unchunk((chunk_t *)(((char *)cnk) + sizeof(chunk_t))); } dont_unchunk_group = 0; /* Remove group from list and free it */ - DELQ (grp, chunk_grp, chunk_group_t *); - unchunk (grp->cg_name); - unchunk (grp); + DELQ(grp, chunk_grp, chunk_group_t *); + unchunk(grp->cg_name); + unchunk(grp); } /* @@ -577,9 +577,9 @@ unchunk_group (const char *name) */ void * #ifdef CHUNK_DIAG -_chunkdup (const void *ptr, size_t len, const char *name, const char *file, int line) +_chunkdup(const void *ptr, size_t len, const char *name, const char *file, int line) #else -chunkdup (const void *ptr, size_t len, const char *name) +chunkdup(const void *ptr, size_t len, const char *name) #endif { void *new; @@ -592,9 +592,9 @@ chunkdup (const void *ptr, size_t len, const char *name) /* Get new chunk */ #ifdef CHUNK_DIAG - new = _chunk (len, name, file, line); + new = _chunk(len, name, file, line); #else - new = chunk (len, name); + new = chunk(len, name); #endif if (!new) return (void *)NULL; @@ -610,7 +610,7 @@ chunkdup (const void *ptr, size_t len, const char *name) * chunksize() - Return size of memory chunk. */ size_t -chunksize (void *ptr) +chunksize(void *ptr) { chunk_t *cnk; chunk_head_t *chead; @@ -621,7 +621,7 @@ chunksize (void *ptr) /* Rewind pointer to beginning of chunk header */ - cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t)); + cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t)); cblk = cnk->c_blk; chead = cblk->cb_head; @@ -635,10 +635,10 @@ chunksize (void *ptr) */ char * #ifdef CHUNK_DIAG -_chunk_strncat (const char *dst, const char *src, size_t n, const char *name, +_chunk_strncat(const char *dst, const char *src, size_t n, const char *name, const char *file, int line) #else -chunk_strncat (const char *dst, const char *src, size_t n, const char *name) +chunk_strncat(const char *dst, const char *src, size_t n, const char *name) #endif { size_t len; @@ -651,7 +651,7 @@ chunk_strncat (const char *dst, const char *src, size_t n, const char *name) #ifdef CHUNK_DIAG ptr = _rechunk(ptr, len, name, file, line); #else - ptr = rechunk (ptr, len, name); + ptr = rechunk(ptr, len, name); #endif if (ptr == NULL) return NULL; @@ -670,10 +670,10 @@ chunk_strncat (const char *dst, const char *src, size_t n, const char *name) */ char * #ifdef CHUNK_DIAG -_chunk_sprintf (const char *name, const char *file, +_chunk_sprintf(const char *name, const char *file, int line, const char *fmt, ...) #else -chunk_sprintf (const char *name, char *fmt, ...) +chunk_sprintf(const char *name, char *fmt, ...) #endif { size_t len; @@ -683,13 +683,13 @@ chunk_sprintf (const char *name, char *fmt, ...) /* Calculate formatted string length */ va_start(args, fmt); len = vsnprintf(NULL, 0, fmt, args) + 1; - va_end (args); + va_end(args); /* get chunk */ #ifdef CHUNK_DIAG - str = _chunk (len, name, file, line); + str = _chunk(len, name, file, line); #else - str = chunk (len, name); + str = chunk(len, name); #endif if (str == NULL) return NULL; @@ -697,7 +697,7 @@ chunk_sprintf (const char *name, char *fmt, ...) /* Format string */ va_start(args, fmt); len = vsnprintf(str, len, fmt, args); - va_end (args); + va_end(args); return str; } @@ -756,7 +756,7 @@ chunk_check(FILE *fout, const char *name) if (chunk_grp) { tmp = chunk_grp; do { - if (!strcmp (tmp->cg_name, name)) { + if (!strcmp(tmp->cg_name, name)) { grp = tmp; break; } diff --git a/datastore/keyvalue/clixon_qdb.c b/datastore/keyvalue/clixon_qdb.c index d0b72b63..b80daf71 100644 --- a/datastore/keyvalue/clixon_qdb.c +++ b/datastore/keyvalue/clixon_qdb.c @@ -436,7 +436,7 @@ db_regexp(char *file, goto quit; } pair = &newpairs[npairs]; - memset (pair, 0, sizeof(*pair)); + memset(pair, 0, sizeof(*pair)); pair->dp_key = chunk_sprintf(label, "%s", key); if (regexp) @@ -451,7 +451,7 @@ db_regexp(char *file, } if ( ! noval) { if (vlen){ - pair->dp_val = chunkdup (val, vlen, label); + pair->dp_val = chunkdup(val, vlen, label); if (pair->dp_val == NULL) { clicon_err(OE_DB, errno, "%s: chunkdup", __FUNCTION__); goto quit; diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 05432227..25cc653c 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -81,6 +81,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 311702b5..5b5789dd 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -46,6 +46,8 @@ static const map_str2int atmap[] = { {NULL, -1} }; * @endcode + * @see clicon_int2str + * @see clicon_str2int */ struct map_str2int{ char *ms_str; diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h new file mode 100644 index 00000000..c045237d --- /dev/null +++ b/lib/clixon/clixon_xpath.h @@ -0,0 +1,95 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + */ +#ifndef _CLIXON_XPATH_H +#define _CLIXON_XPATH_H + +/* + * Types + */ +enum xp_op{ + XO_AND, + XO_OR, + XO_DIV, + XO_MOD, + XO_ADD, + XO_MULT, + XO_SUB, + XO_EQ, + XO_NE, + XO_GE, + XO_LE, + XO_LT, + XO_GT, + XO_UNION, +}; + +/* Axis specifiers according to https://www.w3.org/TR/xpath-10/#NT-AxisName */ +enum axis_type{ + A_NAN = 0, /* Not set */ + A_ANCESTOR, + A_ANCESTOR_OR_SELF, + A_ATTRIBUTE, + A_CHILD, + A_DESCENDANT, + A_DESCENDANT_OR_SELF, + A_FOLLOWING, + A_FOLLOWING_SIBLING, + A_NAMESPACE, + A_PARENT, + A_PRECEEDING, + A_PRECEEDING_SIBLING, + A_SELF, + A_ROOT /* XXX Not in https://www.w3.org/TR/xpath-10 */ +}; + +/* + * Variables + */ +extern const map_str2int xpopmap[]; + +/* + * Prototypes + */ +#if defined(__GNUC__) && __GNUC__ >= 3 +int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); +int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); +#else +int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...); +int xpath_vec_bool(cxobj *xcur, char *format, ...); +#endif +int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp); + +#endif /* _CLIXON_XPATH_H */ diff --git a/lib/clixon/clixon_xpath_ctx.h b/lib/clixon/clixon_xpath_ctx.h new file mode 100644 index 00000000..03854495 --- /dev/null +++ b/lib/clixon/clixon_xpath_ctx.h @@ -0,0 +1,103 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + * This file defines XPATH contexts using in traversing the XPATH parse tree. + */ +#ifndef _CLIXON_XPATH_CTX_H +#define _CLIXON_XPATH_CTX_H + +/* + * Types + */ + +/*! XPATH expression type + * An expression is evaluated to yield an object, which has one of the following four basic types: + * node-set (an unordered collection of nodes without duplicates) + * boolean (true or false) + * number (a floating-point number) + * string (a sequence of UCS characters) + */ +enum xp_objtype{ + XT_NODESET, + XT_BOOL, + XT_NUMBER, + XT_STRING +}; + +/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the context is + * determined for XPath expressions used in XSLT and XPointer respectively. The context consists of: + * a node (the context node) + * a pair of non-zero positive integers (the context position and the context size) + * a set of variable bindings + * a function library + * the set of namespace declarations in scope for the expression + + * For each node in the node-set to be filtered, the PredicateExpr is + * evaluated with that node as the context node, with the number of nodes + * in the node-set as the context size, and with the proximity position + * of the node in the node-set with respect to the axis as the context + * position; if PredicateExpr evaluates to true for that node, the node + * is included in the new node-set; otherwise, it is not included. + */ +struct xp_ctx{ + enum xp_objtype xc_type; + cxobj **xc_nodeset; /* if type XT_NODESET */ + size_t xc_size; /* Length of nodeset */ + int xc_bool; /* if xc_type XT_BOOL */ + double xc_number; /* if xc_type XT_NUMBER */ + char *xc_string; /* if xc_type XT_STRING */ + cxobj *xc_node; /* Node in nodeset XXX maybe not needed*/ + cxobj *xc_initial; /* RFC 7960 10.1.1 extension: for current() */ + int xc_descendant; /* // */ + /* NYI: a set of variable bindings, set of namespace declarations */ +}; +typedef struct xp_ctx xp_ctx; + +/* + * Variables + */ +extern const map_str2int ctxmap[]; + +/* + * Prototypes + */ +int ctx_free(xp_ctx *xc); +xp_ctx *ctx_dup(xp_ctx *xc); +int ctx_nodeset_replace(xp_ctx *xc, cxobj **vec, size_t veclen); +int ctx_print(cbuf *cb, int id, xp_ctx *xc, char *str); +int ctx2boolean(xp_ctx *xc); +int ctx2string(xp_ctx *xc, char **str0); +int ctx2number(xp_ctx *xc, double *n0); + +#endif /* _CLIXON_XPATH_CTX_H */ diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 8da9b087..931bd41d 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -72,18 +72,19 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \ clixon_json.c clixon_yang.c clixon_yang_type.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ - clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_netconf_lib.c + clixon_xsl.c clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ + clixon_xml_db.c clixon_netconf_lib.c YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ - lex.clixon_json_parse.o clixon_json_parse.tab.o - + lex.clixon_json_parse.o clixon_json_parse.tab.o \ + lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o # Extra applications. Utilities, unit testings. Not installed. APPSRC = clixon_util_xml.c APPSRC += clixon_util_json.c APPSRC += clixon_util_yang.c -APPSRC += clixon_util_xsl.c +APPSRC += clixon_util_xpath.c APPS = $(APPSRC:.c=) @@ -107,9 +108,11 @@ clean: rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co] rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co] rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co] + rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co] rm -f lex.clixon_xml_parse.c rm -f lex.clixon_yang_parse.c rm -f lex.clixon_json_parse.c + rm -f lex.clixon_xpath_parse.c ############################################################################# # Implicit rules for lex and yacc. @@ -159,6 +162,18 @@ clixon_json_parse.tab.c clixon_json_parse.tab.h: clixon_json_parse.y lex.clixon_json_parse.o : lex.clixon_json_parse.c clixon_json_parse.tab.h $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $< +# xpath parser +lex.clixon_xpath_parse.c : clixon_xpath_parse.l clixon_xpath_parse.tab.h + $(LEX) -Pclixon_xpath_parse clixon_xpath_parse.l # -d is debug + +clixon_xpath_parse.tab.c clixon_xpath_parse.tab.h: clixon_xpath_parse.y + $(YACC) -l -d -p clixon_xpath_parse clixon_xpath_parse.y # -t is debug + mv y.tab.c clixon_xpath_parse.tab.c + mv y.tab.h clixon_xpath_parse.tab.h + +lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h + $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $< + # APPS clixon_util_xml: clixon_util_xml.c $(MYLIB) $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ @@ -169,7 +184,7 @@ clixon_util_json: clixon_util_json.c $(MYLIB) clixon_util_yang: clixon_util_yang.c $(MYLIB) $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ -clixon_util_xsl: clixon_util_xsl.c $(MYLIB) +clixon_util_xpath: clixon_util_xpath.c $(MYLIB) $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ distclean: clean diff --git a/lib/src/clixon_hash.c b/lib/src/clixon_hash.c index 4debe9c2..aa397d4d 100644 --- a/lib/src/clixon_hash.c +++ b/lib/src/clixon_hash.c @@ -52,24 +52,24 @@ * clicon_hash_t *hash = hash_init(); * * n = 234; - * hash_add (hash, "APA", &n, sizeof(n)); + * hash_add(hash, "APA", &n, sizeof(n)); * hash_dump(hash, stdout); * * puts("----"); * - * hash_add (hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1); + * hash_add(hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1); * puts((char *)hash_value(hash, "BEPA", NULL)); * hash_dump(hash, stdout); * * puts("----"); * * n = 33; - * hash_add (hash, "CEPA", &n, sizeof(n)); + * hash_add(hash, "CEPA", &n, sizeof(n)); * hash_dump(hash, stdout); * * puts("----"); * - * hash_del (hash, "APA"); + * hash_del(hash, "APA"); * hash_dump(hash, stdout); * * hash_free(hash); @@ -118,11 +118,11 @@ hash_init (void) { clicon_hash_t *hash; - if ((hash = (clicon_hash_t *)malloc (sizeof (clicon_hash_t) * HASH_SIZE)) == NULL){ + if ((hash = (clicon_hash_t *)malloc(sizeof(clicon_hash_t) * HASH_SIZE)) == NULL){ clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno)); return NULL; } - memset (hash, 0, sizeof(clicon_hash_t)*HASH_SIZE); + memset(hash, 0, sizeof(clicon_hash_t)*HASH_SIZE); return hash; } @@ -167,7 +167,7 @@ hash_lookup(clicon_hash_t *hash, h = hash[bkt]; if (h) { do { - if (!strcmp (h->h_key, key)) + if (!strcmp(h->h_key, key)) return h; h = NEXTQ(clicon_hash_t, h); } while (h != hash[bkt]); @@ -218,15 +218,15 @@ hash_add(clicon_hash_t *hash, clicon_hash_t new = NULL; /* If variable exist, don't allocate a new. just replace value */ - h = hash_lookup (hash, key); + h = hash_lookup(hash, key); if (h == NULL) { - if ((new = (clicon_hash_t)malloc (sizeof (*new))) == NULL){ + if ((new = (clicon_hash_t)malloc(sizeof(*new))) == NULL){ clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno)); goto catch; } - memset (new, 0, sizeof (*new)); + memset(new, 0, sizeof(*new)); - new->h_key = strdup (key); + new->h_key = strdup(key); if (new->h_key == NULL){ clicon_err(OE_UNIX, errno, "strdup: %s", strerror(errno)); goto catch; @@ -241,11 +241,11 @@ hash_add(clicon_hash_t *hash, clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno)); goto catch; } - memcpy (newval, val, vlen); + memcpy(newval, val, vlen); /* Free old value if existing variable */ if (h->h_val) - free (h->h_val); + free(h->h_val); h->h_val = newval; h->h_vlen = vlen; @@ -258,8 +258,8 @@ hash_add(clicon_hash_t *hash, catch: if (new) { if (new->h_key) - free (new->h_key); - free (new); + free(new->h_key); + free(new); } return NULL; @@ -279,15 +279,15 @@ hash_del(clicon_hash_t *hash, { clicon_hash_t h; - h = hash_lookup (hash, key); + h = hash_lookup(hash, key); if (h == NULL) return -1; DELQ(h, hash[hash_bucket(key)], clicon_hash_t); - free (h->h_key); - free (h->h_val); - free (h); + free(h->h_key); + free(h->h_val); + free(h); return 0; } diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 2386d561..655bc9f1 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -140,11 +140,11 @@ clicon_strjoin(int argc, len += 1; /* '\0' */ if ((str = malloc(len)) == NULL) return NULL; - memset (str, '\0', len); + memset(str, '\0', len); for (i = 0; i < argc; i++) { if (i != 0) - strncat (str, delim, len - strlen(str)); - strncat (str, argv[i], len - strlen(str)); + strncat(str, delim, len - strlen(str)); + strncat(str, argv[i], len - strlen(str)); } return str; } @@ -518,21 +518,21 @@ clicon_str2int(const map_str2int *mstab, */ #ifndef HAVE_STRNDUP char * -clicon_strndup (const char *str, - size_t len) +clicon_strndup(const char *str, + size_t len) { char *new; size_t slen; - slen = strlen (str); + slen = strlen(str); len = (len < slen ? len : slen); - new = malloc (len + 1); + new = malloc(len + 1); if (new == NULL) return NULL; new[len] = '\0'; - memcpy (new, str, len); + memcpy(new, str, len); return new; } diff --git a/lib/src/clixon_util_xpath.c b/lib/src/clixon_util_xpath.c new file mode 100644 index 00000000..7bc849de --- /dev/null +++ b/lib/src/clixon_util_xpath.c @@ -0,0 +1,231 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + +See https://www.w3.org/TR/xpath/ + + * Turn this on to get an xpath test program + * Usage: xpath [] + * read xpath on first line and xml on rest of lines from input + * Example compile: + gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen + * Example run: +echo "a\n" | xpath +*/ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clixon */ +#include + +static int +usage(char *argv0) +{ + fprintf(stderr, "usage:%s [options]\n" + "where options are\n" + "\t-h \t\tHelp\n" + "\t-D \tDebug\n" + "\t-f \tXML file\n" + "\t-p \tPrimary XPATH string\n" + "\t-i \t(optional) Initial XPATH string\n" + "and the following extra rules:\n" + "\tif -f is not given, XML input is expected on stdin\n" + "\tif -p is not given, is expected as the first line on stdin\n" + "This means that with no arguments, and XML is expected on stadin.\n", + argv0 + ); + exit(0); +} + +static int +ctx_print2(cbuf *cb, + xp_ctx *xc) +{ + int i; + + cprintf(cb, "%s:", (char*)clicon_int2str(ctxmap, xc->xc_type)); + switch (xc->xc_type){ + case XT_NODESET: + for (i=0; ixc_size; i++){ + cprintf(cb, "%d:", i); + clicon_xml2cbuf(cb, xc->xc_nodeset[i], 0, 0); + } + break; + case XT_BOOL: + cprintf(cb, "%s", xc->xc_bool?"true":"false"); + break; + case XT_NUMBER: + cprintf(cb, "%lf", xc->xc_number); + break; + case XT_STRING: + cprintf(cb, "%s", xc->xc_string); + break; + } + return 0; +} + +int +main(int argc, char **argv) +{ + int retval = -1; + char *argv0 = argv[0]; + int i; + cxobj **xv = NULL; + cxobj *x0 = NULL; + cxobj *x; + char c; + int len; + char *buf = NULL; + int ret; + int fd = 0; /* unless overriden by argv[1] */ + char *xpath = NULL; + char *xpath0 = NULL; + char *filename; + xp_ctx *xc = NULL; + cbuf *cb; + + clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR); + optind = 1; + opterr = 0; + while ((c = getopt(argc, argv, "hDf:p:i:")) != -1) + switch (c) { + case 'h': + usage(argv0); + break; + case 'D': + debug++; + break; + case 'f': /* XML file */ + filename = optarg; + if ((fd = open(filename, O_RDONLY)) < 0){ + clicon_err(OE_UNIX, errno, "open(%s)", argv[1]); + goto done; + } + break; + case 'p': /* Primary XPATH string */ + xpath = optarg; + break; + case 'i': /* Optional initial XPATH string */ + xpath0 = optarg; + break; + default: + usage(argv[0]); + break; + } + if (xpath==NULL){ + /* First read xpath */ + len = 1024; /* any number is fine */ + if ((buf = malloc(len)) == NULL){ + perror("pt_file malloc"); + return -1; + } + memset(buf, 0, len); + i = 0; + while (1){ + if ((ret = read(0, &c, 1)) < 0){ + perror("read"); + goto done; + } + if (ret == 0) + break; + if (c == '\n') + break; + if (len==i){ + if ((buf = realloc(buf, 2*len)) == NULL){ + fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); + return -1; + } + memset(buf+len, 0, len); + len *= 2; + } + buf[i++] = (char)(c&0xff); + } + xpath = buf; + } + /* + * If fd=0, then continue reading from stdin (after CR) + * If fd>0, reading from file opened as argv[1] + */ + if (xml_parse_file(fd, "", NULL, &x0) < 0){ + fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); + return -1; + } + /* If xpath0 given, position current x */ + if (xpath0){ + if ((x = xpath_first(x0, "%s", xpath0)) == NULL){ + fprintf(stderr, "Error: xpath0 returned NULL\n"); + return -1; + } + } + else + x = x0; + + /* Parse XML */ + if (xpath_vec_ctx(x, xpath, &xc) < 0) + return -1; + /* Print results */ + cb = cbuf_new(); + ctx_print2(cb, xc); + fprintf(stdout, "%s\n", cbuf_get(cb)); + retval = 0; + done: + if (cb) + cbuf_free(cb); + if (xc) + ctx_free(xc); + if (xv) + free(xv); + if (buf) + free(buf); + if (x0) + xml_free(x0); + if (fd > 0) + close(fd); + return retval; +} diff --git a/lib/src/clixon_util_xsl.c b/lib/src/clixon_util_xsl.c deleted file mode 100644 index b23d1c52..00000000 --- a/lib/src/clixon_util_xsl.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - ***** BEGIN LICENSE BLOCK ***** - - Copyright (C) 2009-2018 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 ***** - -See https://www.w3.org/TR/xpath/ - - * Turn this on to get an xpath test program - * Usage: xpath [] - * read xpath on first line and xml on rest of lines from input - * Example compile: - gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen - * Example run: -echo "a\n" | xpath -*/ - -#ifdef HAVE_CONFIG_H -#include "clixon_config.h" /* generated by config & autoconf */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* cligen */ -#include - -/* clixon */ -#include - - -static int -usage(char *argv0) -{ - fprintf(stderr, "usage:%s .\n\tInput on stdin\n", argv0); - exit(0); -} - -int -main(int argc, char **argv) -{ - int retval = -1; - int i; - cxobj **xv; - cxobj *x; - cxobj *xn; - size_t xlen = 0; - int c; - int len; - char *buf = NULL; - int ret; - - if (argc != 1){ - usage(argv[0]); - return -1; - } - /* First read xpath */ - len = 1024; /* any number is fine */ - if ((buf = malloc(len)) == NULL){ - perror("pt_file malloc"); - return -1; - } - memset(buf, 0, len); - i = 0; - while (1){ - if ((ret = read(0, &c, 1)) < 0){ - perror("read"); - goto done; - } - if (ret == 0) - break; - if (c == '\n') - break; - if (len==i){ - if ((buf = realloc(buf, 2*len)) == NULL){ - fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); - return -1; - } - memset(buf+len, 0, len); - len *= 2; - } - buf[i++] = (char)(c&0xff); - } - x = NULL; - if (xml_parse_file(0, "", NULL, &x) < 0){ - fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); - return -1; - } - if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0) - return -1; - if (xv){ - for (i=0; ix_value = strdup(x0->x_value)) == NULL){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 7e1b69e4..8a2b2aa7 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -424,13 +424,15 @@ xml_yang_validate_all(cxobj *xt, void *arg) { int retval = -1; - yang_stmt *ys; - yang_stmt *ytype; + yang_stmt *ys; /* yang node */ + yang_stmt *yc; /* yang child */ + char *xpath; /* if not given by argument (overide) use default link and !Node has a config sub-statement and it is false */ if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){ + /* Node-specific validation */ switch (ys->ys_keyword){ case Y_LEAF: /* fall thru */ @@ -438,20 +440,29 @@ xml_yang_validate_all(cxobj *xt, /* Special case if leaf is leafref, then first check against current xml tree */ - if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ - if (strcmp(ytype->ys_argument, "leafref") == 0){ - if (validate_leafref(xt, ytype) < 0) + if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ + if (strcmp(yc->ys_argument, "leafref") == 0){ + if (validate_leafref(xt, yc) < 0) goto done; } - else if (strcmp(ytype->ys_argument, "identityref") == 0){ - if (validate_identityref(xt, ys, ytype) < 0) + else if (strcmp(yc->ys_argument, "identityref") == 0){ + if (validate_identityref(xt, ys, yc) < 0) goto done; } } break; + case Y_MUST: /* RFC 7950 Sec 7.5.3 */ + break; default: break; } + /* "when" sub-node RFC 7950 Sec 7.21.5 */ + if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){ + xpath = yc->ys_argument; /* "when" has xpath argument */ + if (xpath_first(xt, "%s", xpath)) + ; + fprintf(stderr, "%s %s\n", __FUNCTION__, xpath); + } } retval = 0; done: @@ -1372,7 +1383,6 @@ xml_spec_populate(cxobj *x, return retval; } - /*! Translate from restconf api-path in cvv form to xml xpath * eg a/b=c -> a/[b=c] * @param[in] yspec Yang spec diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c new file mode 100644 index 00000000..96825c7d --- /dev/null +++ b/lib/src/clixon_xpath.c @@ -0,0 +1,1183 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#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_xpath_parse.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" + +/* + * Variables + */ +/* Mapping between XPATH operator string <--> int */ +const map_str2int xpopmap[] = { + {"and", XO_AND}, + {"or", XO_OR}, + {"div", XO_DIV}, + {"mod", XO_MOD}, + {"+", XO_ADD}, + {"*", XO_MULT}, + {"-", XO_SUB}, + {"=", XO_EQ}, + {"!=", XO_NE}, + {">=", XO_GE}, + {"<=", XO_LE}, + {"<", XO_LT}, + {">", XO_GT}, + {"|", XO_UNION}, + {NULL, -1} +}; + +/* Mapping between axis type string <--> int */ +static const map_str2int axismap[] = { + {"self", A_SELF}, + {"child", A_CHILD}, + {"parent", A_PARENT}, + {"root", A_ROOT}, + {"ancestor", A_ANCESTOR}, + {"descendant-or-self", A_DESCENDANT_OR_SELF}, + {NULL, -1} +}; + +static const map_str2int xpath_tree_map[] = { + {"expr", XP_EXP}, + {"andexpr", XP_AND}, + {"relexpr", XP_RELEX}, + {"addexpr", XP_ADD}, + {"unionexpr", XP_UNION}, + {"pathexpr", XP_PATHEXPR}, + {"locationpath", XP_LOCPATH}, + {"abslocpath", XP_ABSPATH}, + {"rellocpath", XP_RELLOCPATH}, + {"step", XP_STEP}, + {"nodetest", XP_NODE}, + {"nodetest fn", XP_NODE_FN}, + {"predicates", XP_PRED}, + {"primaryexpr", XP_PRI0}, + {"primaryexpr nr", XP_PRIME_NR}, + {"primaryexpr str", XP_PRIME_STR}, + {"primaryexpr fn", XP_PRIME_FN}, + {NULL, -1} +}; + +/* + * XPATH parse tree type + */ +/*! Print XPATH parse tree */ +static int +xpath_tree_print0(cbuf *cb, + xpath_tree *xs, + int level) +{ + cprintf(cb, "%*s%s:", level*3, "", clicon_int2str(xpath_tree_map, xs->xs_type)); + if (xs->xs_s0){ + cprintf(cb, "\"%s\" ", xs->xs_s0); + if (xs->xs_s1) + cprintf(cb,"\"%s\" ", xs->xs_s1); + } + cprintf(cb, "\n"); + if (xs->xs_c0) + xpath_tree_print0(cb, xs->xs_c0,level+1); + if (xs->xs_c1) + xpath_tree_print0(cb, xs->xs_c1, level+1); + return 0; +} + +static int +xpath_tree_print(cbuf *cb, + xpath_tree *xs) +{ + xpath_tree_print0(cb, xs, 0); + return 0; +} + +static int +xpath_tree_free( + xpath_tree *xs) +{ + if (xs->xs_s0) + free(xs->xs_s0); + if (xs->xs_s1) + free(xs->xs_s1); + if (xs->xs_c0) + xpath_tree_free(xs->xs_c0); + if (xs->xs_c1) + xpath_tree_free(xs->xs_c1); + free(xs); + return 0; +} + +/*! Make a nodetest + * @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN + * @retval 0 Match + * @retval 1 No match + * - node() is true for any node of any type whatsoever. + * - text() is true for any text node. + */ +static int +nodetest_eval(cxobj *x, + xpath_tree *xs) +{ + char *fn; + + if (xs->xs_type == XP_NODE){ + /* Namespaces is s0, name is s1 */ + if (strcmp(xs->xs_s1, "*")==0) + return 1; + else if (strcmp(xml_name(x), xs->xs_s1)==0) + return 1; + else + return 0; + } + else if (xs->xs_type == XP_NODE_FN){ + fn = xs->xs_s0; + if (strcmp(fn, "node")==0) + return 1; + else if (strcmp(fn, "text")==0) + return 1; + } + return 0; +} + +int +nodetest_recursive(cxobj *xn, + xpath_tree *nodetest, + int node_type, + uint16_t flags, + cxobj ***vec0, + size_t *vec0len) +{ + int retval = -1; + cxobj *xsub; + cxobj **vec = *vec0; + size_t veclen = *vec0len; + + xsub = NULL; + while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) { + if (nodetest_eval(xsub, nodetest) == 1){ + clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags)); + if (flags==0x0 || xml_flag(xsub, flags)) + if (cxvec_append(xsub, &vec, &veclen) < 0) + goto done; + // continue; /* Dont go deeper */ + } + if (nodetest_recursive(xsub, nodetest, node_type, flags, &vec, &veclen) < 0) + goto done; + } + retval = 0; + *vec0 = vec; + *vec0len = veclen; + done: + return retval; +} + +static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp); + +/*! Evaluate xpath step rule of an XML tree + * + * @param[in] xc0 Incoming context + * @param[in] xs XPATH node tree + * @param[out] xrp Resulting context + * + * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model]) + * is the principal node type and has an expanded-name equal to the expanded-name specified by the QName. + * - A node test * is true for any node of the principal node type. + * - node() is true for any node of any type whatsoever. + * - text() is true for any text node. + */ +static int +xp_eval_step(xp_ctx *xc0, + xpath_tree *xs, + xp_ctx **xrp) +{ + int retval = -1; + int i; + cxobj *x; + cxobj *xv; + cxobj *xp; + cxobj **vec = NULL; + size_t veclen = 0; + xpath_tree *nodetest = xs->xs_c0; + xp_ctx *xr0 = NULL; + xp_ctx *xc = NULL; + + /* Create new xc */ + if ((xc = ctx_dup(xc0)) == NULL) + goto done; + switch (xs->xs_int){ + case A_ANCESTOR: + break; + case A_ANCESTOR_OR_SELF: + break; + case A_ATTRIBUTE: /* principal node type is attribute */ + break; + case A_CHILD: + if (xc->xc_descendant){ + for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, &vec, &veclen) < 0) + goto done; + } + xc->xc_descendant = 0; + } + else{ + if (nodetest->xs_type==XP_NODE_FN && + nodetest->xs_s0 && + strcmp(nodetest->xs_s0,"current")==0){ + if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0) + goto done; + } + else for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + x = NULL; + while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { + /* xs->xs_c0 is nodetest */ + if (nodetest == NULL || nodetest_eval(x, nodetest)) + if (cxvec_append(x, &vec, &veclen) < 0) + goto done; + } + } + } + ctx_nodeset_replace(xc, vec, veclen); + break; + case A_DESCENDANT: + case A_DESCENDANT_OR_SELF: + for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, &vec, &veclen) < 0) + goto done; + } + ctx_nodeset_replace(xc, vec, veclen); + break; + case A_FOLLOWING: + break; + case A_FOLLOWING_SIBLING: + break; + case A_NAMESPACE: /* principal node type is namespace */ + break; + case A_PARENT: + if ((xr0 = malloc(sizeof(*xr0))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr0, 0, sizeof(*xr0)); + xr0->xc_initial = xc->xc_initial; + for (i=0; ixc_size; i++){ + x = xc->xc_nodeset[i]; + if ((xp = xml_parent(x)) != NULL) + if (cxvec_append(xp, &xr0->xc_nodeset, &xr0->xc_size) < 0) + goto done; + } + break; + case A_PRECEEDING: + break; + case A_PRECEEDING_SIBLING: + break; + case A_SELF: + xr0 = ctx_dup(xc); + break; + default: + clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int); + goto done; + break; + } + if (xr0) + *xrp = xr0; + if (xs->xs_c1){ + if (xp_eval(xc, xs->xs_c1, xrp) < 0) + goto done; + } + assert(*xrp); + retval = 0; + done: + if (xc) + ctx_free(xc); + return retval; +} + +/*! Evaluate xpath predicates rule + * + * pred -> pred expr + * @param[in] xc Incoming context + * @param[in] xs XPATH node tree + * @param[out] xrp Resulting context + * + * A predicate filters a node-set with respect to an axis to produce a new + * node-set. For each node in the node-set to be filtered, the PredicateExpr is + * evaluated with that node as the context node, with the number of nodes in + * the node-set as the context size, and with the proximity position of the node + * in the node-set with respect to the axis as the context position; if + * PredicateExpr evaluates to true for that node, the node is included in the + * new node-set; otherwise, it is not included. + * A PredicateExpr is evaluated by evaluating the Expr and converting the result + * to a boolean. If the result is a + * - number, the result will be converted to true if the number is equal to the + * context position and will be converted to false otherwise; + * - if the result is not a number, then the result will be converted as if by a + * call to the boolean function. + * Thus a location path para[3] is equivalent to para[position()=3]. + */ +static int +xp_eval_predicate(xp_ctx *xc, + xpath_tree *xs, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx *xr0 = NULL; + xp_ctx *xr1 = NULL; + xp_ctx *xrc = NULL; + int i; + cxobj *x; + xp_ctx *xcc; + + if (xs->xs_c0 == NULL){ /* empty */ + if ((xr0 = ctx_dup(xc)) == NULL) + goto done; + } + else{ /* eval previous predicates */ + if (xp_eval(xc, xs->xs_c0, &xr0) < 0) + goto done; + } + if (xs->xs_c1){ + /* Loop over each node in the nodeset */ + assert (xr0->xc_type == XT_NODESET); + if ((xr1 = malloc(sizeof(*xr1))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr1, 0, sizeof(*xr1)); + xr1->xc_type = XT_NODESET; + xr1->xc_node = xc->xc_node; + xr1->xc_initial = xc->xc_initial; + for (i=0; ixc_size; i++){ + x = xr0->xc_nodeset[i]; + /* Create new context */ + if ((xcc = malloc(sizeof(*xcc))) == NULL){ + clicon_err(OE_XML, errno, "malloc"); + goto done; + } + memset(xcc, 0, sizeof(*xcc)); + xcc->xc_type = XT_NODESET; + xcc->xc_initial = xc->xc_initial; + xcc->xc_node = x; + /* For each node in the node-set to be filtered, the PredicateExpr is + * evaluated with that node as the context node */ + if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0) + goto done; + if (xp_eval(xcc, xs->xs_c1, &xrc) < 0) + goto done; + if (xcc) + ctx_free(xcc); + if (xrc->xc_type == XT_NUMBER){ + /* If the result is a number, the result will be converted to true + if the number is equal to the context position */ + if ((int)xrc->xc_number == i) + if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0) + goto done; + } + else { + /* if PredicateExpr evaluates to true for that node, the node is + included in the new node-set */ + if (ctx2boolean(xrc)) + if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0) + goto done; + } + if (xrc) + ctx_free(xrc); + } + + } + assert(xr0||xr1); + if (xr1){ + *xrp = xr1; + xr1 = NULL; + } + else + if (xr0){ + *xrp = xr0; + xr0 = NULL; + } + retval = 0; + done: + assert(retval==0); + if (xr0) + ctx_free(xr0); + if (xr1) + ctx_free(xr1); + return retval; +} + +/*! Given two XPATH contexts, eval logical operations: or,and + * The logical operators convert their operands to booleans + * @param[in] xc1 Context of operand1 + * @param[in] xc2 Context of operand2 + * @param[in] op Relational operator + * @param[out] xrp Result context + * @retval 0 OK + * @retval -1 Error + */ +static int +xp_logop(xp_ctx *xc1, + xp_ctx *xc2, + enum xp_op op, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx *xr = NULL; + int b1; + int b2; + + if ((xr = malloc(sizeof(*xr))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr, 0, sizeof(*xr)); + xr->xc_initial = xc1->xc_initial; + xr->xc_type = XT_BOOL; + if ((b1 = ctx2boolean(xc1)) < 0) + goto done; + if ((b2 = ctx2boolean(xc2)) < 0) + goto done; + switch (op){ + case XO_AND: + xr->xc_bool = b1 && b2; + break; + case XO_OR: + xr->xc_bool = b1 || b2; + break; + default: + clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context", + __FUNCTION__, clicon_int2str(xpopmap,op)); + goto done; + } + *xrp = xr; + retval = 0; + done: + return retval; +} + +/*! Given two XPATH contexts, eval numeric operations: +-*,div,mod + * The numeric operators convert their operands to numbers as if by + * calling the number function. + * @param[in] xc1 Context of operand1 + * @param[in] xc2 Context of operand2 + * @param[in] op Relational operator + * @param[out] xrp Result context + * @retval 0 OK + * @retval -1 Error + */ +static int +xp_numop(xp_ctx *xc1, + xp_ctx *xc2, + enum xp_op op, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx *xr = NULL; + double n1; + double n2; + + if ((xr = malloc(sizeof(*xr))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr, 0, sizeof(*xr)); + xr->xc_initial = xc1->xc_initial; + xr->xc_type = XT_NUMBER; + if (ctx2number(xc1, &n1) < 0) + goto done; + if (ctx2number(xc2, &n2) < 0) + goto done; + if (isnan(n1) || isnan(n2)) + xr->xc_number = NAN; + else + switch (op){ + case XO_DIV: + xr->xc_number = n1/n2; + break; + case XO_MOD: + xr->xc_number = ((int)n1)%((int)n2); + break; + case XO_ADD: + xr->xc_number = n1+n2; + break; + case XO_MULT: + xr->xc_number = n1*n2; + break; + case XO_SUB: + xr->xc_number = n1-n2; + break; + default: + clicon_err(OE_UNIX, errno, "Invalid operator %s in this context", + clicon_int2str(xpopmap,op)); + goto done; + } + *xrp = xr; + retval = 0; + done: + return retval; +} + +/*! Given two XPATH contexts, eval relational operations: <>= + * A RelationalExpr is evaluated by comparing the objects that result from + * evaluating the two operands. + * This is covered: + * (a) Both are INTs, BOOLs, STRINGs. Result type is boolean + * (b) Both are nodesets and one is empty. Result type is boolean. + * (c) One is nodeset and other is INT or STRING. Result type is nodeset + * (d) All others (eg two nodesets, BOOL+STRING) are not supported. + * Op is = EQ + * From XPATH 1.0 standard, the evaluation has three variants: + * (1) comparisons that involve node-sets are defined in terms of comparisons that + * do not involve node-sets; this is defined uniformly for =, !=, <=, <, >= and >. + * (2) comparisons that do not involve node-sets are defined for = and !=. + * (3) comparisons that do not involve node-sets are defined for <=, <, >= and >. + * @param[in] xc1 Context of operand1 + * @param[in] xc2 Context of operand2 + * @param[in] op Relational operator + * @param[out] xrp Result context + * @retval 0 OK + * @retval -1 Error + */ +static int +xp_relop(xp_ctx *xc1, + xp_ctx *xc2, + enum xp_op op, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx *xr = NULL; + xp_ctx *xc; + cxobj *x; + int i; + int j; + int b; + char *s1; + char *s2; + int reverse = 0; + double n1, n2; + + if ((xr = malloc(sizeof(*xr))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr, 0, sizeof(*xr)); + xr->xc_initial = xc1->xc_initial; + xr->xc_type = XT_BOOL; + if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */ + switch (xc1->xc_type){ + case XT_NODESET: + /* If both are node-sets, then it is true iff the string value of a + node in the first node-set and in the second node-set is true */ + for (i=0; ixc_size; i++){ + if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){ + xr->xc_bool = 0; + goto ok; + } + for (j=0; jxc_size; j++){ + if ((s2 = xml_body(xc2->xc_nodeset[j])) == NULL){ + xr->xc_bool = 0; + goto ok; + } + switch(op){ + case XO_EQ: + xr->xc_bool = (strcmp(s1, s2)==0); + break; + case XO_NE: + xr->xc_bool = (strcmp(s1, s2)!=0); + break; + case XO_GE: + xr->xc_bool = (strcmp(s1, s2)>=0); + break; + case XO_LE: + xr->xc_bool = (strcmp(s1, s2)<=0); + break; + case XO_LT: + xr->xc_bool = (strcmp(s1, s2)<0); + break; + case XO_GT: + xr->xc_bool = (strcmp(s1, s2)>0); + break; + default: + clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op)); + goto done; + break; + } + } + } + break; + case XT_BOOL: + xr->xc_bool = (xc1->xc_bool == xc2->xc_bool); + break; + case XT_NUMBER: + xr->xc_bool = (xc1->xc_number == xc2->xc_number); + break; + case XT_STRING: + xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0); + break; + } + } + else if (xc1->xc_type != XT_NODESET && + xc2->xc_type != XT_NODESET){ + clicon_err(OE_XML, 0, "Mixed types not supported, %d %d", xc1->xc_type, xc2->xc_type); + goto done; + } + else{ /* one is nodeset, ie (1) above */ + if (xc2->xc_type == XT_NODESET){ + xc = xc2; + xc2 = xc1; + xc1 = xc; + reverse++; /* reverse */ + } + /* xc1 is nodeset + * xc2 is something else */ + switch (xc2->xc_type){ + case XT_BOOL: + /* comparison on the boolean and the result of converting the + node-set to a boolean using the boolean function is true. */ + b = ctx2boolean(xc); + switch(op){ + case XO_EQ: + xr->xc_bool = (b == xc2->xc_bool); + break; + case XO_NE: + xr->xc_bool = (b != xc2->xc_bool); + break; + default: + clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and bool", clicon_int2str(xpopmap,op)); + goto done; + break; + } /* switch op */ + break; + case XT_STRING: + /* If one object to be compared is a node-set and the + other is a string, then the comparison will be true if and only + if there is a node in the node-set such that the result of + performing the comparison on the string-value of the node and + the other string is true.*/ + for (i=0; ixc_size; i++){ + x = xc1->xc_nodeset[i]; /* node in nodeset */ + s1 = xml_body(x); + s2 = xc2->xc_string; + switch(op){ + case XO_EQ: + xr->xc_bool = (strcmp(s1, s2)==0); + break; + case XO_NE: + xr->xc_bool = (strcmp(s1, s2)); + break; + default: + clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op)); + goto done; + break; + } + } + break; + case XT_NUMBER: + for (i=0; ixc_size; i++){ + x = xc1->xc_nodeset[i]; /* node in nodeset */ + if (sscanf(xml_body(x), "%lf", &n1) != 1) + n1 = NAN; + n2 = xc2->xc_number; + switch(op){ + case XO_EQ: + xr->xc_bool = (n1 == n2); + break; + case XO_NE: + xr->xc_bool = (n1 != n2); + break; + case XO_GE: + xr->xc_bool = reverse?(n2 >= n1):(n1 >= n2); + break; + case XO_LE: + xr->xc_bool = reverse?(n2 <= n1):(n1 <= n2); + break; + case XO_LT: + xr->xc_bool = reverse?(n2 < n1):(n1 < n2); + break; + case XO_GT: + xr->xc_bool = reverse?(n2 > n1):(n1 > n2); + break; + default: + clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op)); + goto done; + break; + } + } + break; + default: + clicon_err(OE_XML, 0, "Type %d not supported", xc2->xc_type); + } /* switch type */ + } + ok: + *xrp = xr; + retval = 0; + done: + return retval; +} + +/*! Given two XPATH contexts, eval union operation + * Both operands must be nodesets, otherwise empty nodeset is returned + * @param[in] xc1 Context of operand1 + * @param[in] xc2 Context of operand2 + * @param[in] op Relational operator + * @param[out] xrp Result context + * @retval 0 OK + * @retval -1 Error + */ +static int +xp_union(xp_ctx *xc1, + xp_ctx *xc2, + enum xp_op op, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx *xr = NULL; + int i; + + if (op != XO_UNION){ + clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context", + __FUNCTION__, clicon_int2str(xpopmap,op)); + goto done; + } + if ((xr = malloc(sizeof(*xr))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr, 0, sizeof(*xr)); + xr->xc_initial = xc1->xc_initial; + xr->xc_type = XT_NODESET; + + for (i=0; ixc_size; i++) + if (cxvec_append(xc1->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0) + goto done; + for (i=0; ixc_size; i++){ + if (cxvec_append(xc2->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0) + goto done; + } + *xrp = xr; + retval = 0; + done: + return retval; +} + + +/*! Evaluate an XPATH on an XML tree + + * The initial sequence of steps selects a set of nodes relative to a context node. + * Each node in that set is used as a context node for the following step. + * @param[in] xc Incoming context + * @param[in] xs XPATH node tree + * @param[out] xrp Resulting context + */ +static int +xp_eval(xp_ctx *xc, + xpath_tree *xs, + xp_ctx **xrp) + +{ + int retval = -1; + cxobj *x; + xp_ctx *xr0 = NULL; + xp_ctx *xr1 = NULL; + xp_ctx *xr2 = NULL; + int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */ + + assert(xc->xc_initial); + if (debug){ + cbuf *cb; + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + ctx_print(cb, +2, xc, (char*)clicon_int2str(xpath_tree_map, xs->xs_type)); + clicon_debug(1, "%s", cbuf_get(cb)); + cbuf_free(cb); + } + /* Pre-actions before check first child c0 + */ + switch (xs->xs_type){ + case XP_RELLOCPATH: + if (xs->xs_int == A_DESCENDANT_OR_SELF) + xc->xc_descendant = 1; /* XXX need to set to 0 in sub */ + break; + case XP_ABSPATH: + /* Set context node to top node, and nodeset to that node only */ + x = xc->xc_node; + while (xml_parent(x) != NULL) + x = xml_parent(x); + // xc->xc_node = x; + xc->xc_nodeset[0] = x; + xc->xc_size=1; + /* // is short for /descendant-or-self::node()/ */ + if (xs->xs_int == A_DESCENDANT_OR_SELF) + xc->xc_descendant = 1; /* XXX need to set to 0 in sub */ + + break; + case XP_STEP: /* XP_NODE is first argument -not called explicitly */ + if (xp_eval_step(xc, xs, xrp) < 0) + goto done; + goto ok; + break; + case XP_PRED: + if (xp_eval_predicate(xc, xs, xrp) < 0) + goto done; + goto ok; + break; + default: + break; + } + /* Eval first child c0 + */ + if (xs->xs_c0){ + if (xp_eval(xc, xs->xs_c0, &xr0) < 0) + goto done; + } + /* Actions between first and second child + */ + switch (xs->xs_type){ + case XP_EXP: + break; + case XP_AND: + break; + case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */ + break; + case XP_ADD: /* combine mult and add ops */ + break; + case XP_UNION: + break; + case XP_PATHEXPR: + break; + case XP_LOCPATH: + break; + case XP_ABSPATH: + use_xr0++; + /* Special case, no c0 or c1, single "/" */ + if (xs->xs_c0 == NULL){ + if ((xr0 = malloc(sizeof(*xr0))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr0, 0, sizeof(*xr0)); + xr0->xc_initial = xc->xc_initial; + xr0->xc_type = XT_NODESET; + x = NULL; + while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) { + if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0) + goto done; + break; + } + } + break; + case XP_RELLOCPATH: + use_xr0++; + if (xs->xs_int == A_DESCENDANT_OR_SELF) + xc->xc_descendant = 1; /* XXX need to set to 0 in sub */ + break; + case XP_NODE: + break; + case XP_NODE_FN: + break; + case XP_PRI0: + break; + case XP_PRIME_NR: /* primaryexpr -> [] */ + if ((xr0 = malloc(sizeof(*xr0))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr0, 0, sizeof(*xr0)); + xr0->xc_initial = xc->xc_initial; + xr0->xc_type = XT_NUMBER; + xr0->xc_number = xs->xs_double; + break; + case XP_PRIME_STR: + if ((xr0 = malloc(sizeof(*xr0))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xr0, 0, sizeof(*xr0)); + xr0->xc_initial = xc->xc_initial; + xr0->xc_type = XT_STRING; + xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL; + break; + case XP_PRIME_FN: + break; + default: + break; + } + /* Eval second child c0 + * Note, some operators 8like locationpath, need transitive context (use_xr0) + */ + if (xs->xs_c1) + if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, &xr1) < 0) + goto done; + /* Actions after second child + */ + if (xs->xs_c1) + switch (xs->xs_type){ + case XP_AND: /* combine and and or ops */ + if (xp_logop(xr0, xr1, xs->xs_int, &xr2) < 0) + goto done; + break; + case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */ + if (xp_relop(xr0, xr1, xs->xs_int, &xr2) < 0) + goto done; + break; + case XP_ADD: /* combine mult and add ops */ + if (xp_numop(xr0, xr1, xs->xs_int, &xr2) < 0) + goto done; + break; + case XP_UNION: /* combine and and or ops */ + if (xp_union(xr0, xr1, xs->xs_int, &xr2) < 0) + goto done; + default: + break; + } + xc->xc_descendant = 0; + assert(xr0||xr1||xr2); + if (xr2){ + *xrp = xr2; + xr2 = NULL; + } + else if (xr1){ + *xrp = xr1; + xr1 = NULL; + } + else + if (xr0){ + *xrp = xr0; + xr0 = NULL; + } + ok: + if (debug){ + cbuf *cb; + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + ctx_print(cb, -2, *xrp, (char*)clicon_int2str(xpath_tree_map, xs->xs_type)); + clicon_debug(1, "%s", cbuf_get(cb)); + cbuf_free(cb); + } + retval = 0; + done: + if (xr2) + ctx_free(xr2); + if (xr1) + ctx_free(xr1); + if (xr0) + ctx_free(xr0); + return retval; +} + +/*! Given XML tree and xpath, returns xpath context + + * @param[in] xcur XML-tree where to search + * @param[in] xpath String with XPATH 1.0 syntax + * @param[out] xrp Return XPATH context + * @retval 0 OK + * @retval -1 Error + */ +int +xpath_vec_ctx(cxobj *xcur, + char *xpath, + xp_ctx **xrp) +{ + int retval = -1; + xp_ctx xc = {0,}; + struct clicon_xpath_yacc_arg xy = {0,}; + + xy.xy_parse_string = xpath; + xy.xy_name = "xpath parser"; + xy.xy_linenum = 1; + if (xpath_scan_init(&xy) < 0) + goto done; + if (xpath_parse_init(&xy) < 0) + goto done; + if (clixon_xpath_parseparse(&xy) != 0) { /* yacc returns 1 on error */ + clicon_log(LOG_NOTICE, "XPATH error: on line %d", xy.xy_linenum); + if (clicon_errno == 0) + clicon_err(OE_XML, 0, "XPATH parser error with no error code (should not happen)"); + goto done; + } + if (debug){ + cbuf *cb = cbuf_new(); + xpath_tree_print(cb, xy.xy_top); + clicon_debug(1, "xpath parse tree:\n%s", cbuf_get(cb)); + cbuf_free(cb); + } + xc.xc_type = XT_NODESET; + xc.xc_node = xcur; + xc.xc_initial = xcur; + if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0) + goto done; + if (xp_eval(&xc, xy.xy_top, xrp) < 0) + goto done; + if (xc.xc_nodeset) + free(xc.xc_nodeset); + /* done: */ + xpath_parse_exit(&xy); + xpath_scan_exit(&xy); + retval = 0; + done: + if (xy.xy_top) + xpath_tree_free(xy.xy_top); + return retval; +} + +/*! Given XML tree and xpath, returns nodeset as xml node vector + * If result is not nodeset, return empty nodeset + * @param[in] xcur xml-tree where to search + * @param[in] xpath stdarg string with XPATH 1.0 syntax + * @param[out] vec vector of xml-trees. Vector must be free():d after use + * @param[out] veclen returns length of vector in return value + * @retval 0 OK + * @retval -1 Error + */ +int +xpath_vec_nodeset(cxobj *xcur, + char *format, + cxobj ***vec, + size_t *veclen, + ...) +{ + int retval = -1; + va_list ap; + size_t len; + char *xpath = NULL; + xp_ctx *xr = NULL; + + va_start(ap, veclen); + len = vsnprintf(NULL, 0, format, ap); + va_end(ap); + /* allocate a message string exactly fitting the message length */ + if ((xpath = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + /* second round: compute write message from reason and args */ + va_start(ap, veclen); + if (vsnprintf(xpath, len+1, format, ap) < 0){ + clicon_err(OE_UNIX, errno, "vsnprintf"); + va_end(ap); + goto done; + } + va_end(ap); + if (xpath_vec_ctx(xcur, xpath, &xr) < 0) + goto done; + if (xr && xr->xc_type == XT_NODESET){ + *vec = xr->xc_nodeset; + xr->xc_nodeset = NULL; + *veclen = xr->xc_size; + } + retval = 0; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return retval; +} + +/*! Given XML tree and xpath, returns boolean + * @param[in] xcur xml-tree where to search + * @param[in] xpath stdarg string with XPATH 1.0 syntax + * @retval 1 True + * @retval 0 False + * @retval -1 Error + */ +int +xpath_vec_bool(cxobj *xcur, + char *format, + ...) +{ + int retval = -1; + va_list ap; + size_t len; + char *xpath = NULL; + xp_ctx *xr = NULL; + + va_start(ap, format); + len = vsnprintf(NULL, 0, format, ap); + va_end(ap); + /* allocate a message string exactly fitting the message length */ + if ((xpath = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + /* second round: compute write message from reason and args */ + va_start(ap, format); + if (vsnprintf(xpath, len+1, format, ap) < 0){ + clicon_err(OE_UNIX, errno, "vsnprintf"); + va_end(ap); + goto done; + } + va_end(ap); + if (xpath_vec_ctx(xcur, xpath, &xr) < 0) + goto done; + if (xr) + retval = ctx2boolean(xr); + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return retval; +} + diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c new file mode 100644 index 00000000..39221a37 --- /dev/null +++ b/lib/src/clixon_xpath_ctx.c @@ -0,0 +1,294 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 + * This file defines XPATH contexts using in traversing the XPATH parse tree. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#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_xpath_parse.h" +#include "clixon_xpath_ctx.h" + +/* + * Variables + */ +const map_str2int ctxmap[] = { + {"nodeset", XT_NODESET}, + {"bool", XT_BOOL}, + {"number", XT_NUMBER}, + {"string", XT_STRING}, + {NULL, -1} +}; + +/*! Free xpath context */ +int +ctx_free(xp_ctx *xc) +{ + if (xc->xc_nodeset) + free(xc->xc_nodeset); + if (xc->xc_string) + free(xc->xc_string); + free(xc); + return 0; +} + +/*! Duplicate xpath context */ +xp_ctx * +ctx_dup(xp_ctx *xc0) +{ + static xp_ctx *xc = NULL; + + if ((xc = malloc(sizeof(*xc))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(xc, 0, sizeof(*xc)); + *xc = *xc0; + if (xc0->xc_size){ + if ((xc->xc_nodeset = calloc(xc0->xc_size, sizeof(cxobj*))) == NULL){ + clicon_err(OE_UNIX, errno, "calloc"); + goto done; + } + memcpy(xc->xc_nodeset, xc0->xc_nodeset, xc->xc_size*sizeof(cxobj*)); + } + if (xc0->xc_string) + if ((xc->xc_string = strdup(xc0->xc_string)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + done: + return xc; +} + +/*! Print XPATH context */ +int +ctx_print(cbuf *cb, + int id, + xp_ctx *xc, + char *str) +{ + static int ident = 0; + int i; + + if (id<0) + ident += id; + cprintf(cb, "%*s%s ", ident, "", str?str:""); + if (id>0) + ident += id; + if (xc){ + cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type)); + switch (xc->xc_type){ + case XT_NODESET: + for (i=0; ixc_size; i++) + cprintf(cb, "%s ", xml_name(xc->xc_nodeset[i])); + break; + case XT_BOOL: + cprintf(cb, "%s", xc->xc_bool?"true":"false"); + break; + case XT_NUMBER: + cprintf(cb, "%lf", xc->xc_number); + break; + case XT_STRING: + cprintf(cb, "%s", xc->xc_string); + break; + } + } + return 0; +} + +/*! Convert xpath context to boolean according to boolean() function in XPATH spec + * @param[in] xc XPATH context + * @retval 0 False + * @retval 1 True + * a number is true if and only if it is neither positive or negative zero nor NaN + * a node-set is true if and only if it is non-empty + * a string is true if and only if its length is non-zero + * an object of a type other than the four basic types is converted to a boolean + * in a way that is dependent on that type + */ +int +ctx2boolean(xp_ctx *xc) +{ + int b; + switch (xc->xc_type){ + case XT_NODESET: + b = (xc->xc_size != 0); + break; + case XT_BOOL: + b = xc->xc_bool; + break; + case XT_NUMBER: + b = (xc->xc_number != 0.0 && xc->xc_number != NAN); + break; + case XT_STRING: + b = (xc->xc_string && strlen(xc->xc_string)); + break; + } + return b; +} + +/*! Convert xpath context to string according to string() function in XPATH spec + * @param[in] xc XPATH context + * @param[out] str0 Malloced result string + * @retval 0 OK + * @retval -1 Error + * @note string malloced. + */ +int +ctx2string(xp_ctx *xc, + char **str0) +{ + int retval = -1; + char *str = NULL; + int len; + char *b; + + switch (xc->xc_type){ + case XT_NODESET: + if (xc->xc_size && (b = xml_body(xc->xc_nodeset[0]))){ + if ((str = strdup(b)) == NULL){ + clicon_err(OE_XML, errno, "strdup"); + goto done; + } + } + else + if ((str = strdup("")) == NULL){ + clicon_err(OE_XML, errno, "strdup"); + goto done; + } + break; + case XT_BOOL: + if ((str = strdup(xc->xc_bool == 0?"false":"true")) == NULL){ + clicon_err(OE_XML, errno, "strdup"); + goto done; + } + break; + case XT_NUMBER: + len = snprintf(NULL, 0, "%0lf", xc->xc_number); + len++; + if ((str = malloc(len)) == NULL){ + clicon_err(OE_XML, errno, "malloc"); + goto done; + } + snprintf(str, len, "%0lf", xc->xc_number); + break; + case XT_STRING: + if ((str = strdup(xc->xc_string)) == NULL){ + clicon_err(OE_XML, errno, "strdup"); + goto done; + } + break; + } + *str0 = str; + retval = 0; + done: + return retval; +} + +/*! Convert xpath context to number according to number() function in XPATH spec + * @param[in] xc XPATH context + * @param[out] n0 Floating point or NAN + * @retval 0 OK + * @retval -1 Error + */ +int +ctx2number(xp_ctx *xc, + double *n0) +{ + int retval = -1; + char *str = NULL; + double n; + + switch (xc->xc_type){ + case XT_NODESET: + if (ctx2string(xc, &str) < 0) + goto done; + if (sscanf(str, "%lf",&n) != 1) + n = NAN; + break; + case XT_BOOL: + n = (double)xc->xc_bool; + break; + case XT_NUMBER: + n = xc->xc_number; + break; + case XT_STRING: + if (sscanf(xc->xc_string, "%lf",&n) != 1) + n = NAN; + break; + } + *n0 = n; + retval = 0; + done: + if (str) + free(str); + return retval; +} + +/*! Replace a nodeset of a XPATH context with a new nodeset + */ +int +ctx_nodeset_replace(xp_ctx *xc, + cxobj **vec, + size_t veclen) +{ + if (xc->xc_nodeset) + free(xc->xc_nodeset); + xc->xc_nodeset = vec; + xc->xc_size = veclen; + return 0; +} + diff --git a/lib/src/clixon_xpath_parse.h b/lib/src/clixon_xpath_parse.h new file mode 100644 index 00000000..1affce3c --- /dev/null +++ b/lib/src/clixon_xpath_parse.h @@ -0,0 +1,101 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + */ +#ifndef _CLIXON_XPATH_PARSE_H_ +#define _CLIXON_XPATH_PARSE_H_ + +/* + * Types + */ +/* used as non-terminal type in yacc rules */ +enum xp_type{ + XP_EXP, + XP_AND, + XP_RELEX, + XP_ADD, + XP_UNION, + XP_PATHEXPR, + XP_LOCPATH, + XP_ABSPATH, + XP_RELLOCPATH, + XP_STEP, + XP_NODE, + XP_NODE_FN, + XP_PRED, + XP_PRI0, + XP_PRIME_NR, + XP_PRIME_STR, + XP_PRIME_FN, +}; + +/*! XPATH Parsing generates a tree of nodes that is later traversed + */ +struct xpath_tree{ + enum xp_type xs_type; + int xs_int; + double xs_double; + char *xs_s0; + char *xs_s1; + struct xpath_tree *xs_c0; /* child 0 */ + struct xpath_tree *xs_c1; /* child 1 */ +}; +typedef struct xpath_tree xpath_tree; + +struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */ + const char *xy_name; /* Name of syntax (for error string) */ + int xy_linenum; /* Number of \n in parsed buffer */ + char *xy_parse_string; /* original (copy of) parse string */ + void *xy_lexbuf; /* internal parse buffer from lex */ + xpath_tree *xy_top; +}; + +/* + * Variables + */ +extern char *clixon_xpath_parsetext; + +/* + * Prototypes + */ +int xpath_scan_init(struct clicon_xpath_yacc_arg *jy); +int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy); + +int xpath_parse_init(struct clicon_xpath_yacc_arg *jy); +int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy); + +int clixon_xpath_parselex(void *); +int clixon_xpath_parseparse(void *); +void clixon_xpath_parseerror(void *, char*); + +#endif /* _CLIXON_XPATH_PARSE_H_ */ diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l new file mode 100644 index 00000000..5ac54644 --- /dev/null +++ b/lib/src/clixon_xpath_parse.l @@ -0,0 +1,174 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + */ + +%{ + +#include "clixon_config.h" + +#include +#include +#include +#include +#include + +#include "clixon_xpath_parse.tab.h" /* generated */ + +#include + +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_log.h" +#include "clixon_string.h" +#include "clixon_xml.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" +#include "clixon_xpath_parse.h" + +/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */ +#define YY_DECL int clixon_xpath_parselex(void *_yy) + +/* Dont use input function (use user-buffer) */ +#define YY_NO_INPUT + +/* typecast macro */ +#define _XY ((struct clicon_xpath_yacc_arg *)_yy) + +#define MAXBUF 4*4*64*1024 + +#undef clixon_xpath_parsewrap +int +clixon_xpath_parsewrap(void) +{ + return 1; +} + + + +%} + +digit [0-9] +integer {digit}+ +real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+) + +%x TOKEN +%s QLITERAL +%s ALITERAL + +%% +[ \t] +\n { _XY->xy_linenum++; } +\r { } +<> { return X_EOF; } +".." { return DOUBLEDOT; } +[()\[\]\.@,/:|] { return *yytext; } +"::" { return DOUBLECOLON; } +and { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } +or { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } +div { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } +mod { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } +[+*\-] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } +\? { return *yytext; } +"//" { return DOUBLESLASH; } +"!=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; } +">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } +"<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } +[<>=] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } +last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } +position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } +count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } +ancestor { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; } +ancestor-or-self { clixon_xpath_parselval.intval = A_ANCESTOR_OR_SELF; return AXISNAME; } +attribute { clixon_xpath_parselval.intval = A_ATTRIBUTE; return AXISNAME; } +child { clixon_xpath_parselval.intval = A_CHILD; return AXISNAME; } +descendant { clixon_xpath_parselval.intval = A_DESCENDANT; return AXISNAME; } +descendant-or-self { clixon_xpath_parselval.intval = A_DESCENDANT_OR_SELF; return AXISNAME; } +following { clixon_xpath_parselval.intval = A_FOLLOWING; return AXISNAME; } +following-sibling { clixon_xpath_parselval.intval = A_FOLLOWING_SIBLING; return AXISNAME; } +namespace { clixon_xpath_parselval.intval = A_NAMESPACE; return AXISNAME; } +parent { clixon_xpath_parselval.intval = A_PARENT; return AXISNAME; } +preceding { clixon_xpath_parselval.intval = A_PRECEEDING; return AXISNAME; } +preceding-sibling { clixon_xpath_parselval.intval = A_PRECEEDING_SIBLING; return AXISNAME; } +self { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; } +current { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } +comment { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } +text { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } +processing-instructions { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } +node { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } +\" { BEGIN(QLITERAL); return QUOTE; } +\' { BEGIN(ALITERAL); return APOST; } +\-?({integer}|{real}) { sscanf(yytext,"%lf",&clixon_xpath_parselval.dval); return NUMBER;} +[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext); + return NAME; /* rather be catch-all */ + } +. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; } +\" { BEGIN(TOKEN); return QUOTE; } +. { clixon_xpath_parselval.string = strdup(yytext); + return CHAR;} +\' { BEGIN(TOKEN); return APOST; } +. { clixon_xpath_parselval.string = strdup(yytext); + return CHAR;} + +%% + + +/*! Initialize scanner. + */ +int +xpath_scan_init(struct clicon_xpath_yacc_arg *xy) +{ + BEGIN(TOKEN); + xy->xy_lexbuf = yy_scan_string (xy->xy_parse_string); +#if 1 /* XXX: just to use unput to avoid warning */ + if (0) + yyunput(0, ""); +#endif + + return 0; +} + +/* + * free buffers + * Even within Flex version 2.5 (this is assumed), freeing buffers is different. + */ +int +xpath_scan_exit(struct clicon_xpath_yacc_arg *xy) +{ + yy_delete_buffer(xy->xy_lexbuf); + clixon_xpath_parselex_destroy(); /* modern */ + return 0; +} + diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y new file mode 100644 index 00000000..e7eca0a1 --- /dev/null +++ b/lib/src/clixon_xpath_parse.y @@ -0,0 +1,289 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 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 ***** + + * XPATH Parser + * From https://www.w3.org/TR/xpath-10/ + * The primary syntactic construct in XPath is the expression. + * An expression matches the production Expr + * see https://www.w3.org/TR/xpath-10/#NT-Expr) + * Lexical structure is defined by ExprToken, see + * see https://www.w3.org/TR/xpath-10/#exprlex + */ + +%start start + +%union { + int intval; + double dval; + char *string; + void *stack; /* xpath_tree */ +} + +%token AXISNAME +%token LOGOP +%token ADDOP +%token RELOP + +%token NUMBER + +%token X_EOF +%token QUOTE +%token APOST +%token CHAR +%token NAME +%token NODETYPE +%token DOUBLEDOT +%token DOUBLECOLON +%token DOUBLESLASH +%token FUNCTIONNAME + +%type axisspec + +%type string +%type expr +%type andexpr +%type relexpr +%type addexpr +%type unionexpr +%type pathexpr +%type locationpath +%type abslocpath +%type rellocpath +%type step +%type nodetest +%type predicates +%type primaryexpr + +%lex-param {void *_xy} /* Add this argument to parse() and lex() function */ +%parse-param {void *_xy} + +%{ +/* Here starts user C-code */ + +/* typecast macro */ +#define _XY ((struct clicon_xpath_yacc_arg *)_xy) + +#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;} + +/* add _yy to error paramaters */ +#define YY_(msgid) msgid + +#include "clixon_config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clixon_err.h" +#include "clixon_log.h" +#include "clixon_queue.h" +#include "clixon_string.h" +#include "clixon_hash.h" +#include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" + +#include "clixon_xpath_parse.h" + +extern int clixon_xpath_parseget_lineno (void); + +/* + also called from yacc generated code * +*/ + +void +clixon_xpath_parseerror(void *_xy, + char *s) +{ + clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'", + _XY->xy_name, + _XY->xy_linenum , + s, + clixon_xpath_parsetext); + return; +} + +int +xpath_parse_init(struct clicon_xpath_yacc_arg *xy) +{ + // clicon_debug_init(2, NULL); + return 0; +} + +int +xpath_parse_exit(struct clicon_xpath_yacc_arg *xy) +{ + return 0; +} + +static xpath_tree * +xp_new(enum xp_type type, + int i0, + double d0, + char *s0, + char *s1, + xpath_tree *c0, + xpath_tree *c1) +{ + xpath_tree *xs = NULL; + + if ((xs = malloc(sizeof(xpath_tree))) == NULL){ + clicon_err(OE_XML, errno, "malloc"); + goto done; + } + memset(xs, 0, sizeof(*xs)); + xs->xs_type = type; + xs->xs_int = i0; + xs->xs_double = d0; + xs->xs_s0 = s0; + xs->xs_s1 = s1; + xs->xs_c0 = c0; + xs->xs_c1 = c1; + done: + return xs; +} + +%} + +%% + +/* +*/ + +start : expr X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->expr"); YYACCEPT; } + | locationpath X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->locationpath"); YYACCEPT; } + ; + +expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"expr->expr or andexpr"); } + | andexpr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"expr-> andexpr"); } + ; + +andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"andexpr-> andexpr and relexpr"); } + | relexpr { $$=xp_new(XP_AND,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"andexpr-> relexpr"); } + ; + +relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"relexpr-> relexpr relop addexpr"); } + | addexpr { $$=xp_new(XP_RELEX,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"relexpr-> addexpr"); } + ; + +addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"addexpr-> addexpr ADDOP unionexpr"); } + | unionexpr { $$=xp_new(XP_ADD,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"addexpr-> unionexpr"); } + ; + +/* node-set */ +unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"unionexpr-> unionexpr | pathexpr"); } + | pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"unionexpr-> pathexpr"); } + ; + +pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> locationpath"); } + | primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> primaryexpr"); } + ; + +/* location path returns a node-set */ +locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> rellocpath"); } + | abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> abslocpath"); } + ; + +abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,NULL, NULL);clicon_debug(1,"abslocpath-> /"); } + | '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,$2, NULL);clicon_debug(1,"abslocpath->/ rellocpath");} + /* // is short for /descendant-or-self::node()/ */ + | DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$2, NULL); clicon_debug(1,"abslocpath-> // rellocpath"); } + ; + +rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"rellocpath-> step"); } + | rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"rellocpath-> rellocpath / step"); } + | rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$1, $3); clicon_debug(1,"rellocpath-> rellocpath // step"); } + ; + +step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(1,"step->axisspec(%d) nodetest", $1); } + | '.' { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> ."); } + | DOUBLEDOT { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> .."); } + ; + +axisspec : AXISNAME DOUBLECOLON { clicon_debug(1,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;} + | '@' { $$=A_ATTRIBUTE; clicon_debug(1,"axisspec-> @"); } + | { clicon_debug(1,"axisspec-> "); $$=A_CHILD;} + ; + +nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"nodetest-> *"); } + | NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(1,"nodetest-> name(%s)",$1); } + | NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : name(%s)", $1, $3); } + | NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : *", $1); } + | NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype()"); } + ; + +/* evaluates to boolean */ +predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, $1, $3); clicon_debug(1,"predicates-> [ expr ]"); } + | { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"predicates->"); } + ; + +primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2, NULL); clicon_debug(1,"primaryexpr-> ( expr )"); } + | NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> NUMBER(%lf)", $1); } + | QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" string \""); } + | QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" \""); } + | APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' string '"); } + | APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' '"); } + | FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> functionname ( arguments )"); } + ; + +/* XXX Adding this between FUNCTIONNAME() breaks parser,.. +arguments : arguments expr { clicon_debug(1,"arguments-> arguments expr"); } + | { clicon_debug(1,"arguments-> "); } + ; +*/ +string : string CHAR { + int len = strlen($1); + $$ = realloc($1, len+strlen($2) + 1); + sprintf($$+len, "%s", $2); + free($2); + clicon_debug(1,"string-> string CHAR"); + } + | CHAR { clicon_debug(1,"string-> "); } + ; + + +%% + diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index a758be52..ef47eab3 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -35,54 +35,12 @@ * NOTE: there is a main function at the end of this file where you can test out * different xpath expressions. * Look at the end of the file for a test unit program - */ -/* -See https://www.w3.org/TR/xpath/ - -Implementation of a limited xslt xpath syntax. Some examples. Given the following -xml tree: - - 42 - 99 - 22 - - -With the following xpath examples. There are some diffs and many limitations compared -to the xml standards: - / whole tree ... - /bbb - /aaa/bbb 42 - 99 - //bbb as above - //b?b as above - //b\* as above - //b\*\/ccc 42 - 99 - //\*\/ccc 42 - 99 - 22 --- //bbb@x x="hello" - //bbb[@x] 42 - 99 - //bbb[@x=hello] 42 - //bbb[@x="hello"] as above - //bbb[0] 42 - //bbb[ccc=99] 99 ---- //\*\/[ccc=99] same as above - '//bbb | //ddd' 42 - 99 - 22 (NB spaces) - etc - For xpath v1.0 see http://www.w3.org/TR/xpath/ -record[name=c][time=d] -in - - c - - 45654df4-2292-45d3-9ca5-ee72452568a8 - +The code is implemented according to XPATH 1.0: + https://www.w3.org/TR/xpath-10/ +The primary syntactic construct in XPath is the expression. An expression matches +the production Expr (see https://www.w3.org/TR/xpath-10/#NT-Expr) */ #include #include @@ -95,6 +53,7 @@ in #include #include #include +#include /* cligen */ #include @@ -108,14 +67,18 @@ in #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_xml.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" #include "clixon_xsl.h" + /* Constants */ #define XPATH_VEC_START 128 /* * Types */ + struct searchvec{ cxobj **sv_v0; /* here is result */ int sv_v0len; @@ -127,13 +90,22 @@ typedef struct searchvec searchvec; /* Local types */ -enum axis_type{ - A_SELF, - A_CHILD, - A_PARENT, - A_ROOT, - A_ANCESTOR, - A_DESCENDANT_OR_SELF, /* actually descendant-or-self */ + +struct xpath_predicate{ + struct xpath_predicate *xp_next; + char *xp_expr; +}; + +/* XPATH Axis according to https://www.w3.org/TR/xpath-10/#NT-Step + * Axis ::= AxisSpecifier NodeTest Predicate* + * Eg "child:: + */ +struct xpath_element{ + struct xpath_element *xe_next; + enum axis_type xe_type; + char *xe_prefix; /* eg for namespaces */ + char *xe_str; /* eg for child */ + struct xpath_predicate *xe_predicate; /* eg within [] */ }; /* Mapping between axis type string <--> int */ @@ -147,23 +119,12 @@ static const map_str2int axismap[] = { {NULL, -1} }; -struct xpath_predicate{ - struct xpath_predicate *xp_next; - char *xp_expr; -}; - -struct xpath_element{ - struct xpath_element *xe_next; - enum axis_type xe_type; - char *xe_prefix; /* eg for namespaces */ - char *xe_str; /* eg for child */ - struct xpath_predicate *xe_predicate; /* eg within [] */ -}; - static int xpath_split(char *xpathstr, char **pathexpr); +/*! Print xpath structure for debug */ static int -xpath_print(FILE *f, struct xpath_element *xplist) +xpath_print(FILE *f, + struct xpath_element *xplist) { struct xpath_element *xe; struct xpath_predicate *xp; @@ -217,6 +178,11 @@ xpath_parse_predicate(struct xpath_element *xe, return retval; } +/*! XPATH parse, create new child element + * @param[in] atype Axis type, see https://www.w3.org/TR/xpath-10/#axes + * @param[in] str + * @param[out] xpnext + */ static int xpath_element_new(enum axis_type atype, char *str, @@ -309,8 +275,17 @@ xpath_free(struct xpath_element *xplist) return 0; } -/* - * // is short for /descendant-or-self::node()/ +/*! Parse xpath to xpath_element structure + + * + * [1] LocationPath ::= RelativeLocationPath + * | AbsoluteLocationPath + * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? + * | AbbreviatedAbsoluteLocationPath + * [3] RelativeLocationPath ::= Step + | RelativeLocationPath '/' Step + | AbbreviatedRelativeLocationPath + * @see https://www.w3.org/TR/xpath-10/#NT-LocationPath */ static int xpath_parse(char *xpath, @@ -355,6 +330,7 @@ xpath_parse(char *xpath, } s++; } + /* Iterate through steps (s), see https://www.w3.org/TR/xpath-10/#NT-Step */ s = s0; for (i=0; ixe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0) + if (xpath_recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0) goto done; } } @@ -738,7 +701,6 @@ xpath_find(cxobj *xcur, } } } - for (xp = xe->xe_predicate; xp; xp = xp->xp_next){ if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0) goto done; @@ -798,6 +760,7 @@ xpath_split(char *xpathstr, * @param[in] flags if != 0, only match xml nodes matching flags * @param[out] vec2 Result XML node vector * @param[out] vec2len Length of result vector. + * @see https://www.w3.org/TR/xpath-10/#NT-LocationPath */ static int xpath_exec(cxobj *xcur, @@ -831,14 +794,20 @@ xpath_exec(cxobj *xcur, * @param[in] xcur xml-tree where to search * @param[in] xpath string with XPATH syntax * @param[in] flags if != 0, only match xml nodes matching flags - * @param[in] vec1 vector of XML trees - * @param[in] vec1len length of XML trees + * @param[out] vec1 vector of XML trees + * @param[out] vec1len length of XML trees * For example: xpath = //a | //b. * xpath_first+ splits xpath up in several subcalls * (eg xpath=//a and xpath=//b) and collects the results. * Note: if a match is found in both, two (or more) same results will be * returned. * Note, this could be 'folded' into xpath1 but I judged it too complex. + * @see https://www.w3.org/TR/xpath-10/#NT-Expr + * An 'Expr' is composed of compositions of and, or, =, +, -, down to: + * PathExpr ::= LocationPath + * | FilterExpr + * | FilterExpr '/' RelativeLocationPath + * | FilterExpr '//' RelativeLocationPath */ static int xpath_choice(cxobj *xcur, @@ -848,13 +817,13 @@ xpath_choice(cxobj *xcur, size_t *vec1len) { int retval = -1; - char *s0; + char *s0 = NULL; char *s1; char *s2; char *xpath; cxobj **vec0 = NULL; size_t vec0len = 0; - + if ((s0 = strdup(xpath0)) == NULL){ clicon_err(OE_XML, errno, "strdup"); goto done; @@ -878,7 +847,7 @@ xpath_choice(cxobj *xcur, goto done; } retval = 0; - done: + done: if (s0) free(s0); if (vec0) @@ -1020,7 +989,7 @@ xpath_each(cxobj *xcur, /*! A restricted xpath that returns a vector of matches * * See xpath1() on details for subset -. * @param[in] xcur xml-tree where to search + * @param[in] xcur xml-tree where to search * @param[in] xpath string with XPATH syntax * @param[out] vec vector of xml-trees. Vector must be free():d after use * @param[out] veclen returns length of vector in return value @@ -1052,7 +1021,7 @@ xpath_vec(cxobj *xcur, int retval = -1; va_list ap; size_t len; - char *xpath; + char *xpath = NULL; va_start(ap, veclen); len = vsnprintf(NULL, 0, format, ap); @@ -1079,6 +1048,7 @@ xpath_vec(cxobj *xcur, return retval; } + /* A restricted xpath that returns a vector of matches (only nodes marked with flags) * @param[in] xcur xml-tree where to search * @param[in] xpath string with XPATH syntax @@ -1140,96 +1110,3 @@ xpath_vec_flag(cxobj *xcur, return retval; } -/* - * Turn this on to get an xpath test program - * Usage: xpath [] - * read xpath on first line and xml on rest of lines from input - * Example compile: - gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen - * Example run: -echo "a\n" | xpath -*/ -#if 0 /* Test program */ - - -static int -usage(char *argv0) -{ - fprintf(stderr, "usage:%s .\n\tInput on stdin\n", argv0); - exit(0); -} - -int -main(int argc, char **argv) -{ - int retval = -1; - int i; - cxobj **xv; - cxobj *x; - cxobj *xn; - size_t xlen = 0; - int c; - int len; - char *buf = NULL; - char *filename; - int fd; - - if (argc != 2){ - usage(argv[0]); - goto done; - } - filename = argv[1]; - if ((fd = open(filename, O_RDONLY)) < 0){ - clicon_err(OE_UNIX, errno, "open(%s)", filename); - goto done; - } - /* Read xpath */ - len = 1024; /* any number is fine */ - if ((buf = malloc(len)) == NULL){ - perror("pt_file malloc"); - return -1; - } - memset(buf, 0, len); - i = 0; - while (1){ /* read the whole file */ - if ((c = fgetc(stdin)) == EOF) - return -1; - if (c == '\n') - break; - if (len==i){ - if ((buf = realloc(buf, 2*len)) == NULL){ - fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno)); - return -1; - } - memset(buf+len, 0, len); - len *= 2; - } - buf[i++] = (char)(c&0xff); - } - x = NULL; - if (xml_parse_file(fd, "", NULL, &x) < 0){ - fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); - return -1; - } - close (fd); - printf("\n"); - - if (xpath_vec(x, "%s", &xv, &xlen, buf) < 0) - return -1; - if (xv){ - for (i=0; i $dir/clixon-ret echo "$expect"| od -t c > $dir/clixon-expect - diff $dir/clixon-ret $dir/clixon-expect + diff $dir/clixon-expect $dir/clixon-ret exit $testnr } diff --git a/test/test_xpath.sh b/test/test_xpath.sh new file mode 100755 index 00000000..35ee692a --- /dev/null +++ b/test/test_xpath.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Test: XPATH tests +PROG=../lib/src/clixon_util_xpath + +# include err() and new() functions and creates $dir +. ./lib.sh + +# XML file (alt provide it in stdin after xpath) +xml=$dir/xml.xml +xml2=$dir/xml2.xml + +cat < $xml + + 42 + 99 + 22 + +EOF + +cat < $xml2 + + + e0 + + true + + + +e0 +myfamily + + v6ur:ipv6-unicast + foo + + + + + bar + myfamily + + + + 22 + 99 + responder-only + rt:static + bar + 0 + + ethernet + 1500 + + +EOF + +new "xpath /" +expecteof "$PROG -f $xml -p /" 0 "" "^nodeset:0:429922$" + +new "xpath /aaa" +expecteof "$PROG -f $xml -p /aaa" 0 "" "^nodeset:0:429922$" + +new "xpath /bbb" +expecteof "$PROG -f $xml -p /bbb" 0 "" "^nodeset:$" + +new "xpath /aaa/bbb" +expecteof "$PROG -f $xml -p /aaa/bbb" 0 "" "^0:42 +1:99$" + +new "xpath //bbb" +expecteof "$PROG -f $xml -p //bbb" 0 "" "0:42 +1:99" + +new "xpath //b?b" +#expecteof "$PROG -f $xml" 0 "//b?b" "" + +new "xpath //b*" +#expecteof "$PROG -f $xml" 0 "//b*" "" + +new "xpath //b*/ccc" +#expecteof "$PROG -f $xml" 0 "//b*/ccc" "" + +new "xpath //bbb[0]" +expecteof "$PROG -f $xml -p //bbb[0]" 0 "" "^nodeset:0:42$" + +new "xpath //bbb[ccc=99]" +expecteof "$PROG -f $xml -p //bbb[ccc=99]" 0 "" "^nodeset:0:99$" + +new "xpath ../connection-type = 'responder-only'" +expecteof "$PROG -f $xml2 -p ../connection-type='responder-only' -i /aaa/bbb/here" 0 "" "^bool:true$" + +new "xpath ../connection-type = 'no-responder'" +expecteof "$PROG -f $xml2 -p ../connection-type='no-responder' -i /aaa/bbb/here" 0 "" "^bool:false$" + +new "xpath . <= 0.75 * ../max-rtr-adv-interval" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". <= 0.75 * ../max-rtr-adv-interval" "^bool:true$" + +new "xpath . > 0.75 * ../max-rtr-adv-interval" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". > 0.75 * ../max-rtr-adv-interval" "^bool:false$" + +new "xpath . <= ../valid-lifetime" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 ". <= ../valid-lifetime" "^bool:true$" + +new "xpath ../../rt:address-family = 'v6ur:ipv6-unicast'" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 "../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$" + +new "xpath ../../../rt:address-family = 'v6ur:ipv6-unicast'" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here2/here" 0 "../../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$" + +new "xpath /if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" +expecteof "$PROG -f $xml2" 0 "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" "^bool:true$" + +new "xpath rt:address-family='v6ur:ipv6-unicast'" +expecteof "$PROG -f $xml2 -i /aaa" 0 "rt:address-family='v6ur:ipv6-unicast'" "^bool:true$" + +new "xpath ../type='rt:static'" +expecteof "$PROG -f $xml2 -i /aaa/bbb/here" 0 "../type='rt:static'" "^bool:true$" + +new "xpath rib-name != ../../name" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "rib-name != ../../name" "^bool:true$" + +new "xpath routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" "^bool:true$" + +new "xpath ifType = \"ethernet\" or ifMTU = 1500" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1500" "^bool:true$" + +new "xpath ifType != \"ethernet\" or ifMTU = 1500" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1500" "^bool:true$" + +new "xpath ifType = \"ethernet\" or ifMTU = 1400" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1400" "^bool:true$" + +new "xpath ifType != \"ethernet\" or ifMTU = 1400" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1400" "^bool:false$" + +new "xpath ifType = \"ethernet\" and ifMTU = 1500" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1500" "^bool:true$" + +new "xpath ifType != \"ethernet\" and ifMTU = 1500" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1500" "^bool:false$" + +new "xpath ifType = \"ethernet\" and ifMTU = 1400" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1400" "^bool:false$" + +new "xpath ifType != \"ethernet\" and ifMTU = 1400" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1400" "^bool:false$" + +new "xpath ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" +expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" "^bool:true$" + +rm -rf $dir diff --git a/test/test_xsl.sh b/test/test_xsl.sh deleted file mode 100755 index b7e322ad..00000000 --- a/test/test_xsl.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Test: XSL tests -PROG=../lib/src/clixon_util_xsl - -# include err() and new() functions and creates $dir -. ./lib.sh - -new "xsl test" -expecteof $PROG 0 "a -" "^0:$" - -rm -rf $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 1e542501..3522500c 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Test4: Yang specifics: multi-keys and empty type +# Yang specifics: multi-keys and empty type APPNAME=example # include err() and new() functions and creates $dir . ./lib.sh From ba7f84afee8cb7016c3b4b21bb2b8c918db9cb50 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 17 Jul 2018 16:59:32 +0200 Subject: [PATCH 39/53] * Much better support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` * This includes all calls to `xpath_vec, xpath_first`, etc. * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` * The old API is stillenabled. To define the new, define XPATH_USE_NEW in include/clixon_custom.h and recompile --- CHANGELOG.md | 8 +- apps/backend/backend_client.c | 16 ++- apps/cli/cli_common.c | 27 +++- apps/cli/cli_generate.c | 2 +- apps/cli/cli_show.c | 6 +- apps/netconf/netconf_hello.c | 5 +- apps/restconf/restconf_methods.c | 3 + datastore/keyvalue/clixon_keyvalue.c | 4 +- example/example_cli.c | 7 +- example/example_cli.cli | 2 +- include/clixon_custom.h | 5 + lib/clixon/clixon.h.in | 2 +- lib/clixon/clixon_xpath.h | 19 +++ lib/clixon/clixon_xsl.h | 11 +- lib/src/clixon_json.c | 2 +- lib/src/clixon_options.c | 2 + lib/src/clixon_proto_client.c | 3 + lib/src/clixon_xml_map.c | 22 +++- lib/src/clixon_xpath.c | 180 ++++++++++++++++++++++----- lib/src/clixon_xpath_parse.y | 4 +- lib/src/clixon_xsl.c | 21 ++-- lib/src/clixon_yang_type.c | 1 - test/lib.sh | 3 + test/test_cli.sh | 1 + test/test_leafref.sh | 3 + test/test_netconf.sh | 41 +++++- test/test_order.sh | 18 +++ test/test_xpath.sh | 46 ++++++- test/test_yang.sh | 10 +- 29 files changed, 395 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cfa055e..45eae3fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,11 @@ ## 3.7.0 (Upcoming) ### Major changes: -* Full support of XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex - * The previous XPATH imlementation was very restricted. - * The only function implemented is the Yang extension "current()". No other functions are implemented (eg last(), count()). +* Much better support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex + * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` + * This includes all calls to `xpath_vec, xpath_first`, etc. + * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` + * The old API is stillenabled. To define the new, define XPATH_USE_NEW in include/clixon_custom.h and recompile * Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index e917f75d..19184c46 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -985,7 +985,13 @@ nacm_access(clicon_handle h, if (username == NULL) goto step10; /* User's group */ - if (xpath_vec(xacm, "groups/group[user-name=%s]", &gvec, &glen, username) < 0) + if (xpath_vec(xacm, +#ifdef XPATH_USE_NEW + "groups/group[user-name='%s']", +#else + "groups/group[user-name=%s]", +#endif + &gvec, &glen, username) < 0) goto done; /* 5. If no groups are found, continue with step 10. */ if (glen == 0) @@ -1002,7 +1008,13 @@ nacm_access(clicon_handle h, for (j=0; j to , cli_copy_config("candidate", "/sender[%s=%s]", "from", "n1", "n2"); + * copy snd to , cli_copy_config("candidate", "/sender[%s='%s']", "from", "n1", "n2"); * cli command: * copy snd from to to * @endcode @@ -1244,17 +1244,34 @@ cli_copy_config(clicon_handle h, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - /* Sanity check that xpath contains exactly one %s */ + /* Sanity check that xpath contains exactly two %s, ie [%s='%s'] */ j = 0; - for (i=0; i ^/interface/eth0/address/.*$ - --> /interface/[name=eth0]/address + --> /interface/[name="eth0"]/address */ if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0) goto done; @@ -417,10 +417,10 @@ show_yang(clicon_handle h, * Format of argv: * "running"|"candidate"|"startup" * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) - * xpath expression, that may contain one %, eg "/sender[name=%s]" + * xpath expression, that may contain one %, eg "/sender[name="%s"]" * optional name of variable in cvv. If set, xpath must have a '%s' * @code - * show config id , cli_show_config("running","xml","iface[name=%s]","n"); + * show config id , cli_show_config("running","xml","iface[name="%s"]","n"); * @endcode */ int diff --git a/apps/netconf/netconf_hello.c b/apps/netconf/netconf_hello.c index 833a1c5c..9fc0c2c2 100644 --- a/apps/netconf/netconf_hello.c +++ b/apps/netconf/netconf_hello.c @@ -82,12 +82,15 @@ static int netconf_hello(cxobj *xn) { +#ifdef nyi cxobj *x; x = NULL; + while ((x = xpath_each(xn, "//capability", x)) != NULL) { - //fprintf(stderr, "cap: %s\n", xml_body(x)); + } +#endif return 0; } diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 62580cd6..d75dafd9 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -95,6 +95,9 @@ Mapping netconf error-tag -> status code */ +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif #include #include #include diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index aa529477..4e77418d 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -64,7 +64,7 @@ * * Alternative for xmlkeyfmt would be eg: * RESTCONF: /interfaces/interface=%s/ipv4/address/ip=%s (used) - * XPATH: /interfaces/interface[name=%s]/ipv4/address/[ip=%s] + * XPATH: /interfaces/interface[name='%s']/ipv4/address/[ip'=%s'] * * Paths through the code (for coverage) * cli_callback_generate +----------------+ @@ -375,7 +375,7 @@ get(char *dbname, arg = valvec[j++]; if (uri_percent_decode(arg, &argdec) < 0) goto done; - cprintf(cb, "[%s=%s]", cv_string_get(cvi), argdec); + cprintf(cb, "[%s='%s']", cv_string_get(cvi), argdec); free(argdec); argdec=NULL; } diff --git a/example/example_cli.c b/example/example_cli.c index 35aa6b25..5248eef4 100644 --- a/example/example_cli.c +++ b/example/example_cli.c @@ -67,7 +67,12 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv) cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */ /* Show eth0 interfaces config using XPATH */ - if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]", + if (clicon_rpc_get_config(h, "running", +#ifdef XPATH_USE_NEW + "/interfaces/interface[name='eth0']", +#else + "/interfaces/interface[name=eth0]", +#endif &xret) < 0) goto done; diff --git a/example/example_cli.cli b/example/example_cli.cli index 1040f834..3703e8ba 100644 --- a/example/example_cli.cli +++ b/example/example_cli.cli @@ -24,7 +24,7 @@ debug("Debugging parts of the system"), cli_debug_cli((int32)1);{ } copy("Copy and create a new object") { interface("Copy interface"){ - ("name of interface to copy from") to("Copy to interface") ("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s=%s]","name","name","toname"); + ("name of interface to copy from") to("Copy to interface") ("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname"); } } discard("Discard edits (rollback 0)"), discard_changes(); diff --git a/include/clixon_custom.h b/include/clixon_custom.h index d95ce399..2afb9606 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -55,3 +55,8 @@ int strverscmp (__const char *__s1, __const char *__s2); /* Full xmlns validation check is made only if XML has associated YANG spec */ #define XMLNS_YANG_ONLY 1 + +/* Set if you want all old xpath functions in clixon_xsl.* to use the new + * xpath functions in clixon_xpath.* +*/ +#undef XPATH_USE_NEW diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 25cc653c..dfa82bc4 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -81,9 +81,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index c045237d..0c0c6769 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -85,11 +85,30 @@ extern const map_str2int xpopmap[]; */ #if defined(__GNUC__) && __GNUC__ >= 3 int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); +int xpath_vec_nodeset_flag(cxobj *xcur, char *format, uint16_t flags, + cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); +cxobj *xpath_first_nodeset(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); + #else int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...); +int xpath_vec_nodeset_flag(cxobj *xcur, char *format, uint16_t flags, + cxobj ***vec, size_t *veclen, ...); +cxobj *xpath_first_nodeset(cxobj *xcur, char *format, ...); int xpath_vec_bool(cxobj *xcur, char *format, ...); + #endif int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp); +/* backward compatible */ +#ifdef XPATH_USE_NEW +#define xpath_first(cxtop, format, args...) xpath_first_nodeset(cxtop, format, ##args) +#define xpath_vec(cxtop, format, vec, veclen, args...) xpath_vec_nodeset(cxtop, format, vec, veclen, ##args) +#define xpath_vec_flag(cxtop, format, flags, vec, veclen, args...) xpath_vec_nodeset_flag(cxtop, format, flags, vec, veclen, ##args) +#else +#define xpath_first(cxtop, format, args...) xpath_first_xsl(cxtop, format, ##args) +#define xpath_vec(cxtop, format, vec, veclen, args...) xpath_vec_xsl(cxtop, format, vec, veclen, ##args) +#define xpath_vec_flag(cxtop, format, flags, vec, veclen, args...) xpath_vec_flag_xsl(cxtop, format, flags, vec, veclen, ##args) +#endif + #endif /* _CLIXON_XPATH_H */ diff --git a/lib/clixon/clixon_xsl.h b/lib/clixon/clixon_xsl.h index 3499cc97..2f19623d 100644 --- a/lib/clixon/clixon_xsl.h +++ b/lib/clixon/clixon_xsl.h @@ -40,16 +40,17 @@ * Prototypes */ #if defined(__GNUC__) && __GNUC__ >= 3 -cxobj *xpath_first(cxobj *cxtop, char *format, ...) __attribute__ ((format (printf, 2, 3))); -int xpath_vec(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); +cxobj *xpath_first_xsl(cxobj *cxtop, char *format, ...) __attribute__ ((format (printf, 2, 3))); +int xpath_vec_xsl(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); int xpath_vec_flag(cxobj *cxtop, char *format, uint16_t flags, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); #else -cxobj *xpath_first(cxobj *cxtop, char *format, ...); -int xpath_vec(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...); -int xpath_vec_flag(cxobj *cxtop, char *xpath, uint16_t flags, +cxobj *xpath_first_xsl(cxobj *cxtop, char *format, ...); +int xpath_vec_xsl(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...); +int xpath_vec_flag_xsl(cxobj *cxtop, char *xpath, uint16_t flags, cxobj ***vec, size_t *veclen, ...); #endif cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev); + #endif /* _CLIXON_XSL_H */ diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 38418def..794b35f2 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -774,7 +774,7 @@ json_parse_file(int fd, /* * Turn this on to get a json parse and pretty print test program - * Usage: xpath + * Usage: json * read json from input * Example compile: gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 61c8e828..cfd0d76c 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -68,6 +68,8 @@ #include "clixon_xml.h" #include "clixon_plugin.h" #include "clixon_xsl.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" #include "clixon_xml_map.h" /* Mapping between Clicon startup modes string <--> constants, diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 7e255208..35c4b28c 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -65,6 +65,9 @@ #include "clixon_xml.h" #include "clixon_plugin.h" #include "clixon_xsl.h" +#include "clixon_string.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" #include "clixon_proto.h" #include "clixon_err.h" #include "clixon_proto_client.h" diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 2b21b174..08015a59 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -83,6 +83,8 @@ #include "clixon_options.h" #include "clixon_xml.h" #include "clixon_plugin.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" #include "clixon_xsl.h" #include "clixon_log.h" #include "clixon_err.h" @@ -439,7 +441,7 @@ xml_yang_validate_all(cxobj *xt, case Y_LEAF_LIST: /* Special case if leaf is leafref, then first check against current xml tree - */ + */ if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ if (strcmp(yc->ys_argument, "leafref") == 0){ if (validate_leafref(xt, yc) < 0) @@ -995,7 +997,7 @@ api_path_fmt2api_path(char *api_path_fmt, * @example * api_path_fmt: /interface/%s/address/%s * cvv: name=eth0 - * xpath: /interface/[name=eth0]/address + * xpath: /interface/[name='eth0']/address * @example * api_path_fmt: /ip/me/%s (if key) * cvv: - @@ -1038,7 +1040,13 @@ api_path_fmt2xpath(char *api_path_fmt, clicon_err(OE_UNIX, errno, "cv2str_dup"); goto done; } - cprintf(cb, "[%s=%s]", cv_name_get(cv), str); + cprintf(cb, +#ifdef XPATH_USE_NEW + "[%s='%s']", +#else + "[%s=%s]", +#endif + cv_name_get(cv), str); free(str); } } @@ -1455,7 +1463,13 @@ api_path2xpath_cvv(yang_spec *yspec, cprintf(xpath, "/%s", name); v = val; while ((cvi = cvec_each(cvk, cvi)) != NULL){ - cprintf(xpath, "[%s=%s]", cv_string_get(cvi), v); + cprintf(xpath, +#ifdef XPATH_USE_NEW + "[%s='%s']", +#else + "[%s=%s]", +#endif + cv_string_get(cvi), v); v += strlen(v)+1; } if (val) diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 96825c7d..8e5d310a 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -33,6 +33,10 @@ * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 */ +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + #include #include #include @@ -246,16 +250,15 @@ xp_eval_step(xp_ctx *xc0, xpath_tree *xs, xp_ctx **xrp) { - int retval = -1; - int i; - cxobj *x; - cxobj *xv; - cxobj *xp; - cxobj **vec = NULL; - size_t veclen = 0; - xpath_tree *nodetest = xs->xs_c0; - xp_ctx *xr0 = NULL; - xp_ctx *xc = NULL; + int retval = -1; + int i; + cxobj *x; + cxobj *xv; + cxobj *xp; + cxobj **vec = NULL; + size_t veclen = 0; + xpath_tree *nodetest = xs->xs_c0; + xp_ctx *xc = NULL; /* Create new xc */ if ((xc = ctx_dup(xc0)) == NULL) @@ -312,16 +315,14 @@ xp_eval_step(xp_ctx *xc0, case A_NAMESPACE: /* principal node type is namespace */ break; case A_PARENT: - if ((xr0 = malloc(sizeof(*xr0))) == NULL){ - clicon_err(OE_UNIX, errno, "malloc"); - goto done; - } - memset(xr0, 0, sizeof(*xr0)); - xr0->xc_initial = xc->xc_initial; - for (i=0; ixc_size; i++){ - x = xc->xc_nodeset[i]; + veclen = xc->xc_size; + vec = xc->xc_nodeset; + xc->xc_size = 0; + xc->xc_nodeset = NULL; + for (i=0; ixc_nodeset, &xr0->xc_size) < 0) + if (cxvec_append(xp, &xc->xc_nodeset, &xc->xc_size) < 0) goto done; } break; @@ -330,19 +331,20 @@ xp_eval_step(xp_ctx *xc0, case A_PRECEEDING_SIBLING: break; case A_SELF: - xr0 = ctx_dup(xc); break; default: clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int); goto done; break; } - if (xr0) - *xrp = xr0; if (xs->xs_c1){ if (xp_eval(xc, xs->xs_c1, xrp) < 0) goto done; } + else{ + *xrp = xc; + xc = NULL; + } assert(*xrp); retval = 0; done: @@ -623,8 +625,8 @@ xp_relop(xp_ctx *xc1, if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */ switch (xc1->xc_type){ case XT_NODESET: - /* If both are node-sets, then it is true iff the string value of a - node in the first node-set and in the second node-set is true */ + /* If both are node-sets, then it is true iff the string value of one + node in the first node-set and one in the second node-set is true */ for (i=0; ixc_size; i++){ if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){ xr->xc_bool = 0; @@ -658,8 +660,12 @@ xp_relop(xp_ctx *xc1, clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op)); goto done; break; - } + } + if (xr->xc_bool) /* enough to find a single node */ + break; } + if (xr->xc_bool) /* enough to find a single node */ + break; } break; case XT_BOOL: @@ -711,10 +717,10 @@ xp_relop(xp_ctx *xc1, if there is a node in the node-set such that the result of performing the comparison on the string-value of the node and the other string is true.*/ + s2 = xc2->xc_string; for (i=0; ixc_size; i++){ x = xc1->xc_nodeset[i]; /* node in nodeset */ s1 = xml_body(x); - s2 = xc2->xc_string; switch(op){ case XO_EQ: xr->xc_bool = (strcmp(s1, s2)==0); @@ -727,6 +733,8 @@ xp_relop(xp_ctx *xc1, goto done; break; } + if (xr->xc_bool) /* enough to find a single node */ + break; } break; case XT_NUMBER: @@ -759,6 +767,8 @@ xp_relop(xp_ctx *xc1, goto done; break; } + if (xr->xc_bool) /* enough to find a single node */ + break; } break; default: @@ -862,7 +872,7 @@ xp_eval(xp_ctx *xc, x = xc->xc_node; while (xml_parent(x) != NULL) x = xml_parent(x); - // xc->xc_node = x; + xc->xc_node = x; xc->xc_nodeset[0] = x; xc->xc_size=1; /* // is short for /descendant-or-self::node()/ */ @@ -921,7 +931,6 @@ xp_eval(xp_ctx *xc, while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) { if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0) goto done; - break; } } break; @@ -1027,7 +1036,6 @@ xp_eval(xp_ctx *xc, } /*! Given XML tree and xpath, returns xpath context - * @param[in] xcur XML-tree where to search * @param[in] xpath String with XPATH 1.0 syntax * @param[out] xrp Return XPATH context @@ -1081,6 +1089,45 @@ xpath_vec_ctx(cxobj *xcur, return retval; } +cxobj * +xpath_first_nodeset(cxobj *xcur, + char *format, + ...) +{ + cxobj *cx = NULL; + va_list ap; + size_t len; + char *xpath = NULL; + xp_ctx *xr = NULL; + + va_start(ap, format); + len = vsnprintf(NULL, 0, format, ap); + va_end(ap); + /* allocate a message string exactly fitting the message length */ + if ((xpath = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + /* second round: compute write message from reason and args */ + va_start(ap, format); + if (vsnprintf(xpath, len+1, format, ap) < 0){ + clicon_err(OE_UNIX, errno, "vsnprintf"); + va_end(ap); + goto done; + } + va_end(ap); + if (xpath_vec_ctx(xcur, xpath, &xr) < 0) + goto done; + if (xr && xr->xc_type == XT_NODESET && xr->xc_size) + cx = xr->xc_nodeset[0]; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return cx; +} + /*! Given XML tree and xpath, returns nodeset as xml node vector * If result is not nodeset, return empty nodeset * @param[in] xcur xml-tree where to search @@ -1135,6 +1182,81 @@ xpath_vec_nodeset(cxobj *xcur, return retval; } +/* A restricted xpath that returns a vector of matches (only nodes marked with flags) + * @param[in] xcur xml-tree where to search + * @param[in] xpath string with XPATH syntax + * @param[in] flags Set of flags that return nodes must match (0 if all) + * @param[out] vec vector of xml-trees. Vector must be free():d after use + * @param[out] veclen returns length of vector in return value + * @retval 0 OK + * @retval -1 error. + * @code + * cxobj **vec; + * size_t veclen; + * if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0) + * goto err; + * for (i=0; ixc_type == XT_NODESET){ + for (i=0; ixc_size; i++){ + x = xr->xc_nodeset[i]; + if (flags==0x0 || xml_flag(x, flags)) + if (cxvec_append(x, vec, veclen) < 0) + goto done; + } + } + retval = 0; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return retval; +} + /*! Given XML tree and xpath, returns boolean * @param[in] xcur xml-tree where to search * @param[in] xpath stdarg string with XPATH 1.0 syntax diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y index e7eca0a1..0e601167 100644 --- a/lib/src/clixon_xpath_parse.y +++ b/lib/src/clixon_xpath_parse.y @@ -239,8 +239,8 @@ rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL ; step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(1,"step->axisspec(%d) nodetest", $1); } - | '.' { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> ."); } - | DOUBLEDOT { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> .."); } + | '.' predicates { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> ."); } + | DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> .."); } ; axisspec : AXISNAME DOUBLECOLON { clicon_debug(1,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;} diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index ef47eab3..8f34a0a1 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -42,6 +42,10 @@ The code is implemented according to XPATH 1.0: The primary syntactic construct in XPath is the expression. An expression matches the production Expr (see https://www.w3.org/TR/xpath-10/#NT-Expr) */ +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + #include #include #include @@ -896,7 +900,7 @@ xpath_first0(cxobj *xcur, * @see also xpath_vec. */ cxobj * -xpath_first(cxobj *xcur, +xpath_first_xsl(cxobj *xcur, char *format, ...) { @@ -1012,7 +1016,7 @@ xpath_each(cxobj *xcur, * @see also xpath_first, xpath_each. */ int -xpath_vec(cxobj *xcur, +xpath_vec_xsl(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, @@ -1048,7 +1052,6 @@ xpath_vec(cxobj *xcur, return retval; } - /* A restricted xpath that returns a vector of matches (only nodes marked with flags) * @param[in] xcur xml-tree where to search * @param[in] xpath string with XPATH syntax @@ -1073,12 +1076,12 @@ xpath_vec(cxobj *xcur, * @see also xpath_vec This is a specialized version. */ int -xpath_vec_flag(cxobj *xcur, - char *format, - uint16_t flags, - cxobj ***vec, - size_t *veclen, - ...) +xpath_vec_flag_xsl(cxobj *xcur, + char *format, + uint16_t flags, + cxobj ***vec, + size_t *veclen, + ...) { int retval = -1; va_list ap; diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index a67500db..68204c9b 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -545,7 +545,6 @@ cv_validate1(cg_var *cv, if ((vec = clicon_strsep(str, " \t", &nvec)) == NULL) goto done; for (i=0; i]]>]]>" "^]]>]]>$" +new "leafref get config" +expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^eth0' + new "leafref add wrong ref" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth3
10.0.4.6
]]>]]>" "^]]>]]>$" diff --git a/test/test_netconf.sh b/test/test_netconf.sh index e0364e1a..5496f69e 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -6,6 +6,7 @@ APPNAME=example cfg=$dir/conf_yang.xml fyang=$dir/netconf.yang +tmp=$dir/tmp.x cat < $cfg @@ -92,8 +93,18 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" +# Too many quotes, (single inside double inside single) need to fool bash +if [ -n "$XPATH_USE_NEW" ]; then +cat < $tmp # new +]]>]]> +EOF +else +cat < $tmp +]]>]]> +EOF +fi new "Check eth/0/0 added using xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth/0/0ex:ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth/0/0ex:ethtrue]]>]]>$" new "Re-create same eth/0/0 which should generate error" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^" @@ -110,11 +121,33 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" +# Too many quotes +if [ -n "$XPATH_USE_NEW" ]; then +cat < $tmp # new +]]>]]> +EOF +else +cat < $tmp # new +]]>]]> +EOF +fi + new "netconf get config xpath" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth1true]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth1true]]>]]>$" + +# Too many quotes +if [ -n "$XPATH_USE_NEW" ]; then +cat < $tmp # new +]]>]]> +EOF +else +cat < $tmp # old +]]>]]> +EOF +fi new "netconf get config xpath parent" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth/0/0trueeth1truetruefalse
9.2.3.424
]]>]]>$" new "netconf validate missing type" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^" @@ -157,7 +190,7 @@ new "netconf edit CDATA" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0ex:eth]]>]]>" "^]]>]]>$" #new "netconf get CDATA" -#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "eth/0/0true]]>]]>" +#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "eth/0/0true]]>]]>" new "netconf discard-changes" diff --git a/test/test_order.sh b/test/test_order.sh index 64eaace4..d0536dbf 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -121,6 +121,22 @@ fi 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" 0 "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" +if [ -n "$XPATH_USE_NEW" ]; then #new + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" + +new "get each ordered-by user leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" + +else # old + new "get each ordered-by user leaf-list" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" @@ -132,6 +148,8 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" + +fi new "delete candidate" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" diff --git a/test/test_xpath.sh b/test/test_xpath.sh index 35ee692a..4d23996a 100755 --- a/test/test_xpath.sh +++ b/test/test_xpath.sh @@ -8,12 +8,20 @@ PROG=../lib/src/clixon_util_xpath # XML file (alt provide it in stdin after xpath) xml=$dir/xml.xml xml2=$dir/xml2.xml +xml3=$dir/xml3.xml +xml4=$dir/xml4.xml cat < $xml - 42 - 99 - 22 + + 42 + + + 99 + + + 22 + EOF @@ -53,6 +61,19 @@ cat < $xml2 EOF +# Multiple leaf-list +cat < $xml3 + + foo + 42 + bar + + + 99 + foo + +EOF + new "xpath /" expecteof "$PROG -f $xml -p /" 0 "" "^nodeset:0:429922$" @@ -148,4 +169,23 @@ expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 140 new "xpath ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" expecteof "$PROG -f $xml2 -i /aaa/bbb" 0 "ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" "^bool:true$" +new "xpath .[name='bar']" +expecteof "$PROG -f $xml2 -p .[name='bar'] -i /aaa/bbb/routing/ribs/rib" 0 "" "^nodeset:0:barmyfamily$" + +new "Multiple entries" +new "xpath bbb[ccc='foo']" +expecteof "$PROG -f $xml3 -p bbb[ccc='foo']" 0 "" "^nodeset:0:foo42bar1:99foo$" + +new "xpath bbb[ccc='42']" +expecteof "$PROG -f $xml3 -p bbb[ccc='42']" 0 "" "^nodeset:0:foo42bar$" + +new "xpath bbb[ccc=99] (number w/o quotes)" +expecteof "$PROG -f $xml3 -p bbb[ccc=99]" 0 "" "^nodeset:0:99foo$" + +new "xpath bbb[ccc='bar']" +expecteof "$PROG -f $xml3 -p bbb[ccc='bar']" 0 "" "^nodeset:0:foo42bar$" + +new "xpath bbb[ccc='fie']" +expecteof "$PROG -f $xml3 -p bbb[ccc='fie']" 0 "" "^nodeset:$" + rm -rf $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 3522500c..7b5a7af6 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -136,8 +136,13 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" +if [ -n "$XPATH_USE_NEW" ]; then # new +new "netconf get leaf-list path" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" +else # old new "netconf get leaf-list path" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" +fi new "netconf get (should be some)" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^125one]]>]]>$" @@ -154,7 +159,10 @@ new "netconf set presence and not present" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf get presence only" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$" + +new "netconf get presence only" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' "^]]>]]>$" new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" From 0eb9e6c8b28375cec6c373926e4098150ecd60a8 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 18 Jul 2018 09:51:33 +0200 Subject: [PATCH 40/53] * Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5 * XPATH checked at validation time --- CHANGELOG.md | 24 +++--- README.md | 13 ++- apps/backend/backend_client.c | 12 +-- apps/cli/cli_common.c | 2 +- example/example_cli.c | 6 +- include/clixon_custom.h | 14 +++- lib/clixon/clixon_xpath.h | 10 +-- lib/src/clixon_xml_map.c | 43 +++++++--- lib/src/clixon_xpath.c | 4 +- test/lib.sh | 5 +- test/test_netconf.sh | 10 +-- test/test_order.sh | 2 +- test/test_when_must.sh | 151 ++++++++++++++++++++++++++++++++++ test/test_yang.sh | 2 +- 14 files changed, 244 insertions(+), 54 deletions(-) create mode 100755 test/test_when_must.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 45eae3fd..011ea488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,20 +3,22 @@ ## 3.7.0 (Upcoming) ### Major changes: -* Much better support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex - * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` - * This includes all calls to `xpath_vec, xpath_first`, etc. - * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` - * The old API is stillenabled. To define the new, define XPATH_USE_NEW in include/clixon_custom.h and recompile -* Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. - * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 - * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 - * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. -* Support for YANG identity and identityref according to RFC 7950 Sec 7.18 and 9.10 +* Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5 + * XPATH checked at validation time +* Support for YANG identity, identityref, must and when according to RFC 7950 Sec 7.189. * Previous support did no validation of values. * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. +* Much improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex + * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` + * This includes all calls to `xpath_vec, xpath_first`, etc. + * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` + * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. +* Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. + * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 + * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 + * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. ### Minor changes: * Added systemd example files under example/systemd @@ -33,7 +35,7 @@ * Added xmlns validation * for eg * Added yang identityref runtime validation -* Removed cli callback vector functions. Set COMPAT_COMPAT_CLIV if you need to keep these functions in clixon_custom.h. +* Removed cli callback vector functions. Set COMPAT_CLIV if you need to keep these functions in include/clixon_custom.h. * Replace functions as follows in CLI SPEC files: * cli_setv --> cli_set * cli_mergev --> cli_merge diff --git a/README.md b/README.md index bf4d82c5..74812d4f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ transaction support from a YANG specification. * [Support](#support) * [Dependencies](#dependencies) * [Extending](#extending) + * [XML and XPATH](#xml) * [Yang](#yang) * [Netconf](#netconf) * [Restconf](#restconf) @@ -89,9 +90,17 @@ are also available. Plugins are written in C and easiest is to look at [example](example/README.md) or consulting the [FAQ](doc/FAQ.md). +XML +=== +Clixon has its own implementation of XML and XPATH implementation. + +The standards covered include: +- [XML](https://www.w3.org/TR/2008/REC-xml-20081126) +- [Namespaces](https://www.w3.org/TR/2009/REC-xml-names-20091208) +- [XPATH](https://www.w3.org/TR/xpath-10) + Yang ==== - YANG and XML is at the heart of Clixon. Yang modules are used as a specification for handling XML configuration data. The YANG spec is used to generate an interactive CLI, netconf and restconf clients. It @@ -100,7 +109,7 @@ also manages an XML datastore. Clixon mainly follows [YANG 1.0 RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) with some exceptions: - conformance: feature, if-feature, deviation - list features: min/max-elements, unique -- when, must, action statements +- action statements - notifications The aim is also to cover new features in YANG 1.1 [YANG RFC 7950](https://www.rfc-editor.org/rfc/rfc7950.txt) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 19184c46..9528ac74 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -986,10 +986,10 @@ nacm_access(clicon_handle h, goto step10; /* User's group */ if (xpath_vec(xacm, -#ifdef XPATH_USE_NEW - "groups/group[user-name='%s']", -#else +#ifdef COMPAT_XSL "groups/group[user-name=%s]", +#else + "groups/group[user-name='%s']", #endif &gvec, &glen, username) < 0) goto done; @@ -1009,10 +1009,10 @@ nacm_access(clicon_handle h, char *gname; gname = xml_find_body(gvec[j], "name"); if (xpath_first(xrlist, -#ifdef XPATH_USE_NEW - ".[group='%s']", -#else +#ifdef COMPAT_XSL ".[group=%s]", +#else + ".[group='%s']", #endif gname)!=NULL) break; /* found */ diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 1d3c3e69..03ba734c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -1249,7 +1249,7 @@ cli_copy_config(clicon_handle h, for (i=0; iys_keyword != Y_MUST) + continue; + xpath = yc->ys_argument; /* "must" has xpath argument */ + if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + goto done; + if (!b){ + if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL) + clicon_err(OE_DB, 0, "%s", ye->ys_argument); + else + clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); + goto done; + } + } + /* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){ xpath = yc->ys_argument; /* "when" has xpath argument */ - if (xpath_first(xt, "%s", xpath)) - ; - fprintf(stderr, "%s %s\n", __FUNCTION__, xpath); + if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + goto done; + if (!b){ + clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); + goto done; + } } } retval = 0; @@ -1041,10 +1060,10 @@ api_path_fmt2xpath(char *api_path_fmt, goto done; } cprintf(cb, -#ifdef XPATH_USE_NEW - "[%s='%s']", -#else +#ifdef COMPAT_XSL "[%s=%s]", +#else + "[%s='%s']", #endif cv_name_get(cv), str); free(str); @@ -1464,10 +1483,10 @@ api_path2xpath_cvv(yang_spec *yspec, v = val; while ((cvi = cvec_each(cvk, cvi)) != NULL){ cprintf(xpath, -#ifdef XPATH_USE_NEW - "[%s='%s']", -#else +#ifdef COMPAT_XSL "[%s=%s]", +#else + "[%s='%s']", #endif cv_string_get(cvi), v); v += strlen(v)+1; diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 8e5d310a..34633e8a 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -776,6 +776,9 @@ xp_relop(xp_ctx *xc1, } /* switch type */ } ok: + /* Just ensure bool is 0 or 1 */ + if (xr->xc_type == XT_BOOL && xr->xc_bool != 0) + xr->xc_bool = 1; *xrp = xr; retval = 0; done: @@ -849,7 +852,6 @@ xp_eval(xp_ctx *xc, xp_ctx *xr2 = NULL; int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */ - assert(xc->xc_initial); if (debug){ cbuf *cb; if ((cb = cbuf_new()) == NULL){ diff --git a/test/lib.sh b/test/lib.sh index f361e6ec..35ab1c7a 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -5,8 +5,9 @@ testnr=0 testname= -# Set to 1 if new xpath. Set to nothing, or comment if old -#XPATH_USE_NEW=1 +# Set to 1 to enable old XSL implementation. Set to nothing, or comment if new. +# @see include/clixon_custom.h +#COMPAT_XSL=1 # For memcheck #clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli" diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 5496f69e..1a78f1cc 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -94,11 +94,11 @@ new "Add subtree eth/0/0 using none and create which should add eth/0/0" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0ex:ethnone ]]>]]>' "^]]>]]>$" # Too many quotes, (single inside double inside single) need to fool bash -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF -else +else # old cat < $tmp ]]>]]> EOF @@ -122,12 +122,12 @@ new "netconf edit config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" # Too many quotes -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF else -cat < $tmp # new +cat < $tmp # old ]]>]]> EOF fi @@ -136,7 +136,7 @@ new "netconf get config xpath" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth1true]]>]]>$" # Too many quotes -if [ -n "$XPATH_USE_NEW" ]; then +if [ -z "$COMPAT_XSL" ]; then cat < $tmp # new ]]>]]> EOF diff --git a/test/test_order.sh b/test/test_order.sh index d0536dbf..aa7f3413 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -121,7 +121,7 @@ fi 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" 0 "]]>]]>" "^hejhoppdbcaabcddbarabarcbarbbarabarbbarcbardbar]]>]]>$" -if [ -n "$XPATH_USE_NEW" ]; then #new +if [ -z "$COMPAT_XSL" ]; then #new new "get each ordered-by user leaf-list" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^abar]]>]]>$" diff --git a/test/test_when_must.sh b/test/test_when_must.sh new file mode 100755 index 00000000..dd8f2af8 --- /dev/null +++ b/test/test_when_must.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Yang when and must conditional xpath specification +# Testing of validation phase. + +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang + +cat < $cfg + + $cfg + /usr/local/share/$APPNAME/yang + $APPNAME + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + +EOF + +cat < $fyang +module $APPNAME{ + prefix ex; + identity routing-protocol { + description + "Base identity from which routing protocol identities are + derived."; + } + identity direct { + base routing-protocol; + description + "Routing pseudo-protocol which provides routes to directly + connected networks."; + } + identity static { + base routing-protocol; + description + "Static routing pseudo-protocol."; + } + list whenex { + key "type name"; + leaf type { + type identityref { + base routing-protocol; + } + } + leaf name { + type string; + } + leaf route-preference { + type uint32; + } + container static-routes { + when "../type='static'" { + description + "This container is only valid for the 'static' + routing protocol."; + } + presence true; + } + } + container interface { + leaf ifType { + type enumeration { + enum ethernet; + enum atm; + } + } + leaf ifMTU { + type uint32; + } + must 'ifType != "ethernet" or ifMTU = 1500' { + error-message "An Ethernet MTU must be 1500"; + } + must 'ifType != "atm" or' + + ' (ifMTU <= 17966 and ifMTU >= 64)' { + error-message "An ATM MTU must be 64 .. 17966"; + } + } +} +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 -s init -f $cfg -y $fyang" +# start new backend +sudo clixon_backend -s init -f $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +new "when: add static route" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "staticr1]]>]]>" "^]]>]]>$" + +new "when: validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "when: add direct route" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "directr2]]>]]>" "^]]>]]>$" + +new "when get config" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^directr2staticr1]]>]]>$" + +new "when: validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorxpath static-routes validation failed]]>]]>$" + +new "when: discard-changes" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "must: add interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet1500]]>]]>" "^]]>]]>$" + +new "must: validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "must: add atm interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "atm32]]>]]>" "^]]>]]>$" + +new "must: atm validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn ATM MTU must be 64 .. 17966]]>]]>$" + +new "must: add eth interface" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet989]]>]]>" "^]]>]]>$" + +new "must: eth validate fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn Ethernet MTU must be 1500]]>]]>" + +# Check if still alive +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 + +rm -rf $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 7b5a7af6..8b5ec20c 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -136,7 +136,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" -if [ -n "$XPATH_USE_NEW" ]; then # new +if [ -z "$COMPAT_XSL" ]; then # new new "netconf get leaf-list path" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$" else # old From cc6c7ae7a4b43125b3f42c56b41a106fd9c17059 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 18 Jul 2018 21:45:13 +0200 Subject: [PATCH 41/53] started min/max-element --- CHANGELOG.md | 4 +- lib/src/clixon_xml_map.c | 37 +++++++++-- test/test_list.sh | 129 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 7 deletions(-) create mode 100755 test/test_list.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 011ea488..b2626d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ ## 3.7.0 (Upcoming) ### Major changes: -* Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5 +* Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5. * XPATH checked at validation time -* Support for YANG identity, identityref, must and when according to RFC 7950 Sec 7.189. +* Support for YANG identity, identityref, according to RFC 7950 Sec 7.18 and 9.10. * Previous support did no validation of values. * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 368c5822..761f2b65 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -430,7 +430,7 @@ xml_yang_validate_all(cxobj *xt, yang_stmt *yc; /* yang child */ yang_stmt *ye; /* yang must error-message */ char *xpath; - int b; + int nr; /* if not given by argument (overide) use default link and !Node has a config sub-statement and it is false */ @@ -453,6 +453,33 @@ xml_yang_validate_all(cxobj *xt, if (validate_identityref(xt, ys, yc) < 0) goto done; } + } + if ((yc = yang_find((yang_node*)ys, Y_MIN_ELEMENTS, NULL)) != NULL){ + /* The behavior of the constraint depends on the type of the + * leaf-list's or list's closest ancestor node in the schema tree + * that is not a non-presence container (see Section 7.5.1): + * o If no such ancestor exists in the schema tree, the constraint + * is enforced. + * o Otherwise, if this ancestor is a case node, the constraint is + * enforced if any other node from the case exists. + * o Otherwise, it is enforced if the ancestor node exists. + */ +#if 0 + cxobj *xp; + cxobj *x; + int i; + + if ((xp = xml_parent(xt)) != NULL){ + nr = atoi(yc->ys_argument); + x = NULL; + i = 0; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) + i++; + } +#endif + } + if ((yc = yang_find((yang_node*)ys, Y_MAX_ELEMENTS, NULL)) != NULL){ + } break; default: @@ -464,9 +491,9 @@ xml_yang_validate_all(cxobj *xt, if (yc->ys_keyword != Y_MUST) continue; xpath = yc->ys_argument; /* "must" has xpath argument */ - if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0) goto done; - if (!b){ + if (!nr){ if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL) clicon_err(OE_DB, 0, "%s", ye->ys_argument); else @@ -477,9 +504,9 @@ xml_yang_validate_all(cxobj *xt, /* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){ xpath = yc->ys_argument; /* "when" has xpath argument */ - if ((b = xpath_vec_bool(xt, "%s", xpath)) < 0) + if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0) goto done; - if (!b){ + if (!nr){ clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); goto done; } diff --git a/test/test_list.sh b/test/test_list.sh new file mode 100755 index 00000000..657b5457 --- /dev/null +++ b/test/test_list.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Yang list / leaf-list operations. min/max-elements + +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh + +exit 0 # NYI + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang + +cat < $cfg + + $cfg + /usr/local/share/$APPNAME/yang + $APPNAME + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + +EOF + +cat < $fyang +module $APPNAME{ + container c{ + presence true; + list a0{ + key k; + leaf k { + type string; + } + min-elements 0; + max-elements "unbounded"; + } + list a1{ + key k; + leaf k { + type string; + } + description "There must be at least one of these"; + min-elements 1; + max-elements 2; + } + leaf-list b0{ + type string; + min-elements 0; + max-elements "unbounded"; + } + leaf-list b1{ + description "There must be at least one of these"; + type string; + min-elements 1; + max-elements 2; + } + } +} +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 -s init -f $cfg -y $fyang" +# start new backend +sudo clixon_backend -s init -f $cfg -y $fyang +if [ $? -ne 0 ]; then + err +fi + +new "minmax: minimal" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace00]]>]]>" "^]]>]]>$" + +new "minmax: minimal validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: maximal" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace01unbounded0101unbounded01]]>]]>" "^]]>]]>$" + +new "minmax: validate ok" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: empty" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace]]>]]>" "^]]>]]>$" + +new "minmax: validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: no list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace0]]>]]>" "^]]>]]>$" + +new "minmax: validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: no leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace0]]>]]>" "^]]>]]>$" + +new "minmax: validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: Too large list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace0120]]>]]>" "^]]>]]>$" + +new "minmax: validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + +new "minmax: Too large leaf-list" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace0012]]>]]>" "^]]>]]>$" + +new "minmax: validate should fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" + + + +# kill backend +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir From c71791f1684381f10fca13a8358b72db066d2a22 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 19 Jul 2018 09:57:38 +0200 Subject: [PATCH 42/53] Added util directory containing test applications --- CHANGELOG.md | 2 +- Makefile.in | 2 +- configure | 3 +- configure.ac | 3 +- lib/src/Makefile.in | 25 +----- lib/src/clixon_xpath.c | 2 +- test/test_json.sh | 2 +- test/test_xml.sh | 2 +- test/test_xpath.sh | 2 +- test/test_yang_parse.sh | 2 +- util/Makefile.in | 112 ++++++++++++++++++++++++++ util/README.md | 5 ++ {lib/src => util}/clixon_util_json.c | 0 {lib/src => util}/clixon_util_xml.c | 0 {lib/src => util}/clixon_util_xpath.c | 0 {lib/src => util}/clixon_util_yang.c | 0 16 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 util/Makefile.in create mode 100644 util/README.md rename {lib/src => util}/clixon_util_json.c (100%) rename {lib/src => util}/clixon_util_xml.c (100%) rename {lib/src => util}/clixon_util_xpath.c (100%) rename {lib/src => util}/clixon_util_yang.c (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2626d1f..a95f4535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ ### Minor changes: * Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. -* Dedicated standalone xml,json,yang and xpath parser utility test programs added under lib/src/. +* Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. * CDATA xml support (patch by David Cornejo, Netgate) * Encode and decode (parsing) support * Validation of yang bits type space-separated list value diff --git a/Makefile.in b/Makefile.in index 04c72ab5..9c914381 100644 --- a/Makefile.in +++ b/Makefile.in @@ -52,7 +52,7 @@ INSTALL = @INSTALL@ INCLUDES = -I. -I@srcdir@ @INCLUDES@ SHELL = /bin/sh -SUBDIRS = lib apps include etc datastore yang +SUBDIRS = lib apps include etc datastore util yang .PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker diff --git a/configure b/configure index 88710c13..3312a01a 100755 --- a/configure +++ b/configure @@ -4325,7 +4325,7 @@ _ACEOF -ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile yang/Makefile doc/Makefile" +ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5043,6 +5043,7 @@ do "docker/netconf/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Dockerfile" ;; "datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;; "datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;; + "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; "yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; diff --git a/configure.ac b/configure.ac index 6c122768..44614911 100644 --- a/configure.ac +++ b/configure.ac @@ -211,7 +211,8 @@ AC_OUTPUT(Makefile docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile + util/Makefile yang/Makefile - doc/Makefile + doc/Makefile ) diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 931bd41d..a37680c9 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -80,14 +80,6 @@ YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ lex.clixon_json_parse.o clixon_json_parse.tab.o \ lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o -# Extra applications. Utilities, unit testings. Not installed. -APPSRC = clixon_util_xml.c -APPSRC += clixon_util_json.c -APPSRC += clixon_util_yang.c -APPSRC += clixon_util_xpath.c - -APPS = $(APPSRC:.c=) - # Generated src GENSRC = build.c @@ -101,10 +93,10 @@ MYLIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) MYLIBSO = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR) MYLIBLINK = libclixon$(SH_SUFFIX) -all: $(MYLIB) $(MYLIBLINK) $(APPS) +all: $(MYLIB) $(MYLIBLINK) clean: - rm -f $(OBJS) $(MYLIB) $(APPS) $(MYLIBLINK) $(GENOBJS) $(GENSRC) *.core + rm -f $(OBJS) $(MYLIB) $(MYLIBLINK) $(GENOBJS) $(GENSRC) *.core rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co] rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co] rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co] @@ -174,19 +166,6 @@ clixon_xpath_parse.tab.c clixon_xpath_parse.tab.h: clixon_xpath_parse.y lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $< -# APPS -clixon_util_xml: clixon_util_xml.c $(MYLIB) - $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ - -clixon_util_json: clixon_util_json.c $(MYLIB) - $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ - -clixon_util_yang: clixon_util_yang.c $(MYLIB) - $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ - -clixon_util_xpath: clixon_util_xpath.c $(MYLIB) - $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ - distclean: clean rm -f Makefile *~ .depend diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 34633e8a..05fbf56e 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -697,7 +697,7 @@ xp_relop(xp_ctx *xc1, case XT_BOOL: /* comparison on the boolean and the result of converting the node-set to a boolean using the boolean function is true. */ - b = ctx2boolean(xc); + b = ctx2boolean(xc1); switch(op){ case XO_EQ: xr->xc_bool = (b == xc2->xc_bool); diff --git a/test/test_json.sh b/test/test_json.sh index 63543abd..5c33ccd3 100755 --- a/test/test_json.sh +++ b/test/test_json.sh @@ -1,6 +1,6 @@ #!/bin/bash # Test: JSON parser tests -PROG=../lib/src/clixon_util_json +PROG=../util/clixon_util_json # include err() and new() functions and creates $dir . ./lib.sh diff --git a/test/test_xml.sh b/test/test_xml.sh index 4dd63631..9e4e8c70 100755 --- a/test/test_xml.sh +++ b/test/test_xml.sh @@ -1,6 +1,6 @@ #!/bin/bash # Test: XML parser tests -PROG=../lib/src/clixon_util_xml +PROG=../util/clixon_util_xml # include err() and new() functions and creates $dir . ./lib.sh diff --git a/test/test_xpath.sh b/test/test_xpath.sh index 4d23996a..b0b53a1c 100755 --- a/test/test_xpath.sh +++ b/test/test_xpath.sh @@ -1,6 +1,6 @@ #!/bin/bash # Test: XPATH tests -PROG=../lib/src/clixon_util_xpath +PROG=../util/clixon_util_xpath # include err() and new() functions and creates $dir . ./lib.sh diff --git a/test/test_yang_parse.sh b/test/test_yang_parse.sh index 1a80c89d..c98546ca 100755 --- a/test/test_yang_parse.sh +++ b/test/test_yang_parse.sh @@ -1,6 +1,6 @@ #!/bin/bash # Test: XML parser tests -PROG=../lib/src/clixon_util_yang +PROG=../util/clixon_util_yang # include err() and new() functions and creates $dir . ./lib.sh diff --git a/util/Makefile.in b/util/Makefile.in new file mode 100644 index 00000000..24379f48 --- /dev/null +++ b/util/Makefile.in @@ -0,0 +1,112 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright (C) 2009-2018 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 ***** +# +prefix = @prefix@ +datarootdir = @datarootdir@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +dbdir = @prefix@/db +mandir = @mandir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +sysconfdir = @sysconfdir@ +HOST_VENDOR = @host_vendor@ + +SH_SUFFIX = @SH_SUFFIX@ + +CLIXON_VERSION = @CLIXON_VERSION@ +CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ +CLIXON_MINOR = @CLIXON_VERSION_MINOR@ + +VPATH = @srcdir@ +CC = @CC@ +CFLAGS = @CFLAGS@ +INSTALL = @INSTALL@ +INSTALL_LIB = @INSTALL@ +INSTALLFLAGS = @INSTALLFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +CPPFLAGS = @CPPFLAGS@ + +INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir) + +MYLIB = ../lib/src/libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) + +# Utilities, unit testings. Not installed. +APPSRC = clixon_util_xml.c +APPSRC += clixon_util_json.c +APPSRC += clixon_util_yang.c +APPSRC += clixon_util_xpath.c + +APPS = $(APPSRC:.c=) + +all: $(APPS) + +clean: + rm -f $(APPS) *.core + +# APPS +clixon_util_xml: clixon_util_xml.c $(MYLIB) + $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $^ $(LIBS) -o $@ + +clixon_util_json: clixon_util_json.c $(MYLIB) + $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $^ $(LIBS) -o $@ + +clixon_util_yang: clixon_util_yang.c $(MYLIB) + $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $^ $(LIBS) -o $@ + +clixon_util_xpath: clixon_util_xpath.c $(MYLIB) + $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $^ $(LIBS) -o $@ + +distclean: clean + rm -f Makefile *~ .depend + +install: + +install-include: + +install-lib: + +uninstall: + +TAGS: + find . -name '*.[ch]' -print | etags - + +depend: + $(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPSRC) > .depend + +#include .depend + diff --git a/util/README.md b/util/README.md new file mode 100644 index 00000000..542cb620 --- /dev/null +++ b/util/README.md @@ -0,0 +1,5 @@ +# Clixon utils + +This directory contains Clixon utility programs, ie, programs that are +good to have for testing, analysis, etc, but not an actual part of +delivered code. \ No newline at end of file diff --git a/lib/src/clixon_util_json.c b/util/clixon_util_json.c similarity index 100% rename from lib/src/clixon_util_json.c rename to util/clixon_util_json.c diff --git a/lib/src/clixon_util_xml.c b/util/clixon_util_xml.c similarity index 100% rename from lib/src/clixon_util_xml.c rename to util/clixon_util_xml.c diff --git a/lib/src/clixon_util_xpath.c b/util/clixon_util_xpath.c similarity index 100% rename from lib/src/clixon_util_xpath.c rename to util/clixon_util_xpath.c diff --git a/lib/src/clixon_util_yang.c b/util/clixon_util_yang.c similarity index 100% rename from lib/src/clixon_util_yang.c rename to util/clixon_util_yang.c From 9245ef222aa86f70535480f6e68d65c6d84c36b4 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Thu, 19 Jul 2018 08:03:07 +0000 Subject: [PATCH 43/53] util warnings --- lib/src/clixon_xpath_ctx.c | 2 +- util/Makefile.in | 2 +- util/clixon_util_xpath.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c index 39221a37..721df45c 100644 --- a/lib/src/clixon_xpath_ctx.c +++ b/lib/src/clixon_xpath_ctx.c @@ -162,7 +162,7 @@ ctx_print(cbuf *cb, int ctx2boolean(xp_ctx *xc) { - int b; + int b = -1; switch (xc->xc_type){ case XT_NODESET: b = (xc->xc_size != 0); diff --git a/util/Makefile.in b/util/Makefile.in index 24379f48..d8961628 100644 --- a/util/Makefile.in +++ b/util/Makefile.in @@ -61,7 +61,7 @@ LIBS = @LIBS@ CPPFLAGS = @CPPFLAGS@ -INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir) +INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib -I$(top_srcdir)/include MYLIB = ../lib/src/libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 7bc849de..2ee8d2e8 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -62,7 +62,7 @@ echo "a\n" | xpath #include /* clixon */ -#include +#include "clixon/clixon.h" static int usage(char *argv0) @@ -128,7 +128,7 @@ main(int argc, char **argv) char *xpath0 = NULL; char *filename; xp_ctx *xc = NULL; - cbuf *cb; + cbuf *cb = NULL; clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR); optind = 1; From 226c59f25a9ed606408bba062fd4d28b342b71ca Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 20 Jul 2018 10:51:12 +0200 Subject: [PATCH 44/53] Backward compatible XPATH via symbols, not macros --- CHANGELOG.md | 23 +++--- lib/clixon/clixon_xpath.h | 23 ++---- lib/clixon/clixon_xsl.h | 13 +--- lib/src/clixon_xpath.c | 67 ++++++++++++++---- lib/src/clixon_xsl.c | 142 +++++++++----------------------------- util/Makefile.in | 2 +- util/clixon_util_json.c | 2 +- util/clixon_util_xml.c | 2 +- util/clixon_util_xpath.c | 2 +- util/clixon_util_yang.c | 2 +- 10 files changed, 114 insertions(+), 164 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a95f4535..28c19d6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Clixon Changelog ## 3.7.0 (Upcoming) -### Major changes: + +### Major New features * Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5. * XPATH checked at validation time @@ -10,17 +11,23 @@ * Validation of types and CLI expansion * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. -* Much improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex - * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` - * This includes all calls to `xpath_vec, xpath_first`, etc. - * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` - * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. + +### API changes on existing features (you may need to change your code) + * Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. - -### Minor changes: + +* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex + * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` + * This includes all calls to `xpath_vec, xpath_first`, etc. + * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` + * xpath_each() is removed + * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. + +### Minor changes + * Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. * Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 15f241db..086903c8 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -84,31 +84,20 @@ extern const map_str2int xpopmap[]; * Prototypes */ #if defined(__GNUC__) && __GNUC__ >= 3 -int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); -int xpath_vec_nodeset_flag(cxobj *xcur, char *format, uint16_t flags, +int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); +int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); -cxobj *xpath_first_nodeset(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); +cxobj *xpath_first(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); #else -int xpath_vec_nodeset(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...); -int xpath_vec_nodeset_flag(cxobj *xcur, char *format, uint16_t flags, +int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...); +int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags, cxobj ***vec, size_t *veclen, ...); -cxobj *xpath_first_nodeset(cxobj *xcur, char *format, ...); +cxobj *xpath_first(cxobj *xcur, char *format, ...); int xpath_vec_bool(cxobj *xcur, char *format, ...); #endif int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp); -/* backward compatible */ -#ifdef COMPAT_XSL -#define xpath_first(cxtop, format, args...) xpath_first_xsl(cxtop, format, ##args) -#define xpath_vec(cxtop, format, vec, veclen, args...) xpath_vec_xsl(cxtop, format, vec, veclen, ##args) -#define xpath_vec_flag(cxtop, format, flags, vec, veclen, args...) xpath_vec_flag_xsl(cxtop, format, flags, vec, veclen, ##args) -#else -#define xpath_first(cxtop, format, args...) xpath_first_nodeset(cxtop, format, ##args) -#define xpath_vec(cxtop, format, vec, veclen, args...) xpath_vec_nodeset(cxtop, format, vec, veclen, ##args) -#define xpath_vec_flag(cxtop, format, flags, vec, veclen, args...) xpath_vec_nodeset_flag(cxtop, format, flags, vec, veclen, ##args) -#endif - #endif /* _CLIXON_XPATH_H */ diff --git a/lib/clixon/clixon_xsl.h b/lib/clixon/clixon_xsl.h index 2f19623d..5f470fef 100644 --- a/lib/clixon/clixon_xsl.h +++ b/lib/clixon/clixon_xsl.h @@ -39,17 +39,10 @@ /* * Prototypes */ -#if defined(__GNUC__) && __GNUC__ >= 3 -cxobj *xpath_first_xsl(cxobj *cxtop, char *format, ...) __attribute__ ((format (printf, 2, 3))); -int xpath_vec_xsl(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); -int xpath_vec_flag(cxobj *cxtop, char *format, uint16_t flags, - cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); -#else -cxobj *xpath_first_xsl(cxobj *cxtop, char *format, ...); -int xpath_vec_xsl(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...); +int xpath_vec_xsl(cxobj *cxtop, char *xpath, cxobj ***vec, size_t *veclen); int xpath_vec_flag_xsl(cxobj *cxtop, char *xpath, uint16_t flags, - cxobj ***vec, size_t *veclen, ...); -#endif + cxobj ***vec, size_t *veclen); +cxobj *xpath_first_xsl(cxobj *cxtop, char *xpath); cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev); diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 05fbf56e..7039c6de 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -62,6 +62,7 @@ #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_xml.h" +#include "clixon_xsl.h" #include "clixon_xpath_parse.h" #include "clixon_xpath_ctx.h" #include "clixon_xpath.h" @@ -1091,10 +1092,27 @@ xpath_vec_ctx(cxobj *xcur, return retval; } +/*! Xpath nodeset function where only the first matching entry is returned + * args: + * @param[in] xcur xml-tree where to search + * @param[in] xpath string with XPATH syntax + * @retval xml-tree of first match + * @retval NULL Error or not found + * + * @code + * cxobj *x; + * if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) { + * ... + * } + * @endcode + * @note the returned pointer points into the original tree so should not be freed fter use. + * @note return value does not see difference between error and not found + * @see also xpath_vec. + */ cxobj * -xpath_first_nodeset(cxobj *xcur, - char *format, - ...) +xpath_first(cxobj *xcur, + char *format, + ...) { cxobj *cx = NULL; va_list ap; @@ -1118,10 +1136,16 @@ xpath_first_nodeset(cxobj *xcur, goto done; } va_end(ap); +#ifdef COMPAT_XSL + if ((cx = xpath_first_xsl(xcur, xpath)) == NULL) + goto done; +#else if (xpath_vec_ctx(xcur, xpath, &xr) < 0) goto done; + if (xr && xr->xc_type == XT_NODESET && xr->xc_size) cx = xr->xc_nodeset[0]; +#endif done: if (xr) ctx_free(xr); @@ -1140,11 +1164,11 @@ xpath_first_nodeset(cxobj *xcur, * @retval -1 Error */ int -xpath_vec_nodeset(cxobj *xcur, - char *format, - cxobj ***vec, - size_t *veclen, - ...) +xpath_vec(cxobj *xcur, + char *format, + cxobj ***vec, + size_t *veclen, + ...) { int retval = -1; va_list ap; @@ -1168,6 +1192,10 @@ xpath_vec_nodeset(cxobj *xcur, goto done; } va_end(ap); +#ifdef COMPAT_XSL + if (xpath_vec_xsl(xcur, xpath, vec, veclen) < 0) + goto done; +#else if (xpath_vec_ctx(xcur, xpath, &xr) < 0) goto done; if (xr && xr->xc_type == XT_NODESET){ @@ -1175,6 +1203,7 @@ xpath_vec_nodeset(cxobj *xcur, xr->xc_nodeset = NULL; *veclen = xr->xc_size; } +#endif retval = 0; done: if (xr) @@ -1184,7 +1213,7 @@ xpath_vec_nodeset(cxobj *xcur, return retval; } -/* A restricted xpath that returns a vector of matches (only nodes marked with flags) +/* Xpath that returns a vector of matches (only nodes marked with flags) * @param[in] xcur xml-tree where to search * @param[in] xpath string with XPATH syntax * @param[in] flags Set of flags that return nodes must match (0 if all) @@ -1208,20 +1237,22 @@ xpath_vec_nodeset(cxobj *xcur, * @see also xpath_vec This is a specialized version. */ int -xpath_vec_nodeset_flag(cxobj *xcur, - char *format, - uint16_t flags, - cxobj ***vec, - size_t *veclen, - ...) +xpath_vec_flag(cxobj *xcur, + char *format, + uint16_t flags, + cxobj ***vec, + size_t *veclen, + ...) { int retval = -1; va_list ap; size_t len; char *xpath = NULL; xp_ctx *xr = NULL; +#ifndef COMPAT_XSL int i; cxobj *x; +#endif va_start(ap, veclen); len = vsnprintf(NULL, 0, format, ap); @@ -1239,6 +1270,10 @@ xpath_vec_nodeset_flag(cxobj *xcur, goto done; } va_end(ap); +#ifdef COMPAT_XSL + if (xpath_vec_flag_xsl(xcur, xpath, flags, vec, veclen) < 0) + goto done; +#else if (xpath_vec_ctx(xcur, xpath, &xr) < 0) goto done; @@ -1250,6 +1285,8 @@ xpath_vec_nodeset_flag(cxobj *xcur, goto done; } } +#endif + retval = 0; done: if (xr) diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 8f34a0a1..873b8532 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -859,11 +859,26 @@ xpath_choice(cxobj *xcur, return retval; } -/*! Help function to xpath_first +/*! Xpath nodeset function where only the first matching entry is returned + * args: + * @param[in] xcur xml-tree where to search + * @param[in] xpath string with XPATH syntax + * @retval xml-tree of first match + * @retval NULL Error or not found + * + * @code + * cxobj *x; + * if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) { + * ... + * } + * @endcode + * @note the returned pointer points into the original tree so should not be freed fter use. + * @note return value does not see difference between error and not found + * @see xpath_first. This is obsolete and only enabled with COMPAT_XSL */ -static cxobj * -xpath_first0(cxobj *xcur, - char *xpath) +cxobj * +xpath_first_xsl(cxobj *xcur, + char *xpath) { cxobj **vec1 = NULL; size_t vec1len = 0; @@ -881,57 +896,6 @@ xpath_first0(cxobj *xcur, return xn; } -/*! A restricted xpath function where the first matching entry is returned - * See xpath1() on details for subset. - * args: - * @param[in] xcur xml-tree where to search - * @param[in] xpath string with XPATH syntax - * @retval xml-tree of first match - * @retval NULL Error or not found - * - * @code - * cxobj *x; - * if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) { - * ... - * } - * @endcode - * @note the returned pointer points into the original tree so should not be freed after use. - * @note return value does not see difference between error and not found - * @see also xpath_vec. - */ -cxobj * -xpath_first_xsl(cxobj *xcur, - char *format, - ...) -{ - cxobj *retval = NULL; - va_list ap; - size_t len; - char *xpath; - - va_start(ap, format); - len = vsnprintf(NULL, 0, format, ap); - va_end(ap); - /* allocate a message string exactly fitting the message length */ - if ((xpath = malloc(len+1)) == NULL){ - clicon_err(OE_UNIX, errno, "malloc"); - goto done; - } - /* second round: compute write message from reason and args */ - va_start(ap, format); - if (vsnprintf(xpath, len+1, format, ap) < 0){ - clicon_err(OE_UNIX, errno, "vsnprintf"); - va_end(ap); - goto done; - } - va_end(ap); - retval = xpath_first0(xcur, xpath); - done: - if (xpath) - free(xpath); - return retval; -} - /*! A restricted xpath iterator that loops over all matching entries. Dont use. * * See xpath1() on details for subset. @@ -949,8 +913,7 @@ xpath_first_xsl(cxobj *xcur, * * @note The returned pointer points into the original tree so should not be freed * after use. - * @see also xpath, xpath_vec. - * @note uses a static variable: consider replacing with xpath_vec() instead + * @note obsolete. Dont use */ cxobj * xpath_each(cxobj *xcur, @@ -1014,41 +977,22 @@ xpath_each(cxobj *xcur, * @note Although the returned vector must be freed after use, * the returned xml trees should not. * @see also xpath_first, xpath_each. + * @see xpath_vec. This is obsolete and only enabled with COMPAT_XSL */ int xpath_vec_xsl(cxobj *xcur, - char *format, - cxobj ***vec, - size_t *veclen, - ...) + char *xpath, + cxobj ***vec, + size_t *veclen) { int retval = -1; - va_list ap; - size_t len; - char *xpath = NULL; - va_start(ap, veclen); - len = vsnprintf(NULL, 0, format, ap); - va_end(ap); - /* allocate a message string exactly fitting the message length */ - if ((xpath = malloc(len+1)) == NULL){ - clicon_err(OE_UNIX, errno, "malloc"); - goto done; - } - /* second round: compute write message from reason and args */ - va_start(ap, veclen); - if (vsnprintf(xpath, len+1, format, ap) < 0){ - clicon_err(OE_UNIX, errno, "vsnprintf"); - va_end(ap); - goto done; - } - va_end(ap); *vec = NULL; *veclen = 0; - retval = xpath_choice(xcur, xpath, 0x0, vec, veclen); + if (xpath_choice(xcur, xpath, 0x0, vec, veclen) < 0) + goto done; + retval = 0; done: - if (xpath) - free(xpath); return retval; } @@ -1073,43 +1017,23 @@ xpath_vec_xsl(cxobj *xcur, * @endcode * @Note that although the returned vector must be freed after use, the returned xml * trees need not be. - * @see also xpath_vec This is a specialized version. + * @see xpath_vec_flag. This is obsolete and only enabled with COMPAT_XSL */ int xpath_vec_flag_xsl(cxobj *xcur, - char *format, + char *xpath, uint16_t flags, cxobj ***vec, - size_t *veclen, - ...) + size_t *veclen) { int retval = -1; - va_list ap; - size_t len; - char *xpath; - va_start(ap, veclen); - len = vsnprintf(NULL, 0, format, ap); - va_end(ap); - /* allocate a message string exactly fitting the message length */ - if ((xpath = malloc(len+1)) == NULL){ - clicon_err(OE_UNIX, errno, "malloc"); - goto done; - } - /* second round: compute write message from reason and args */ - va_start(ap, veclen); - if (vsnprintf(xpath, len+1, format, ap) < 0){ - clicon_err(OE_UNIX, errno, "vsnprintf"); - va_end(ap); - goto done; - } - va_end(ap); *vec=NULL; *veclen = 0; - retval = xpath_choice(xcur, xpath, flags, vec, veclen); + if (xpath_choice(xcur, xpath, flags, vec, veclen) < 0) + goto done; + retval = 0; done: - if (xpath) - free(xpath); return retval; } diff --git a/util/Makefile.in b/util/Makefile.in index 24379f48..a17db716 100644 --- a/util/Makefile.in +++ b/util/Makefile.in @@ -61,7 +61,7 @@ LIBS = @LIBS@ CPPFLAGS = @CPPFLAGS@ -INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir) +INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) MYLIB = ../lib/src/libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index 8cffdf3a..dd16e0e0 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -56,7 +56,7 @@ #include /* clixon */ -#include +#include "clixon/clixon.h" /* * Turn this on to get a json parse and pretty print test program diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index ee32a9bb..72730000 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -54,7 +54,7 @@ #include /* clixon */ -#include +#include "clixon/clixon.h" /* * Turn this on to get a xml parse and pretty print test program diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 7bc849de..5daccb14 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -62,7 +62,7 @@ echo "a\n" | xpath #include /* clixon */ -#include +#include "clixon/clixon.h" static int usage(char *argv0) diff --git a/util/clixon_util_yang.c b/util/clixon_util_yang.c index 1526ae62..22847b0d 100644 --- a/util/clixon_util_yang.c +++ b/util/clixon_util_yang.c @@ -58,7 +58,7 @@ #include /* clixon */ -#include +#include "clixon/clixon.h" /* */ From e79e48b46fdb0c9f23e67315b01f081e03dc1974 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Fri, 20 Jul 2018 10:02:35 +0000 Subject: [PATCH 45/53] xpath unitialized vecs --- lib/clixon/clixon_xpath.h | 4 ++-- lib/src/clixon_xpath.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 086903c8..ce62fc23 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -86,14 +86,14 @@ extern const map_str2int xpopmap[]; #if defined(__GNUC__) && __GNUC__ >= 3 int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags, - cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); + cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6))); cxobj *xpath_first(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3))); #else int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...); int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags, - cxobj ***vec, size_t *veclen, ...); + cxobj ***vec, size_t *veclen, ...); cxobj *xpath_first(cxobj *xcur, char *format, ...); int xpath_vec_bool(cxobj *xcur, char *format, ...); diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 7039c6de..0f0575e4 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -1192,6 +1192,8 @@ xpath_vec(cxobj *xcur, goto done; } va_end(ap); + *vec=NULL; + *veclen = 0; #ifdef COMPAT_XSL if (xpath_vec_xsl(xcur, xpath, vec, veclen) < 0) goto done; @@ -1270,13 +1272,14 @@ xpath_vec_flag(cxobj *xcur, goto done; } va_end(ap); + *vec=NULL; + *veclen = 0; #ifdef COMPAT_XSL if (xpath_vec_flag_xsl(xcur, xpath, flags, vec, veclen) < 0) goto done; #else if (xpath_vec_ctx(xcur, xpath, &xr) < 0) goto done; - if (xr && xr->xc_type == XT_NODESET){ for (i=0; ixc_size; i++){ x = xr->xc_nodeset[i]; From b894c30bfb6fc74b2dfca46297eb1fd3e66ee092 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Fri, 20 Jul 2018 15:52:32 +0000 Subject: [PATCH 46/53] obsoleted xpath_each --- CHANGELOG.md | 50 ++++++++++++++++++++++------------------- lib/clixon/clixon_xsl.h | 3 ++- lib/src/clixon_xpath.c | 11 +++++++++ lib/src/clixon_xsl.c | 2 ++ 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c19d6b..44d6e9a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,36 +12,23 @@ * Example extended with inclusion of iana-if-type RFC 7224 interface identities * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. +* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex, see also API changes below. + ### API changes on existing features (you may need to change your code) * Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. +* New XPATH 1.0 leads to some API changes and corrections + * Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` + * This includes all calls to `xpath_vec, xpath_first`, etc. + * In CLI specs, calls to cli_copy_config() must change 2nd argument from `x[%s=%s]` to `x[%s='%s']` + * In CLI specs, calls to cli_show_config() may need to change third argument, eg + * `cli_show_config("running","text","/profile[name=%s]","name")` to `cli_show_config("running","text","/profile[name='%s']","name")` + * xpath_each() is removed + * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. -* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex - * NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]` - * This includes all calls to `xpath_vec, xpath_first`, etc. - * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']` - * xpath_each() is removed - * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. - -### Minor changes - -* Added systemd example files under example/systemd -* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. -* Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. -* CDATA xml support (patch by David Cornejo, Netgate) - * Encode and decode (parsing) support -* Validation of yang bits type space-separated list value -* Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests -* Added a generated CLI show command that works on the generated parse tree with auto completion. - * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); - * The example contains a more elaborate example. - * Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24 -* Added xmlns validation - * for eg -* Added yang identityref runtime validation * Removed cli callback vector functions. Set COMPAT_CLIV if you need to keep these functions in include/clixon_custom.h. * Replace functions as follows in CLI SPEC files: * cli_setv --> cli_set @@ -63,6 +50,23 @@ * show_yangv --> show_yang * show_confv_xpath --> show_conf_xpath +### Minor changes + +* Added systemd example files under example/systemd +* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. +* Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. +* CDATA xml support (patch by David Cornejo, Netgate) + * Encode and decode (parsing) support +* Validation of yang bits type space-separated list value +* Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests +* Added a generated CLI show command that works on the generated parse tree with auto completion. + * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); + * The example contains a more elaborate example. + * Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24 +* Added xmlns validation + * for eg +* Added yang identityref runtime validation + * Added --enable-debug. * Added cligen variable translation. * See FAQ and example diff --git a/lib/clixon/clixon_xsl.h b/lib/clixon/clixon_xsl.h index 5f470fef..5d572528 100644 --- a/lib/clixon/clixon_xsl.h +++ b/lib/clixon/clixon_xsl.h @@ -43,7 +43,8 @@ int xpath_vec_xsl(cxobj *cxtop, char *xpath, cxobj ***vec, size_t *veclen); int xpath_vec_flag_xsl(cxobj *cxtop, char *xpath, uint16_t flags, cxobj ***vec, size_t *veclen); cxobj *xpath_first_xsl(cxobj *cxtop, char *xpath); +#ifdef COMPAT_XSL cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev); - +#endif #endif /* _CLIXON_XSL_H */ diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 0f0575e4..92c4e496 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -1162,6 +1162,17 @@ xpath_first(cxobj *xcur, * @param[out] veclen returns length of vector in return value * @retval 0 OK * @retval -1 Error + * @code + * cxobj **vec; + * size_t veclen; + * if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0) + * goto err; + * for (i=0; i Date: Fri, 20 Jul 2018 23:16:26 +0200 Subject: [PATCH 47/53] memleaks --- apps/backend/backend_client.c | 2 ++ lib/src/clixon_xml_map.c | 2 ++ lib/src/clixon_xpath.c | 8 +++++++- lib/src/clixon_yang.c | 8 ++++---- lib/src/clixon_yang_parse.y | 3 ++- test/test_auth.sh | 3 ++- test/test_auth_ext.sh | 8 +++++--- test/test_cli.sh | 3 ++- test/test_identity.sh | 2 +- test/test_json.sh | 5 +++-- test/test_leafref.sh | 2 +- test/test_list.sh | 2 +- test/test_netconf.sh | 2 +- test/test_order.sh | 2 +- test/test_perf.sh | 2 +- test/test_restconf.sh | 3 +-- test/test_restconf2.sh | 3 ++- test/test_type.sh | 2 +- test/test_when_must.sh | 2 +- test/test_xml.sh | 9 +++++---- test/test_xpath.sh | 1 + test/test_yang.sh | 2 +- test/test_yang_parse.sh | 5 +++-- 23 files changed, 50 insertions(+), 31 deletions(-) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 9528ac74..7ffdbf2b 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -531,6 +531,8 @@ from_client_unlock(clicon_handle h, ok: retval = 0; done: + if (cbx) + cbuf_free(cbx); return retval; } diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 761f2b65..301dfa0d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -333,6 +333,8 @@ validate_identityref(cxobj *xt, } retval = 0; done: + if (cb) + cbuf_free(cb); return retval; } diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 92c4e496..173b30e5 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -326,6 +326,10 @@ xp_eval_step(xp_ctx *xc0, if (cxvec_append(xp, &xc->xc_nodeset, &xc->xc_size) < 0) goto done; } + if (vec){ + free(vec); + vec = NULL; + } break; case A_PRECEEDING: break; @@ -1080,8 +1084,10 @@ xpath_vec_ctx(cxobj *xcur, goto done; if (xp_eval(&xc, xy.xy_top, xrp) < 0) goto done; - if (xc.xc_nodeset) + if (xc.xc_nodeset){ free(xc.xc_nodeset); + xc.xc_nodeset = NULL; + } /* done: */ xpath_parse_exit(&xy); xpath_scan_exit(&xy); diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index d034798b..7008ae71 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1616,7 +1616,6 @@ yang_parse_str(char *str, yy.yy_parse_string = str; yy.yy_stack = NULL; yy.yy_module = NULL; /* this is the return value - the module/sub-module */ - if (ystack_push(&yy, (yang_node*)yspec) == NULL) goto done; if (strlen(str)){ /* Not empty */ @@ -1640,6 +1639,8 @@ yang_parse_str(char *str, ymod = yy.yy_module; done: ystack_pop(&yy); + if (yy.yy_stack) + free (yy.yy_stack); return ymod; /* top-level (sub)module */ } @@ -2281,7 +2282,7 @@ ys_parse(yang_stmt *ys, * That is, siblings, etc, may not be there. Complete checks are made in * ys_populate instead. * @param[in] ys yang statement - * @param[in] extra Yang extra for cornercases (unknown/extension) + * @param[in] extra Yang extra for cornercases (unknown/extension). * * The cv:s created in parse-tree as follows: * fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass) @@ -2335,12 +2336,11 @@ ys_parse_sub(yang_stmt *ys, clicon_err(OE_YANG, errno, "Parsing CV: %s", reason); goto done; } - free(extra); break; default: break; } - ok: + ok: retval = 0; done: if (prefix) diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index 219f9766..2dc7d8f2 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -343,8 +343,9 @@ unknown_stmt : ustring ':' ustring ';' } | ustring ':' ustring ' ' string ';' { char *id; if ((id=prefix_id_join($1, $3)) == NULL) _YYERROR("0"); - if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL) _YYERROR("0"); + if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("0"); } clicon_debug(2,"unknown-stmt -> ustring : ustring string"); + if ($5) free($5); } ; diff --git a/test/test_auth.sh b/test/test_auth.sh index 47679cd4..6837f8e8 100755 --- a/test/test_auth.sh +++ b/test/test_auth.sh @@ -131,7 +131,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi @@ -195,6 +195,7 @@ expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf +new "Kill backend" pid=`pgrep clixon_backend` if [ -z "$pid" ]; then err "backend already dead" diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh index 43c0aa88..c53dcf55 100755 --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -169,17 +169,18 @@ sudo clixon_backend -zf $cfg -y $fyang if [ $? -ne 0 ]; then err fi - +sleep 1 new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi +sleep 1 new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf -sleep 1 + new "start restconf daemon (-a is enable http basic auth)" sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang -- -a @@ -246,6 +247,7 @@ expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf +new "Kill backend" pid=`pgrep clixon_backend` if [ -z "$pid" ]; then err "backend already dead" diff --git a/test/test_cli.sh b/test/test_cli.sh index 1b9adce8..716fb9d3 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -37,7 +37,8 @@ if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg" -sudo $clixon_backend -s init -f $cfg +sudo $clixon_backend -s init -f $cfg + if [ $? -ne 0 ]; then err fi diff --git a/test/test_identity.sh b/test/test_identity.sh index 3df22c73..684d7416 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -114,7 +114,7 @@ if [ $? -ne 0 ]; then fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang # -D 1 +sudo $clixon_backend -s init -f $cfg -y $fyang # -D 1 if [ $? -ne 0 ]; then err fi diff --git a/test/test_json.sh b/test/test_json.sh index 5c33ccd3..b3f9e4d6 100755 --- a/test/test_json.sh +++ b/test/test_json.sh @@ -1,14 +1,15 @@ #!/bin/bash # Test: JSON parser tests +#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_json" PROG=../util/clixon_util_json # include err() and new() functions and creates $dir . ./lib.sh new "json parse" -expecteof $PROG 0 '{"foo": -23}' "^-23$" +expecteof "$PROG" 0 '{"foo": -23}' "^-23$" new "json parse list" -expecteof $PROG 0 '{"a":[0,1,2,3]}' "^0123$" +expecteof "$PROG" 0 '{"a":[0,1,2,3]}' "^0123$" rm -rf $dir diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 30f49a78..0e93e704 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -78,7 +78,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_list.sh b/test/test_list.sh index 657b5457..25e2ee37 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -71,7 +71,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 1a78f1cc..a520c09a 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -76,7 +76,7 @@ if [ $? -ne 0 ]; then fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang # -D 1 +sudo $clixon_backend -s init -f $cfg -y $fyang # -D 1 if [ $? -ne 0 ]; then err fi diff --git a/test/test_order.sh b/test/test_order.sh index aa7f3413..848e839a 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -112,7 +112,7 @@ fi new "start backend" # start new backend -sudo clixon_backend -s running -f $cfg -y $fyang +sudo $clixon_backend -s running -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_perf.sh b/test/test_perf.sh index 16f7b19a..7fe241a0 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -63,7 +63,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index f4856e81..7201eb74 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -83,7 +83,7 @@ if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg -y $fyang" -sudo clixon_backend -s init -f $cfg -y $fyang # -D 1 +sudo $clixon_backend -s init -f $cfg -y $fyang # -D 1 if [ $? -ne 0 ]; then err fi @@ -291,7 +291,6 @@ fi # Cant get shell macros to work, inline matching from lib.sh - new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 26cf56d7..dfd7a265 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -50,7 +50,8 @@ if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg -y $fyang" -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang + if [ $? -ne 0 ]; then err fi diff --git a/test/test_type.sh b/test/test_type.sh index 1535f3b3..0c1f11ef 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -144,7 +144,7 @@ if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg -y $fyang" -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_when_must.sh b/test/test_when_must.sh index dd8f2af8..ab4126e1 100755 --- a/test/test_when_must.sh +++ b/test/test_when_must.sh @@ -96,7 +96,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_xml.sh b/test/test_xml.sh index 9e4e8c70..9550c402 100755 --- a/test/test_xml.sh +++ b/test/test_xml.sh @@ -1,12 +1,13 @@ #!/bin/bash # Test: XML parser tests +#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_xml" PROG=../util/clixon_util_xml # include err() and new() functions and creates $dir . ./lib.sh new "xml parse" -expecteof $PROG 0 "" "^$" +expecteof "$PROG" 0 "" "^$" XML=$(cat <An example of escaped CENDs @@ -24,7 +25,7 @@ XML=$(cat <An example of escaped CENDs +expecteof "$PROG" 0 "$XML" "^An example of escaped CENDs y\" so I guess that means that z > x ]]> ]]>]]>$" @@ -33,14 +34,14 @@ XML=$(cat <&" -expecteof $PROG 0 "$XML" "^$XML$" +expecteof "$PROG" 0 "$XML" "^$XML$" XML=$(cat <To allow attribute values to contain both single and double quotes, the apostrophe or single-quote character ' may be represented as ' and the double-quote character as " EOF ) new "xml optional encode single and double quote" -expecteof $PROG 0 "$XML" "^To allow attribute values to contain both single and double quotes, the apostrophe or single-quote character ' may be represented as ' and the double-quote character as \"$" +expecteof "$PROG" 0 "$XML" "^To allow attribute values to contain both single and double quotes, the apostrophe or single-quote character ' may be represented as ' and the double-quote character as \"$" rm -rf $dir diff --git a/test/test_xpath.sh b/test/test_xpath.sh index b0b53a1c..d8fcca34 100755 --- a/test/test_xpath.sh +++ b/test/test_xpath.sh @@ -1,5 +1,6 @@ #!/bin/bash # Test: XPATH tests +#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_xpath" PROG=../util/clixon_util_xpath # include err() and new() functions and creates $dir diff --git a/test/test_yang.sh b/test/test_yang.sh index 8b5ec20c..7ebbbf12 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -105,7 +105,7 @@ fi new "start backend -s init -f $cfg -y $fyang" # start new backend -sudo clixon_backend -s init -f $cfg -y $fyang +sudo $clixon_backend -s init -f $cfg -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_yang_parse.sh b/test/test_yang_parse.sh index c98546ca..5c3736eb 100755 --- a/test/test_yang_parse.sh +++ b/test/test_yang_parse.sh @@ -1,5 +1,6 @@ #!/bin/bash # Test: XML parser tests +#PROG="valgrind --leak-check=full --show-leak-kinds=all ../util/clixon_util_yang" PROG=../util/clixon_util_yang # include err() and new() functions and creates $dir @@ -12,13 +13,13 @@ module test{ description "Example from RFC 6020"; argument "name"; } - ex:not-defined ARGUMENT; + ex:not-defined ARGUMENT;; } EOF ) new "yang parse" -expecteof $PROG 0 "$YANG" "^$YANG$" +expecteof "$PROG" 0 "$YANG" "^$YANG$" rm -rf $dir From c7aed5e7a70ce9e006c9c8ade992b5cd3da0c49a Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sat, 21 Jul 2018 13:55:21 +0000 Subject: [PATCH 48/53] er image build and changed it to build a single clixon docker\ image which can be found at olofhagsand/clixon --- CHANGELOG.md | 2 +- README.md | 1 + configure | 8 +-- configure.ac | 6 -- doc/FAQ.md | 4 +- docker/{cli/Dockerfile.in => Dockerfile} | 37 +++++++++--- docker/Makefile.in | 47 ++++++--------- docker/README | 12 ---- docker/README.md | 19 ++++++ docker/backend/Dockerfile.in | 53 ---------------- docker/backend/Makefile.in | 77 ------------------------ docker/backend/README | 6 -- docker/cli/Makefile.in | 76 ----------------------- docker/cli/README | 6 -- docker/netconf/Dockerfile.in | 52 ---------------- docker/netconf/Makefile.in | 76 ----------------------- docker/netconf/README | 6 -- 17 files changed, 70 insertions(+), 418 deletions(-) rename docker/{cli/Dockerfile.in => Dockerfile} (72%) delete mode 100644 docker/README create mode 100644 docker/README.md delete mode 100644 docker/backend/Dockerfile.in delete mode 100644 docker/backend/Makefile.in delete mode 100644 docker/backend/README delete mode 100644 docker/cli/Makefile.in delete mode 100644 docker/cli/README delete mode 100644 docker/netconf/Dockerfile.in delete mode 100644 docker/netconf/Makefile.in delete mode 100644 docker/netconf/README diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d6e9a7..030813db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ ### Minor changes +* Updated the docker image build and changed it to build a single clixon docker image which can be found at olofhagsand/clixon. * Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. * Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. @@ -66,7 +67,6 @@ * Added xmlns validation * for eg * Added yang identityref runtime validation - * Added --enable-debug. * Added cligen variable translation. * See FAQ and example diff --git a/README.md b/README.md index 74812d4f..e422bb35 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ transaction support from a YANG specification. * [Runtime](#runtime) * [Clixon project page](http://www.clicon.org) * [Tests](test/) + * [Docker](docker/) * [Reference manual](http://www.clicon.org/doxygen/index.html) (Note: the link may not be up-to-date. It is better to build your own: `cd doc; make doc`) Background diff --git a/configure b/configure index 3312a01a..a71a99fe 100755 --- a/configure +++ b/configure @@ -4325,7 +4325,7 @@ _ACEOF -ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile" +ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5035,12 +5035,6 @@ do "example/docker/Makefile") CONFIG_FILES="$CONFIG_FILES example/docker/Makefile" ;; "extras/rpm/Makefile") CONFIG_FILES="$CONFIG_FILES extras/rpm/Makefile" ;; "docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;; - "docker/cli/Makefile") CONFIG_FILES="$CONFIG_FILES docker/cli/Makefile" ;; - "docker/cli/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/cli/Dockerfile" ;; - "docker/backend/Makefile") CONFIG_FILES="$CONFIG_FILES docker/backend/Makefile" ;; - "docker/backend/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/backend/Dockerfile" ;; - "docker/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Makefile" ;; - "docker/netconf/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Dockerfile" ;; "datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;; "datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;; "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; diff --git a/configure.ac b/configure.ac index 44614911..bf9b7c3a 100644 --- a/configure.ac +++ b/configure.ac @@ -203,12 +203,6 @@ AC_OUTPUT(Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile - docker/cli/Makefile - docker/cli/Dockerfile - docker/backend/Makefile - docker/backend/Dockerfile - docker/netconf/Makefile - docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile util/Makefile diff --git a/doc/FAQ.md b/doc/FAQ.md index 880e34dc..31fa0fb8 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -101,6 +101,8 @@ You can change where CLixon looks for the configuration FILE as follows: - FILE is /usr/local/etc/clixon.xml ## Can I run Clixon as docker containers? + +(Not updated) Yes, the example works as docker containers as well. backend and cli needs a common file-system so they need to run as a composed pair. ``` @@ -108,7 +110,7 @@ common file-system so they need to run as a composed pair. make docker # Prepares /data as shared file-system mount run.sh # Starts an example backend and a cli ``` -The containers are by default downloaded from dockerhib, but you may +The containers are by default downloaded from dockerhub, but you may build the containers locally: ``` cd docker diff --git a/docker/cli/Dockerfile.in b/docker/Dockerfile similarity index 72% rename from docker/cli/Dockerfile.in rename to docker/Dockerfile index 6b316d58..b07bc32b 100644 --- a/docker/cli/Dockerfile.in +++ b/docker/Dockerfile @@ -31,19 +31,38 @@ # ***** END LICENSE BLOCK ***** # -FROM ubuntu:14.04 -# 12.04 +FROM debian MAINTAINER Olof Hagsand ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y libqdbm-dev + +RUN apt-get update && apt-get install -y \ + git make gcc flex bison RUN groupadd clicon -COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/ -COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY libclixon_cli.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY clixon_cli /usr/bin/ + +# Create a directory to hold source-code, dependencies etc +RUN mkdir /clixon +WORKDIR /clixon + +# Clone cligen and clixon +RUN git clone https://github.com/olofhagsand/cligen.git +RUN git clone https://github.com/clicon/clixon.git + +# Build cligen +WORKDIR /clixon/cligen +RUN ./configure +RUN make +RUN make install + +# Build clixon +WORKDIR /clixon/clixon +RUN git checkout -b develop origin/develop +RUN ./configure --without-restconf +RUN make +RUN make install +RUN make install-include + +#RUN rm -rf /clixon RUN ldconfig -CMD ["/usr/bin/clixon_cli", "-f", "/data/clixon.conf"] - diff --git a/docker/Makefile.in b/docker/Makefile.in index 2a653879..58905f5b 100644 --- a/docker/Makefile.in +++ b/docker/Makefile.in @@ -38,46 +38,33 @@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ +# Change this +IMAGE = olofhagsand/clixon + SHELL = /bin/sh -SUBDIRS = cli backend netconf -.PHONY: all clean depend install $(SUBDIRS) docker push +.PHONY: all clean depend install docker push -all: $(SUBDIRS) - -depend: - for i in $(SUBDIRS); \ - do (cd $$i; $(MAKE) $(MFLAGS) $@); done - -$(SUBDIRS): - (cd $@; $(MAKE) $(MFLAGS) all) - -install-include: - for i in $(SUBDIRS); \ - do (cd $$i ; $(MAKE) $(MFLAGS) $@); done; - -install: - for i in $(SUBDIRS); \ - do (cd $$i; $(MAKE) $(MFLAGS) $@); done - -uninstall: - for i in $(SUBDIRS); \ - do (cd $$i; $(MAKE) $(MFLAGS) $@); done +all: + @echo "Run make docker to build docker image" clean: - for i in $(SUBDIRS); \ - do (cd $$i; $(MAKE) $(MFLAGS) $@); done distclean: clean rm -f Makefile *~ .depend - for i in $(SUBDIRS); \ - do (cd $$i; $(MAKE) $(MFLAGS) $@); done docker: - for i in $(SUBDIRS); \ - do (cd $$i && $(MAKE) $(MFLAGS) $@); done + sudo docker build -t $(IMAGE) . push: - for i in $(SUBDIRS); \ - do (cd $$i && $(MAKE) $(MFLAGS) $@); done + sudo docker push $(IMAGE) + +depend: + +install-include: + +install: + +uninstall: + diff --git a/docker/README b/docker/README deleted file mode 100644 index 5fcd6d49..00000000 --- a/docker/README +++ /dev/null @@ -1,12 +0,0 @@ -This dir is not updated - -This dir contains docker code - how to build clixon as docker containers - cli Build olofhagsand/clixon_cli container - backend Build olofhagsand/clixon_backend container - netconf Build olofhagsand/clixon_netconf container - -Perform the build by 'make docker'. -You may also do 'make push' if you want to push the image, but you may then consider changing the image name (in the makefile:s). - -You may run the container directly by going directly to example and -the docker runtime scripts there \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..95c9af33 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,19 @@ +# Clixon base docker image + +This directory contains code for building and pushing a Clixon docker +container. By default it is pushed to olofhagsand/clixon, but you can change +the IMAGE in Makefile.in and push it to another name. + +The clixon docker image is a base image that can be used to build +clixon applications. It has all the whole code for a clixon release +which it downloads from git - it does not use local code. + +See example/docker for how to build a docker application using the base image. + +Build and push +============== +Perform the build by 'make docker'. +You may also do 'make push' if you want to push the image, but you may then consider changing the image name (in the makefile:s). + +You may run the container directly by going directly to example and +the docker runtime scripts there \ No newline at end of file diff --git a/docker/backend/Dockerfile.in b/docker/backend/Dockerfile.in deleted file mode 100644 index 69929f80..00000000 --- a/docker/backend/Dockerfile.in +++ /dev/null @@ -1,53 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# - -FROM ubuntu:14.04 -# 12.04 -MAINTAINER Olof Hagsand -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y libqdbm-dev -RUN groupadd clicon -COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/ -COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY libclixon_backend.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY clixon_backend /usr/sbin/ -RUN ldconfig -RUN sudo groupadd clixon -CMD ["/usr/sbin/clixon_backend", "-F", "-f", "/data/clixon.conf"] - - - - - - - diff --git a/docker/backend/Makefile.in b/docker/backend/Makefile.in deleted file mode 100644 index 21c1287e..00000000 --- a/docker/backend/Makefile.in +++ /dev/null @@ -1,77 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -prefix = @prefix@ -bindir = @bindir@ -sbindir = @sbindir@ -libdir = @libdir@ -includedir = @includedir@ -datarootdir = @datarootdir@ -# You may consider changing this -image = olofhagsand/clixon_backend - -all: - @echo "Run make docker to build docker image" -clean: - -distclean: clean - rm -f Makefile *~ .depend libcligen* libclixon* clixon_backend Dockerfile - -# Kind of reverse install, could have copied from src dir,... -.PHONY: docker push -docker: - cp $(DESTDIR)$(libdir)/libcligen.so.@CLIGEN_VERSION@ . - cp $(DESTDIR)$(libdir)/libclixon.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(libdir)/libclixon_backend.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(sbindir)/clixon_backend . - sudo docker build -t $(image) . - -push: - docker push $(image) - - -install: - -uninstall: - -install-include: - -depend: -# $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - -#include .depend - diff --git a/docker/backend/README b/docker/backend/README deleted file mode 100644 index d452c07a..00000000 --- a/docker/backend/README +++ /dev/null @@ -1,6 +0,0 @@ -Ensure that cligen and clixon has been built and installed. -sudo make docker -make push - -Then go to example and run the example as a docker container - diff --git a/docker/cli/Makefile.in b/docker/cli/Makefile.in deleted file mode 100644 index 349d4136..00000000 --- a/docker/cli/Makefile.in +++ /dev/null @@ -1,76 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -prefix = @prefix@ -bindir = @bindir@ -sbindir = @sbindir@ -libdir = @libdir@ -includedir = @includedir@ -datarootdir = @datarootdir@ -# You may consider changing this -image = olofhagsand/clixon_cli - -all: - @echo "Run make docker to build docker image" -clean: - -distclean: clean - rm -f Makefile *~ .depend libcligen* libclixon* clixon_cli Dockerfile - -# Kind of reverse install, could have copied from src dir,... -.PHONY: docker push -docker: - cp $(DESTDIR)$(libdir)/libcligen.so.@CLIGEN_VERSION@ . - cp $(DESTDIR)$(libdir)/libclixon.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(libdir)/libclixon_cli.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(bindir)/clixon_cli . - sudo docker build -t $(image) . - -push: - sudo docker push $(image) - -install: - -uninstall: - -install-include: - -depend: -# $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - -#include .depend - diff --git a/docker/cli/README b/docker/cli/README deleted file mode 100644 index d452c07a..00000000 --- a/docker/cli/README +++ /dev/null @@ -1,6 +0,0 @@ -Ensure that cligen and clixon has been built and installed. -sudo make docker -make push - -Then go to example and run the example as a docker container - diff --git a/docker/netconf/Dockerfile.in b/docker/netconf/Dockerfile.in deleted file mode 100644 index 9a8bcc45..00000000 --- a/docker/netconf/Dockerfile.in +++ /dev/null @@ -1,52 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# - -FROM ubuntu:14.04 -# 12.04 -MAINTAINER Olof Hagsand -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y libqdbm-dev -RUN groupadd clicon -COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/ -COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY libclixon_netconf.so.@CLIXON_VERSION_MAJOR@ /usr/lib/ -COPY clixon_netconf /usr/bin/ -RUN ldconfig -CMD ["/usr/bin/clixon_netconf", "-f", "/data/clixon.conf"] - - - - - - - diff --git a/docker/netconf/Makefile.in b/docker/netconf/Makefile.in deleted file mode 100644 index d099d3d2..00000000 --- a/docker/netconf/Makefile.in +++ /dev/null @@ -1,76 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -prefix = @prefix@ -bindir = @bindir@ -sbindir = @sbindir@ -libdir = @libdir@ -includedir = @includedir@ -datarootdir = @datarootdir@ -# You may consider changing this -image = olofhagsand/clixon_netconf - -all: - @echo "Run make docker to build docker image" -clean: - -distclean: clean - rm -f Makefile *~ .depend libcligen* libclixon* clixon_netconf Dockerfile - -# Kind of reverse install, could have copied from src dir,... -.PHONY: docker push -docker: - cp $(DESTDIR)$(libdir)/libcligen.so.@CLIGEN_VERSION@ . - cp $(DESTDIR)$(libdir)/libclixon.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(libdir)/libclixon_netconf.so.@CLIXON_VERSION_MAJOR@ . - cp $(DESTDIR)$(bindir)/clixon_netconf . - sudo docker build -t $(image) . - -push: - sudo docker push $(image) - -install: - -uninstall: - -install-include: - -depend: -# $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - -#include .depend - diff --git a/docker/netconf/README b/docker/netconf/README deleted file mode 100644 index d452c07a..00000000 --- a/docker/netconf/README +++ /dev/null @@ -1,6 +0,0 @@ -Ensure that cligen and clixon has been built and installed. -sudo make docker -make push - -Then go to example and run the example as a docker container - From 3a39b0361a4c051a51ada995c685ef8957a700af Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sat, 21 Jul 2018 17:01:56 +0000 Subject: [PATCH 49/53] docker update --- configure | 3 +- configure.ac | 1 - doc/FAQ.md | 18 ++---- docker/Dockerfile | 9 ++- example/{docker/Makefile.in => Dockerfile} | 57 +++++++------------ example/Makefile.in | 15 +++-- example/README.md | 14 +++++ example/docker/README | 7 --- example/docker/routing.conf.local | 66 ---------------------- example/docker/run.sh | 8 --- 10 files changed, 55 insertions(+), 143 deletions(-) rename example/{docker/Makefile.in => Dockerfile} (58%) delete mode 100644 example/docker/README delete mode 100644 example/docker/routing.conf.local delete mode 100755 example/docker/run.sh diff --git a/configure b/configure index a71a99fe..85acbab1 100755 --- a/configure +++ b/configure @@ -4325,7 +4325,7 @@ _ACEOF -ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile" +ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5032,7 +5032,6 @@ do "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;; "example/Makefile") CONFIG_FILES="$CONFIG_FILES example/Makefile" ;; - "example/docker/Makefile") CONFIG_FILES="$CONFIG_FILES example/docker/Makefile" ;; "extras/rpm/Makefile") CONFIG_FILES="$CONFIG_FILES extras/rpm/Makefile" ;; "docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;; "datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;; diff --git a/configure.ac b/configure.ac index bf9b7c3a..81f480f5 100644 --- a/configure.ac +++ b/configure.ac @@ -200,7 +200,6 @@ AC_OUTPUT(Makefile etc/Makefile etc/clixonrc example/Makefile - example/docker/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile diff --git a/doc/FAQ.md b/doc/FAQ.md index 31fa0fb8..37154618 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -102,21 +102,13 @@ You can change where CLixon looks for the configuration FILE as follows: ## Can I run Clixon as docker containers? -(Not updated) -Yes, the example works as docker containers as well. backend and cli needs a -common file-system so they need to run as a composed pair. +Yes, the example works as docker containers as well. There should be a +prepared container in docker hib for the example where the backend and +CLI is bundled. ``` - cd example/docker - make docker # Prepares /data as shared file-system mount - run.sh # Starts an example backend and a cli +sudo docker run -ti --rm olofhagsand/clixon_example ``` -The containers are by default downloaded from dockerhub, but you may -build the containers locally: -``` - cd docker - make docker -``` -You may also push the containers with 'make push' but you may then consider changing the image name in the makefile. +Look in the example documentation for more info. ## How do I use netconf? diff --git a/docker/Dockerfile b/docker/Dockerfile index b07bc32b..2a2ce179 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -36,7 +36,11 @@ MAINTAINER Olof Hagsand ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y \ - git make gcc flex bison + git make gcc flex bison \ + libfcgi-dev \ + libcurl4-openssl-dev + + RUN groupadd clicon # Create a directory to hold source-code, dependencies etc @@ -56,12 +60,11 @@ RUN make install # Build clixon WORKDIR /clixon/clixon RUN git checkout -b develop origin/develop -RUN ./configure --without-restconf +RUN ./configure RUN make RUN make install RUN make install-include -#RUN rm -rf /clixon RUN ldconfig diff --git a/example/docker/Makefile.in b/example/Dockerfile similarity index 58% rename from example/docker/Makefile.in rename to example/Dockerfile index 492c727f..24403dc5 100644 --- a/example/docker/Makefile.in +++ b/example/Dockerfile @@ -30,51 +30,32 @@ # # ***** END LICENSE BLOCK ***** # -VPATH = @srcdir@ -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -prefix = @prefix@ -bindir = @bindir@ -includedir = @includedir@ -datarootdir = @datarootdir@ -APPNAME = routing +FROM olofhagsand/clixon +MAINTAINER Olof Hagsand -all: $(APPNAME).conf +RUN apt-get update && apt-get install -y \ + procps # ps for debugging +# Create a directory to hold source-code, dependencies etc +RUN mkdir /example +WORKDIR /example --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk +# Clone clixon (again) since example application is there. +# Replace this with your application +RUN git clone https://github.com/clicon/clixon.git -# Kind of reverse install, could have copied from src dir,... -.PHONY: docker push -docker: $(APPNAME).conf - install -d data - install -d data/yang - install -d data/backend - install -d data/cli - install -d data/netconf - install -d data/clispec - install $(APPNAME).conf data/clixon.conf # docker image assumes /data/clixon.conf - install ../*.yang data/yang/ - install ../routing_cli.so data/cli/ - install ../routing_backend.so data/backend/ - install ../routing_netconf.so data/netconf/ - install ../*.cli data/clispec +# Build clixon +WORKDIR /example/clixon +RUN git checkout -b develop origin/develop +RUN ./configure +WORKDIR /example/clixon/example +RUN make +RUN make install +RUN install example.xml /usr/local/etc/clixon.xml -clean: - rm -f $(APPNAME).conf +CMD /usr/local/sbin/clixon_backend && /usr/local/bin/clixon_cli -distclean: clean - rm -f Makefile *~ .depend - rm -rf data -install: -uninstall: -install-include: - -depend: - $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - -#include .depend diff --git a/example/Makefile.in b/example/Makefile.in index ac5f73d6..336968bf 100644 --- a/example/Makefile.in +++ b/example/Makefile.in @@ -51,6 +51,9 @@ CLI_PLUGIN = $(APPNAME)_cli.so NETCONF_PLUGIN = $(APPNAME)_netconf.so RESTCONF_PLUGIN = $(APPNAME)_restconf.so +# Example docker image +IMAGE = olofhagsand/clixon_example + PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) all: $(PLUGINS) @@ -106,11 +109,9 @@ OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ) clean: rm -f $(PLUGINS) $(OBJS) - (cd docker && $(MAKE) $(MFLAGS) $@) distclean: clean rm -f Makefile *~ .depend - (cd docker && $(MAKE) $(MFLAGS) $@) install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) $(APPNAME).xml install -d -m 0755 $(DESTDIR)$(clixon_SYSCONFDIR) @@ -128,20 +129,24 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NET install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/clispec install -m 0644 $(CLISPECS) $(DESTDIR)$(clixon_LIBDIR)/clispec install -d -m 0755 $(DESTDIR)$(clixon_LOCALSTATEDIR) - (cd docker && $(MAKE) $(MFLAGS) $@) + +docker: + sudo docker build -t $(IMAGE) . + +push: + sudo docker push $(IMAGE) + uninstall: rm -rf $(DESTDIR)$(clixon_SYSCONFDIR)/$(APPNAME).xml rm -rf $(DESTDIR)$(clixon_DBSPECDIR) rm -rf $(DESTDIR)$(clixon_LOCALSTATEDIR) rm -rf $(DESTDIR)$(clixon_LIBDIR) - (cd docker && $(MAKE) $(MFLAGS) $@) install-include: depend: $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - (cd docker && $(MAKE) $(MFLAGS) $@) #include .depend diff --git a/example/README.md b/example/README.md index be751154..c2368c2f 100644 --- a/example/README.md +++ b/example/README.md @@ -204,5 +204,19 @@ cd docker # look in README ``` +## Docker + +Run the example as a docker container as follows: +``` +sudo docker run -ti --rm olofhagsand/clixon_example +``` + +Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push: +``` +make docker +make push +sudo docker run -ti --rm you/clixon_example +``` + diff --git a/example/docker/README b/example/docker/README deleted file mode 100644 index 2d93a6a9..00000000 --- a/example/docker/README +++ /dev/null @@ -1,7 +0,0 @@ -Run the ietf routing example as docker container. -Use the dockerhub container, or alternatively, build clicon as docker images -by doing make docker in the top builddir. - - (cd ..; make) # Make example - make docker # Create config file and shared file system - run.sh # Run a backend and a cli container diff --git a/example/docker/routing.conf.local b/example/docker/routing.conf.local deleted file mode 100644 index a5f96dd2..00000000 --- a/example/docker/routing.conf.local +++ /dev/null @@ -1,66 +0,0 @@ -# Main YANG module first parsed by parser (in CLICON_YANG_DIR). eg clicon.yang. - -# Location of configuration-file for default values (this file) -CLICON_CONFIGFILE /data/clixon.conf -# Location of YANG module and submodule files. Only if CLICON_DBSPEC_TYPE is YANG -CLICON_YANG_DIR /data/yang -# Option used to construct initial yang file: -# [@] -# This option is only relevant if CLICON_DBSPEC_TYPE is YANG -# CLICON_YANG_MODULE_MAIN clicon -# Option used to construct initial yang file: -# [@] -# This option is only relevant if CLICON_DBSPEC_TYPE is YANG -CLICON_YANG_MODULE_REVISION -# Candidate qdbm database -CLICON_CANDIDATE_DB /data/candidate_db -# Running qdbm database -CLICON_RUNNING_DB /data/running_db -# Location of backend .so plugins -CLICON_BACKEND_DIR /data/backend -# Location of netconf (frontend) .so plugins -CLICON_NETCONF_DIR /data/netconf -# Location of cli frontend .so plugins -CLICON_CLI_DIR /data/cli -# Location of frontend .cli cligen spec files -CLICON_CLISPEC_DIR /data/clispec -# Directory where to save configuration commit history (in XML). Snapshots -# are saved chronologically -CLICON_ARCHIVE_DIR /data -# XXX Name of startup configuration file (in XML) -CLICON_STARTUP_CONFIG /data/startup-config -# Address family for communicating with clixon_backend (UNIX|IPv4|IPv6) -CLICON_SOCK_FAMILY UNIX -# If family above is AF_UNIX: Unix socket for communicating with clixon_backend -# If family above is AF_INET: IPv4 address -CLICON_SOCK /data/routing.sock -# Inet socket port for communicating with clixon_backend (only IPv4|IPv6) -CLICON_SOCK_PORT 4535 -# Process-id file -CLICON_BACKEND_PIDFILE /data/routing.pidfile - -# Save values as XML in database instead of lvec:s. -# This is optimized for yang specified applications -# But not compatible with key-based application (eg Rost) -CLICON_DB_XML 1 - -# Startup CLI mode. This should match the CLICON_MODE in your startup clispec file -CLICON_CLI_MODE routing - -# Option used to construct initial yang file: -# [@] -# This option is only relevant if CLICON_DBSPEC_TYPE is YANG -CLICON_YANG_MODULE_MAIN ietf-ip - -# Option used to construct initial yang file: -# [@] -# This option is only relevant if CLICON_DBSPEC_TYPE is YANG -CLICON_YANG_MODULE_REVISION 2014-06-16 - -# Generate code for CLI completion of existing db symbols -# CLICON_CLI_GENMODEL_COMPLETION 0 -CLICON_CLI_GENMODEL_COMPLETION 1 - -# How to generate and show CLI syntax: VARS|ALL -# CLICON_CLI_GENMODEL_TYPE VARS -CLICON_CLI_GENMODEL_TYPE VARS diff --git a/example/docker/run.sh b/example/docker/run.sh deleted file mode 100755 index 295d5d4e..00000000 --- a/example/docker/run.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Start daemon and a cli docker containers . -# Note that they have a common file-system at /data -# -sudo docker run -td --net host -v $(pwd)/data:/data olofhagsand/clixon_backend -sudo docker run -ti --rm --net host -v $(pwd)/data:/data olofhagsand/clixon_cli - - From 524e7e38f030ae0ed325e9e1098edc5d4cc542da Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Sat, 21 Jul 2018 17:08:50 +0000 Subject: [PATCH 50/53] docker_example --- CHANGELOG.md | 2 +- example/Makefile.in | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 030813db..b6b02135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ ### Minor changes -* Updated the docker image build and changed it to build a single clixon docker image which can be found at olofhagsand/clixon. +* Updated the docker image build and changed it to build a single clixon docker image which can be found at olofhagsand/clixon. And the example at olofhagsand/clixon_example. * Added systemd example files under example/systemd * Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. * Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. diff --git a/example/Makefile.in b/example/Makefile.in index 336968bf..ce86c28b 100644 --- a/example/Makefile.in +++ b/example/Makefile.in @@ -51,11 +51,13 @@ CLI_PLUGIN = $(APPNAME)_cli.so NETCONF_PLUGIN = $(APPNAME)_netconf.so RESTCONF_PLUGIN = $(APPNAME)_restconf.so -# Example docker image +# Example docker image. PLEASE CHANGE THIS IMAGE = olofhagsand/clixon_example PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) +.PHONY: all clean depend install docker push + all: $(PLUGINS) # Note: clixon.mk has rules for clixon_DBSPECDIR, clixon_SYSCONFDIR, etc used below @@ -131,10 +133,10 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NET install -d -m 0755 $(DESTDIR)$(clixon_LOCALSTATEDIR) docker: - sudo docker build -t $(IMAGE) . + sudo docker build -t $(IMAGE) . push: - sudo docker push $(IMAGE) + sudo docker push $(IMAGE) uninstall: From faea537d2577c42f06285f8b7881e3dbd3067d1d Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 22 Jul 2018 16:44:58 +0200 Subject: [PATCH 51/53] 3.7.0 --- CHANGELOG.md | 65 ++++++++++++++++++++++++++++++++-------------------- configure | 2 +- configure.ac | 2 +- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b02135..84c8ac7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,34 @@ # Clixon Changelog -## 3.7.0 (Upcoming) +## 3.7.0 (22 July 2018) ### Major New features -* Support for YANG conditionals "must" and "when" according to RFC 7950 Sec 7.5.3 and 7.21.5. - * XPATH checked at validation time -* Support for YANG identity, identityref, according to RFC 7950 Sec 7.18 and 9.10. - * Previous support did no validation of values. - * Validation of types and CLI expansion - * Example extended with inclusion of iana-if-type RFC 7224 interface identities - * Applications which have not strictly enforced the identities may now have problems with validation and may need to be modified. - -* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex, see also API changes below. - +* YANG "must" and "when" Xpath-basedconstraints according to RFC 7950 Sec 7.5.3 and 7.21.5. + * Must and when Xpath constrained checked at validation/commit. +* YANG "identity" and "identityref", according to RFC 7950 Sec 7.18 and 9.10. + * Identities checked at validation/commit. + * CLI completion support of identity values. + * Example extended with iana-if-type RFC 7224 interface identities. +* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex, + * Full suport of boolean constraints for "when"/"must", not only nodesets. + * See also API changes below. +* CDATA XML support (patch by David Cornejo, Netgate) + * Encode and decode (parsing) support +* Added cligen variable translation. Useful for processing input such as hashing, encryption. + * More info in example and FAQ. + * Example: +``` +cli> translate value HAL +cli> show configuration +translate { + value IBM; +} +``` ### API changes on existing features (you may need to change your code) -* Conformance of restconf(RFC-8040) operations where prefix was used instead of module name. +* YANG identity, identityref, must, and when validation support may cause applications that had not strictly enforced identities and constraints before. +* Restconf operations changed from prefix to module name. * Proper specification for an operation is POST /restconf/operations/: HTTP/1.1 * See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30 * Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out. @@ -50,36 +62,39 @@ * show_yangv --> show_yang * show_confv_xpath --> show_conf_xpath +* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. + ### Minor changes -* Updated the docker image build and changed it to build a single clixon docker image which can be found at olofhagsand/clixon. And the example at olofhagsand/clixon_example. +* Clixon docker upgrade + * Updated the docker image build and example to a single clixon docker image. + * Example pushed to https://hub.docker.com/r/olofhagsand/clixon_example/ * Added systemd example files under example/systemd -* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled. -* Added util subdir, with dedicated standalone xml,json,yang and xpath parser utility test programs. -* CDATA xml support (patch by David Cornejo, Netgate) - * Encode and decode (parsing) support +* Added util subdir, with dedicated standalone xml, json, yang and xpath parser utility test programs. * Validation of yang bits type space-separated list value -* Added -U command line to clixon_cli and clixon_netconf for NACM pseudo-user tests +* Added -U command line to clixon_cli and clixon_netconf for changing user. + * This is primarily for NACM pseudo-user tests * Added a generated CLI show command that works on the generated parse tree with auto completion. * A typical call is: show @datamodel:example, cli_show_auto("candidate", "json"); * The example contains a more elaborate example. * Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24 -* Added xmlns validation +* Added XML namespace (xmlns) validation * for eg -* Added yang identityref runtime validation -* Added --enable-debug. -* Added cligen variable translation. - * See FAQ and example +* ./configure extended with --enable-debug flag + * CFLAGS=-g ./configure deprecated ### Corrected Bugs * Prefix of rpc was ignored (thanks Dmitri at netgate) * https://github.com/clicon/clixon/issues/30 -* Added cli returna value also for single commands (eg -1) +* Added cli return value also for single commands (eg -1) * Fixed JSON unbalanced braces resulting in assert. ### Known issues * Namespace name relabeling is not supported. - * Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlfns is not supported, such as: x:des3 + * Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlfns is not supported, such as: +``` + x:des3 +``` ## 3.6.1 (29 May 2018) diff --git a/configure b/configure index 85acbab1..6ee0a7d2 100755 --- a/configure +++ b/configure @@ -2156,7 +2156,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu CLIXON_VERSION_MAJOR="3" CLIXON_VERSION_MINOR="7" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" # Fix to specific CLIgen version (eg 3.5) or head (3) CLIGEN_VERSION="3" diff --git a/configure.ac b/configure.ac index 81f480f5..46c5fd07 100644 --- a/configure.ac +++ b/configure.ac @@ -45,7 +45,7 @@ AC_INIT(lib/clixon/clixon.h.in) CLIXON_VERSION_MAJOR="3" CLIXON_VERSION_MINOR="7" CLIXON_VERSION_PATCH="0" -CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\"" +CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" # Fix to specific CLIgen version (eg 3.5) or head (3) CLIGEN_VERSION="3" From 9776ee864f5e567cfe3ad3fc6de1c1436604b7ef Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 22 Jul 2018 18:33:13 +0200 Subject: [PATCH 52/53] * Makefile change: Removed the make include file: clixon.mk and clixon.mk.in * These generated the Makefile variables: clixon_DBSPECDIR, clixon_SYSCONFDIR, clixon_LOCALSTATEDIR, clixon_LIBDIR, clixon_DATADIR which have been replaced by generic autoconf variables instead. --- CHANGELOG.md | 3 + Makefile.in | 12 +-- clixon.mk.cpp | 46 ----------- configure | 8 +- configure.ac | 7 +- datastore/Makefile.in | 7 +- datastore/keyvalue/Makefile.in | 1 - datastore/text/Makefile.in | 2 - example/Makefile.in | 49 ++++++------ include/clixon_config.h.in~ | 142 --------------------------------- include/clixon_config.h~ | 134 ------------------------------- lib/src/clixon_yang.c | 2 +- test/test_auth_ext.sh | 2 +- test/test_datastore.sh | 2 +- test/test_install.sh | 3 - yang/Makefile.in | 15 ++-- 16 files changed, 51 insertions(+), 384 deletions(-) delete mode 100644 clixon.mk.cpp delete mode 100644 include/clixon_config.h.in~ delete mode 100644 include/clixon_config.h~ diff --git a/CHANGELOG.md b/CHANGELOG.md index 84c8ac7d..ff9c8650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ translate { * xpath_each() is removed * The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile. +* Makefile change: Removed the make include file: clixon.mk and clixon.mk.in + * These generated the Makefile variables: clixon_DBSPECDIR, clixon_SYSCONFDIR, clixon_LOCALSTATEDIR, clixon_LIBDIR, clixon_DATADIR which have been replaced by generic autoconf variables instead. + * Removed cli callback vector functions. Set COMPAT_CLIV if you need to keep these functions in include/clixon_custom.h. * Replace functions as follows in CLI SPEC files: * cli_setv --> cli_set diff --git a/Makefile.in b/Makefile.in index 9c914381..84368869 100644 --- a/Makefile.in +++ b/Makefile.in @@ -56,7 +56,7 @@ SUBDIRS = lib apps include etc datastore util yang .PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker -all: $(SUBDIRS) clixon.mk +all: $(SUBDIRS) $(SUBDIRS): (cd $@ && $(MAKE) $(MFLAGS) all) @@ -65,12 +65,7 @@ depend: for i in $(SUBDIRS) doc example docker; \ do (cd $$i && $(MAKE) $(MFLAGS) depend); done -clixon.mk: clixon.mk.cpp - $(CPP) -P -traditional-cpp -x assembler-with-cpp -Dprefix=$(prefix) -Dlocalstatedir=$(localstatedir) -Dsysconfdir=$(sysconfdir) -Ddatadir=$(datadir) -Dlibdir=$(libdir) $< > $@ - -install: clixon.mk - install -d -m 0755 $(DESTDIR)$(datadir)/clixon - install -m 0644 clixon.mk $(DESTDIR)$(datadir)/clixon +install: for i in $(SUBDIRS) doc; \ do (cd $$i; $(MAKE) $(MFLAGS) $@)||exit 1; done; \ echo "Install for compilation by: make install-include" @@ -83,7 +78,6 @@ install-include: uninstall: for i in $(SUBDIRS) doc example docker; \ do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done; - rm -f $(DESTDIR)$(datadir)/clixon/clixon.mk doc: cd $@; $(MAKE) $(MFLAGS) $@ @@ -100,7 +94,7 @@ clean: distclean: rm -f Makefile TAGS config.status config.log *~ .depend - rm -rf autom4te.cache clixon.mk build-root/rpmbuild + rm -rf autom4te.cache build-root/rpmbuild rm -f build-root/*.tar.xz build-root/*.rpm extras/rpm/Makefile for i in $(SUBDIRS) doc example docker; \ do (cd $$i && $(MAKE) $(MFLAGS) $@); done diff --git a/clixon.mk.cpp b/clixon.mk.cpp deleted file mode 100644 index 2f27c669..00000000 --- a/clixon.mk.cpp +++ /dev/null @@ -1,46 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# -# Copyright (C) 2009-2018 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 ***** -# - -# -# Include this file in your application Makefile using eg: -# -include $(datarootdir)/clixon/clixon.mk -# then you can use the DIRS below in your install rules. -# You also get rules for the application configure file. -# NOTE: APPNAME must be defined in the local Makefile - -clixon_DBSPECDIR=prefix/share/$(APPNAME) -clixon_SYSCONFDIR=sysconfdir -clixon_LOCALSTATEDIR=localstatedir/$(APPNAME) -clixon_LIBDIR=libdir/$(APPNAME) -clixon_DATADIR=datadir/clixon # for system yang files - diff --git a/configure b/configure index 6ee0a7d2..a6e6a366 100755 --- a/configure +++ b/configure @@ -621,6 +621,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS +CLIXON_DATADIR EGREP GREP LEXLIB @@ -4308,11 +4309,12 @@ fi done -# This is to find clixon system files in the source code. -# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles +# This is to find clixon system files in the source code and Makefile + +CLIXON_DATADIR="${prefix}/share/clixon" cat >>confdefs.h <<_ACEOF -#define CLIXON_DATADIR "${prefix}/share/clixon" +#define CLIXON_DATADIR "${CLIXON_DATADIR}" _ACEOF diff --git a/configure.ac b/configure.ac index 46c5fd07..964141c1 100644 --- a/configure.ac +++ b/configure.ac @@ -178,9 +178,10 @@ AC_CHECK_LIB(dl, dlopen) AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp) -# This is to find clixon system files in the source code. -# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles -AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${prefix}/share/clixon", [Clixon data dir for system yang files etc]) +# This is to find clixon system files in the source code and Makefile +AC_SUBST(CLIXON_DATADIR) +CLIXON_DATADIR="${prefix}/share/clixon" +AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${CLIXON_DATADIR}", [Clixon data dir for system yang files etc]) # Default location for config file AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${DEFAULT_CONFIG}",[Location for apps to find default config file]) diff --git a/datastore/Makefile.in b/datastore/Makefile.in index 16beab79..98bc8a0e 100644 --- a/datastore/Makefile.in +++ b/datastore/Makefile.in @@ -78,8 +78,6 @@ APPL = datastore_client all: $(SUBDIRS) $(APPL) --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk - .SUFFIXES: .SUFFIXES: .c .o @@ -101,14 +99,11 @@ install-include: for i in $(SUBDIRS); \ do (cd $$i ; $(MAKE) $(MFLAGS) $@)||exit 1; done; -install: $(APPL) - install -d -m 0755 $(DESTDIR)$(bindir) - install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(bindir) +install: for i in $(SUBDIRS); \ do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done uninstall: - rm -f $(DESTDIR)$(bindir)/$(APPL) for i in $(SUBDIRS); \ do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done diff --git a/datastore/keyvalue/Makefile.in b/datastore/keyvalue/Makefile.in index 19aa6fe6..08fc997f 100644 --- a/datastore/keyvalue/Makefile.in +++ b/datastore/keyvalue/Makefile.in @@ -63,7 +63,6 @@ OBJS = $(SRC:.c=.o) all: $(PLUGIN) --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk $(PLUGIN): $(SRC) $(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS) diff --git a/datastore/text/Makefile.in b/datastore/text/Makefile.in index cdc2adb5..23c2a7bf 100644 --- a/datastore/text/Makefile.in +++ b/datastore/text/Makefile.in @@ -63,8 +63,6 @@ OBJS = $(SRC:.c=.o) all: $(PLUGIN) --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk - $(PLUGIN): $(SRC) ifeq ($(HOST_VENDOR),apple) $(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ -lc $^ $(LIBS) diff --git a/example/Makefile.in b/example/Makefile.in index ce86c28b..c95ff1de 100644 --- a/example/Makefile.in +++ b/example/Makefile.in @@ -37,8 +37,13 @@ prefix = @prefix@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ +sysconfdir = @sysconfdir@ +datarootdir = @datarootdir@ +localstatedir = @localstatedir@ +libdir = @exec_prefix@/lib APPNAME = example + CC = @CC@ CFLAGS = @CFLAGS@ -rdynamic -fPIC INSTALLFLAGS = @INSTALLFLAGS@ @@ -60,9 +65,6 @@ PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_ all: $(PLUGINS) -# Note: clixon.mk has rules for clixon_DBSPECDIR, clixon_SYSCONFDIR, etc used below --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk - CLISPECS = $(APPNAME)_cli.cli YANGSPECS = $(APPNAME).yang @@ -116,21 +118,21 @@ distclean: clean rm -f Makefile *~ .depend install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) $(APPNAME).xml - install -d -m 0755 $(DESTDIR)$(clixon_SYSCONFDIR) - install -m 0644 $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR) - install -d -m 0755 $(DESTDIR)$(clixon_DBSPECDIR)/yang - install -m 0644 $(YANGSPECS) $(DESTDIR)$(clixon_DBSPECDIR)/yang - install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/cli - install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli - install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/backend - install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend - install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/netconf - install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf - install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/restconf - install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/restconf - install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/clispec - install -m 0644 $(CLISPECS) $(DESTDIR)$(clixon_LIBDIR)/clispec - install -d -m 0755 $(DESTDIR)$(clixon_LOCALSTATEDIR) + install -d -m 0755 $(DESTDIR)$(sysconfdir) + install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir) + install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang + install -m 0644 $(YANGSPECS) $(DESTDIR)$(datarootdir)/$(APPNAME)/yang + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli + install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend + install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/backend + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/netconf + install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/netconf + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/restconf + install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/restconf + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/clispec + install -m 0644 $(CLISPECS) $(DESTDIR)$(libdir)/$(APPNAME)/clispec + install -d -m 0755 $(DESTDIR)$(localstatedir)/$(APPNAME) docker: sudo docker build -t $(IMAGE) . @@ -138,12 +140,13 @@ docker: push: sudo docker push $(IMAGE) - uninstall: - rm -rf $(DESTDIR)$(clixon_SYSCONFDIR)/$(APPNAME).xml - rm -rf $(DESTDIR)$(clixon_DBSPECDIR) - rm -rf $(DESTDIR)$(clixon_LOCALSTATEDIR) - rm -rf $(DESTDIR)$(clixon_LIBDIR) + echo "libdir:$(libdir)" + echo "libdir2:$(libdir2)" + rm -rf $(DESTDIR)$(sysconfdir)/$(APPNAME).xml + rm -rf $(DESTDIR)$(datarootdir)/$(APPNAME) + rm -rf $(DESTDIR)$(localstatedir)/$(APPNAME) + rm -rf $(DESTDIR)$(libdir)/$(APPNAME) install-include: diff --git a/include/clixon_config.h.in~ b/include/clixon_config.h.in~ deleted file mode 100644 index d356c165..00000000 --- a/include/clixon_config.h.in~ +++ /dev/null @@ -1,142 +0,0 @@ -/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */ - -/* Clixon data dir for system yang files etc */ -#undef CLIXON_DATADIR - -/* Location for apps to find default config file */ -#undef CLIXON_DEFAULT_CONFIG - -/* Clixon major release */ -#undef CLIXON_VERSION_MAJOR - -/* Clixon minor release */ -#undef CLIXON_VERSION_MINOR - -/* Clixon path version */ -#undef CLIXON_VERSION_PATCH - -/* Clixon version string */ -#undef CLIXON_VERSION_STRING - -/* Define to 1 if you have the `alphasort' function. */ -#undef HAVE_ALPHASORT - -/* Define to 1 if you have the header file. */ -#undef HAVE_CLIGEN_CLIGEN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_CRYPT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_DEPOT_H - -/* Define to 1 if you have the `inet_aton' function. */ -#undef HAVE_INET_ATON - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `cligen' library (-lcligen). */ -#undef HAVE_LIBCLIGEN - -/* Define to 1 if you have the `crypt' library (-lcrypt). */ -#undef HAVE_LIBCRYPT - -/* Define to 1 if you have the `dl' library (-ldl). */ -#undef HAVE_LIBDL - -/* Define to 1 if you have the `fcgi' library (-lfcgi). */ -#undef HAVE_LIBFCGI - -/* Define to 1 if you have the `m' library (-lm). */ -#undef HAVE_LIBM - -/* Define to 1 if you have the `nsl' library (-lnsl). */ -#undef HAVE_LIBNSL - -/* Define to 1 if you have the `qdbm' library (-lqdbm). */ -#undef HAVE_LIBQDBM - -/* Define to 1 if you have the `socket' library (-lsocket). */ -#undef HAVE_LIBSOCKET - -/* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_IF_VLAN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_QDBM_DEPOT_H - -/* Define to 1 if you have the `sigaction' function. */ -#undef HAVE_SIGACTION - -/* Define to 1 if you have the `sigvec' function. */ -#undef HAVE_SIGVEC - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strlcpy' function. */ -#undef HAVE_STRLCPY - -/* Define to 1 if you have the `strndup' function. */ -#undef HAVE_STRNDUP - -/* Define to 1 if you have the `strsep' function. */ -#undef HAVE_STRSEP - -/* Define to 1 if you have the `strverscmp' function. */ -#undef HAVE_STRVERSCMP - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_UCRED_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `versionsort' function. */ -#undef HAVE_VERSIONSORT - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a - `char[]'. */ -#undef YYTEXT_POINTER - -#include diff --git a/include/clixon_config.h~ b/include/clixon_config.h~ deleted file mode 100644 index 5be37864..00000000 --- a/include/clixon_config.h~ +++ /dev/null @@ -1,134 +0,0 @@ -/* include/clixon_config.h. Generated from clixon_config.h.in by configure. */ -/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */ - -/* Clixon major release */ -#define CLIXON_VERSION_MAJOR 3 - -/* Clixon minor release */ -#define CLIXON_VERSION_MINOR 3 - -/* Clixon path version */ -#define CLIXON_VERSION_PATCH 2 - -/* Clixon version string */ -#define CLIXON_VERSION_STRING "3.3.2" - -/* Define to 1 if you have the `alphasort' function. */ -#define HAVE_ALPHASORT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CLIGEN_CLIGEN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CRYPT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DEPOT_H */ - -/* Define to 1 if you have the `inet_aton' function. */ -#define HAVE_INET_ATON 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the `crypt' library (-lcrypt). */ -#define HAVE_LIBCRYPT 1 - -/* Define to 1 if you have the `dl' library (-ldl). */ -#define HAVE_LIBDL 1 - -/* Define to 1 if you have the `fcgi' library (-lfcgi). */ -#define HAVE_LIBFCGI 1 - -/* Define to 1 if you have the `m' library (-lm). */ -#define HAVE_LIBM 1 - -/* Define to 1 if you have the `nsl' library (-lnsl). */ -#define HAVE_LIBNSL 1 - -/* Define to 1 if you have the `qdbm' library (-lqdbm). */ -#define HAVE_LIBQDBM 1 - -/* Define to 1 if you have the `socket' library (-lsocket). */ -/* #undef HAVE_LIBSOCKET */ - -/* Define to 1 if you have the header file. */ -#define HAVE_LINUX_IF_VLAN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_QDBM_DEPOT_H 1 - -/* Define to 1 if you have the `sigaction' function. */ -#define HAVE_SIGACTION 1 - -/* Define to 1 if you have the `sigvec' function. */ -#define HAVE_SIGVEC 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strlcpy' function. */ -/* #undef HAVE_STRLCPY */ - -/* Define to 1 if you have the `strndup' function. */ -#define HAVE_STRNDUP 1 - -/* Define to 1 if you have the `strsep' function. */ -#define HAVE_STRSEP 1 - -/* Define to 1 if you have the `strverscmp' function. */ -#define HAVE_STRVERSCMP 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_UCRED_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the `versionsort' function. */ -#define HAVE_VERSIONSORT 1 - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a - `char[]'. */ -#define YYTEXT_POINTER 1 - -#include diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 7008ae71..afdedbc1 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1835,7 +1835,7 @@ yang_parse_recurse(const char *yang_dir, if ((nr = yang_parse_find_match(yang_dir, module, fbuf)) < 0) goto done; if (nr == 0){ - clicon_err(OE_YANG, errno, "No matching %s yang files found (expected module name or absolute filename)", module); + clicon_err(OE_YANG, errno, "No matching %s yang files found in %s (expected module name or absolute filename)", module, yang_dir); goto done; } } diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh index c53dcf55..c1806891 100755 --- a/test/test_auth_ext.sh +++ b/test/test_auth_ext.sh @@ -16,7 +16,7 @@ nacmfile=$dir/nacmfile cat < $cfg $cfg - /usr/local/share/clixon + /usr/local/share/example/yang $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend diff --git a/test/test_datastore.sh b/test/test_datastore.sh index d9155b0e..8427ed9f 100755 --- a/test/test_datastore.sh +++ b/test/test_datastore.sh @@ -6,7 +6,7 @@ . ./lib.sh fyang=$dir/ietf-ip.yang -datastore=datastore_client +datastore=../datastore/datastore_client cat < $fyang module ietf-ip{ diff --git a/test/test_install.sh b/test/test_install.sh index 5e88336b..032bba4a 100755 --- a/test/test_install.sh +++ b/test/test_install.sh @@ -19,9 +19,6 @@ fi if [ ! -d $dir/www-data ]; then err $dir/www-data fi -if [ ! -f $dir/usr/local/share/clixon/clixon.mk ]; then - err $dir/usr/local/share/clixon/clixon.mk -fi if [ ! -f $dir/usr/local/share/clixon/clixon-config* ]; then err $dir/usr/local/share/clixon/clixon-config* fi diff --git a/yang/Makefile.in b/yang/Makefile.in index 66b2621f..469f026b 100644 --- a/yang/Makefile.in +++ b/yang/Makefile.in @@ -38,6 +38,8 @@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ +CLIXON_DATADIR = @CLIXON_DATADIR@ + YANGSPECS = clixon-config@2018-04-30.yang YANGSPECS += ietf-netconf@2011-06-01.yang YANGSPECS += ietf-netconf-acm@2018-02-14.yang @@ -48,22 +50,17 @@ APPNAME = clixon # subdir ehere these files are installed all: -# Note: clixon.mk has a rule for: --include $(DESTDIR)$(datarootdir)/clixon/clixon.mk - clean: distclean: clean rm -f Makefile *~ .depend install: $(YANGSPECS) - echo $(DESTDIR)$(datarootdir)/clixon/clixon.mk - echo $(DESTDIR)$(clixon_DATADIR) - install -d -m 0755 $(DESTDIR)$(clixon_DATADIR) - install -m 0644 $(YANGSPECS) $(DESTDIR)$(clixon_DATADIR) + install -d -m 0755 $(DESTDIR)$(CLIXON_DATADIR) + install -m 0644 $(YANGSPECS) $(DESTDIR)$(CLIXON_DATADIR) -uninstall: - (cd $(DESTDIR)$(clixon_DATADIR); rm -rf $(YANGSPECS)) +uninstall: + (cd $(DESTDIR)$(CLIXON_DATADIR); rm -rf *.yang) install-include: From f2d2b0a8c010c76511f6309fabbe809ec165f83b Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 22 Jul 2018 20:49:16 +0200 Subject: [PATCH 53/53] readme start nginx --- apps/restconf/README.md | 5 +++++ doc/FAQ.md | 20 +++++++++++++++++++- example/README.md | 4 +++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 06ec90bb..ce077300 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -22,6 +22,11 @@ Start nginx daemon ``` sudo /etc/init.d nginx start ``` +Alternatively, start it via systemd: +``` +sudo /etc/init.d/nginx start +sudo systemctl start start.service +``` Start clixon restconf daemon ``` diff --git a/doc/FAQ.md b/doc/FAQ.md index 37154618..c1d130d0 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -132,7 +132,25 @@ You can access clixon via REST API using restconf, such as using curl. GET, PUT, POST are supported. You need a web-server, such as nginx, and start a restconf fcgi -daemon, clixon_restconf. Read more in the restconf docs. +daemon, clixon_restconf. + +For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default: +``` +server { + ... + location /restconf { + root /usr/share/nginx/html/restconf; + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } +} +``` +Start nginx daemon +``` +sudo /etc/init.d/nginx start +``` + +Read more in the restconf docs. Example: ``` diff --git a/example/README.md b/example/README.md index c2368c2f..0c6d6356 100644 --- a/example/README.md +++ b/example/README.md @@ -19,7 +19,9 @@ routing example. It contains the following files: ## Compile and run -Before you start, see [preparation](../doc/FAQ.md#do-i-need-to-setup-anything-important). +Before you start, +* Make [group setup](../doc/FAQ.md#do-i-need-to-setup-anything-important) +* Setup [restconf](../doc/FAQ.md#how-do-i-use-restconf) ``` cd example