diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a5c564..d5dbae38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## 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. diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 3fe9603c..c6263576 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) @@ -108,6 +109,8 @@ backend_terminate(clicon_handle h) xml_free(x); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); + if ((nsctx = clicon_nsctx_global_get(h)) != NULL) + cvec_free(nsctx); if ((yspec = clicon_config_yang(h)) != NULL) yspec_free(yspec); if ((x = clicon_nacm_ext(h)) != NULL) @@ -451,6 +454,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); @@ -722,10 +726,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 +740,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 +907,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/cli/cli_main.c b/apps/cli/cli_main.c index a9e2f7bf..c0981710 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); @@ -286,6 +289,7 @@ main(int argc, char **argv) int tabmode; char *dir; uint32_t id = 0; + cvec *nsctx_global = NULL; /* Global namespace context */ /* Defaults */ once = 0; @@ -513,6 +517,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 diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 02e8d94e..ec527cb8 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -323,6 +323,7 @@ static int netconf_terminate(clicon_handle h) { yang_stmt *yspec; + cvec *nsctx; cxobj *x; clixon_plugin_exit(h); @@ -332,6 +333,8 @@ netconf_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); event_exit(); @@ -395,6 +398,7 @@ main(int argc, yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */ char *str; uint32_t id; + cvec *nsctx_global = NULL; /* Global namespace context */ /* Create handle */ if ((h = clicon_handle_init()) == NULL) @@ -554,6 +558,14 @@ main(int argc, /* Add netconf yang spec, used by netconf client and as internal protocol */ 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; + /* Call start function is all plugins before we go interactive */ if (clixon_plugin_start(h) < 0) goto done; diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index fe8a2544..22d474cb 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -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..d7c6dc48 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -589,6 +589,7 @@ main(int argc, 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); @@ -760,6 +761,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/lib/clixon/clixon_data.h b/lib/clixon/clixon_data.h index 7dfd556a..b1b61926 100644 --- a/lib/clixon/clixon_data.h +++ b/lib/clixon/clixon_data.h @@ -61,6 +61,9 @@ typedef struct { yang_stmt * clicon_dbspec_yang(clicon_handle h); int clicon_dbspec_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); 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/src/clixon_data.c b/lib/src/clixon_data.c index 4f23a4f2..3e6a6298 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -75,6 +75,8 @@ /*! Get YANG specification for application * Must use hash functions directly since they are not strings. + * @param[in] h Clicon handle + * @retval yspec Yang spec */ yang_stmt * clicon_dbspec_yang(clicon_handle h) @@ -90,6 +92,8 @@ clicon_dbspec_yang(clicon_handle h) /*! Set yang specification for application * ys must be a malloced pointer + * @param[in] h Clicon handle + * @param[in] yspec Yang spec */ int clicon_dbspec_yang_set(clicon_handle h, @@ -105,6 +109,42 @@ clicon_dbspec_yang_set(clicon_handle h, 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) + */ +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 diff --git a/lib/src/clixon_xml_nsctx.c b/lib/src/clixon_xml_nsctx.c index e588c204..1134d000 100644 --- a/lib/src/clixon_xml_nsctx.c +++ b/lib/src/clixon_xml_nsctx.c @@ -182,7 +182,6 @@ xml_nsctx_add(cvec *cvv, return retval; } - static int xml_nsctx_node1(cxobj *xn, cvec *nsc) @@ -268,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 @@ -354,3 +353,44 @@ 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; + } + 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_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; }