diff --git a/.travis.yml b/.travis.yml index b8c1a54e..23573a00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: c branches: only: - master - - develop before_script: - sudo apt-get install -y libfcgi-dev - ./test/travis/before_script.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index de9c6367..13a27af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Clixon Changelog +## 4.3.0 (Expected: December 2019) + +### Minor changes +* Added "canonical" global namespace context: `nsctx_global` + * This is a normalized XML prefix:namespace pair vector computed from all loaded Yang modules. Useful when writing XML and XPATH expressions in callbacks. + * Get it with `clicon_nsctx_global_get(h)` +* Added wildcard `*` as a mode to `CLICON_MODE` in clispec files + * If you set "CLICON_MODE="*";" in a clispec file it means that syntax will appear in all CLI spec modes. +* State callbacks provided by user are validated. If they are invalid an internal error is returned. +* Fixed multi-namespace for augmented state which was not covered in 4.2.0. + +### API changes on existing features (you may need to change your code) +* Yang files reorganized into three classes: clixon, mandatory, optional (previous "standard" split into mandatory and optional). + * Clixon and mandatory yang spec are always installed + * Optional yang files are loaded only if configured with `--enable-optyangs` (flipped logic and changed from `disable-stdyangs`). NOTE: you must do this to run examples and tests. + * Optional yang files can be installed in a separate dir with `--with-opt-yang-installdir=DIR` (renamed from `with-std-yang-installdir`) +* C-API + * Added namespace-context parameter `nsc` to `xpath_first` and `xpath_vec`, (`xpath_vec_nsc` and xpath_first_nsc` are removed). + * Added clicon_handle as parameter to all `clicon_connect_` functions to get better error message + * Added nsc parameter to `xmldb_get()` +* The multi-namespace augment state may rearrange the XML namespace attributes. +* Main example yang changed to incorporate augmented state, new revision is 2019-11-15. + +### Corrected Bugs +* [filter in netconf - one specific entry #100](https://github.com/clicon/clixon/issues/100) +* [xpath_tree2cbuf() changes integers into floating point representations #99](https://github.com/clicon/clixon/issues/99) +* [xml_parse_string() is slow for a long XML string #96](https://github.com/clicon/clixon/issues/96) +* Mandatory variables can no longer be deleted. +* [Add missing includes](https://github.com/clicon/clixon/pulls) + ## 4.2.0 (October 27 2019) ### Summary @@ -12,7 +42,7 @@ The main improvement in thus release concerns security in terms of priveleges an * use `-U ` clixon_backend command-line option to drop to `user` * Generic options are the following: * `CLICON_BACKEND_USER` sets the user to drop priveleges to - * CLICON_BACKEND_PRIVELEGES can have the following values: + * `CLICON_BACKEND_PRIVELEGES` can have the following values: * `none` Make no drop/change in privileges. This is currently the default. * `drop_perm` After initialization, drop privileges permanently * `drop_perm` After initialization, drop privileges temporarily (to a euid) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 8a379c0d..943e9510 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -170,8 +170,8 @@ client_get_capabilities(clicon_handle h, int retval = -1; cxobj *xrstate = NULL; /* xml restconf-state node */ cxobj *xcap = NULL; /* xml capabilities node */ - - if ((xrstate = xpath_first(*xret, "restconf-state")) == NULL){ + + if ((xrstate = xpath_first(*xret, NULL, "restconf-state")) == NULL){ clicon_err(OE_YANG, ENOENT, "restconf-state not found in config node"); goto done; } @@ -321,10 +321,10 @@ client_statedata(clicon_handle h, if (ret == 0) goto fail; /* Code complex to filter out anything that is outside of xpath - * Actually this is a safety catch, should realy be done in plugins + * Actually this is a safety catch, should really be done in plugins * and modules_state functions. */ - if (xpath_vec_nsc(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* If vectors are specified then mark the nodes found and * then filter out everything else, @@ -434,7 +434,7 @@ from_client_get_config(clicon_handle h, if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0) goto done; if (ret == 0){ /* Do NACM validation */ - if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* NACM datanode/module read validation */ if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0) @@ -529,14 +529,14 @@ from_client_edit_config(clicon_handle h, goto done; goto ok; } - if ((x = xpath_first(xn, "default-operation")) != NULL){ + if ((x = xpath_first(xn, NULL, "default-operation")) != NULL){ if (xml_operation(xml_body(x), &operation) < 0){ if (netconf_invalid_value(cbret, "protocol", "Wrong operation")< 0) goto done; goto ok; } } - if ((xc = xpath_first(xn, "config")) == NULL){ + if ((xc = xpath_first(xn, NULL, "config")) == NULL){ if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0) goto done; goto ok; @@ -967,7 +967,7 @@ from_client_get(clicon_handle h, if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0) goto done; if (ret == 0){ /* Do NACM validation */ - if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* NACM datanode/module read validation */ if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0) @@ -1122,9 +1122,9 @@ from_client_create_subscription(clicon_handle h, if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netmod:notification")) == NULL) goto done; - if ((x = xpath_first_nsc(xe, nsc, "//stream")) != NULL) + if ((x = xpath_first(xe, nsc, "//stream")) != NULL) stream = xml_find_value(x, "body"); - if ((x = xpath_first_nsc(xe, nsc, "//stopTime")) != NULL){ + if ((x = xpath_first(xe, nsc, "//stopTime")) != NULL){ if ((stoptime = xml_find_value(x, "body")) != NULL && str2time(stoptime, &stop) < 0){ if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0) @@ -1132,7 +1132,7 @@ from_client_create_subscription(clicon_handle h, goto ok; } } - if ((x = xpath_first_nsc(xe, nsc, "//startTime")) != NULL){ + if ((x = xpath_first(xe, nsc, "//startTime")) != NULL){ if ((starttime = xml_find_value(x, "body")) != NULL && str2time(starttime, &start) < 0){ if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0) @@ -1140,7 +1140,7 @@ from_client_create_subscription(clicon_handle h, goto ok; } } - if ((xfilter = xpath_first_nsc(xe, nsc, "//filter")) != NULL){ + if ((xfilter = xpath_first(xe, nsc, "//filter")) != NULL){ if ((ftype = xml_find_value(xfilter, "type")) != NULL){ /* Only accept xpath as filter type */ if (strcmp(ftype, "xpath") != 0){ @@ -1367,8 +1367,8 @@ from_client_msg(clicon_handle h, goto reply; } - if ((x = xpath_first_nsc(xt, NULL, "/rpc")) == NULL){ - if ((x = xpath_first_nsc(xt, NULL, "/hello")) != NULL){ + if ((x = xpath_first(xt, NULL, "/rpc")) == NULL){ + if ((x = xpath_first(xt, NULL, "/hello")) != NULL){ if ((ret = from_client_hello(h, x, ce, cbret)) <0) goto done; goto reply; diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 7c45145c..a22e8360 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -118,10 +118,14 @@ generic_validate(clicon_handle h, for (i=0; itd_dlen; i++){ x1 = td->td_dvec[i]; ys = xml_spec(x1); - if (ys && yang_mandatory(ys) && yang_config(ys)==0){ - if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "Missing mandatory variable") < 0) - goto done; - goto fail; + if (ys && yang_mandatory(ys) && yang_config(ys)==1){ + yang_stmt *yp =yang_parent_get(ys); + if (yp== NULL || + (yang_keyword_get(yp)!=Y_MODULE && yang_keyword_get(yp)!=Y_SUBMODULE)){ + if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "May not remove mandatory variable") < 0) + goto done; + goto fail; + } } } /* added entries */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 3fe9603c..b1091f3d 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -92,6 +92,7 @@ backend_terminate(clicon_handle h) cxobj *x; struct stat st; int ss; + cvec *nsctx; clicon_debug(1, "%s", __FUNCTION__); if ((ss = clicon_socket_get(h)) != -1) @@ -110,6 +111,8 @@ backend_terminate(clicon_handle h) yspec_free(yspec); if ((yspec = clicon_config_yang(h)) != NULL) yspec_free(yspec); + if ((nsctx = clicon_nsctx_global_get(h)) != NULL) + cvec_free(nsctx); if ((x = clicon_nacm_ext(h)) != NULL) xml_free(x); if ((x = clicon_conf_xml(h)) != NULL) @@ -443,7 +446,6 @@ main(int argc, char *nacm_mode; int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR; yang_stmt *yspec = NULL; - yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */ char *str; int ss = -1; /* server socket */ cbuf *cbret = NULL; /* startup cbuf if invalid */ @@ -451,6 +453,7 @@ main(int argc, int ret; char *dir; gid_t gid = -1; + cvec *nsctx_global = NULL; /* Global namespace context */ /* In the startup, logs to stderr & syslog and debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); @@ -505,17 +508,13 @@ main(int argc, clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst); clicon_debug_init(debug, NULL); - /* Create configure yang-spec */ - if ((yspecfg = yspec_new()) == NULL) - goto done; - /* Find and read configfile */ - if (clicon_options_main(h, yspecfg) < 0){ + if (clicon_options_main(h) < 0){ if (help) usage(h, argv[0]); return -1; } - clicon_config_yang_set(h, yspecfg); + /* External NACM file? */ nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE"); if (nacm_mode && strcmp(nacm_mode, "external") == 0) @@ -722,10 +721,10 @@ main(int argc, if ((str = clicon_yang_main_dir(h)) != NULL) if (yang_spec_load_dir(h, str, yspec) < 0) goto done; - /* Load clixon lib yang module */ + /* Load clixon lib yang module */ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0) goto done; - /* Load yang module library, RFC7895 */ + /* Load yang module library, RFC7895 */ if (yang_modules_init(h) < 0) goto done; /* Add netconf yang spec, used by netconf client and as internal protocol @@ -736,13 +735,21 @@ main(int argc, if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0) goto done; /* Load yang Restconf stream discovery */ - if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && - yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0) - goto done; - /* Load yang Netconf stream discovery */ - if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && - yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) - goto done; + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && + yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0) + goto done; + /* Load yang Netconf stream discovery */ + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && + yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) + goto done; + /* Here all modules are loaded + * Compute and set canonical namespace context + */ + if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0) + goto done; + if (clicon_nsctx_global_set(h, nsctx_global) < 0) + goto done; + /* Initialize server socket and save it to handle */ if (backend_rpc_init(h) < 0) goto done; @@ -895,7 +902,7 @@ main(int argc, goto done; ok: retval = 0; - done: + done: if (cbret) cbuf_free(cbret); clicon_log(LOG_NOTICE, "%s: %u Terminated retval:%d", __PROGRAM__, getpid(), retval); diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index a0dc92d3..6ca91325 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -111,9 +111,11 @@ clixon_plugin_statedata(clicon_handle h, { int retval = -1; int ret; + cxobj *xerr = NULL; cxobj *x = NULL; clixon_plugin *cp = NULL; plgstatedata_t *fn; /* Plugin statedata fn */ + cbuf *cberr = NULL; while ((cp = clixon_plugin_each(h, cp)) != NULL) { if ((fn = cp->cp_api.ca_statedata) == NULL) @@ -124,6 +126,38 @@ clixon_plugin_statedata(clicon_handle h, goto fail; /* Dont quit here on user callbacks */ if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; + /* Check XML from state callback by validating it. return internal + * error with error cause + */ + if ((ret = xml_yang_validate_all_top(h, x, &xerr)) < 0) + goto done; + if (ret > 0 && (ret = xml_yang_validate_add(h, x, &xerr)) < 0) + goto done; + if (ret == 0){ + if ((cberr = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + cprintf(cberr, "Internal error: state callback returned invalid XML: "); + if (netconf_err2cb(xpath_first(xerr, NULL, "rpc-error"), cberr) < 0) + goto done; + if (*xret){ + xml_free(*xret); + *xret = NULL; + } + if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr))< 0) + goto done; + goto fail; + } +#if 1 + if (debug){ + cbuf *ccc=cbuf_new(); + if (clicon_xml2cbuf(ccc, x, 0, 0, -1) < 0) + goto done; + clicon_debug(1, "%s MERGE: %s", __FUNCTION__, cbuf_get(ccc)); + cbuf_free(ccc); + } +#endif if ((ret = netconf_trymerge(x, yspec, xret)) < 0) goto done; if (ret == 0) @@ -135,8 +169,12 @@ clixon_plugin_statedata(clicon_handle h, } retval = 1; done: + if (cberr) + cbuf_free(cberr); if (x) xml_free(x); + if (xerr) + xml_free(xerr); return retval; fail: retval = 0; diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c index 018f6b6c..5d6bc82f 100644 --- a/apps/backend/backend_socket.c +++ b/apps/backend/backend_socket.c @@ -270,10 +270,12 @@ backend_accept_client(int fd, #error "Need getsockopt O_PEERCRED or getpeereid for unix socket peer cred" #endif if (name != NULL){ - if ((ce->ce_username = strdup(name)) == NULL){ + if ((ce->ce_username = name) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); + name = NULL; goto done; } + name = NULL; } break; case AF_INET: @@ -291,5 +293,7 @@ backend_accept_client(int fd, goto done; retval = 0; done: + if (name) + free(name); return retval; } diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c index 96f35baa..9314968c 100644 --- a/apps/backend/backend_startup.c +++ b/apps/backend/backend_startup.c @@ -241,7 +241,7 @@ startup_extraxml(clicon_handle h, * It should be empty if extra-xml is null and reset plugins did nothing * then skip validation. */ - if (xmldb_get(h, tmp_db, NULL, &xt0) < 0) + if (xmldb_get(h, tmp_db, NULL, NULL, &xt0) < 0) goto done; if (xt0==NULL || xml_child_nr(xt0)==0) goto ok; diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index cd320bd2..6f5ca55e 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index d9488cb1..48f92e34 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -714,13 +714,13 @@ compare_dbs(clicon_handle h, astext = 0; if (clicon_rpc_get_config(h, NULL, "running", "/", NULL, &xc1) < 0) goto done; - if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xc1, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } if (clicon_rpc_get_config(h, NULL, "candidate", "/", NULL, &xc2) < 0) goto done; - if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xc2, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } @@ -884,7 +884,7 @@ save_config_file(clicon_handle h, clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */ goto done; } - if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } @@ -992,7 +992,7 @@ cli_notification_cb(int s, } if (clicon_msg_decode(reply, NULL, NULL, &xt) < 0) /* XXX pass yang_spec */ goto done; - if ((xe = xpath_first(xt, "//event")) != NULL){ + if ((xe = xpath_first(xt, NULL, "//event")) != NULL){ x = NULL; while ((x = xml_child_each(xe, x, -1)) != NULL) { switch (format){ @@ -1224,7 +1224,7 @@ cli_copy_config(clicon_handle h, /* Get from object configuration and store in x1 */ if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cb), nsc, &x1) < 0) goto done; - if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(x1, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } @@ -1243,7 +1243,7 @@ cli_copy_config(clicon_handle h, xml_name_set(x2, "config"); cprintf(cb, "/%s", keyname); - if ((x = xpath_first_nsc(x2, nsc, "%s", cbuf_get(cb))) == NULL){ + if ((x = xpath_first(x2, nsc, "%s", cbuf_get(cb))) == NULL){ clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname); goto done; } diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c index 01fd3e32..7b0ebc79 100644 --- a/apps/cli/cli_handle.c +++ b/apps/cli/cli_handle.c @@ -162,10 +162,10 @@ cli_cligen(clicon_handle h) int cli_parse_file(clicon_handle h, - FILE *f, - char *name, /* just for errs */ - parse_tree *pt, - cvec *globals) + FILE *f, + char *name, /* just for errs */ + parse_tree *pt, + cvec *globals) { cligen_handle ch = cligen(h); @@ -182,7 +182,7 @@ cli_susp_hook(clicon_handle h, return cligen_susp_hook(ch, fn); } int -cli_interrupt_hook(clicon_handle h, +cli_interrupt_hook(clicon_handle h, cligen_interrupt_cb_t *fn) { cligen_handle ch = cligen(h); @@ -200,14 +200,16 @@ cli_nomatch(clicon_handle h) } int -cli_prompt_set(clicon_handle h, char *prompt) +cli_prompt_set(clicon_handle h, + char *prompt) { cligen_handle ch = cligen(h); return cligen_prompt_set(ch, prompt); } int -cli_logsyntax_set(clicon_handle h, int status) +cli_logsyntax_set(clicon_handle h, + int status) { cligen_handle ch = cligen(h); return cligen_logsyntax_set(ch, status); diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index a9e2f7bf..2beb7867 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -163,6 +163,7 @@ static int cli_terminate(clicon_handle h) { yang_stmt *yspec; + cvec *nsctx; cxobj *x; clicon_rpc_close_session(h); @@ -170,6 +171,8 @@ cli_terminate(clicon_handle h) yspec_free(yspec); if ((yspec = clicon_config_yang(h)) != NULL) yspec_free(yspec); + if ((nsctx = clicon_nsctx_global_get(h)) != NULL) + cvec_free(nsctx); if ((x = clicon_conf_xml(h)) != NULL) xml_free(x); cli_plugin_finish(h); @@ -280,12 +283,12 @@ main(int argc, char **argv) int logdst = CLICON_LOG_STDERR; char *restarg = NULL; /* what remains after options */ yang_stmt *yspec; - yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */ struct passwd *pw; char *str; int tabmode; char *dir; uint32_t id = 0; + cvec *nsctx_global = NULL; /* Global namespace context */ /* Defaults */ once = 0; @@ -348,16 +351,13 @@ main(int argc, char **argv) clicon_debug_init(debug, NULL); - /* Create top-level yang spec and store as option */ - if ((yspecfg = yspec_new()) == NULL) - goto done; - /* Find and read configfile */ - if (clicon_options_main(h, yspecfg) < 0){ + /* Find, read and parse configfile */ + if (clicon_options_main(h) < 0){ if (help) usage(h, argv[0]); return -1; } - clicon_config_yang_set(h, yspecfg); + /* Now rest of options */ opterr = 0; optind = 1; @@ -513,6 +513,14 @@ main(int argc, char **argv) if (netconf_module_load(h) < 0) goto done; + /* Here all modules are loaded + * Compute and set canonical namespace context + */ + if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0) + goto done; + if (clicon_nsctx_global_set(h, nsctx_global) < 0) + goto done; + /* Create tree generated from dataspec. If no other trees exists, this is * the only one. * The following code creates the tree @datamodel @@ -533,7 +541,7 @@ main(int argc, char **argv) cligen_tree_add(cli_cligen(h), treeref, pt); if (printgen) - cligen_print(stdout, pt, 1); + cligen_print(stdout, pt, 1); /* pt_print */ } /* Initialize cli syntax */ diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 31331f21..964c4497 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -209,27 +208,29 @@ clixon_str2fn(char *name, return NULL; } -/*! Append to syntax mode from file - * @param[in] h Clixon handle - * @param[in] filename Name of file where syntax is specified (in syntax-group dir) - * @param[in] dir Name of dir, or NULL +/*! Load a file containing syntax and append to specified modes, also load C plugin + * @param[in] h Clixon handle + * @param[in] filename Name of file where syntax is specified (in syntax-group dir) + * @param[in] dir Name of dir, or NULL + * @param[out] allpt Universal CLIgen parse tree: apply to all modes */ static int -cli_load_syntax(clicon_handle h, - const char *filename, - const char *dir) +cli_load_syntax_file(clicon_handle h, + const char *filename, + const char *dir, + parse_tree *ptall) { - void *handle = NULL; /* Handle to plugin .so module */ - char *mode = NULL; /* Name of syntax mode to append new syntax */ - parse_tree pt = {0,}; - int retval = -1; - FILE *f; - char filepath[MAXPATHLEN]; - cvec *cvv = NULL; - char *prompt = NULL; - char **vec = NULL; - int i, nvec; - char *plgnam; + void *handle = NULL; /* Handle to plugin .so module */ + char *mode = NULL; /* Name of syntax mode to append new syntax */ + parse_tree pt = {0,}; + int retval = -1; + FILE *f; + char filepath[MAXPATHLEN]; + cvec *cvv = NULL; + char *prompt = NULL; + char **vec = NULL; + int i, nvec; + char *plgnam; clixon_plugin *cp; if (dir) @@ -253,10 +254,18 @@ cli_load_syntax(clicon_handle h, goto done; } fclose(f); - /* Get CLICON specific global variables */ + /* Get CLICON specific global variables: + * CLICON_MODE: which mode(s) this syntax applies to + * CLICON_PROMPT: Cli prompt in this mode + * CLICON_PLUGIN: Name of C API plugin + * Note: the base case is that it is: + * (1) a single mode or + * (2) "*" all modes or "m1:m2" - a list of modes + * but for (2), prompt and plgnam may have unclear semantics + */ + mode = cvec_find_str(cvv, "CLICON_MODE"); prompt = cvec_find_str(cvv, "CLICON_PROMPT"); plgnam = cvec_find_str(cvv, "CLICON_PLUGIN"); - mode = cvec_find_str(cvv, "CLICON_MODE"); if (plgnam != NULL) { /* Find plugin for callback resolving */ if ((cp = clixon_plugin_find(h, plgnam)) != NULL) @@ -288,8 +297,19 @@ cli_load_syntax(clicon_handle h, goto done; } } + /* Find all modes in CLICON_MODE string: where to append the pt syntax tree */ if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL) goto done; + + if (nvec == 1 && strcmp(vec[0], "*") == 0){ + /* Special case: Add this to all modes. Add to special "universal" syntax + * and add to all syntaxes after all files have been loaded. At this point + * all modes may not be known (not yet loaded) + */ + if (cligen_parsetree_merge(ptall, NULL, pt) < 0) + return -1; + } + else { for (i = 0; i < nvec; i++) { if (syntax_append(h, cli_syntax(h), @@ -300,6 +320,7 @@ cli_load_syntax(clicon_handle h, if (prompt) cli_set_prompt(h, vec[i], prompt); } + } cligen_parsetree_free(pt, 1); retval = 0; @@ -329,6 +350,7 @@ cli_syntax_load(clicon_handle h) cligen_susp_cb_t *fns = NULL; cligen_interrupt_cb_t *fni = NULL; clixon_plugin *cp; + parse_tree ptall = {0,}; /* Universal CLIgen parse tree all modes */ /* Syntax already loaded. XXX should we re-load?? */ if ((stx = cli_syntax(h)) != NULL) @@ -347,30 +369,38 @@ cli_syntax_load(clicon_handle h) cli_syntax_set(h, stx); + /* Load single specific clispec file */ if (clispec_file){ - if (cli_load_syntax(h, clispec_file, NULL) < 0) + if (cli_load_syntax_file(h, clispec_file, NULL, &ptall) < 0) goto done; } + /* Load all clispec .cli files in directory */ if (clispec_dir){ - /* load syntaxfiles */ + /* Get directory list of files */ if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0) goto done; - /* Load the rest */ + /* Load the syntax parse trees into cli_syntax stx structure */ for (i = 0; i < ndp; i++) { clicon_debug(1, "DEBUG: Loading syntax '%.*s'", (int)strlen(dp[i].d_name)-4, dp[i].d_name); - if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0) + if (cli_load_syntax_file(h, dp[i].d_name, clispec_dir, &ptall) < 0) goto done; } } - /* Did we successfully load any syntax modes? */ + /* Were any syntax modes successfully loaded? If not, leave */ if (stx->stx_nmodes <= 0) { retval = 0; goto done; } - /* Parse syntax tree for all modes */ + + /* Go thorugh all modes and : + * 1) Add the universal syntax + * 2) add syntax tree (of those modes - "activate" syntax from stx to CLIgen) + */ m = stx->stx_modes; do { + if (cligen_parsetree_merge(&m->csm_pt, NULL, ptall) < 0) + return -1; if (gen_parse_tree(h, m) != 0) goto done; m = NEXTQ(cli_syntaxmode_t *, m); @@ -389,13 +419,13 @@ cli_syntax_load(clicon_handle h) /* All good. We can now proudly return a new group */ retval = 0; - done: if (retval != 0) { clixon_plugin_exit(h); cli_syntax_unload(h); cli_syntax_set(h, NULL); } + cligen_parsetree_free(ptall, 1); if (dp) free(dp); return retval; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 18af35cc..c0897b61 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -157,7 +157,7 @@ expand_dbvar(void *h, /* Get configuration */ if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */ goto done; - if ((xe = xpath_first(xt, "/rpc-error")) != NULL){ + if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xe); goto ok; } @@ -204,12 +204,12 @@ expand_dbvar(void *h, fprintf(stderr, "%s\n", reason); goto done; } - if ((xcur = xpath_first_nsc(xt, nsc, "%s", xpath)) == NULL){ + if ((xcur = xpath_first(xt, nsc, "%s", xpath)) == NULL){ clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath); goto done; } } - if (xpath_vec_nsc(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0) + if (xpath_vec(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0) goto done; /* Loop for inserting into commands cvec. * Detect duplicates: for ordered-by system assume list is ordered, so you need @@ -486,7 +486,7 @@ cli_show_config1(clicon_handle h, if (clicon_rpc_get(h, cbuf_get(cbxpath), nsc, CONTENT_ALL, -1, &xt) < 0) goto done; } - if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } @@ -634,12 +634,12 @@ show_conf_xpath(clicon_handle h, goto done; if (clicon_rpc_get_config(h, NULL, str, xpath, nsc, &xt) < 0) goto done; - if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } - if (xpath_vec_nsc(xt, nsc, "%s", &xv, &xlen, xpath) < 0) + if (xpath_vec(xt, nsc, "%s", &xv, &xlen, xpath) < 0) goto done; for (i=0; i */ +static int +netconf_get_config_subtree(clicon_handle h, + cxobj *xfilter, + cxobj **xret) +{ + int retval = -1; + cxobj *xdata; + + /* a subtree filter is comprised of zero or more element subtrees*/ + if ((xdata = xpath_first(*xret, NULL, "/rpc-reply/data")) == NULL) + goto ok; + if (xml_filter(xfilter, xdata) < 0){ + xml_parse_va(xret, NULL, "" + "operation-failed" + "applicatio" + "error" + "filtering" + ""); + } +ok: + retval = 0; + // done: + return retval; +} + /*! Get configuration * @param[in] h Clicon handle * @param[in] xn Sub-tree (under xorig) at ... level. @@ -136,37 +161,22 @@ netconf_get_config(clicon_handle h, cxobj *xfilter; /* filter */ int retval = -1; char *ftype = NULL; - cxobj *xfilterconf; - cxobj *xconf; /* ie ... */ - if ((xfilter = xpath_first(xn, "filter")) != NULL) + if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL) ftype = xml_find_value(xfilter, "type"); - if (ftype == NULL || strcmp(ftype, "xpath")==0){ + if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; } else if (strcmp(ftype, "subtree")==0){ - /* Default rfc filter is subtree. I prefer xpath and use it internally. - Get whole subtree and then filter aftwerwards. This is suboptimal. - Therefore please use xpath. + /* Get whole config first, then filter. This is suboptimal */ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; - if (xfilter && - (xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL && - (xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){ - /* xml_filter removes parts of xml tree not matching */ - if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) || - xml_filter(xfilterconf, xconf) < 0){ - xml_parse_va(xret, NULL, "" - "operation-failed" - "applicatio" - "error" - "filtering" - ""); - } - } + /* Now filter on whole tree */ + if (netconf_get_config_subtree(h, xfilter, xret) < 0) + goto done; } else{ xml_parse_va(xret, NULL, "" @@ -208,7 +218,7 @@ get_edit_opts(cxobj *xn, cxobj *x; char *optstr; - if ((x = xpath_first(xn, "test-option")) != NULL){ + if ((x = xpath_first(xn, NULL, "test-option")) != NULL){ if ((optstr = xml_body(x)) != NULL){ if (strcmp(optstr, "test-then-set") == 0) *testopt = TEST_THEN_SET; @@ -220,7 +230,7 @@ get_edit_opts(cxobj *xn, goto parerr; } } - if ((x = xpath_first(xn, "error-option")) != NULL){ + if ((x = xpath_first(xn, NULL, "error-option")) != NULL){ if ((optstr = xml_body(x)) != NULL){ if (strcmp(optstr, "stop-on-error") == 0) *erropt = STOP_ON_ERROR; @@ -348,37 +358,22 @@ netconf_get(clicon_handle h, cxobj *xfilter; /* filter */ int retval = -1; char *ftype = NULL; - cxobj *xfilterconf; - cxobj *xconf; /* ie ... */ - if ((xfilter = xpath_first(xn, "filter")) != NULL) + if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL) ftype = xml_find_value(xfilter, "type"); - if (ftype == NULL || strcmp(ftype, "xpath")==0){ + if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; } else if (strcmp(ftype, "subtree")==0){ - /* Default rfc filter is subtree. I prefer xpath and use it internally. - Get whole subtree and then filter aftwerwards. This is suboptimal. - Therefore please use xpath. + /* Get whole config + state first, then filter. This is suboptimal */ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; - if (xfilter && - (xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL && - (xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){ - /* xml_filter removes parts of xml tree not matching */ - if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) || - xml_filter(xfilterconf, xconf) < 0){ - xml_parse_va(xret, NULL, "" - "operation-failed" - "applicatio" - "error" - "filtering" - ""); - } - } + /* Now filter on whole tree */ + if (netconf_get_config_subtree(h, xfilter, xret) < 0) + goto done; } else{ xml_parse_va(xret, NULL, "" @@ -448,7 +443,7 @@ netconf_notification_cb(int s, if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netconf:notification:1.0")) == NULL) goto done; - if ((xn = xpath_first_nsc(xt, nsc, "notification")) == NULL) + if ((xn = xpath_first(xt, nsc, "notification")) == NULL) goto ok; /* create netconf message */ if ((cb = cbuf_new()) == NULL){ @@ -500,7 +495,7 @@ netconf_create_subscription(clicon_handle h, int s; char *ftype; - if ((xfilter = xpath_first(xn, "//filter")) != NULL){ + if ((xfilter = xpath_first(xn, NULL, "//filter")) != NULL){ if ((ftype = xml_find_value(xfilter, "type")) != NULL){ if (strcmp(ftype, "xpath") != 0){ xml_parse_va(xret, NULL, "" @@ -516,7 +511,7 @@ netconf_create_subscription(clicon_handle h, } if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, &s) < 0) goto done; - if (xpath_first(*xret, "rpc-reply/rpc-error") != NULL) + if (xpath_first(*xret, NULL, "rpc-reply/rpc-error") != NULL) goto ok; if (event_reg_fd(s, netconf_notification_cb, @@ -622,7 +617,7 @@ netconf_application_rpc(clicon_handle h, */ if (0) if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){ - xoutput=xpath_first(*xret, "/"); + xoutput=xpath_first(*xret, NULL, "/"); xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index fe8a2544..a87ceec9 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -476,7 +476,7 @@ api_return_err(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); if ((cb = cbuf_new()) == NULL) goto done; - if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){ + if ((xtag = xpath_first(xerr, NULL, "//error-tag")) == NULL){ restconf_notfound(r); goto ok; } @@ -593,6 +593,7 @@ int restconf_terminate(clicon_handle h) { yang_stmt *yspec; + cvec *nsctx; cxobj *x; int fs; /* fgcx socket */ @@ -606,6 +607,8 @@ restconf_terminate(clicon_handle h) yspec_free(yspec); if ((yspec = clicon_config_yang(h)) != NULL) yspec_free(yspec); + if ((nsctx = clicon_nsctx_global_get(h)) != NULL) + cvec_free(nsctx); if ((x = clicon_conf_xml(h)) != NULL) xml_free(x); clicon_handle_exit(h); diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 1bcadbfd..3ee07a19 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -418,7 +418,7 @@ api_restconf(clicon_handle h, else{ if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0) goto done; goto ok; @@ -582,13 +582,13 @@ main(int argc, char *dir; int logdst = CLICON_LOG_SYSLOG; yang_stmt *yspec = NULL; - yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */ char *stream_path; int finish = 0; int start = 1; char *str; clixon_plugin *cp = NULL; uint32_t id = 0; + cvec *nsctx_global = NULL; /* Global namespace context */ /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); @@ -641,13 +641,10 @@ main(int argc, goto done; } - /* Create configure yang-spec */ - if ((yspecfg = yspec_new()) == NULL) - goto done; /* Find and read configfile */ - if (clicon_options_main(h, yspecfg) < 0) + if (clicon_options_main(h) < 0) goto done; - clicon_config_yang_set(h, yspecfg); + stream_path = clicon_option_str(h, "CLICON_STREAM_PATH"); /* Now rest of options, some overwrite option file */ optind = 1; @@ -760,6 +757,14 @@ main(int argc, yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) goto done; + /* Here all modules are loaded + * Compute and set canonical namespace context + */ + if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0) + goto done; + if (clicon_nsctx_global_set(h, nsctx_global) < 0) + goto done; + /* Dump configuration options on debug */ if (debug) clicon_option_dump(h, debug); diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 0ecf18f4..0545cb72 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -283,7 +283,7 @@ api_data_write(clicon_handle h, if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -296,7 +296,7 @@ api_data_write(clicon_handle h, "candidate", cbuf_get(cbpath), nsc, &xret) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -341,7 +341,7 @@ api_data_write(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -358,7 +358,7 @@ api_data_write(clicon_handle h, if (data == NULL || strlen(data) == 0){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -373,7 +373,7 @@ api_data_write(clicon_handle h, if (xml_parse_string(data, yspec, &xdata0) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -392,7 +392,7 @@ api_data_write(clicon_handle h, if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -401,7 +401,7 @@ api_data_write(clicon_handle h, goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -422,7 +422,7 @@ api_data_write(clicon_handle h, if (xml_child_nr(xdata0) != 1){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -450,7 +450,7 @@ api_data_write(clicon_handle h, if (ymoddata != ymodapi){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -493,7 +493,7 @@ api_data_write(clicon_handle h, if (strcmp(dname, xml_name(xbot))){ if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -518,7 +518,7 @@ api_data_write(clicon_handle h, if (match_list_keys(ybot, xdata, xbot) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -542,7 +542,7 @@ api_data_write(clicon_handle h, if (parbod == NULL || strcmp(parbod, xml_body(xdata))){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -603,7 +603,7 @@ api_data_write(clicon_handle h, clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -616,14 +616,14 @@ api_data_write(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", username?username:""); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; @@ -646,7 +646,7 @@ api_data_write(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } @@ -840,7 +840,7 @@ api_data_delete(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -871,7 +871,7 @@ api_data_delete(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -885,14 +885,14 @@ api_data_delete(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", clicon_nacm_recovery_user(h)); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; @@ -915,7 +915,7 @@ api_data_delete(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index b62da0da..124b35b1 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -130,7 +130,7 @@ api_data_get2(clicon_handle h, if (netconf_bad_attribute_xml(&xerr, "application", "content", "Unrecognized value of content attribute") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -152,7 +152,7 @@ api_data_get2(clicon_handle h, if (netconf_bad_attribute_xml(&xerr, "application", "depth", "Unrecognized value of depth attribute") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -172,7 +172,7 @@ api_data_get2(clicon_handle h, if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -196,7 +196,7 @@ api_data_get2(clicon_handle h, if (ret < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -218,7 +218,7 @@ api_data_get2(clicon_handle h, } #endif /* Check if error return */ - if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -247,10 +247,10 @@ api_data_get2(clicon_handle h, } } else{ - if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){ + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){ if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index c7700f41..40763d88 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -147,7 +147,7 @@ api_data_post(clicon_handle h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -171,7 +171,7 @@ api_data_post(clicon_handle h, if (data == NULL || strlen(data) == 0){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -186,7 +186,7 @@ api_data_post(clicon_handle h, if (xml_parse_string(data, NULL, &xdata0) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -208,7 +208,7 @@ api_data_post(clicon_handle h, if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -217,7 +217,7 @@ api_data_post(clicon_handle h, goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -237,7 +237,7 @@ api_data_post(clicon_handle h, if (xml_child_nr(xdata0) != 1){ if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -268,7 +268,7 @@ api_data_post(clicon_handle h, if (ys_real_module(ydata) != ymoddata){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -282,7 +282,7 @@ api_data_post(clicon_handle h, if (ybot && yang_parent_get(ydata) != ybot){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -338,7 +338,7 @@ api_data_post(clicon_handle h, clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -352,14 +352,14 @@ api_data_post(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", username?username:""); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) /* Use original xe */ goto done; @@ -382,7 +382,7 @@ api_data_post(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } @@ -466,7 +466,7 @@ api_operations_post_input(clicon_handle h, if (xml_parse_string(data, yspec, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -479,7 +479,7 @@ api_operations_post_input(clicon_handle h, if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -488,7 +488,7 @@ api_operations_post_input(clicon_handle h, goto fail; } if (ret == 0){ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -527,7 +527,7 @@ api_operations_post_input(clicon_handle h, else if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -601,7 +601,7 @@ api_operations_post_output(clicon_handle h, xml_child_nr_type(xret, CX_ELMNT) != 1){ if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -639,7 +639,7 @@ api_operations_post_output(clicon_handle h, (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, "rpc-reply/rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -770,7 +770,7 @@ api_operations_post(clicon_handle h, if (oppath == NULL || strcmp(oppath,"/")==0){ if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -789,7 +789,7 @@ api_operations_post(clicon_handle h, if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -800,7 +800,7 @@ api_operations_post(clicon_handle h, if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){ if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0) goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -826,7 +826,7 @@ api_operations_post(clicon_handle h, if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -867,7 +867,7 @@ api_operations_post(clicon_handle h, if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0) goto done; if (ret == 0){ - if ((xe = xpath_first(xret, "rpc-error")) == NULL){ + if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto ok; } @@ -900,7 +900,7 @@ api_operations_post(clicon_handle h, if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0) goto done; /* Local error: return it and quit */ - if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -909,7 +909,7 @@ api_operations_post(clicon_handle h, else { /* Send to backend */ if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c index 47375a98..a6b6ecdb 100644 --- a/apps/restconf/restconf_stream.c +++ b/apps/restconf/restconf_stream.c @@ -187,11 +187,11 @@ restconf_stream_cb(int s, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - if ((xn = xpath_first(xtop, "notification")) == NULL) + if ((xn = xpath_first(xtop, NULL, "notification")) == NULL) goto ok; #ifdef notused - xt = xpath_first(xn, "eventTime"); - if ((xe = xpath_first(xn, "event")) == NULL) /* event can depend on yang? */ + xt = xpath_first(xn, NULL, "eventTime"); + if ((xe = xpath_first(xn, NULL, "event")) == NULL) /* event can depend on yang? */ goto ok; if (xt) @@ -268,7 +268,7 @@ restconf_stream(clicon_handle h, cprintf(cb, "]]>]]>"); if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0) goto done; - if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; @@ -417,7 +417,7 @@ api_stream(clicon_handle h, else{ if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0) goto done; goto ok; diff --git a/configure b/configure index 2bf8b1a8..e0146b78 100755 --- a/configure +++ b/configure @@ -621,7 +621,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS -STD_YANG_INSTALLDIR +OPT_YANG_INSTALLDIR YANG_INSTALLDIR EGREP GREP @@ -633,7 +633,7 @@ YACC CPP wwwuser wwwdir -enable_stdyangs +enable_optyangs with_restconf SH_SUFFIX CLIXON_DEFAULT_CONFIG @@ -711,14 +711,14 @@ ac_user_opts=' enable_option_checking enable_debug with_cligen -enable_stdyangs +enable_optyangs enable_publish with_restconf with_wwwuser with_configfile with_libxml2 with_yang_installdir -with_std_yang_installdir +with_opt_yang_installdir ' ac_precious_vars='build_alias host_alias @@ -1355,8 +1355,8 @@ Optional Features: --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 - --disable-stdyangs Include standard yang files in clixon install, - default: yes + --enable-optyangs Include standard yang files in clixon install, + default: no --enable-publish Enable publish of notification streams using SSE and curl @@ -1369,7 +1369,7 @@ Optional Packages: --with-configfile=FILE set default path to config file --with-libxml2 use gnome/libxml2 regex engine --with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon) - --with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon) + --with-opt-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon) Some influential environment variables: CC C compiler command @@ -2172,9 +2172,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="4" -CLIXON_VERSION_MINOR="2" +CLIXON_VERSION_MINOR="3" 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\"" # Check CLIgen if test "$prefix" = "NONE"; then @@ -4247,22 +4247,22 @@ fi # Disable/enable standard Yang files. # If enable - include yang/standard/*.yang in clixon yang files (default) # If disable - get standard yang files from elsewhere -# Check whether --enable-stdyangs was given. -if test "${enable_stdyangs+set}" = set; then : - enableval=$enable_stdyangs; +# Check whether --enable-optyangs was given. +if test "${enable_optyangs+set}" = set; then : + enableval=$enable_optyangs; if test "$enableval" = no; then - enable_stdyangs=no + enable_optyangs=no else - enable_stdyangs=yes + enable_optyangs=yes fi else - enable_stdyangs=yes + enable_optyangs=no fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: stdyangs is $enable_stdyangs" >&5 -$as_echo "stdyangs is $enable_stdyangs" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: optyangs is $enable_optyangs" >&5 +$as_echo "optyangs is $enable_optyangs" >&6; } # Experimental: Curl publish notification stream to eg Nginx nchan. # Check whether --enable-publish was given. @@ -4933,8 +4933,8 @@ $as_echo "Have getsockopt SO_PEERCRED" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# YANG_INSTALLDIR is where clixon installs the Clixon yang files -# (the files in in yang/clixon) +# YANG_INSTALLDIR is where clixon installs the Clixon yang files and mandatory +# standard yang files: the files in in yang/clixon and yang/mandatory # Each application designer may need to place YANG_INSTALLDIR in their config: # $YANG_INSTALLDIR @@ -4950,22 +4950,22 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Clixon yang files are installed in ${YANG_INSTALLDIR}" >&5 $as_echo "Clixon yang files are installed in ${YANG_INSTALLDIR}" >&6; } -# STD_YANG_INSTALLDIR is where clixon installs standard yang files -# (the files in in yang/standard) +# OPT_YANG_INSTALLDIR is where clixon installs standard yang files +# ( the files in in yang/standard) # that Clixon needs to run (or examples rely on). These may be retreived from # elsewhere (eg yangmodels repo) -# Check whether --with-std-yang-installdir was given. -if test "${with_std_yang_installdir+set}" = set; then : - withval=$with_std_yang_installdir; STD_YANG_INSTALLDIR="$withval" +# Check whether --with-opt-yang-installdir was given. +if test "${with_opt_yang_installdir+set}" = set; then : + withval=$with_opt_yang_installdir; OPT_YANG_INSTALLDIR="$withval" else - STD_YANG_INSTALLDIR="${prefix}/share/clixon" + OPT_YANG_INSTALLDIR="${prefix}/share/clixon" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&5 -$as_echo "Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled)" >&5 +$as_echo "Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled)" >&6; } # Default location for config file @@ -4976,7 +4976,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/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/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/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5692,7 +5692,8 @@ do "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; "yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;; "yang/clixon/Makefile") CONFIG_FILES="$CONFIG_FILES yang/clixon/Makefile" ;; - "yang/standard/Makefile") CONFIG_FILES="$CONFIG_FILES yang/standard/Makefile" ;; + "yang/mandatory/Makefile") CONFIG_FILES="$CONFIG_FILES yang/mandatory/Makefile" ;; + "yang/optional/Makefile") CONFIG_FILES="$CONFIG_FILES yang/optional/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; diff --git a/configure.ac b/configure.ac index fa281d81..e82bf576 100644 --- a/configure.ac +++ b/configure.ac @@ -43,9 +43,9 @@ AC_INIT(lib/clixon/clixon.h.in) : ${INSTALLFLAGS="-s"} CLIXON_VERSION_MAJOR="4" -CLIXON_VERSION_MINOR="2" +CLIXON_VERSION_MINOR="3" 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\"" # Check CLIgen if test "$prefix" = "NONE"; then @@ -89,7 +89,7 @@ AC_SUBST(CLIXON_DEFAULT_CONFIG) AC_SUBST(LIBS) AC_SUBST(SH_SUFFIX) AC_SUBST(with_restconf) # If yes, compile apps/restconf -AC_SUBST(enable_stdyangs) +AC_SUBST(enable_optyangs) AC_SUBST(wwwdir,/www-data) AC_SUBST(wwwuser,www-data) @@ -151,16 +151,16 @@ fi # Disable/enable standard Yang files. # If enable - include yang/standard/*.yang in clixon yang files (default) # If disable - get standard yang files from elsewhere -AC_ARG_ENABLE(stdyangs, AS_HELP_STRING([--disable-stdyangs],[Include standard yang files in clixon install, default: yes]),[ +AC_ARG_ENABLE(optyangs, AS_HELP_STRING([--enable-optyangs],[Include optional yang files for examples and testing in clixon install, default: no]),[ if test "$enableval" = no; then - enable_stdyangs=no + enable_optyangs=no else - enable_stdyangs=yes + enable_optyangs=yes fi ], - [ enable_stdyangs=yes]) + [ enable_optyangs=no]) -AC_MSG_RESULT(stdyangs is $enable_stdyangs) +AC_MSG_RESULT(optyangs is $enable_optyangs) # Experimental: Curl publish notification stream to eg Nginx nchan. AC_ARG_ENABLE(publish, AS_HELP_STRING([--enable-publish],[Enable publish of notification streams using SSE and curl]),[ @@ -229,8 +229,8 @@ AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versi AC_TRY_COMPILE([#include ], [getsockopt(1, SOL_SOCKET, SO_PEERCRED, 0, 0);], [AC_DEFINE(HAVE_SO_PEERCRED, 1, [Have getsockopt SO_PEERCRED]) AC_MSG_RESULT(Have getsockopt SO_PEERCRED)]) -# YANG_INSTALLDIR is where clixon installs the Clixon yang files -# (the files in in yang/clixon) +# YANG_INSTALLDIR is where clixon installs the Clixon yang files and mandatory +# standard yang files: the files in in yang/clixon and yang/mandatory # Each application designer may need to place YANG_INSTALLDIR in their config: # $YANG_INSTALLDIR AC_ARG_WITH(yang-installdir, @@ -241,17 +241,17 @@ AC_ARG_WITH(yang-installdir, AC_SUBST(YANG_INSTALLDIR) AC_MSG_RESULT(Clixon yang files are installed in ${YANG_INSTALLDIR}) -# STD_YANG_INSTALLDIR is where clixon installs standard yang files -# (the files in in yang/standard) +# OPT_YANG_INSTALLDIR is where clixon installs standard yang files +# ( the files in in yang/standard) # that Clixon needs to run (or examples rely on). These may be retreived from # elsewhere (eg yangmodels repo) -AC_ARG_WITH(std-yang-installdir, - [ --with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon) ], - [STD_YANG_INSTALLDIR="$withval"], - [STD_YANG_INSTALLDIR="${prefix}/share/clixon"] +AC_ARG_WITH(opt-yang-installdir, + [ --with-opt-yang-installdir=DIR Install optional yang files here (default: ${prefix}/share/clixon) ], + [OPT_YANG_INSTALLDIR="$withval"], + [OPT_YANG_INSTALLDIR="${prefix}/share/clixon"] ) -AC_SUBST(STD_YANG_INSTALLDIR) -AC_MSG_RESULT(Standard yang files are installed in ${STD_YANG_INSTALLDIR}) +AC_SUBST(OPT_YANG_INSTALLDIR) +AC_MSG_RESULT(Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled)) # Default location for config file AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${CLIXON_DEFAULT_CONFIG}",[Location for apps to find default config file]) @@ -280,7 +280,8 @@ AC_OUTPUT(Makefile util/Makefile yang/Makefile yang/clixon/Makefile - yang/standard/Makefile + yang/mandatory/Makefile + yang/optional/Makefile doc/Makefile test/Makefile ) diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md index 46b69d74..834c1e58 100644 --- a/doc/DEVELOP.md +++ b/doc/DEVELOP.md @@ -122,6 +122,12 @@ After release: ``` * Run autoconf +Create release branch: +``` + git checkout -b release-4.2 4.2.0 + git push origin release-4.2 +``` + ## Use of constants etc -Use MAXPATHLEN (not PATH_MAX) \ No newline at end of file +Use MAXPATHLEN (not PATH_MAX) diff --git a/doc/FAQ.md b/doc/FAQ.md index 6c0cccc1..ae97a24a 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -77,12 +77,14 @@ One of the examples is [a hello world example](../example/hello). Please start w ## How do you build and install Clixon? Clixon: ``` - ./configure; + ./configure --enable-optyang; make; sudo make install; sudo make install-include ``` -The main example: +(note: optyang enable only if you need to run the main example, otherwise it is not necessary). + +The main example: ``` cd example; make; diff --git a/docker/main/Dockerfile b/docker/main/Dockerfile index dfa05546..630dc43b 100644 --- a/docker/main/Dockerfile +++ b/docker/main/Dockerfile @@ -62,7 +62,7 @@ RUN adduser -D -H www-data RUN apk add --update nginx # Configure, build and install clixon -RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-wwwuser=www-data +RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-wwwuser=www-data --enable-optyangs RUN make RUN make install RUN make install-include diff --git a/docker/main/start.sh b/docker/main/start.sh index 909f72fd..88995157 100755 --- a/docker/main/start.sh +++ b/docker/main/start.sh @@ -23,7 +23,7 @@ PORT=${PORT:-80} STORE=${STORE:-} CONFIG0=$(cat < + /usr/local/etc/example.xml *:* /usr/local/share/clixon @@ -42,7 +42,7 @@ CONFIG0=$(cat <0 init disabled - + EOF ) diff --git a/example/hello/README.md b/example/hello/README.md index 0c7c3a0b..3f851677 100644 --- a/example/hello/README.md +++ b/example/hello/README.md @@ -82,7 +82,7 @@ Start restconf daemon Start sending restconf commands (using Curl): ``` - olof@vandal> curl -X POST http://localhost/restconf/data -d '{"clixon-hello:hello":{"world":null}}' + olof@vandal> curl -X POST http://localhost/restconf/data -H "Content-Type: application/yang-data+json" -d '{"clixon-hello:hello":{"world":null}}' olof@vandal> curl -X GET http://localhost/restconf/data { "data": { diff --git a/example/main/Makefile.in b/example/main/Makefile.in index 98596fd8..cc8cbbfb 100644 --- a/example/main/Makefile.in +++ b/example/main/Makefile.in @@ -79,7 +79,7 @@ all: $(PLUGINS) CLISPECS = $(APPNAME)_cli.cli -YANGSPECS = clixon-example@2019-07-23.yang +YANGSPECS = clixon-example@2019-11-05.yang # Backend plugin BE_SRC = $(APPNAME)_backend.c diff --git a/example/main/README.md b/example/main/README.md index 8abd26ee..d5e98d50 100644 --- a/example/main/README.md +++ b/example/main/README.md @@ -30,13 +30,14 @@ This directory contains a Clixon example used primarily for testing. It can be u * `example_netconf.c` Netconf callback plugin * `Makefile.in` Example makefile where plugins are built and installed - ## Compile and run Before you start, +* You must configure with: `--enable-optyangs` to run the main example. * 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 make && sudo make install @@ -206,7 +207,7 @@ clixon_netconf -qf /usr/local/etc/example.xml Restconf (assuming nginx started): ``` sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data& -curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}' +curl -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"ipv4"}}' { "clixon-example:output": { "x": "ipv4", diff --git a/example/main/clixon-example@2019-07-23.yang b/example/main/clixon-example@2019-11-05.yang similarity index 92% rename from example/main/clixon-example@2019-07-23.yang rename to example/main/clixon-example@2019-11-05.yang index da93468f..b0c8e492 100644 --- a/example/main/clixon-example@2019-07-23.yang +++ b/example/main/clixon-example@2019-11-05.yang @@ -2,6 +2,9 @@ module clixon-example { yang-version 1.1; namespace "urn:example:clixon"; prefix ex; + revision 2019-11-05 { + description "Augment interface. Released in Clixon 4.3.0"; + } revision 2019-07-23 { description "Extension e4. Released in Clixon 4.1.0"; } @@ -42,6 +45,18 @@ module clixon-example { type string; } } + augment "/if:interfaces/if:interface" { + container my-status { + config false; + description "For testing augment+state"; + leaf int { + type int32; + } + leaf str { + type string; + } + } + } /* yang extension implemented by the example backend code. */ extension e4 { description diff --git a/example/main/example_backend.c b/example/main/example_backend.c index 25a38514..95542477 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -317,30 +317,23 @@ example_statedata(clicon_handle h, * state information. In this case adding dummy interface operation state * to configured interfaces. * Get config according to xpath */ - if (xmldb_get0(h, "running", nsc, xpath, 1, &xt, NULL) < 0) + if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL) goto done; - - if (yang_find_module_by_namespace(yspec, "urn:ietf:params:xml:ns:yang:ietf-interfaces") != NULL){ - /* Here a separate namespace context nsc1 is created. The original nsc - * created by the system cannot be used trivially, since we dont know - * the prefixes, although we could by a complex mechanism find the prefix - * (if it exists) and use that when creating our xpath. - * But it is easier creating a new namespace context nsc1. - */ - if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL) - goto done; - if (xpath_vec_nsc(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0) - goto done; - if (xlen){ - cprintf(cb, ""); - for (i=0; i%sup", name); - } - cprintf(cb, ""); - if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) - goto done; + if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0) + goto done; + if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0) + goto done; + if (xlen){ + cprintf(cb, ""); + for (i=0; i%sex:ethup", name); + cprintf(cb, "42foo"); + cprintf(cb, ""); } + cprintf(cb, ""); + if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) + goto done; } /* State in test_yang.sh , test_restconf.sh and test_order.sh */ if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){ @@ -356,32 +349,14 @@ example_statedata(clicon_handle h, * (2) event-count is XOR on name, so is not 42 and 4 */ if (yang_find_module_by_namespace(yspec, "urn:example:events") != NULL){ - if ((nsc2 = xml_nsctx_init(NULL, "urn:example:events")) == NULL) + cbuf_reset(cb); + cprintf(cb, ""); + cprintf(cb, "interface-down90"); + cprintf(cb, "interface-up77"); + cprintf(cb, ""); + if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) goto done; - if (xvec){ - free(xvec); - xvec = NULL; - } - if (xpath_vec_nsc(xt, nsc2, "/events/event/name", &xvec, &xlen) < 0) - goto done; - if (xlen){ - int j = 0; - int c; - cprintf(cb, ""); - - for (i=0; i%s%d", name, c); - } - cprintf(cb, ""); - if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) - goto done; - } } - ok: retval = 0; done: @@ -500,7 +475,7 @@ upgrade_2016(clicon_handle h, if ((name = xml_find_body(xi, "name")) == NULL) continue; /* shouldnt happen */ /* Get corresponding /interfaces/interface entry */ - xif = xpath_first(xt, "/interfaces/interface[name=\"%s\"]", name); + xif = xpath_first(xt, NULL, "/interfaces/interface[name=\"%s\"]", name); /* - Move /if:interfaces-state/if:interface/if:admin-status to * /if:interfaces/if:interface/ */ if ((x = xml_find(xi, "admin-status")) != NULL && xif){ @@ -603,7 +578,7 @@ upgrade_2018(clicon_handle h, /* Change type /interfaces/interface/statistics/in-octets to * decimal64 with fraction-digits 3 and divide values with 1000 */ - if ((x = xpath_first(xi, "statistics/in-octets")) != NULL){ + if ((x = xpath_first(xi, NULL, "statistics/in-octets")) != NULL){ if ((xb = xml_body_get(x)) != NULL){ uint64_t u64; cbuf *cb = cbuf_new(); diff --git a/example/main/example_backend_nacm.c b/example/main/example_backend_nacm.c index c0f49a40..a3c42657 100644 --- a/example/main/example_backend_nacm.c +++ b/example/main/example_backend_nacm.c @@ -87,7 +87,7 @@ nacm_validate(clicon_handle h, if (_transaction_log){ transaction_log(h, td, LOG_NOTICE, __FUNCTION__); if (_transaction_error_toggle==0 && - xpath_first(transaction_target(td), "%s", _transaction_xpath)){ + xpath_first(transaction_target(td), NULL, "%s", _transaction_xpath)){ _transaction_error_toggle=1; /* toggle if triggered */ clicon_err(OE_XML, 0, "User error"); return -1; /* induce fail */ @@ -116,7 +116,7 @@ nacm_commit(clicon_handle h, if (_transaction_log){ transaction_log(h, td, LOG_NOTICE, __FUNCTION__); if (_transaction_error_toggle==1 && - xpath_first(target, "%s", _transaction_xpath)){ + xpath_first(target, NULL, "%s", _transaction_xpath)){ _transaction_error_toggle=0; /* toggle if triggered */ clicon_err(OE_XML, 0, "User error"); return -1; /* induce fail */ diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 8cc88abf..330c38e4 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -43,7 +43,6 @@ #include #include #include -#include /* matching strings */ #include /* matching strings */ /* clicon */ @@ -110,7 +109,7 @@ example_client_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, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index c810acd9..7de4a0d0 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -51,20 +51,14 @@ #include /* MAXPATHLEN */ /* - * CLIXON version macros + * CLIXON version macros, set in configure and resolved when expanding to + * clixon.h */ - #undef CLIXON_VERSION_STRING #undef CLIXON_VERSION_MAJOR #undef CLIXON_VERSION_MINOR #undef CLIXON_VERSION_PATCH -/* - * Use this constant to disable some prototypes that should not be visible outside the lib. - * This is an alternative to use separate internal include files. - */ -#define LIBCLIXON_API 1 - #include #include #include diff --git a/lib/clixon/clixon_data.h b/lib/clixon/clixon_data.h index 7dfd556a..961ac7aa 100644 --- a/lib/clixon/clixon_data.h +++ b/lib/clixon/clixon_data.h @@ -40,12 +40,6 @@ #ifndef _CLIXON_DATA_H_ #define _CLIXON_DATA_H_ -/* - * Constants - */ -/* default group membership to access config unix socket */ -#define CLICON_SOCK_GROUP "clicon" - /* * Types */ @@ -61,12 +55,15 @@ typedef struct { yang_stmt * clicon_dbspec_yang(clicon_handle h); int clicon_dbspec_yang_set(clicon_handle h, yang_stmt *ys); -cxobj * clicon_nacm_ext(clicon_handle h); -int clicon_nacm_ext_set(clicon_handle h, cxobj *xn); - yang_stmt * clicon_config_yang(clicon_handle h); int clicon_config_yang_set(clicon_handle h, yang_stmt *ys); +cvec *clicon_nsctx_global_get(clicon_handle h); +int clicon_nsctx_global_set(clicon_handle h, cvec *nsctx); + +cxobj * clicon_nacm_ext(clicon_handle h); +int clicon_nacm_ext_set(clicon_handle h, cxobj *xn); + cxobj *clicon_conf_xml(clicon_handle h); int clicon_conf_xml_set(clicon_handle h, cxobj *x); diff --git a/lib/clixon/clixon_datastore.h b/lib/clixon/clixon_datastore.h index bb4e5e24..ba0cf9af 100644 --- a/lib/clixon/clixon_datastore.h +++ b/lib/clixon/clixon_datastore.h @@ -49,9 +49,9 @@ int xmldb_validate_db(const char *db); int xmldb_connect(clicon_handle h); int xmldb_disconnect(clicon_handle h); /* in clixon_datastore_read.[ch] */ -int xmldb_get(clicon_handle h, const char *db, char *xpath, cxobj **xtop); +int xmldb_get(clicon_handle h, const char *db, cvec *nsc, char *xpath, cxobj **xtop); int xmldb_get0(clicon_handle h, const char *db, - cvec *nc, char *xpath, + cvec *nsc, char *xpath, int copy, cxobj **xtop, modstate_diff_t *msd); int xmldb_get0_clear(clicon_handle h, cxobj *x); int xmldb_get0_free(clicon_handle h, cxobj **xp); diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index 463671c2..06e5c83b 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -90,7 +90,7 @@ int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret); int netconf_module_features(clicon_handle h); int netconf_module_load(clicon_handle h); char *netconf_db_find(cxobj *xn, char *name); -int netconf_err2cb(cxobj *xerr, cbuf **cberr); +int netconf_err2cb(cxobj *xerr, cbuf *cberr); const netconf_content netconf_content_str2int(char *str); const char *netconf_content_int2str(netconf_content nr); int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id); diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index fdca3460..51eaedc7 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -38,12 +38,6 @@ #ifndef _CLIXON_OPTIONS_H_ #define _CLIXON_OPTIONS_H_ -/* - * Constants - */ -/* default group membership to access config unix socket */ -#define CLICON_SOCK_GROUP "clicon" - /* * Types */ @@ -115,7 +109,7 @@ int clicon_option_dump(clicon_handle h, int dblevel); int clicon_option_add(clicon_handle h, char *name, char *value); /* Initialize options: set defaults, read config-file, etc */ -int clicon_options_main(clicon_handle h, yang_stmt *yspec); +int clicon_options_main(clicon_handle h); /*! Check if a clicon option has a value */ int clicon_option_exists(clicon_handle h, const char *name); diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h index 5e77fcb2..97f005e4 100644 --- a/lib/clixon/clixon_proto.h +++ b/lib/clixon/clixon_proto.h @@ -69,14 +69,17 @@ struct clicon_msg *clicon_msg_encode(uint32_t id, char *format, ...); #endif int clicon_msg_decode(struct clicon_msg *msg, yang_stmt *yspec, uint32_t *id, cxobj **xml); -int clicon_connect_unix(char *sockpath); +int clicon_connect_unix(clicon_handle h, char *sockpath); -int clicon_rpc_connect_unix(struct clicon_msg *msg, + +int clicon_rpc_connect_unix(clicon_handle h, + struct clicon_msg *msg, char *sockpath, char **ret, int *sock0); -int clicon_rpc_connect_inet(struct clicon_msg *msg, +int clicon_rpc_connect_inet(clicon_handle h, + struct clicon_msg *msg, char *dst, uint16_t port, char **ret, diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index 56bcf9ef..bf1ffc0a 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -44,7 +44,7 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0, int *sock0); int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp); int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp); -int clicon_rpc_generate_error(char *format, cxobj *xerr); +int clicon_rpc_generate_error(const char *format, cxobj *xerr); int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, cxobj **xret); int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op, char *xml); diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 90717b74..10ff73a3 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -58,6 +58,10 @@ typedef struct map_str2int map_str2int; /*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */ #define align4(s) (((s)/4)*4 + 4) +/* Required for the inline to compile */ +#include +#include + /*! A strdup version that aligns on 4 bytes. To avoid warning from valgrind */ static inline char * strdup4(char *str) { diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 73d06f96..429fb191 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -129,7 +129,7 @@ int xml_flag_reset(cxobj *xn, uint16_t flag); char *xml_value(cxobj *xn); int xml_value_set(cxobj *xn, char *val); -char *xml_value_append(cxobj *xn, char *val); +int xml_value_append(cxobj *xn, char *val); enum cxobj_type xml_type(cxobj *xn); int xml_type_set(cxobj *xn, enum cxobj_type type); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 8a908f03..250acfe5 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -78,6 +78,7 @@ int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop, int xml2xpath(cxobj *x, char **xpath); int xml2api_path_1(cxobj *x, cbuf *cb); +int check_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); int yang_enum_int_value(cxobj *node, int32_t *val); diff --git a/lib/clixon/clixon_xml_nsctx.h b/lib/clixon/clixon_xml_nsctx.h index 3c383a85..5d6987ee 100644 --- a/lib/clixon/clixon_xml_nsctx.h +++ b/lib/clixon/clixon_xml_nsctx.h @@ -55,5 +55,6 @@ int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix); int xml_nsctx_add(cvec *nsc, char *prefix, char *namespace); int xml_nsctx_node(cxobj *x, cvec **ncp); int xml_nsctx_yang(yang_stmt *yn, cvec **ncp); +int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp); #endif /* _CLIXON_XML_NSCTX_H */ diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 0998da16..1b2dfbb9 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -106,6 +106,7 @@ struct xpath_tree{ enum xp_type xs_type; int xs_int; /* step-> axis-type */ double xs_double; + char *xs_strnr; /* original string xs_double: numeric value */ char *xs_s0; char *xs_s1; struct xpath_tree *xs_c0; /* child 0 */ @@ -142,21 +143,13 @@ int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *xpformat, uint16_t flags, * If you do not know what a namespace context is, see README.md#xml-and-xpath */ #if defined(__GNUC__) && __GNUC__ >= 3 -cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); -int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6))); +cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); +int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6))); #else -cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...); -int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...); +cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...); +int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...); #endif -/* Functions with nsc == NULL (implicit xpath context). */ -#if defined(__GNUC__) && __GNUC__ >= 3 -cxobj *xpath_first(cxobj *xcur, char *xpformat, ...) __attribute__ ((format (printf, 2, 3))); -int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); -#else -cxobj *xpath_first(cxobj *xcur, char *xpformat, ...); -int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...); -#endif int xpath2canonical(char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1); #endif /* _CLIXON_XPATH_H */ diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index 4f23a4f2..45e5bc46 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -73,8 +73,11 @@ #include "clixon_xpath.h" #include "clixon_data.h" -/*! Get YANG specification for application +/*! Get YANG specification for application specs * Must use hash functions directly since they are not strings. + * @param[in] h Clicon handle + * @retval yspec Yang spec + * @see clicon_config_yang for the configuration yang */ yang_stmt * clicon_dbspec_yang(clicon_handle h) @@ -88,8 +91,10 @@ clicon_dbspec_yang(clicon_handle h) return NULL; } -/*! Set yang specification for application - * ys must be a malloced pointer +/*! Set yang specification for application specifications + * @param[in] h Clicon handle + * @param[in] yspec Yang spec (malloced pointer) + * @see clicon_config_yang_set for the configuration yang */ int clicon_dbspec_yang_set(clicon_handle h, @@ -105,6 +110,82 @@ clicon_dbspec_yang_set(clicon_handle h, return 0; } +/*! Get YANG specification for clixon config (separate from application yangs) + * @param[in] h Clicon handle + * @retval yspec Yang spec + * @see clicon_dbspec_yang for the application specs + */ +yang_stmt * +clicon_config_yang(clicon_handle h) +{ + clicon_hash_t *cdat = clicon_data(h); + size_t len; + void *p; + + if ((p = clicon_hash_value(cdat, "control_yang", &len)) != NULL) + return *(yang_stmt **)p; + return NULL; +} + +/*! Set yang specification for configuration + * @param[in] h Clicon handle + * @param[in] yspec Yang spec (malloced pointer) + * @see clicon_dbspec_yang_set for the application specs + */ +int +clicon_config_yang_set(clicon_handle h, + yang_stmt *ys) +{ + clicon_hash_t *cdat = clicon_data(h); + + /* It is the pointer to ys that should be copied by hash, + so we send a ptr to the ptr to indicate what to copy. + */ + if (clicon_hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL) + return -1; + return 0; +} + +/*! Get Global "canonical" namespace context + * Canonical: use prefix and namespace specified in the yang modules. + * @param[in] h Clicon handle + * @retval nsctx Namespace context (malloced) + * @code + * cvec *nsctx; + * nsctx = clicon_nsctx_global_get(h); + * @endcode + */ +cvec * +clicon_nsctx_global_get(clicon_handle h) +{ + clicon_hash_t *cdat = clicon_data(h); + size_t len; + void *p; + + if ((p = clicon_hash_value(cdat, "nsctx_global", &len)) != NULL) + return *(cvec **)p; + return NULL; +} + +/*! Set global "canonical" namespace context + * Canonical: use prefix and namespace specified in the yang modules. + * @param[in] h Clicon handle + * @param[in] nsctx Namespace context (malloced) + */ +int +clicon_nsctx_global_set(clicon_handle h, + cvec *nsctx) +{ + clicon_hash_t *cdat = clicon_data(h); + + /* It is the pointer to cvec that should be copied by hash, + so we send a ptr to the ptr to indicate what to copy. + */ + if (clicon_hash_add(cdat, "nsctx_global", &nsctx, sizeof(nsctx)) == NULL) + return -1; + return 0; +} + /*! Get NACM (rfc 8341) XML parse tree if external not in std xml config * @param[in] h Clicon handle * @retval xn XML NACM tree, or NULL @@ -146,41 +227,6 @@ clicon_nacm_ext_set(clicon_handle h, return 0; } - -#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */ -/*! Get YANG specification for clixon config - * Must use hash functions directly since they are not strings. - */ -yang_stmt * -clicon_config_yang(clicon_handle h) -{ - clicon_hash_t *cdat = clicon_data(h); - size_t len; - void *p; - - if ((p = clicon_hash_value(cdat, "control_yang", &len)) != NULL) - return *(yang_stmt **)p; - return NULL; -} - -/*! Set yang specification for control - * ys must be a malloced pointer - */ -int -clicon_config_yang_set(clicon_handle h, - yang_stmt *ys) -{ - clicon_hash_t *cdat = clicon_data(h); - - /* It is the pointer to ys that should be copied by hash, - so we send a ptr to the ptr to indicate what to copy. - */ - if (clicon_hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL) - return -1; - return 0; -} -#endif - /*! Get YANG specification for Clixon system options and features * Must use hash functions directly since they are not strings. * Example: features are typically accessed directly in the config tree. diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 2150502c..29e096e5 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -264,7 +263,7 @@ text_read_modstate(clicon_handle h, if ((name = xml_find_body(xm, "name")) == NULL) continue; /* 3a) There is no such module in the system */ - if ((xs = xpath_first(xmcache, "module[name=\"%s\"]", name)) == NULL){ + if ((xs = xpath_first(xmcache, NULL, "module[name=\"%s\"]", name)) == NULL){ // fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name); if ((xm2 = xml_dup(xm)) == NULL) goto done; @@ -426,7 +425,7 @@ xmldb_get_nocache(clicon_handle h, goto done; /* Here xt looks like: ... */ /* Given the xpath, return a vector of matches in xvec */ - if (xpath_vec_nsc(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* If vectors are specified then mark the nodes found with all ancestors @@ -532,7 +531,7 @@ xmldb_get_cache(clicon_handle h, */ /* Here xt looks like: ... */ - if (xpath_vec_nsc(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* Make new tree by copying top-of-tree from x0t to x1t */ @@ -621,7 +620,7 @@ xmldb_get_zerocopy(clicon_handle h, else x0t = de->de_xml; /* Here xt looks like: ... */ - if (xpath_vec_nsc(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* Iterate through the match vector * For every node found in x0, mark the tree up to t1 @@ -648,12 +647,13 @@ xmldb_get_zerocopy(clicon_handle h, /*! Get content of datastore and return a copy of the XML tree * @param[in] h Clicon handle * @param[in] db Name of database to search in (filename including dir path + * @param[in] nsc XML namespace context for XPATH * @param[in] xpath String with XPATH syntax. or NULL for all * @param[out] xret Single return XML tree. Free with xml_free() * @retval 0 OK * @retval -1 Error * @code - * if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0) + * if (xmldb_get(xh, "running", NULL, "/interfaces/interface[name="eth"]", &xt) < 0) * err; * xml_free(xt); * @endcode @@ -662,10 +662,11 @@ xmldb_get_zerocopy(clicon_handle h, int xmldb_get(clicon_handle h, const char *db, + cvec *nsc, char *xpath, cxobj **xret) { - return xmldb_get0(h, db, NULL, xpath, 1, xret, NULL); + return xmldb_get0(h, db, nsc, xpath, 1, xret, NULL); } /*! Zero-copy variant of get content of database diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 8ddc69fb..95eb5379 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -133,138 +132,6 @@ attr_ns_value(cxobj *x, goto done; } -/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace - * @param[in] x0 Source XML tree - * @param[in] x1 Target XML tree - * 1. Find N=namespace(x0) - * 2. Detect if N is declared in x1 parent - * 3. If yes, assign prefix to x1 - * 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root) - * 5. Add prefix to x1, if any - * 6. Ensure x1 cache is updated - * @note switch use of x0 and x1 compared to datastore text_modify - * @see xml2ns - * XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0) - goto done; - */ -static int -check_namespaces(cxobj *x0, - cxobj *x1, - cxobj *x1p) -{ - int retval = -1; - char *namespace = NULL; - char *prefix0 = NULL;; - char *prefix10 = NULL; /* extra just for malloc problem */ - char *prefix1 = NULL;; - char *prefixb = NULL; /* identityref body prefix */ - cvec *nsc0 = NULL; - cvec *nsc = NULL; - cxobj *xa = NULL; - cxobj *x; - int isroot; - - /* XXX: need to identify root better than hiereustics and strcmp,... */ - isroot = xml_parent(x1p)==NULL && - strcmp(xml_name(x1p), "config") == 0 && - xml_prefix(x1p)==NULL; - - /* 1. Find N=namespace(x0) */ - prefix0 = xml_prefix(x0); - if (xml2ns(x0, prefix0, &namespace) < 0) - goto done; - if (namespace == NULL){ - clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s", - prefix0?prefix0:"NULL"); - goto done; - } - /* 2. Detect if namespace is declared in x1:s parent */ - if (xml2prefix(x1p, namespace, &prefix10) == 1){ - if (prefix10){ - if ((prefix1 = strdup(prefix10)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; - } - } - else - prefix1 = NULL; - /* 3. If yes, assign prefix to x1 */ - if (prefix1 && xml_prefix_set(x1, prefix1) < 0) - goto done; - /* And copy namespace context from parent to child */ - if ((nsc0 = nscache_get_all(x1p)) != NULL){ - if ((nsc = cvec_dup(nsc0)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); - goto done; - } - nscache_replace(x1, nsc); - } - /* Just in case */ - if (nscache_set(x1, prefix1, namespace) < 0) - goto done; - } - else{ - /* 4. If no, create new prefix/namespace binding and assign that to x1p - * use modules own default prefix (some chance for clash) - */ - if (prefix0 == NULL && !isroot){ - assert(xml_spec(x1) != NULL); - prefix0 = yang_find_myprefix(xml_spec(x1)); - } - if (prefix0) - if ((prefix1 = strdup(prefix0)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; - } - - /* Add binding to x1p. We add to parent due to heurestics, so we dont - * end up in adding it to large number of siblings - */ - if (isroot) - x = x1; - else - x = x1p; - if (nscache_set(x, prefix1, namespace) < 0) - goto done; - if (x == x1p){ - if ((nsc0 = nscache_get_all(x1p)) != NULL) - if ((nsc = cvec_dup(nsc0)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_dup"); - goto done; - } - /* Copy x1p cache to x1 */ - nscache_replace(x1, nsc); - } - /* Create xmlns attribute to x1p/x1 XXX same code v */ - if (prefix1){ - if ((xa = xml_new(prefix1, x, NULL)) == NULL) - goto done; - if (xml_prefix_set(xa, "xmlns") < 0) - goto done; - } - else{ - if ((xa = xml_new("xmlns", x, NULL)) == NULL) - goto done; - } - xml_type_set(xa, CX_ATTR); - if (xml_value_set(xa, namespace) < 0) - goto done; - xml_sort(x, NULL); /* Ensure attr is first / XXX xml_insert? */ - - /* 5. Add prefix to x1, if any */ - if (prefix1 && xml_prefix_set(x1, prefix1) < 0) - goto done; - } - /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */ - retval = 0; - done: - if (prefixb) - free(prefixb); - if (prefix1) - free(prefix1); - return retval; -} - static int check_identityref(cxobj *x0, cxobj *x1, @@ -1020,7 +887,7 @@ xmldb_put(clicon_handle h, if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-netconf-acm")) == NULL) goto done; if (xnacm0 != NULL && - (xnacm = xpath_first_nsc(xnacm0, nsc, "nacm")) != NULL){ + (xnacm = xpath_first(xnacm0, nsc, "nacm")) != NULL){ /* Pre-NACM access step, if permit, then dont do any nacm checks in * text_modify_* below */ if ((permit = nacm_access(h, mode, xnacm, username)) < 0) diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 6d989e42..d4fb484c 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y index 8bbc6396..5a5503da 100644 --- a/lib/src/clixon_json_parse.y +++ b/lib/src/clixon_json_parse.y @@ -228,7 +228,7 @@ json_current_body(struct clicon_json_yacc_arg *jy, if ((xn = xml_new("body", jy->jy_current, NULL)) == NULL) goto done; xml_type_set(xn, CX_BODY); - if (value && xml_value_append(xn, value)==NULL) + if (value && xml_value_append(xn, value) < 0) goto done; retval = 0; done: diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c index 3e2848e1..51a83c51 100644 --- a/lib/src/clixon_nacm.c +++ b/lib/src/clixon_nacm.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -214,7 +213,7 @@ nacm_rpc(char *rpc, goto step10; /* User's group */ - if (xpath_vec_nsc(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) + if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) goto done; /* 5. If no groups are found, continue with step 10. */ if (glen == 0) @@ -223,14 +222,14 @@ nacm_rpc(char *rpc, configuration. If a rule-list's "group" leaf-list does not match any of the user's groups, proceed to the next rule-list entry. */ - if (xpath_vec_nsc(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0) + if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0) goto done; for (i=0; i #include #include -#include #include #include @@ -1339,41 +1338,37 @@ netconf_db_find(cxobj *xn, } /*! Generate netconf error msg to cbuf to use in string printout or logs - * @param[in] xerr Netconf error message on the level: - * @param[out] cberr Translation from netconf err to cbuf. Free with cbuf_free. + * @param[in] xerr Netconf error message on the level: + * @param[in,out] cberr Translation from netconf err to cbuf. * @retval 0 OK, with cberr set * @retval -1 Error * @code - * cbuf *cb = NULL; - * if (netconf_err2cb(xerr, &cb) < 0) + * cbuf *cb = NULL; + * if ((cb = cbuf_new()) ==NULL){ + * err; + * if (netconf_err2cb(xerr, cb) < 0) * err; * printf("%s", cbuf_get(cb)); + * cbuf_free(cb); * @endcode * @see clicon_rpc_generate_error */ int netconf_err2cb(cxobj *xerr, - cbuf **cberr) + cbuf *cberr) { int retval = -1; - cbuf *cb = NULL; cxobj *x; - if ((cb = cbuf_new()) ==NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if ((x=xpath_first(xerr, "error-type"))!=NULL) - cprintf(cb, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, "error-tag"))!=NULL) - cprintf(cb, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, "error-message"))!=NULL) - cprintf(cb, "%s ", xml_body(x)); - if ((x=xpath_first(xerr, "error-info"))!=NULL) - clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0, -1); - *cberr = cb; + if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL) + cprintf(cberr, "%s ", xml_body(x)); + if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL) + cprintf(cberr, "%s ", xml_body(x)); + if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL) + cprintf(cberr, "%s ", xml_body(x)); + if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL) + clicon_xml2cbuf(cberr, xml_child_i(x,0), 0, 0, -1); retval = 0; - done: return retval; } diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 42b8326f..95da6fee 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -246,7 +246,7 @@ parse_configfile(clicon_handle h, /* Hard-coded config for < 3.10 and clixon-config for >= 3.10 */ if ((nsc = xml_nsctx_init(NULL, CLIXON_CONF_NS)) == NULL) goto done; - if ((xc = xpath_first_nsc(xt, nsc, "clixon-config")) == NULL){ + if ((xc = xpath_first(xt, nsc, "clixon-config")) == NULL){ clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: ", filename, CLIXON_CONF_NS); goto done; @@ -256,7 +256,11 @@ parse_configfile(clicon_handle h, if ((ret = xml_yang_validate_add(h, xc, &xret)) < 0) goto done; if (ret == 0){ - if (netconf_err2cb(xret, &cbret) < 0) + if ((cbret = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (netconf_err2cb(xret, cbret) < 0) goto done; clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret)); goto done; @@ -351,8 +355,7 @@ clicon_option_add(clicon_handle h, * other yang modules. */ int -clicon_options_main(clicon_handle h, - yang_stmt *yspec) +clicon_options_main(clicon_handle h) { int retval = -1; char *configfile; @@ -360,7 +363,11 @@ clicon_options_main(clicon_handle h, char *suffix; char xml = 0; /* Configfile is xml, otherwise legacy */ cxobj *xconfig = NULL; + yang_stmt *yspec = NULL; + /* Create configure yang-spec */ + if ((yspec = yspec_new()) == NULL) + goto done; /* * Set configure file if not set by command-line above */ @@ -409,8 +416,12 @@ clicon_options_main(clicon_handle h, clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: or clixon-config.yang not found?", configfile, CLIXON_CONF_NS); goto done; } + /* Set yang config spec (must store to free at exit, since conf_xml below uses it) */ + if (clicon_config_yang_set(h, yspec) < 0) + goto done; /* Set clixon_conf pointer to handle */ - clicon_conf_xml_set(h, xconfig); + if (clicon_conf_xml_set(h, xconfig) < 0) + goto done; retval = 0; done: diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 5c7d98f7..6c1dbd04 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -71,6 +71,7 @@ #include "clixon_yang.h" #include "clixon_sig.h" #include "clixon_xml.h" +#include "clixon_options.h" #include "clixon_proto.h" static int _atomicio_sig = 0; @@ -191,12 +192,14 @@ clicon_msg_decode(struct clicon_msg *msg, } /*! Open local connection using unix domain sockets + * @param[in] h Clicon handle * @param[in] sockpath Unix domain file path - * @retval s socket - * @retval -1 error + * @retval s socket + * @retval -1 error */ int -clicon_connect_unix(char *sockpath) +clicon_connect_unix(clicon_handle h, + char *sockpath) { struct sockaddr_un addr; int retval = -1; @@ -213,9 +216,9 @@ clicon_connect_unix(char *sockpath) clicon_debug(2, "%s: connecting to %s", __FUNCTION__, addr.sun_path); if (connect(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){ if (errno == EACCES) - clicon_err(OE_CFG, errno, "connecting unix socket: %s." - "Client should be member of group $CLICON_SOCK_GROUP: ", - sockpath); + clicon_err(OE_CFG, errno, "connecting unix socket: %s. " + "Is user not member of group: \"%s\"?", + sockpath, clicon_sock_group(h)); else clicon_err(OE_CFG, errno, "connecting unix socket: %s", sockpath); close(s); @@ -394,6 +397,7 @@ clicon_msg_rcv(int s, /*! Connect to server, send a clicon_msg message and wait for result using unix socket * + * @param[in] h Clicon handle * @param[in] msg CLICON msg data structure. It has fixed header and variable body. * @param[in] sockpath Unix domain file path * @param[out] retdata Returned data as string netconf xml tree. @@ -403,7 +407,8 @@ clicon_msg_rcv(int s, * @see clicon_rpc But this is one-shot rpc: open, send, get reply and close. */ int -clicon_rpc_connect_unix(struct clicon_msg *msg, +clicon_rpc_connect_unix(clicon_handle h, + struct clicon_msg *msg, char *sockpath, char **retdata, int *sock0) @@ -422,7 +427,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg, clicon_err(OE_PROTO, EIO, "%s: Not unix socket", sockpath); goto done; } - if ((s = clicon_connect_unix(sockpath)) < 0) + if ((s = clicon_connect_unix(h, sockpath)) < 0) goto done; if (clicon_rpc(s, msg, retdata) < 0) goto done; @@ -436,7 +441,8 @@ clicon_rpc_connect_unix(struct clicon_msg *msg, } /*! Connect to server, send a clicon_msg message and wait for result using an inet socket - * This uses unix domain socket communication + * + * @param[in] h Clicon handle * @param[in] msg CLICON msg data structure. It has fixed header and variable body. * @param[in] dst IPv4 address * @param[in] port TCP port @@ -447,7 +453,8 @@ clicon_rpc_connect_unix(struct clicon_msg *msg, * @see clicon_rpc But this is one-shot rpc: open, send, get reply and close. */ int -clicon_rpc_connect_inet(struct clicon_msg *msg, +clicon_rpc_connect_inet(clicon_handle h, + struct clicon_msg *msg, char *dst, uint16_t port, char **retdata, @@ -597,9 +604,9 @@ send_msg_notify(int s, /*! Send a clicon_msg NOTIFY message asynchronously to client * + * @param[in] h Clicon handle * @param[in] s Socket to communicate with client - * @param[in] level - * @param[in] xml Event as XML + * @param[in] xev Event as XML * @retval 0 OK * @retval -1 Error * @see send_msg_notify XXX beauty contest diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 785b1eb0..5d4b8dba 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -108,7 +108,7 @@ clicon_rpc_msg(clicon_handle h, /* What to do if inet socket? */ switch (clicon_sock_family(h)){ case AF_UNIX: - if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){ + if (clicon_rpc_connect_unix(h, msg, sock, &retdata, sock0) < 0){ #if 0 if (errno == ESHUTDOWN) /* Maybe could reconnect on a higher layer, but lets fail @@ -127,7 +127,7 @@ clicon_rpc_msg(clicon_handle h, clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set"); goto done; } - if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0) + if (clicon_rpc_connect_inet(h, msg, sock, port, &retdata, sock0) < 0) goto done; break; } @@ -227,16 +227,20 @@ clicon_rpc_netconf_xml(clicon_handle h, /*! Generate and log clicon error function call from Netconf error message * @param[in] prefix Print this string (if given) before: ": " - * @param[in] xerr Netconf error message on the level: + * @param[in] xerr Netconf error message on the level: */ int -clicon_rpc_generate_error(char *prefix, - cxobj *xerr) +clicon_rpc_generate_error(const char *prefix, + cxobj *xerr) { int retval = -1; cbuf *cb = NULL; - if (netconf_err2cb(xerr, &cb) < 0) + if ((cb = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (netconf_err2cb(xerr, cb) < 0) goto done; if (prefix) clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb)); @@ -268,7 +272,7 @@ clicon_rpc_generate_error(char *prefix, * err; * if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &xt) < 0) * err; - * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ * clicon_rpc_generate_error("", xerr); * err; * } @@ -324,9 +328,9 @@ clicon_rpc_get_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; /* Send xml error back: first check error, then ok */ - if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL) + if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; if (xt){ @@ -390,7 +394,7 @@ clicon_rpc_edit_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Editing configuration", xerr); goto done; } @@ -437,7 +441,7 @@ clicon_rpc_copy_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Copying configuration", xerr); goto done; } @@ -477,7 +481,7 @@ clicon_rpc_delete_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Deleting configuration", xerr); goto done; } @@ -513,7 +517,7 @@ clicon_rpc_lock(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Locking configuration", xerr); goto done; } @@ -548,7 +552,7 @@ clicon_rpc_unlock(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Configuration unlock", xerr); goto done; } @@ -582,7 +586,7 @@ clicon_rpc_unlock(clicon_handle h, * err; * if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &xt) < 0) * err; - * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ * clicon_rpc_generate_error(xerr); * err; * } @@ -646,9 +650,9 @@ clicon_rpc_get(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; /* Send xml error back: first check error, then ok */ - if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL) + if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; if (xt){ @@ -689,7 +693,7 @@ clicon_rpc_close_session(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Close session", xerr); goto done; } @@ -725,7 +729,7 @@ clicon_rpc_kill_session(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Kill session", xerr); goto done; } @@ -760,7 +764,7 @@ clicon_rpc_validate(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr); goto done; } @@ -793,7 +797,7 @@ clicon_rpc_commit(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr); goto done; } @@ -826,7 +830,7 @@ clicon_rpc_discard_changes(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Discard changes", xerr); goto done; } @@ -872,7 +876,7 @@ clicon_rpc_create_subscription(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, s0) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Create subscription", xerr); goto done; } @@ -907,11 +911,11 @@ clicon_rpc_debug(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Debug",xerr); goto done; } - if (xpath_first(xret, "//rpc-reply/ok") == NULL){ + if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){ clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */ goto done; } @@ -950,11 +954,11 @@ clicon_hello_req(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Hello", xerr); goto done; } - if ((x = xpath_first(xret, "hello/session-id")) == NULL){ + if ((x = xpath_first(xret, NULL, "hello/session-id")) == NULL){ clicon_err(OE_XML, 0, "hello session-id"); goto done; } diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 397b91db..85e48edd 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -514,7 +514,7 @@ stream_notify1(clicon_handle h, else{ /* xpath match */ if (ss->ss_xpath == NULL || strlen(ss->ss_xpath)==0 || - xpath_first(xevent, "%s", ss->ss_xpath) != NULL) + xpath_first(xevent, NULL, "%s", ss->ss_xpath) != NULL) if ((*ss->ss_fn)(h, 0, xevent, ss->ss_arg) < 0) goto done; ss = NEXTQ(struct stream_subscription *, ss); diff --git a/lib/src/clixon_uid.c b/lib/src/clixon_uid.c index b08cef5b..21160e4c 100644 --- a/lib/src/clixon_uid.c +++ b/lib/src/clixon_uid.c @@ -121,7 +121,7 @@ name2uid(const char *name, /*! Translate uid to user name * @param[in] uid User id - * @param[out] name User name + * @param[out] name User name (Malloced, need to be freed) * @retval 0 OK * @retval -1 Error. or not found */ @@ -131,7 +131,7 @@ uid2name(const uid_t uid, { int retval = -1; char buf[1024]; - struct passwd pwbuf; + struct passwd pwbuf = {0,}; struct passwd *pwbufp = NULL; if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pwbufp) != 0){ @@ -139,17 +139,21 @@ uid2name(const uid_t uid, goto done; } if (pwbufp == NULL){ - clicon_err(OE_UNIX, 0, "No such user: %u", uid); + clicon_err(OE_UNIX, ENOENT, "No such user: %u", uid); goto done; } - if (name) - *name = pwbufp->pw_name; + + if (name){ + if ((*name = strdup(pwbufp->pw_name)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + } retval = 0; done: return retval; } - /* Privileges drop perm, temp and restore * @see https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 38723f7b..add0961b 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include @@ -79,6 +78,8 @@ #define XML_TOP_SYMBOL "top" /* How many XML children to start with if any (and then add exponentialy) */ #define XML_CHILDVEC_MAX_DEFAULT 4 +/* Initial length of x_value malloced string */ +#define XML_VALUE_MAX_DEFAULT 32 /* * Types @@ -125,7 +126,7 @@ struct xml{ int x_childvec_len;/* Number of children */ int x_childvec_max;/* Length of allocated vector */ enum cxobj_type x_type; /* type of node: element, attribute, body */ - char *x_value; /* attribute and body nodes have values */ + cbuf *x_value_cb; /* attribute and body nodes have values */ int x_flags; /* Flags according to XML_FLAG_* */ yang_stmt *x_spec; /* Pointer to specification, eg yang, by reference, dont free */ @@ -150,7 +151,6 @@ static const map_str2int xsmap[] = { {NULL, -1} }; - /*! Translate from xml type in enum form to string keyword * @param[in] type Xml type * @retval str String keyword @@ -400,7 +400,7 @@ xml2ns(cxobj *x, /*! Add a namespace attribute to an XML node, either default or specific prefix * @param[in] x XML tree * @param[in] prefix prefix/ns localname. If NULL then set default xmlns - * @param[out] namespace URI namespace (or NULL). Will be copied + * @param[in] ns URI namespace (or NULL). Will be copied * @retval 0 OK * @retval -1 Error * @see xml2ns @@ -621,7 +621,7 @@ xml_flag_reset(cxobj *xn, char* xml_value(cxobj *xn) { - return xn->x_value; + return xn->x_value_cb?cbuf_get(xn->x_value_cb):NULL; } /*! Set value of xml node, value is copied @@ -634,17 +634,20 @@ int xml_value_set(cxobj *xn, char *val) { - if (xn->x_value){ - free(xn->x_value); - xn->x_value = NULL; - } - if (val){ - if ((xn->x_value = strdup(val)) == NULL){ - clicon_err(OE_XML, errno, "strdup"); - return -1; + int retval = -1; + + if (xn->x_value_cb == NULL){ + if ((xn->x_value_cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; } } - return 0; + else + cbuf_reset(xn->x_value_cb); + cprintf(xn->x_value_cb, "%s", val); + retval = 0; + done: + return retval; } /*! Append value of xnode, value is copied @@ -653,23 +656,25 @@ xml_value_set(cxobj *xn, * @retval NULL on error with clicon-err set, or if value is set to NULL * @retval new value */ -char * +int xml_value_append(cxobj *xn, char *val) { - int len0; - int len; + int retval = -1; - len0 = xn->x_value?strlen(xn->x_value):0; - if (val){ - len = len0 + strlen(val); - if ((xn->x_value = realloc(xn->x_value, len+1)) == NULL){ - clicon_err(OE_XML, errno, "realloc"); - return NULL; + if (xn->x_value_cb == NULL){ + if ((xn->x_value_cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; } - strncpy(xn->x_value + len0, val, len-len0+1); } - return xn->x_value; + if (cprintf(xn->x_value_cb, "%s", val) < 0){ + clicon_err(OE_XML, errno, "cprintf"); + goto done; + } + retval = 0; + done: + return retval; } /*! Get type of xnode @@ -1594,8 +1599,8 @@ xml_free(cxobj *x) if (x->x_name) free(x->x_name); - if (x->x_value) - free(x->x_value); + if (x->x_value_cb) + cbuf_free(x->x_value_cb); if (x->x_prefix) free(x->x_prefix); for (i=0; ix_childvec_len; i++){ @@ -2142,22 +2147,23 @@ int xml_copy_one(cxobj *x0, cxobj *x1) { + int retval = -1; char *s; xml_type_set(x1, xml_type(x0)); if ((s = xml_value(x0))){ /* malloced string */ - if ((x1->x_value = strdup(s)) == NULL){ - clicon_err(OE_XML, errno, "strdup"); - return -1; - } + if (xml_value_set(x1, s) < 0) + goto done; } if ((s = xml_name(x0))) /* malloced string */ if ((xml_name_set(x1, s)) < 0) - return -1; + goto done; if ((s = xml_prefix(x0))) /* malloced string */ if ((xml_prefix_set(x1, s)) < 0) - return -1; - return 0; + goto done; + retval = 0; + done: + return retval; } /*! Copy xml tree x0 to other existing tree x1 @@ -2618,8 +2624,10 @@ clicon_log_xml(int level, int retval = -1; /* Print xml as cbuf */ - if ((cb = cbuf_new()) == NULL) + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); goto done; + } if (clicon_xml2cbuf(cb, x, 0, 0, -1) < 0) goto done; diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c index f74ab713..833ee800 100644 --- a/lib/src/clixon_xml_changelog.c +++ b/lib/src/clixon_xml_changelog.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -200,7 +199,7 @@ changelog_move(clicon_handle h, int retval = -1; cxobj *xp; /* destination parent node */ - if ((xp = xpath_first_nsc(xt, nsc, "%s", dst)) == NULL){ + if ((xp = xpath_first(xt, nsc, "%s", dst)) == NULL){ clicon_err(OE_XML, 0, "path required"); goto done; } @@ -253,7 +252,7 @@ changelog_op(clicon_handle h, if ((wxpath = xml_find_body(xi, "where")) == NULL) goto ok; /* Get vector of target nodes meeting the where requirement */ - if (xpath_vec_nsc(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0) + if (xpath_vec(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0) goto done; for (i=0; i #include #include -#include #include #include @@ -183,7 +182,6 @@ xml_nsctx_add(cvec *cvv, return retval; } - static int xml_nsctx_node1(cxobj *xn, cvec *nsc) @@ -269,10 +267,10 @@ xml_nsctx_node(cxobj *xn, return retval; } -/*! Create and initialize XML namespace from Yang node +/*! Create and initialize XML namespace context from Yang node * Primary use is Yang path statements, eg leafrefs and others * Fully explore all prefix:namespace pairs from context of one node - * @param[in] xn XML node + * @param[in] yn Yang statement in module tree (or module itself) * @param[out] ncp XML namespace context * @retval 0 OK * @retval -1 Error @@ -355,3 +353,47 @@ xml_nsctx_yang(yang_stmt *yn, return retval; } +/*! Create and initialize XML namespace context from Yang spec + * + * That is, create a "canonical" XML namespace mapping from all loaded yang + * modules which are children of the yang specification. + * ALso add netconf base namespace: nc , urn:ietf:params:xml:ns:netconf:base:1.0 + * Fully explore all prefix:namespace pairs of all yang modules + * @param[in] yspec Yang spec + * @param[out] ncp XML namespace context + */ +int +xml_nsctx_yangspec(yang_stmt *yspec, + cvec **ncp) +{ + int retval = -1; + cvec *nc = NULL; + yang_stmt *ymod = NULL; + yang_stmt *yprefix; + yang_stmt *ynamespace; + + if ((nc = cvec_new(0)) == NULL){ + clicon_err(OE_XML, errno, "cvec_new"); + goto done; + } + ymod = NULL; + while ((ymod = yn_each(yspec, ymod)) != NULL){ + if (yang_keyword_get(ymod) != Y_MODULE) + continue; + if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL) + continue; + if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL) + continue; + if (xml_nsctx_add(nc, yang_argument_get(yprefix), yang_argument_get(ynamespace)) < 0) + goto done; + } + /* Add base netconf namespace as default and "nc" prefix */ + if (xml_nsctx_add(nc, NULL, NETCONF_BASE_NAMESPACE) < 0) + goto done; + if (xml_nsctx_add(nc, NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE) < 0) + goto done; + *ncp = nc; + retval = 0; + done: + return retval; +} diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l index 8d1826c2..f57fb896 100644 --- a/lib/src/clixon_xml_parse.l +++ b/lib/src/clixon_xml_parse.l @@ -94,6 +94,7 @@ ncname {namestart}{namechar}* %s STATEA %s AMPERSAND %s CDATA +%s CDATAEND %s CMNT %s STR %s TEXTDECL @@ -145,9 +146,13 @@ ncname {namestart}{namechar}* "apos;" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;} "quot;" { 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(CDATAEND);clixon_xml_parselval.string = yytext;return (CHARDATA);} +[^]\n]+ { 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(CDATA); clixon_xml_parselval.string = yytext;return (CHARDATA);} "-->" { BEGIN(START); return ECOMMENT; } . diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index 6d735d62..3a1e9419 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -104,7 +104,7 @@ xml_parse_content(struct xml_parse_yacc_arg *ya, goto done; xml_type_set(xn, CX_BODY); } - if (xml_value_append(xn, str)==NULL) + if (xml_value_append(xn, str) < 0) goto done; ya->ya_xelement = xn; retval = 0; @@ -140,7 +140,7 @@ xml_parse_whitespace(struct xml_parse_yacc_arg *ya, goto done; xml_type_set(xn, CX_BODY); } - if (xml_value_append(xn, str)==NULL) + if (xml_value_append(xn, str) < 0) goto done; ya->ya_xelement = xn; ok: @@ -148,7 +148,6 @@ xml_parse_whitespace(struct xml_parse_yacc_arg *ya, done: return retval; } - static int xml_parse_version(struct xml_parse_yacc_arg *ya, diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 83b19a61..6caaa1ef 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -539,7 +538,7 @@ xml_insert_userorder(cxobj *xp, else{ switch (yang_keyword_get(yn)){ case Y_LEAF_LIST: - if ((xc = xpath_first_nsc(xp, nsc_key, "%s[.='%s']", xml_name(xn), key_val)) == NULL) + if ((xc = xpath_first(xp, nsc_key, "%s[.='%s']", xml_name(xn), key_val)) == NULL) clicon_err(OE_YANG, 0, "bad-attribute: value, missing-instance: %s", key_val); else { if ((i = xml_child_order(xp, xc)) < 0) @@ -549,7 +548,7 @@ xml_insert_userorder(cxobj *xp, } break; case Y_LIST: - if ((xc = xpath_first_nsc(xp, nsc_key, "%s%s", xml_name(xn), key_val)) == NULL) + if ((xc = xpath_first(xp, nsc_key, "%s%s", xml_name(xn), key_val)) == NULL) clicon_err(OE_YANG, 0, "bad-attribute: key, missing-instance: %s", key_val); else { if ((i = xml_child_order(xp, xc)) < 0) @@ -618,8 +617,8 @@ xml_insert2(cxobj *xp, xc = xml_child_i(xp, mid); if ((yc = xml_spec(xc)) == NULL){ if (xml_type(xc) != CX_ELMNT) - clicon_err(OE_XML, 0, "No spec found %s (wrong xml type:%d)", - xml_name(xc), xml_type(xc)); + clicon_err(OE_XML, 0, "No spec found %s (wrong xml type:%s)", + xml_name(xc), xml_type2str(xml_type(xc))); else clicon_err(OE_XML, 0, "No spec found %s", xml_name(xc)); goto done; diff --git a/lib/src/clixon_xpath b/lib/src/clixon_xpath deleted file mode 100644 index 472df3d5..00000000 Binary files a/lib/src/clixon_xpath and /dev/null differ diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 3e7a7056..d6d77303 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -65,7 +65,6 @@ #include #include #include -#include #include #include #include @@ -181,8 +180,8 @@ xpath_tree_print0(cbuf *cb, cprintf(cb, "%d ", xs->xs_int); break; } - if (xs->xs_double) - cprintf(cb,"%lf ", xs->xs_double); + if (xs->xs_strnr) + cprintf(cb,"%s ", xs->xs_strnr); cprintf(cb, "\n"); if (xs->xs_c0) xpath_tree_print0(cb, xs->xs_c0,level+1); @@ -229,7 +228,7 @@ xpath_tree_print(FILE *f, /*! Create an xpath string from an xpath tree, ie "unparsing" * @param[in] xs XPATH tree - * @param[out] xpath Xpath string as CLIgen buf + * @param[out] xpath XPath string as CLIgen buf * @see xpath_tree_print * @note NOT COMPLETE, just simple xpaths eg a/b */ @@ -254,7 +253,7 @@ xpath_tree2cbuf(xpath_tree *xs, cprintf(xcb, "'%s'", xs->xs_s0); break; case XP_PRIME_NR: - cprintf(xcb, "%lf", xs->xs_double); + cprintf(xcb, "%s", xs->xs_strnr?xs->xs_strnr:"0"); break; case XP_STEP: switch (xs->xs_int){ @@ -314,6 +313,8 @@ xpath_tree2cbuf(xpath_tree *xs, int xpath_tree_free(xpath_tree *xs) { + if (xs->xs_strnr) + free(xs->xs_strnr); if (xs->xs_s0) free(xs->xs_s0); if (xs->xs_s1) @@ -328,7 +329,7 @@ xpath_tree_free(xpath_tree *xs) /*! Given xpath, parse it, and return structured xpath tree * @param[in] xpath String with XPATH 1.0 syntax - * @param[out] xptree Xpath-tree, parsed, structured XPATH, free:xpath_tree_free + * @param[out] xptree XPath-tree, parsed, structured XPATH, free:xpath_tree_free * @retval 0 OK * @retval -1 Error * @code @@ -427,7 +428,7 @@ xpath_vec_ctx(cxobj *xcur, return retval; } -/*! Xpath nodeset function where only the first matching entry is returned +/*! XPath nodeset function where only the first matching entry is returned * * @param[in] xcur XML tree where to search * @param[in] nsc External XML namespace context, or NULL @@ -438,7 +439,7 @@ xpath_vec_ctx(cxobj *xcur, * @code * cxobj *x; * cvec *nsc; // namespace context - * if ((x = xpath_first_nsc(xtop, nsc, "//symbol/foo")) != NULL) { + * if ((x = xpath_first(xtop, nsc, "//symbol/foo")) != NULL) { * ... * } * @endcode @@ -447,65 +448,8 @@ xpath_vec_ctx(cxobj *xcur, * @see also xpath_vec. */ cxobj * -xpath_first_nsc(cxobj *xcur, - cvec *nsc, - char *xpformat, - ...) -{ - cxobj *cx = NULL; - va_list ap; - size_t len; - char *xpath = NULL; - xp_ctx *xr = NULL; - - va_start(ap, xpformat); - len = vsnprintf(NULL, 0, xpformat, 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, xpformat); - if (vsnprintf(xpath, len+1, xpformat, ap) < 0){ - clicon_err(OE_UNIX, errno, "vsnprintf"); - va_end(ap); - goto done; - } - va_end(ap); - if (xpath_vec_ctx(xcur, nsc, 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; -} - -/*! Xpath nodeset function where only the first matching entry is returned - * - * @param[in] xcur XML tree where to search - * @param[in] xpformat Format string for XPATH syntax - * @retval xml-tree 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. - * @see xpath_first_nsc which is more generic with namespace context - */ -cxobj * xpath_first(cxobj *xcur, + cvec *nsc, char *xpformat, ...) { @@ -531,7 +475,7 @@ xpath_first(cxobj *xcur, goto done; } va_end(ap); - if (xpath_vec_ctx(xcur, NULL, xpath, &xr) < 0) + if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0) goto done; if (xr && xr->xc_type == XT_NODESET && xr->xc_size) cx = xr->xc_nodeset[0]; @@ -556,7 +500,7 @@ xpath_first(cxobj *xcur, * cvec *nsc; // namespace context * cxobj **vec; * size_t veclen; - * if (xpath_vec_nsc(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0) + * if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0) * goto err; * for (i=0; ixc_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 nodeset as xml node vector - * If result is not nodeset, return empty nodeset - * @param[in] xcur xml-tree where to search - * @param[in] xpformat Format string for 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 - * @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; ixc_type == XT_NODESET){ *vec = xr->xc_nodeset; @@ -681,7 +557,7 @@ xpath_vec(cxobj *xcur, return retval; } -/* 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] xpformat Format string for XPATH syntax * @param[in] nsc External XML namespace context, or NULL diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c index 30f44af6..cf8d6a0b 100644 --- a/lib/src/clixon_xpath_ctx.c +++ b/lib/src/clixon_xpath_ctx.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c index d6b0eb7b..077c4fb2 100644 --- a/lib/src/clixon_xpath_eval.c +++ b/lib/src/clixon_xpath_eval.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l index 31699443..8676b9d1 100644 --- a/lib/src/clixon_xpath_parse.l +++ b/lib/src/clixon_xpath_parse.l @@ -140,7 +140,7 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+) \" { BEGIN(QLITERAL); return QUOTE; } \' { BEGIN(ALITERAL); return APOST; } -\-?({integer}|{real}) { sscanf(yytext,"%lf",&clixon_xpath_parselval.dval); return NUMBER;} +\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; } [0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext); return NAME; /* rather be catch-all */ } diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y index f014b6a4..a5050aaa 100644 --- a/lib/src/clixon_xpath_parse.y +++ b/lib/src/clixon_xpath_parse.y @@ -44,7 +44,6 @@ %union { int intval; - double dval; char *string; void *stack; /* xpath_tree */ } @@ -54,8 +53,7 @@ %token ADDOP %token RELOP -%token NUMBER - +%token NUMBER %token X_EOF %token QUOTE %token APOST @@ -105,7 +103,6 @@ #include #include #include -#include #include #include #include @@ -162,13 +159,13 @@ xpath_parse_exit(struct clicon_xpath_yacc_arg *xy) static xpath_tree * xp_new(enum xp_type type, int i0, - double d0, + char *numstr, char *s0, char *s1, xpath_tree *c0, xpath_tree *c1) { - xpath_tree *xs = NULL; + xpath_tree *xs = NULL; if ((xs = malloc(sizeof(xpath_tree))) == NULL){ clicon_err(OE_XML, errno, "malloc"); @@ -177,7 +174,15 @@ xp_new(enum xp_type type, memset(xs, 0, sizeof(*xs)); xs->xs_type = type; xs->xs_int = i0; - xs->xs_double = d0; + if (numstr){ + xs->xs_strnr = numstr; + if (sscanf(numstr, "%lf", &xs->xs_double) == EOF){ + clicon_err(OE_XML, errno, "sscanf"); + goto done; + } + } + else + xs->xs_double = 0.0; xs->xs_s0 = s0; xs->xs_s1 = s1; xs->xs_c0 = c0; @@ -197,50 +202,50 @@ start : expr X_EOF { _XY->xy_top=$1;clicon_debug(2,"start->expr"); | locationpath X_EOF { _XY->xy_top=$1;clicon_debug(2,"start->locationpath"); YYACCEPT; } ; -expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"expr->expr or andexpr"); } - | andexpr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"expr-> andexpr"); } +expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"expr->expr or andexpr"); } + | andexpr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"expr-> andexpr"); } ; -andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"andexpr-> andexpr and relexpr"); } - | relexpr { $$=xp_new(XP_AND,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"andexpr-> relexpr"); } +andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"andexpr-> andexpr and relexpr"); } + | relexpr { $$=xp_new(XP_AND,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"andexpr-> relexpr"); } ; -relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"relexpr-> relexpr relop addexpr"); } - | addexpr { $$=xp_new(XP_RELEX,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"relexpr-> addexpr"); } +relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"relexpr-> relexpr relop addexpr"); } + | addexpr { $$=xp_new(XP_RELEX,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"relexpr-> addexpr"); } ; -addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"addexpr-> addexpr ADDOP unionexpr"); } - | unionexpr { $$=xp_new(XP_ADD,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"addexpr-> unionexpr"); } +addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"addexpr-> addexpr ADDOP unionexpr"); } + | unionexpr { $$=xp_new(XP_ADD,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"addexpr-> unionexpr"); } ; /* node-set */ -unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(2,"unionexpr-> unionexpr | pathexpr"); } - | pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"unionexpr-> pathexpr"); } +unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,NULL,NULL,NULL,$1, $3);clicon_debug(2,"unionexpr-> unionexpr | pathexpr"); } + | pathexpr { $$=xp_new(XP_UNION,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"unionexpr-> pathexpr"); } ; -pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> locationpath"); } - | primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> primaryexpr"); } +pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> locationpath"); } + | primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> primaryexpr"); } ; /* location path returns a node-set */ -locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> rellocpath"); } - | abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> abslocpath"); } +locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> rellocpath"); } + | abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> abslocpath"); } ; -abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,NULL, NULL);clicon_debug(2,"abslocpath-> /"); } - | '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,$2, NULL);clicon_debug(2,"abslocpath->/ rellocpath");} +abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,NULL,NULL,NULL,NULL, NULL);clicon_debug(2,"abslocpath-> /"); } + | '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,NULL,NULL,NULL,$2, NULL);clicon_debug(2,"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(2,"abslocpath-> // rellocpath"); } + | DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,NULL,NULL,NULL,$2, NULL); clicon_debug(2,"abslocpath-> // rellocpath"); } ; -rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"rellocpath-> step"); } - | rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(2,"rellocpath-> rellocpath / step"); } - | rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$1, $3); clicon_debug(2,"rellocpath-> rellocpath // step"); } +rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"rellocpath-> step"); } + | rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,NULL,NULL,NULL,$1, $3);clicon_debug(2,"rellocpath-> rellocpath / step"); } + | rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,NULL,NULL,NULL,$1, $3); clicon_debug(2,"rellocpath-> rellocpath // step"); } ; -step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(2,"step->axisspec(%d) nodetest", $1); } - | '.' predicates { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, $2); clicon_debug(2,"step-> ."); } - | DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, $2); clicon_debug(2,"step-> .."); } +step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,NULL, NULL, NULL, $2, $3);clicon_debug(2,"step->axisspec(%d) nodetest", $1); } + | '.' predicates { $$=xp_new(XP_STEP,A_SELF, NULL,NULL, NULL, NULL, $2); clicon_debug(2,"step-> ."); } + | DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, NULL,NULL, NULL, NULL, $2); clicon_debug(2,"step-> .."); } ; axisspec : AXISNAME { clicon_debug(2,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;} @@ -248,31 +253,31 @@ axisspec : AXISNAME { clicon_debug(2,"axisspec-> AXISNAME(%d) ::", $1); $$=$1 | { clicon_debug(2,"axisspec-> "); $$=A_CHILD;} ; -nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(2,"nodetest-> *"); } - | NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(2,"nodetest-> name(%s)",$1); } - | NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : name(%s)", $1, $3); } - | NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : *", $1); } - | NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype():%s", $1); } +nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(2,"nodetest-> *"); } + | NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); clicon_debug(2,"nodetest-> name(%s)",$1); } + | NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : name(%s)", $1, $3); } + | NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : *", $1); } + | NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,NULL, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype():%s", $1); } ; /* evaluates to boolean */ -predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, $1, $3); clicon_debug(2,"predicates-> [ expr ]"); } - | { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(2,"predicates->"); } +predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, $1, $3); clicon_debug(2,"predicates-> [ expr ]"); } + | { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(2,"predicates->"); } ; -primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2, NULL); clicon_debug(2,"primaryexpr-> ( expr )"); } - | NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> NUMBER(%lf)", $1); } - | QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" string \""); } - | QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" \""); } - | APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' string '"); } - | APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' '"); } - | FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); } - | FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, $3, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); } +primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,NULL, NULL, NULL, $2, NULL); clicon_debug(2,"primaryexpr-> ( expr )"); } + | NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> NUMBER(%s)", $1); /*XXX*/} + | QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" string \""); } + | QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" \""); } + | APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' string '"); } + | APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' '"); } + | FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); } + | FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, $3, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); } ; -args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, $3); +args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3); clicon_debug(2,"args -> args expr");} - | expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL); + | expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"args -> expr "); } ; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 051627d7..6168f791 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -766,7 +766,7 @@ yang_find_myprefix(yang_stmt *ys) } if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL) goto done; - prefix = yprefix->ys_argument; + prefix = yang_argument_get(yprefix); done: return prefix; } diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 0459da1b..cc1fe48a 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -306,7 +306,7 @@ yang_modules_state_get(clicon_handle h, /* xc is also original tree, need to copy it */ if ((xw = xml_wrap(xc, "top")) == NULL) goto done; - if (xpath_first(xw, "%s", xpath)){ + if (xpath_first(xw, NULL, "%s", xpath)){ if ((x = xml_dup(xc)) == NULL) /* Make copy and use below */ goto done; } @@ -338,7 +338,7 @@ yang_modules_state_get(clicon_handle h, if ((x = xml_wrap(x, "top")) < 0) goto done; /* extract xpath part of module-state tree */ - if (xpath_vec_nsc(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; if (xvec != NULL){ for (i=0; i typedef-stmt"); } | grouping_stmt { clicon_debug(2,"notification-substmt -> grouping-stmt"); } | data_def_stmt { clicon_debug(2,"notification-substmt -> data-def-stmt"); } + | unknown_stmt { clicon_debug(2,"notification-substmt -> unknown-stmt");} | { clicon_debug(2,"notification-substmt -> "); } ; diff --git a/test/lib.sh b/test/lib.sh index 4d43e4df..3e8e3c32 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -213,7 +213,7 @@ start_restconf(){ } stop_restconf(){ - sudo pkill -u $wwwuser -f "$clixon_restconf" + sudo pkill -u $wwwuser -f clixon_restconf # Dont use $clixon_restoconf doesnt work in valgrind if [ $valgrindtest -eq 3 ]; then sleep 1 checkvalgrind diff --git a/test/test_augment.sh b/test/test_augment.sh index fcbc9251..ccb7af20 100755 --- a/test/test_augment.sh +++ b/test/test_augment.sh @@ -12,6 +12,8 @@ # 2. example-augment - urn:example:augment - mymod # (augmented): mandatory-leaf, me, other, # (uses/grouping): ip, port, lid, lport +# Note augment+state not tested here (need plugin), simple test in test_restconf.sh +# # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi @@ -167,7 +169,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg @@ -217,13 +219,12 @@ new "restconf get augment json" expectpart "$(curl -s -i -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}} ' new "restconf get augment xml" -expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' 'e1mymod:some-new-iftypetrue808080e2fdditrueif:fddi808080e3fdditruemymod:you808080' - +expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' 'e1mymod:some-new-iftypetrue808080e2fdditrueif:fddi808080e3fdditruemymod:you808080' #e123' XML=$(cat <e123' +e1mymod:some-new-iftypetrue23' EOF ) @@ -240,10 +241,10 @@ new "restconf POST augment multi-namespace path e2 (middle path)" expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 '' new "restconf GET augment multi-namespace top" -expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' new "restconf GET augment multi-namespace level 1" -expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080}\]}' +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}' new "restconf GET augment multi-namespace cross" expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}' diff --git a/test/test_choice.sh b/test/test_choice.sh index 9bb5e8dc..129908db 100755 --- a/test/test_choice.sh +++ b/test/test_choice.sh @@ -111,7 +111,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg" start_backend -s init -f $cfg @@ -121,7 +121,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_cli_submodes.sh b/test/test_cli_submodes.sh new file mode 100755 index 00000000..af77d92e --- /dev/null +++ b/test/test_cli_submodes.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# CLIgen mode tests +# Have two modes: AAA and BBB +# Have the following clispec files with syntax for: +# 1) * 2) AAA, 3) BBB, 4) CCC, 5) AAA:BBB, 6) BBB:CCC +# Verify then that modes AAA and BBB have right syntax (also negative) +# AAA should have syntax from 1,2,5 (and not 3,4,6) +# BBB should have syntax from 1,3,5,6 (and not 2,4) + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +# include err() and new() functions and creates $dir + +cfg=$dir/conf_yang.xml +clidir=$dir/clidir + +if [ ! -d $clidir ]; then + mkdir $clidir +fi + +# Use yang in example + +cat < $cfg + + $cfg + /usr/local/share/clixon + $IETFRFC + clixon-example + /usr/local/lib/$APPNAME/backend + $clidir + /usr/local/lib/$APPNAME/cli + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + +EOF + +# clispec files 1..6 for submodes AAA and BBB as described in top comment + +cat < $clidir/cli1.cli + CLICON_MODE="*"; + cmd1; +EOF + +cat < $clidir/cli2.cli + CLICON_MODE="AAA"; + cmd2; +EOF + +cat < $clidir/cli3.cli + CLICON_MODE="BBB"; + cmd3; +EOF + +cat < $clidir/cli4.cli + CLICON_MODE="CCC"; + cmd4; +EOF + +cat < $clidir/cli5.cli + CLICON_MODE="AAA:BBB"; + cmd5; +EOF + +cat < $clidir/cli6.cli + CLICON_MODE="BBB:CCC"; + cmd6; +EOF + +new "test params: -f $cfg" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -z -f $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg + + new "waiting" + wait_backend +fi + + +m=AAA +# Tests using mode AAA that should pass +for c in 1 2 5; do + new "cli mode $m 1 cmd$c OK" + expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 0 "^$" +done +# Tests using mode AAA that should fail +for c in 3 4 6; do + new "cli mode $m 1 cmd$c Not OK" + expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 255 "^$" +done + +m=BBB +# Tests using mode BBB that should pass +for c in 1 3 5 6; do + new "cli mode $m 1 cmd$c OK" + expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 0 "^$" +done +# Tests using mode BBB that should fail +for c in 2 4; do + new "cli mode $m 1 cmd$c Not OK" + expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 255 "^$" +done + +if [ $BE -eq 0 ]; then + exit # BE +fi + +new "Kill backend" +# Check if premature kill +pid=$(pgrep -u root -f clixon_backend) +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +stop_backend -f $cfg + +rm -rf $dir diff --git a/test/test_identity.sh b/test/test_identity.sh index 4759723c..ce272ef6 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -155,7 +155,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_nacm.sh b/test/test_nacm.sh index dedf6799..2da70b4b 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -127,7 +127,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh index 97b3c90f..84ef39b2 100755 --- a/test/test_nacm_default.sh +++ b/test/test_nacm_default.sh @@ -103,7 +103,7 @@ EOF wait_backend new "kill old restconf daemon" - sudo pkill -u $wwwuser clixon_restconf + sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index 9cfd720e..da066b30 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -147,7 +147,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable http basic auth)" start_restconf -f $cfg -- -a diff --git a/test/test_nacm_module_read.sh b/test/test_nacm_module_read.sh index 1f420f2f..4d2a3550 100755 --- a/test/test_nacm_module_read.sh +++ b/test/test_nacm_module_read.sh @@ -141,7 +141,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_nacm_module_write.sh b/test/test_nacm_module_write.sh index 25aa75ee..67082c42 100755 --- a/test/test_nacm_module_write.sh +++ b/test/test_nacm_module_write.sh @@ -149,7 +149,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index 123d4f91..5f8d64d8 100755 --- a/test/test_nacm_protocol.sh +++ b/test/test_nacm_protocol.sh @@ -150,7 +150,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_netconf.sh b/test/test_netconf.sh index b08d3af9..c451f061 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -102,7 +102,7 @@ cat < $tmp # new EOF new "netconf get config xpath" -expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^eth1true]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^eth1true]]>]]>$' # Too many quotes cat < $tmp # new @@ -110,7 +110,7 @@ cat < $tmp # new EOF new "netconf get config xpath parent" -expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^eth/0/0trueeth1truetruefalse9.2.3.424]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^eth/0/0trueeth1truetruefalse9.2.3.424]]>]]>$' new "netconf validate missing type" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^" @@ -136,7 +136,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" -new "netconf edit config merge" +new "netconf edit config merge eth2" expecteof "$clixon_netconf -qf $cfg" 0 'eth2ex:ethmerge]]>]]>' "^]]>]]>$" # Note, the type here is non-existant identityref, fails on validation @@ -163,7 +163,7 @@ new "netconf edit state operation should fail" expecteof "$clixon_netconf -qf $cfg" 0 'e0up]]>]]>' "^protocolinvalid-valueerrorState data not allowed]]>]]>" new "netconf get state operation" -expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^eth1ex:ethtrueup]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^eth1ex:ethtrueup42foo]]>]]>$' new "netconf lock/unlock" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" diff --git a/test/test_netconf_filter.sh b/test/test_netconf_filter.sh new file mode 100755 index 00000000..8328c2ad --- /dev/null +++ b/test/test_netconf_filter.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Test netconf filter, subtree and xpath +# Note subtree namespaces not implemented + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +cfg=$dir/conf_yang.xml +fyang=$dir/filter.yang + +cat < $cfg + + $cfg + /usr/local/share/clixon + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/$APPNAME/backend + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + +EOF + +cat < $fyang +module filter{ + yang-version 1.1; + namespace "urn:example:filter"; + prefix fi; + container x{ + list y { + key a; + leaf a{ + type string; + } + leaf b{ + type string; + } + } + } +} +EOF + +new "test params: -f $cfg" + +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg +fi + +new "waiting" +wait_backend + +new "Add two entries" +expecteof "$clixon_netconf -qf $cfg" 0 '1122]]>]]>' '^]]>]]>$' + +new "netconf commit" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +new "wrong filter type" +expecteof "$clixon_netconf -qf $cfg" 0 "1]]>]]>" '^operation-failedapplicatioerrorfilter type not supportedtype]]>]]>$' + +new "get-config subtree one" +expecteof "$clixon_netconf -qf $cfg" 0 "1]]>]]>" '^11]]>]]>$' + +new "get subtree one" +expecteof "$clixon_netconf -qf $cfg" 0 "1]]>]]>" '^11]]>]]>$' + +new "get-config xpath one" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^11]]>]]>$' + +new "get xpath one" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^11]]>]]>$' + +if [ $BE -eq 0 ]; then + exit # BE +fi + +new "Kill backend" +# Check if premature kill +pid=$(pgrep -u root -f clixon_backend) +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +stop_backend -f $cfg + +rm -rf $dir diff --git a/test/test_openconfig.sh b/test/test_openconfig.sh index b11db787..1f1158ae 100755 --- a/test/test_openconfig.sh +++ b/test/test_openconfig.sh @@ -37,6 +37,7 @@ cat < $cfg $OCDIR/lacp $OCDIR/lldp $OCDIR/local-routing + $OCDIR/macsec $OCDIR/mpls $OCDIR/multicast $OCDIR/network-instance diff --git a/test/test_perf.sh b/test/test_perf.sh index 385cc1a6..063e5d02 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -83,7 +83,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_perf_state.sh b/test/test_perf_state.sh index 61a2359a..05935413 100755 --- a/test/test_perf_state.sh +++ b/test/test_perf_state.sh @@ -61,7 +61,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg @@ -90,7 +90,7 @@ expecteof "time $clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>" -expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^e1ex:ethtrueup]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^e1ex:ethtrueup42foo]]>]]>$' new "netconf get $perfreq single reqs" { time -p for (( i=0; i<$perfreq; i++ )); do @@ -101,7 +101,7 @@ done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}' # RESTCONF get new "restconf get test single req" -expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]} +expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]} ' new "restconf get $perfreq single reqs" diff --git a/test/test_perf_xml.sh b/test/test_perf_xml.sh new file mode 100755 index 00000000..17d7d6ce --- /dev/null +++ b/test/test_perf_xml.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Test: XML performance test +# See https://github.com/clicon/clixon/issues/96 +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +: ${clixon_util_xml:="clixon_util_xml"} + +# Number of list/leaf-list entries in file +: ${perfnr:=30000} + +fxml=$dir/long.xml + +new "generate long file $fxml" +echo -n " $fxml +for (( i=0; i<$perfnr; i++ )); do + echo "*>i10.0.0.$i/32 10.255.0.20 0 100 0 i" >> $fxml +done +echo "]]>" >> $fxml + +# 32-bit i386: +#0.37user 1.94system 0:02.47elapsed 93%CPU (0avgtext+0avgdata 9336maxresident)k +#256inputs+0outputs (2major+2049minor)pagefaults 0swa +new "xml parse long CDATA" +expecteof_file "time $clixon_util_xml" 0 "$fxml" + +rm -rf $dir + diff --git a/test/test_privileges.sh b/test/test_privileges.sh index 27a37a44..0e927819 100755 --- a/test/test_privileges.sh +++ b/test/test_privileges.sh @@ -60,7 +60,7 @@ testrun(){ err fi # Kill all backends regardless of user or pid files (we mess with them in this test) - sudo pkill clixon_backend + sudo pkill -f clixon_backend # start backend as user diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 80997b19..e4e12451 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -43,7 +43,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg -- -s" start_backend -s init -f $cfg -- -s fi @@ -52,7 +52,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg @@ -194,12 +194,16 @@ new "restconf Add interfaces subtree eth/0/0 using POST" expectpart "$(curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}')" 0 "" new "restconf Check eth/0/0 added config" -expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}\]}} - ' +expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' + +new "restconf Check eth/0/0 GET augmented state level 1" +expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 '{"ietf-interfaces:interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}' + +new "restconf Check eth/0/0 GET augmented state level 2" +expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0/clixon-example:my-status)" 0 '{"clixon-example:my-status":{"int":42,"str":"foo"}}' new "restconf Check eth/0/0 added state" -expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}} - ' +expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}}' new "restconf Re-post eth/0/0 which should generate error" expectpart "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}} ' @@ -211,7 +215,7 @@ new "Add nothing using POST (expect fail)" expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}' new "restconf Check description added" -expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}} +expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}} ' new "restconf delete eth/0/0" @@ -227,7 +231,7 @@ new "restconf Add subtree eth/0/0 using PUT" expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "" new "restconf get subtree" -expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}} +expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}} ' new "restconf rpc using POST json" diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 162139a7..598baf11 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -74,7 +74,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg" start_backend -s init -f $cfg fi @@ -83,7 +83,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index 192abc71..39ab119c 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -120,7 +120,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg" start_backend -s init -f $cfg fi @@ -129,7 +129,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index ccb1de91..4a36fa97 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -69,7 +69,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg -- -s" start_backend -s init -f "$cfg" -- -s fi @@ -78,7 +78,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg @@ -99,7 +99,7 @@ expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://loca # This just catches the header and the jukebox module, the RFC has foo and bar which # seems wrong to recreate new "B.1.2. Retrieve the Server Module Information" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":' '"module":\[{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}' +expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2019-08-13","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"clixon-rfc5277","revision":"2008-07-01","namespace":"urn:ietf:params:xml:ns:netmod:notification","conformance-type":"implement"},{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"' new "B.1.3. Retrieve the Server Capability Information" expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=expliciturn:ietf:params:restconf:capability:depth diff --git a/test/test_restconf_listkey.sh b/test/test_restconf_listkey.sh index 24571e4d..ee778a4a 100755 --- a/test/test_restconf_listkey.sh +++ b/test/test_restconf_listkey.sh @@ -71,7 +71,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s init -f $cfg" start_backend -s init -f $cfg @@ -81,7 +81,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_restconf_patch.sh b/test/test_restconf_patch.sh index afb5510e..6facdc0c 100755 --- a/test/test_restconf_patch.sh +++ b/test/test_restconf_patch.sh @@ -100,7 +100,7 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - sudo pkill clixon_backend # to be sure + sudo pkill -f clixon_backend # to be sure new "start backend -s startup -f $cfg" start_backend -s startup -f $cfg fi @@ -109,7 +109,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon (-a is enable basic authentication)" start_restconf -f $cfg -- -a diff --git a/test/test_restconf_startup.sh b/test/test_restconf_startup.sh index e9fa9326..5aaeb4da 100755 --- a/test/test_restconf_startup.sh +++ b/test/test_restconf_startup.sh @@ -65,7 +65,7 @@ testrun(){ wait_backend new "kill old restconf daemon" - sudo pkill -u $wwwuser clixon_restconf + sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg -y $fyang $option diff --git a/test/test_rpc.sh b/test/test_rpc.sh index 1aaa5ee6..4174e37e 100755 --- a/test/test_rpc.sh +++ b/test/test_rpc.sh @@ -47,7 +47,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_stream.sh b/test/test_stream.sh index 27fafd1a..280f84fa 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -118,7 +118,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_submodule.sh b/test/test_submodule.sh index 7ee65882..823de222 100755 --- a/test/test_submodule.sh +++ b/test/test_submodule.sh @@ -163,7 +163,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_type.sh b/test/test_type.sh index 1538fbbd..2ef9a177 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -196,6 +196,14 @@ module example{ description "For testing different truth values in CLI"; type boolean; } + container manc{ + presence true; + description "mandatory test"; + leaf man{ + type string; + mandatory true; + } + } } EOF @@ -247,7 +255,6 @@ EOF new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" - new "netconf set transitive string error" expecteof "$clixon_netconf -qf $cfg" 0 '9xx]]>]]>' "^]]>]]>" @@ -593,6 +600,32 @@ EOF new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + #------ Mandatory + + new "netconf set container w/o mandatory leaf" + expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' "^]]>]]>" + + new "netconf validate should fail" + expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" 'applicationmissing-elementmanerrorMandatory variable]]>]]>' + + new "netconf set container with mandatory leaf" + expecteof "$clixon_netconf -qf $cfg" 0 'foo]]>]]>' "^]]>]]>" + + new "netconf commit" + expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + + new "netconf delete mandatory variable" + expecteof "$clixon_netconf -qf $cfg" 0 'foonone]]>]]>' '^]]>]]>$' + + new "get mandatory" + expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^]]>]]>$' + + new "netconf validate should fail" + expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" '^protocolmissing-elementmanerrorMay not remove mandatory variable]]>]]>$' + + new "netconf discard-changes" + expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + #------ minus new "type with minus" diff --git a/test/test_upgrade_auto.sh b/test/test_upgrade_auto.sh index 0dabb323..0169f29c 100755 --- a/test/test_upgrade_auto.sh +++ b/test/test_upgrade_auto.sh @@ -259,7 +259,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh index 20d071a7..169e5cab 100755 --- a/test/test_upgrade_interfaces.sh +++ b/test/test_upgrade_interfaces.sh @@ -270,7 +270,7 @@ testrun(){ wait_backend new "kill old restconf daemon" - sudo pkill -u $wwwuser clixon_restconf + sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_upgrade_repair.sh b/test/test_upgrade_repair.sh index 9f01b0fe..bb0a3301 100755 --- a/test/test_upgrade_repair.sh +++ b/test/test_upgrade_repair.sh @@ -117,7 +117,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/test/test_yang_models.sh b/test/test_yang_models.sh index 0da1e5fd..0e3fec47 100755 --- a/test/test_yang_models.sh +++ b/test/test_yang_models.sh @@ -35,8 +35,10 @@ cat < $cfg $cfg $YANGMODELS/standard/ietf/RFC - $YANGMODELS/standard/ieee/draft/802.1 + $YANGMODELS/standard/ieee/draft/802.1/Qcr $YANGMODELS/standard/ieee/draft/802 + $YANGMODELS/standard/ieee/draft/802 + $YANGMODELS/standard/ieee/published/802.1 /usr/local/share/clixon /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli @@ -59,11 +61,27 @@ new "yangmodel Experimental IEEE 1588: $YANGMODELS/experimental/ieee/1588" expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/1588 show version" 0 "$version." # Standard IEEE -new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1" -expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1 show version" 0 "$version." +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/ABcu" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/ABcu show version" 0 "$version." -new "yangmodel Standard IEEE 802.3: $YANGMODELS/standard/ieee/draft/802.3" -expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.3 show version" 0 "$version." +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcr" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcr show version" 0 "$version." + +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcw" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcw show version" 0 "$version." + +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcx" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcx -p $YANGMODELS/standard/ieee/draft/802.1/ABcu show version" 0 "$version." + +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/x" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/x show version" 0 "$version." + +# Published +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/published/802.1" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.1 show version" 0 "$version." + +new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/published/802.3" +expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.3 show version" 0 "$version." # Standard IETF new "yangmodel Standard IETF: $YANGMODELS/standard/ietf/RFC" diff --git a/test/test_yang_namespace.sh b/test/test_yang_namespace.sh index 60d49b1b..6c700a74 100755 --- a/test/test_yang_namespace.sh +++ b/test/test_yang_namespace.sh @@ -80,7 +80,7 @@ new "waiting" wait_backend new "kill old restconf daemon" -sudo pkill -u $wwwuser clixon_restconf +sudo pkill -u $wwwuser -f clixon_restconf new "start restconf daemon" start_restconf -f $cfg diff --git a/util/clixon_util_datastore.c b/util/clixon_util_datastore.c index 099e8446..ec069a34 100644 --- a/util/clixon_util_datastore.c +++ b/util/clixon_util_datastore.c @@ -198,7 +198,7 @@ main(int argc, char **argv) xpath = argv[1]; else xpath = "/"; - if (xmldb_get(h, db, xpath, &xt) < 0) + if (xmldb_get(h, db, NULL, xpath, &xt) < 0) goto done; clicon_xml2file(stdout, xt, 0, 0); fprintf(stdout, "\n"); @@ -217,7 +217,7 @@ main(int argc, char **argv) else xpath = "/"; for (i=0;i #include #include -#include #include #include #include @@ -154,7 +153,7 @@ main(int argc, char **argv) } if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if ((xb = xpath_first(x0, "%s", xpath)) == NULL){ + if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){ clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath); goto done; } @@ -169,7 +168,7 @@ main(int argc, char **argv) } if (xml_apply(xi, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if ((xi = xpath_first(xi, "%s", xpath)) == NULL){ + if ((xi = xpath_first(xi, NULL, "%s", xpath)) == NULL){ clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath); goto done; } diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index c1083690..4c333c96 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c index 787b20ad..ed2c66c2 100644 --- a/util/clixon_util_socket.c +++ b/util/clixon_util_socket.c @@ -95,10 +95,14 @@ main(int argc, char *family = "UNIX"; int ret; cbuf *cb = cbuf_new(); - + clicon_handle h; + /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR); + if ((h = clicon_handle_init()) == NULL) + goto done; + optind = 1; opterr = 0; while ((c = getopt(argc, argv, "hD:s:f:Ja:")) != -1) @@ -161,11 +165,11 @@ main(int argc, if ((msg = clicon_msg_encode(getpid(), "%s", cbuf_get(cb))) < 0) goto done; if (strcmp(family, "UNIX")==0){ - if (clicon_rpc_connect_unix(msg, sockpath, &retdata, NULL) < 0) + if (clicon_rpc_connect_unix(h, msg, sockpath, &retdata, NULL) < 0) goto done; } else - if (clicon_rpc_connect_inet(msg, sockpath, 4535, &retdata, NULL) < 0) + if (clicon_rpc_connect_inet(h, msg, sockpath, 4535, &retdata, NULL) < 0) goto done; fprintf(stdout, "%s\n", retdata); retval = 0; diff --git a/util/clixon_util_stream.c b/util/clixon_util_stream.c index 6b52fb3e..511c62ea 100644 --- a/util/clixon_util_stream.c +++ b/util/clixon_util_stream.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index 45a1b0e7..30b4d9e2 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -234,7 +234,11 @@ main(int argc, if (ret > 0 && (ret = xml_yang_validate_add(h, xc, &xerr)) < 0) goto done; if (ret == 0){ - if (netconf_err2cb(xerr, &cbret) < 0) + if ((cbret = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (netconf_err2cb(xerr, cbret) < 0) goto done; fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret)); goto done; diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 57d5cac6..9cacc0ce 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -46,7 +46,6 @@ See https://www.w3.org/TR/xpath/ #include #include #include -#include #include #include #include @@ -297,7 +296,11 @@ main(int argc, if (ret > 0 && (ret = xml_yang_validate_add(h, x1, &xerr)) < 0) goto done; if (ret == 0){ - if (netconf_err2cb(xerr, &cbret) < 0) + if ((cbret = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (netconf_err2cb(xerr, cbret) < 0) goto done; fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret)); goto done; @@ -306,7 +309,7 @@ main(int argc, /* If xpath0 given, position current x (ie somewhere else than root) */ if (xpath0){ - if ((x = xpath_first(x0, "%s", xpath0)) == NULL){ + if ((x = xpath_first(x0, NULL, "%s", xpath0)) == NULL){ fprintf(stderr, "Error: xpath0 returned NULL\n"); return -1; } diff --git a/yang/Makefile.in b/yang/Makefile.in index d9ee3301..f816c8c9 100644 --- a/yang/Makefile.in +++ b/yang/Makefile.in @@ -37,12 +37,13 @@ prefix = @prefix@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ -enable_stdyangs = @enable_stdyangs@ +enable_optyangs = @enable_optyangs@ SUBDIRS = clixon +SUBDIRS += mandatory # See configure.ac -ifeq ($(enable_stdyangs),yes) -SUBDIRS += standard +ifeq ($(enable_optyangs),yes) +SUBDIRS += optional endif .PHONY: all clean depend install $(SUBDIRS) diff --git a/yang/README.md b/yang/README.md new file mode 100644 index 00000000..89accf31 --- /dev/null +++ b/yang/README.md @@ -0,0 +1,14 @@ +# Yang files + +There are three classes of Yang files + * Clixon yang files. + * Mandatory: "Standard" yang files necessary for clixon lib/client/backend to run + * Optional: "Standard" yang files for examples and tests + +The first two (clixon and mandatory) are always installed. If you want +to change where the are installed, configure with: `--with-yang-installdir=DIR` + +The third (optional) is only installed if configure flag +`--enable-optyang` is set. Further, the optional yang files are +installed in `--with-opt-yang-installdir=DIR` if given, otherwise in +the same dir as the mandatory. \ No newline at end of file diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index d924bf37..b199ba2d 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -38,7 +38,7 @@ bindir = @bindir@ includedir = @includedir@ datarootdir = @datarootdir@ -# See also STD_YANG_INSTALLDIR for the standard yang files +# See also OPT_YANG_INSTALLDIR for the standard yang files YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANGSPECS = clixon-config@2019-09-11.yang diff --git a/yang/clixon/clixon-config@2019-09-11.yang b/yang/clixon/clixon-config@2019-09-11.yang index 7eb343cd..6cfd354f 100644 --- a/yang/clixon/clixon-config@2019-09-11.yang +++ b/yang/clixon/clixon-config@2019-09-11.yang @@ -338,26 +338,27 @@ module clixon-config { leaf CLICON_CLI_DIR { type string; description - "Location of cli frontend .so plugins. Load all .so - plugins in this dir as CLI object plugins"; + "Directory containing frontend cli loadable plugins. Load all .so + plugins in this directory 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"; + "Directory containing frontend cligen spec files. Load all .cli + files in this directory as CLI specification files. + See also CLICON_CLISPEC_FILE."; } leaf CLICON_CLISPEC_FILE { type string; - description "Specific frontend .cli cligen spec file as simple - alternative to CLICON_CLISPEC_DIR. Also available as - -c in clixon_cli."; + description + "Specific frontend cligen spec file as aletrnative or complement + to CLICON_CLISPEC_DIR. Also available as -c in clixon_cli."; } leaf CLICON_CLI_MODE { type string; default "base"; description - "Startup CLI mode. This should match a CLICON_MODE set in + "Startup CLI mode. This should match a CLICON_MODE variable set in one of the clispec files"; } leaf CLICON_CLI_GENMODEL { diff --git a/yang/standard/Makefile.in b/yang/mandatory/Makefile.in similarity index 81% rename from yang/standard/Makefile.in rename to yang/mandatory/Makefile.in index 44909ddb..eb7f016c 100644 --- a/yang/standard/Makefile.in +++ b/yang/mandatory/Makefile.in @@ -39,20 +39,15 @@ includedir = @includedir@ datarootdir = @datarootdir@ # See also YANG_INSTALLDIR for the clixon-specific yang files -STD_YANG_INSTALLDIR = @STD_YANG_INSTALLDIR@ +YANG_INSTALLDIR = @YANG_INSTALLDIR@ -YANGSPECS = iana-if-type@2014-05-08.yang -YANGSPECS += ietf-interfaces@2018-02-20.yang -YANGSPECS += ietf-yang-types@2013-07-15.yang -YANGSPECS += ietf-ip@2014-06-16.yang -YANGSPECS += ietf-inet-types@2013-07-15.yang -YANGSPECS += ietf-routing@2018-03-13.yang -YANGSPECS += ietf-yang-library@2016-06-21.yang +YANGSPECS = ietf-inet-types@2013-07-15.yang YANGSPECS += ietf-netconf@2011-06-01.yang YANGSPECS += ietf-netconf-acm@2018-02-14.yang YANGSPECS += ietf-restconf@2017-01-26.yang YANGSPECS += ietf-restconf-monitoring@2017-01-26.yang -YANGSPECS += ietf-netconf-monitoring@2010-10-04.yang +YANGSPECS += ietf-yang-library@2016-06-21.yang +YANGSPECS += ietf-yang-types@2013-07-15.yang all: @@ -62,11 +57,11 @@ distclean: clean rm -f Makefile *~ .depend install: $(YANGSPECS) - install -d -m 0755 $(DESTDIR)$(STD_YANG_INSTALLDIR) - install -m 0644 $(YANGSPECS) $(DESTDIR)$(STD_YANG_INSTALLDIR) + install -d -m 0755 $(DESTDIR)$(YANG_INSTALLDIR) + install -m 0644 $(YANGSPECS) $(DESTDIR)$(YANG_INSTALLDIR) uninstall: - (cd $(DESTDIR)$(STD_YANG_INSTALLDIR); rm -rf *.yang) + (cd $(DESTDIR)$(YANG_INSTALLDIR); rm -rf *.yang) install-include: diff --git a/yang/standard/ietf-inet-types@2013-07-15.yang b/yang/mandatory/ietf-inet-types@2013-07-15.yang similarity index 100% rename from yang/standard/ietf-inet-types@2013-07-15.yang rename to yang/mandatory/ietf-inet-types@2013-07-15.yang diff --git a/yang/standard/ietf-netconf-acm@2018-02-14.yang b/yang/mandatory/ietf-netconf-acm@2018-02-14.yang similarity index 100% rename from yang/standard/ietf-netconf-acm@2018-02-14.yang rename to yang/mandatory/ietf-netconf-acm@2018-02-14.yang diff --git a/yang/standard/ietf-netconf@2011-06-01.yang b/yang/mandatory/ietf-netconf@2011-06-01.yang similarity index 100% rename from yang/standard/ietf-netconf@2011-06-01.yang rename to yang/mandatory/ietf-netconf@2011-06-01.yang diff --git a/yang/standard/ietf-restconf-monitoring@2017-01-26.yang b/yang/mandatory/ietf-restconf-monitoring@2017-01-26.yang similarity index 100% rename from yang/standard/ietf-restconf-monitoring@2017-01-26.yang rename to yang/mandatory/ietf-restconf-monitoring@2017-01-26.yang diff --git a/yang/standard/ietf-restconf@2017-01-26.yang b/yang/mandatory/ietf-restconf@2017-01-26.yang similarity index 100% rename from yang/standard/ietf-restconf@2017-01-26.yang rename to yang/mandatory/ietf-restconf@2017-01-26.yang diff --git a/yang/standard/ietf-yang-library@2016-06-21.yang b/yang/mandatory/ietf-yang-library@2016-06-21.yang similarity index 100% rename from yang/standard/ietf-yang-library@2016-06-21.yang rename to yang/mandatory/ietf-yang-library@2016-06-21.yang diff --git a/yang/standard/ietf-yang-types@2013-07-15.yang b/yang/mandatory/ietf-yang-types@2013-07-15.yang similarity index 100% rename from yang/standard/ietf-yang-types@2013-07-15.yang rename to yang/mandatory/ietf-yang-types@2013-07-15.yang diff --git a/yang/optional/Makefile.in b/yang/optional/Makefile.in new file mode 100644 index 00000000..c95bb922 --- /dev/null +++ b/yang/optional/Makefile.in @@ -0,0 +1,70 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright (C) 2009-2019 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@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +prefix = @prefix@ +bindir = @bindir@ +includedir = @includedir@ +datarootdir = @datarootdir@ + +# See also OPT_YANG_INSTALLDIR for optionali yang files +OPT_YANG_INSTALLDIR = @OPT_YANG_INSTALLDIR@ + +YANGSPECS = iana-if-type@2014-05-08.yang +YANGSPECS += ietf-interfaces@2018-02-20.yang +YANGSPECS += ietf-ip@2014-06-16.yang +YANGSPECS += ietf-netconf-monitoring@2010-10-04.yang +YANGSPECS += ietf-routing@2018-03-13.yang + +all: + +clean: + +distclean: clean + rm -f Makefile *~ .depend + +install: $(YANGSPECS) + install -d -m 0755 $(DESTDIR)$(OPT_YANG_INSTALLDIR) + install -m 0644 $(YANGSPECS) $(DESTDIR)$(OPT_YANG_INSTALLDIR) + +uninstall: + (cd $(DESTDIR)$(OPT_YANG_INSTALLDIR); rm -rf *.yang) + +install-include: + +depend: + + +#include .depend + diff --git a/yang/standard/iana-if-type@2014-05-08.yang b/yang/optional/iana-if-type@2014-05-08.yang similarity index 100% rename from yang/standard/iana-if-type@2014-05-08.yang rename to yang/optional/iana-if-type@2014-05-08.yang diff --git a/yang/standard/ietf-interfaces@2018-02-20.yang b/yang/optional/ietf-interfaces@2018-02-20.yang similarity index 100% rename from yang/standard/ietf-interfaces@2018-02-20.yang rename to yang/optional/ietf-interfaces@2018-02-20.yang diff --git a/yang/standard/ietf-ip@2014-06-16.yang b/yang/optional/ietf-ip@2014-06-16.yang similarity index 100% rename from yang/standard/ietf-ip@2014-06-16.yang rename to yang/optional/ietf-ip@2014-06-16.yang diff --git a/yang/standard/ietf-netconf-monitoring@2010-10-04.yang b/yang/optional/ietf-netconf-monitoring@2010-10-04.yang similarity index 100% rename from yang/standard/ietf-netconf-monitoring@2010-10-04.yang rename to yang/optional/ietf-netconf-monitoring@2010-10-04.yang diff --git a/yang/standard/ietf-routing@2018-03-13.yang b/yang/optional/ietf-routing@2018-03-13.yang similarity index 100% rename from yang/standard/ietf-routing@2018-03-13.yang rename to yang/optional/ietf-routing@2018-03-13.yang diff --git a/yang/standard/ietf-module-revision@2018-08-08.yang b/yang/standard/ietf-module-revision@2018-08-08.yang deleted file mode 100644 index b7b6b7a1..00000000 --- a/yang/standard/ietf-module-revision@2018-08-08.yang +++ /dev/null @@ -1,392 +0,0 @@ -module ietf-module-revision { - yang-version 1.1; - namespace "urn:ietf:params:xml:ns:yang:ietf-module-revision"; - prefix ml; - - import ietf-yang-library { - prefix yanglib; - } - import ietf-yang-types { - prefix yang; - } - - organization - "IETF Network Modeling (NETMOD) Working Group"; - contact - "WG Web: - - WG List: - - Author: Qin Wu - - Zitao Wang - "; - description - "This YANG module defines an module log."; - - revision 2018-08-08 { - description - "Initial revision."; - reference "RFC XXXX: Using Metadata with YANG for Module revisions"; - } - - identity operation-type { - description - "Abstract base identity for the operation type "; - } - - identity create { - base operation-type; - description - "Denotes create new data nodes"; - } - - identity delete { - base operation-type; - description - "Denotes delete the target node"; - } - - identity move { - base operation-type; - description - "Denote move the target node."; - } - - identity modify { - base operation-type; - description - "Denote modify the target data node."; - } - - identity statement-type { - description - "Base identity for statement type"; - } - - identity feature-statement { - base statement-type; - description - "feature statement, if this type be chose, it means that the - feature or if-feature statement been modified"; - } - identity identity-statement { - base statement-type; - description - "identity statement, if this type be chose, it means that the - identity statement been modified, for example, add new identity, etc."; - } - - identity grouping-statement { - base statement-type; - description - "grouping statement, if this type be chose, it means that the grouping - statement been modified."; - } - - identity typedef-statement { - base statement-type; - description - "typedef statement, if this type be chose, it means that the typedef - statement been modified."; - } - - identity augment-statement { - base statement-type; - description - "augment statement, if this type be chose, it means that the augment - statement been modified."; - } - - identity rpc-statement { - base statement-type; - description - "rpc statement, if this type be chose, it means that the rpc - statement been modified."; - } - - identity notification-statement { - base statement-type; - description - "notification statement, if this type be chose, it means that the notification - statement been modified."; - } - - extension purpose { - argument name; - description - "The purpose can be used to mark the data nodes change purpose. - The name argument can be specified in the following recommended mode - - bug-fix, which can help user to understand the data nodes' changes present bug fix, - - new-function, which can help user to understand the data nodes' changes present new function, - - nmda-conform, which can help user to understand the data nodes' changes conform to NMDA, - - and note that the user can argument the purpose name according to their sepcific requirements."; - } - - grouping data-definition { - container data-definition { - leaf target-node { - type yang:xpath1.0; - mandatory true; - description - "Identifies the target data node for update. - Notice that, if the update-type equal to move or delete, - this target-node must point to the data node of old version. - \t - For example, suppose the target node is a YANG leaf named a, - and the previous version is: - \t - container foo { - leaf a { type string; } - leaf b { type int32; } - } - \t - the new version is: - container foo { - leaf b {type int32;} - } - \t - Therefore, the targe-node should be /foo/a."; - } - leaf location-point { - type yang:xpath1.0; - description - "Identifies the location point where the updates happened."; - } - leaf where { - when "derived-from-or-self(../../change-operation, 'move')" { - description - "This leaf only applies for 'move' - updates."; - } - type enumeration { - enum "before" { - description - "Insert or move a data node before the data resource - identified by the 'point' parameter."; - } - enum "after" { - description - "Insert or move a data node after the data resource - identified by the 'point' parameter."; - } - enum "first" { - description - "Insert or move a data node so it becomes ordered - as the first entry."; - } - enum "last" { - description - "Insert or move a data node so it becomes ordered - as the last entry."; - } - } - default "last"; - description - "Identifies where a data resource will be inserted - or moved."; - } - anydata data-definition { - when "derived-from-or-self(../../change-operation, 'modify')" { - description - "This nodes only be present when - the 'change-operation' equal to 'modify'."; - } - description - "This nodes used for present the definitions before updated. - And this nodes only be present when - the 'change-operation' equal to 'modify'."; - } - description - "Container for data statement"; - } - description - "Grouping for data definition"; - } - - grouping other-statement { - container other-statement { - leaf statement-name { - type identityref { - base statement-type; - } - description - "Statement name, for example, identity, feature, typedef, etc."; - } - anydata statement-definition { - description - "This nodes used for present new the definitions."; - } - list substatements { - key "statement-name"; - leaf statement-name { - type identityref { - base statement-type; - } - description - "Statement name, for example, identity, feature, typedef, etc."; - } - anydata substatement-definition { - description - "This nodes used for present new the definitions."; - } - description - "List for substatements updates"; - } - description - "Container for header statement updates"; - } - description - "Grouping for header statement"; - } - - grouping change-log { - list revision-change-log { - key "index"; - leaf index { - type uint32; - description - "Index for module change log"; - } - leaf change-operation { - type identityref { - base operation-type; - } - mandatory true; - description - "This leaf indicate the change operation, such as create, move, delete, modify, etc."; - } - choice yang-statements { - description - "Choice for various YANG statements that have been impacted."; - case data-definition-statement { - uses data-definition; - } - case other-statement { - uses other-statement; - } - } - description - "List for module revision change log"; - } - description - "Grouping for module revision change log"; - } - - container yang-modules { - config false; - list module { - key "name revision"; - leaf name { - type yang:yang-identifier; - description - "The YANG module or submodule name."; - } - leaf revision { - type yanglib:revision-identifier; - description - "The YANG module or submodule revision date. If no revision - statement is present in the YANG module or submodule, this - leaf is not instantiated."; - } - leaf backward-compatible { - type boolean; - description - "Indicates whether it is a backward compatible version. - If this parameter is set to true, it means that this version is - a backwards compatible version"; - } - uses change-log; - description - "List for module updated log"; - } - description - "This container present the modules updated log."; - } - augment "/yanglib:yang-library/yanglib:module-set/yanglib:module" { - description - "Augment the yang library with backward compatibility indication."; - leaf backward-compatible { - type boolean; - description - "backward compatibility indication."; - } - } - augment "/yanglib:yang-library/yanglib:module-set/yanglib:module/yanglib:submodule" { - description - "Augment the yang library with backward compatibility indication."; - leaf backward-compatible { - type boolean; - description - "backward compatibility indication."; - } - } - rpc module-revision-change { - description - "Module Node change query operation."; - input { - leaf source-module-name { - type yang:yang-identifier; - mandatory true; - description - "The Source YANG module or submodule name."; - } - leaf source-revision { - type yanglib:revision-identifier; - description - "The Source YANG module revision date. If no revision - statement is present in the YANG module or submodule, this - leaf is not instantiated."; - } - leaf target-module-name { - type yang:yang-identifier; - mandatory true; - description - "The Target YANG module or submodule name."; - } - leaf target-revision { - type yanglib:revision-identifier; - description - "The target YANG module revision date. If no revision - statement is present in the YANG module or submodule, this - leaf is not instantiated."; - } - } - output { - choice status-response{ - leaf wrong-match{ - type empty; - description - "This leaf indicates that two modules have nothing in common."; - } - list data-nodes { - key "data-node-name"; - description - "Each entry represents a data node of a given module that - have been changed from source revision of - a module to target revision of the module."; - leaf data-node-name { - type string; - description - "a data node name of a given module that - has been changed."; - } - leaf is-new-node { - type boolean; - description - "indicate the data node is newly introduced node in the target revision."; - } - leaf change-operation { - type identityref { - base operation-type; - } - description - "This leaf indicate the change operation, - such as create, move, delete, modify, etc."; - } - } - } - } - } -}