diff --git a/.gitignore b/.gitignore index 1b2cb35f..e9ed0e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ Makefile apps/Makefile apps/*/Makefile docker/Makefile -docker/*/Makefile etc/Makefile example/Makefile lib/Makefile @@ -37,8 +36,6 @@ docker/netconf/Dockerfile etc/clixonrc -example/*.conf - include/clixon_config.h lib/src/build.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d7ce430..c9a1e5ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,18 @@ ### Major New features ### API changes on existing features (you may need to change your code) -* Limited support of RFC 7895 YANG Module Library to list modules: - * That is, limited support of: ietf-yang-library.yang - * For example: `exampleietf-restconf-monitoring2017-01-26...` -* Notification event stream enhancements - * Yang 1.1 notification support - * Event stream discovery support according to RFC 5277 Sec 3.2.5.1 - * That is, support of ietf-restconf-monitoring.yang (mimics schema in 3.2.5.1) - * Event stream discovery support according to RFC 8040 (restconf) - * That is, support of ietf-netconf-notification.yang +* YANG Module Library support + * According to RFC 7895 and implemented by ietf-yang-library.yang + * Supported: module, name, revision, namespace + * Not supported: notification, deviation, module-set-id, etc. + * Enabled by default, disable by resetting CLICON_MODULE_LIBRARY_RFC7895 +* Yang 1.1 notification support (RFC 7950: Sec 7.16) +* Event stream discovery support according to RFC 5277 for netconf + * Implemented by ietf-netconf-notification.yang + * Disabled by default. Enable by setting CLICON_STREAM_DISCOVERY_RFC5277 +* Event stream discovery support according to RFC 8040 for restconf + * Implemented by ietf-restconf-monitoring.yang (mimics schema in 3.2.5.1) + * Disabled by default. Enable by setting CLICON_STREAM_DISCOVERY_RFC8040. * clixon_restconf and clixon_netconf now take -D as command-line option instead of just -D * This aligns to clixon_cli and clixon_backend * Application command option -S to clixon_netconf is obsolete. Use `clixon_netconf -l s` instead. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index bede591f..189e2dc5 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -122,21 +122,6 @@ client_subscription_delete(struct client_entry *ce, return 0; } -#ifdef notused /* xxx */ -static struct client_subscription * -client_subscription_find(struct client_entry *ce, - char *stream) -{ - struct client_subscription *su = NULL; - - for (su = ce->ce_subscription; su; su = su->su_next) - if (strcmp(su->su_stream, stream) == 0) - break; - - return su; -} -#endif - static struct client_entry * ce_find_bypid(struct client_entry *ce_list, int pid) @@ -261,146 +246,138 @@ from_client_get_config(clicon_handle h, return retval; } -/*! Get streams state according to RFC 5277 and RCC 8040 +/*! Get streams state according to RFC 8040 or RFC5277 common function + * @param[in] h Clicon handle + * @param[in] yspec Yang spec + * @param[in] xpath Xpath selection, not used but may be to filter early + * @param[in] module Name of yang module + * @param[in] top Top symbol, ie netconf or restconf-state + * @param[in,out] xret Existing XML tree, merge x into this * @retval -1 Error (fatal) * @retval 0 OK * @retval 1 Statedata callback failed */ static int -client_get_streams(clicon_handle h, - char *xpath, - cxobj **xtop) +client_get_streams(clicon_handle h, + yang_spec *yspec, + char *xpath, + char *module, + char *top, + cxobj **xret) { int retval = -1; + yang_stmt *ystream = NULL; /* yang stream module */ + yang_stmt *yns = NULL; /* yang namespace */ cxobj *x = NULL; - cxobj *xc; - char *reason = NULL; - yang_spec *yspec; - cbuf *cb; + cbuf *cb = NULL; - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_YANG, ENOENT, "No yang spec"); + if ((ystream = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){ + clicon_err(OE_YANG, 0, "%s yang module not found", module); goto done; } - if (*xtop==NULL){ - clicon_err(OE_CFG, ENOENT, "XML tree expected"); + if ((yns = yang_find((yang_node*)ystream, Y_NAMESPACE, NULL)) == NULL){ + clicon_err(OE_YANG, 0, "%s yang namespace not found", module); goto done; } if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, 0, "clicon buffer"); goto done; } - cprintf(cb,""); - if (stream_get_xml(h, cb) < 0) + cprintf(cb,"<%s xmlns=\"%s\">", top, yns->ys_argument); + if (stream_get_xml(h, strcmp(top,"restconf-state")==0, cb) < 0) goto done; - cprintf(cb,""); - cprintf(cb,""); - if (stream_get_xml(h, cb) < 0) - goto done; - cprintf(cb,""); + cprintf(cb,"", top); - /* XXX. yspec */ - if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){ - if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0) + if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ + if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) goto done; retval = 1; goto done; } - if (xml_merge(*xtop, x, yspec, &reason) < 0) - goto done; - if (reason){ - while ((xc = xml_child_i(*xtop, 0)) != NULL) - xml_purge(xc); - if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) - goto done; - retval = 1; - goto done; - } - retval = 0; + retval = netconf_trymerge(x, yspec, xret); done: if (cb) cbuf_free(cb); - if (reason) - free(reason); if (x) xml_free(x); return retval; } /*! Get modules state according to RFC 7895 + * @param[in] h Clicon handle + * @param[in] yspec Yang spec + * @param[in] xpath Xpath selection, not used but may be to filter early + * @param[in] module Name of yang module + * @param[in,out] xret Existing XML tree, merge x into this * @retval -1 Error (fatal) * @retval 0 OK * @retval 1 Statedata callback failed */ static int -client_get_modules(clicon_handle h, - char *xpath, - cxobj **xtop) +client_get_modules(clicon_handle h, + yang_spec *yspec, + char *xpath, + char *module, + cxobj **xret) { int retval = -1; cxobj *x = NULL; - cxobj *xc; - char *reason = NULL; - yang_spec *yspec; - cbuf *cb; - yang_stmt *ymod = NULL; - yang_stmt *yrev; + cbuf *cb = NULL; + yang_stmt *ylib = NULL; /* ietf-yang-library */ + yang_stmt *yns = NULL; /* namespace */ + yang_stmt *ymod; /* generic module */ + yang_stmt *ys; - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_YANG, ENOENT, "No yang spec"); + if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){ + clicon_err(OE_YANG, 0, "%s not found", module); goto done; } - if (*xtop==NULL){ - clicon_err(OE_CFG, ENOENT, "XML tree expected"); + if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){ + clicon_err(OE_YANG, 0, "%s yang namespace not found", module); goto done; } if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, 0, "clicon buffer"); goto done; } - cprintf(cb,""); + cprintf(cb,"", yns->ys_argument); cprintf(cb,"1"); /* NYI */ + ymod = NULL; while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { cprintf(cb,""); cprintf(cb,"%s", ymod->ys_argument); - if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL) - cprintf(cb,"%s", yrev->ys_argument); + if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL) + cprintf(cb,"%s", ys->ys_argument); else cprintf(cb,""); + if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL) + cprintf(cb,"%s", ys->ys_argument); + else + cprintf(cb,""); cprintf(cb,""); } cprintf(cb,""); - /* XXX. yspec */ - if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){ - if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0) + if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ + if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) goto done; retval = 1; goto done; } - if (xml_merge(*xtop, x, yspec, &reason) < 0) - goto done; - if (reason){ - while ((xc = xml_child_i(*xtop, 0)) != NULL) - xml_purge(xc); - if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) - goto done; - retval = 1; - goto done; - } - retval = 0; + retval = netconf_trymerge(x, yspec, xret); done: if (cb) cbuf_free(cb); - if (reason) - free(reason); if (x) xml_free(x); return retval; } /*! Get system state-data, including streams and plugins + * @param[in] h Clicon handle + * @param[in] xpath Xpath selection, not used but may be to filter early + * @param[in,out] xret Existing XML tree, merge x into this * @retval -1 Error (fatal) * @retval 0 OK * @retval 1 Statedata callback failed @@ -410,18 +387,27 @@ client_statedata(clicon_handle h, char *xpath, cxobj **xret) { - int retval = -1; - cxobj **xvec = NULL; - size_t xlen; - int i; - - if ((retval = client_get_streams(h, xpath, xret)) != 0) - goto done; - if ((retval = client_get_modules(h, xpath, xret)) != 0) - goto done; - if ((retval = clixon_plugin_statedata(h, xpath, xret)) != 0) + int retval = -1; + cxobj **xvec = NULL; + size_t xlen; + int i; + yang_spec *yspec; + + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_YANG, ENOENT, "No yang spec"); + goto done; + } + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && + (retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0) + goto done; + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && + (retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0) + goto done; + if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && + (retval = client_get_modules(h, yspec, xpath, "ietf-yang-library", xret)) != 0) + goto done; + if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0) goto done; -#if 1 /* Code complex to filter out anything that is outside of xpath */ if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; @@ -441,7 +427,6 @@ client_statedata(clicon_handle h, /* reset flag */ if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) goto done; -#endif retval = 0; done: if (xvec) diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 0873fc1c..6640dce3 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -773,12 +773,16 @@ main(int argc, /* Read and parse application yang specification */ if (yang_spec_main(h) == NULL) goto done; - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) - goto done; - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) - goto done; - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) - goto done; + /* Add system modules */ + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) + goto done; + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) + goto done; + if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) + goto done; /* Set options: database dir and yangspec (could be hidden in connect?)*/ if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0) goto done; diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 255b0f1d..e8ce2972 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -112,6 +112,7 @@ clixon_plugin_reset(clicon_handle h, * This is internal system call, plugin is invoked (does not call) this function * Backend plugins can register * @param[in] h clicon handle + * @param[in] yspec Yang spec * @param[in] xpath String with XPATH syntax. or NULL for all * @param[in,out] xtop State XML tree is merged with existing tree. * @retval -1 Error @@ -120,26 +121,17 @@ clixon_plugin_reset(clicon_handle h, * @note xtop can be replaced */ int -clixon_plugin_statedata(clicon_handle h, - char *xpath, - cxobj **xtop) +clixon_plugin_statedata(clicon_handle h, + yang_spec *yspec, + char *xpath, + cxobj **xret) { - int retval = -1; - cxobj *x = NULL; - yang_spec *yspec; - cxobj *xc; + int retval = -1; + int ret; + cxobj *x = NULL; clixon_plugin *cp = NULL; plgstatedata_t *fn; /* Plugin statedata fn */ - char *reason = NULL; - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_YANG, ENOENT, "No yang spec"); - goto done; - } - if (*xtop==NULL){ - clicon_err(OE_CFG, ENOENT, "XML tree expected"); - goto done; - } while ((cp = clixon_plugin_each(h, cp)) != NULL) { if ((fn = cp->cp_api.ca_statedata) == NULL) continue; @@ -149,27 +141,17 @@ clixon_plugin_statedata(clicon_handle h, retval = 1; goto done; /* Dont quit here on user callbacks */ } - if (xml_merge(*xtop, x, yspec, &reason) < 0) + if ((ret = netconf_trymerge(x, yspec, xret)) != 0){ + retval = ret; goto done; - if (reason){ - while ((xc = xml_child_i(*xtop, 0)) != NULL) - xml_purge(xc); - clicon_log(LOG_NOTICE, "%s: Plugin '%s' state callback failed", - __FUNCTION__, cp->cp_name); - if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) - goto done; - goto ok; } if (x){ xml_free(x); x = NULL; } } - ok: retval = 0; done: - if (reason) - free(reason); if (x) xml_free(x); return retval; diff --git a/apps/backend/backend_plugin.h b/apps/backend/backend_plugin.h index 3dff37b2..a2736b19 100644 --- a/apps/backend/backend_plugin.h +++ b/apps/backend/backend_plugin.h @@ -71,7 +71,7 @@ int backend_plugin_initiate(clicon_handle h); int clixon_plugin_reset(clicon_handle h, char *db); -int clixon_plugin_statedata(clicon_handle h, char *xpath, cxobj **xtop); +int clixon_plugin_statedata(clicon_handle h, yang_spec *yspec, char *xpath, cxobj **xtop); transaction_data_t * transaction_new(void); int transaction_free(transaction_data_t *); diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index 89aa84e0..32366f89 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -75,6 +75,7 @@ APPL = clixon_restconf # Not accessible from plugin APPSRC = restconf_main.c APPSRC += restconf_methods.c +APPSRC += restconf_stream.c APPOBJ = $(APPSRC:.c=.o) # Accessible from plugin diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index be343612..9f1ac618 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -73,11 +73,12 @@ /* clicon */ #include -#include /* Need to be after clixon_xml-h due to attribute format */ +#include /* Need to be after clixon_xml.h due to attribute format */ /* restconf */ #include "restconf_lib.h" #include "restconf_methods.h" +#include "restconf_stream.h" /* Command line options to be passed to getopt(3) */ #define RESTCONF_OPTS "hD:f:l:p:y:a:u:" @@ -90,6 +91,7 @@ #define RESTCONF_API "restconf" #define RESTCONF_API_ROOT "/restconf" +#define RESTCONF_STREAM_ROOT "/stream" #define RESTCONF_LOGFILE "/www-data/clixon_restconf.log" @@ -622,12 +624,16 @@ main(int argc, if (yang_spec_main(h) == NULL) goto done; /* Add system modules */ - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) - goto done; - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) - goto done; - if (yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) - goto done; + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) + goto done; + if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) + goto done; + if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && + yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) + goto done; + /* Call start function in all plugins before we go interactive Pass all args after the standard options to plugin_start */ @@ -660,9 +666,12 @@ main(int argc, } clicon_debug(1, "------------"); if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){ - clicon_debug(1, "path:%s", path); + clicon_debug(1, "path: %s", path); if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0) api_restconf(h, r); /* This is the function */ + else if (strncmp(path, RESTCONF_STREAM_ROOT, strlen(RESTCONF_STREAM_ROOT)) == 0) { + api_stream(h, r); + } else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) { api_well_known(h, r); /* */ } diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c new file mode 100644 index 00000000..5a99c702 --- /dev/null +++ b/apps/restconf/restconf_stream.c @@ -0,0 +1,85 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + Restconf event stream implementation. + See RFC 8040 RESTCONF Protocol + Sections 3.8, 6, 9.3 + + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +#include /* Need to be after clixon_xml-h due to attribute format */ + +/* restconf */ +#include "restconf_lib.h" +#include "restconf_stream.h" + +/*! Process a FastCGI request + * @param[in] r Fastcgi request handle + */ +int +api_stream(clicon_handle h, + FCGX_Request *r) +{ + int retval = -1; + + clicon_debug(1, "%s", __FUNCTION__); + retval = 0; + // done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + return retval; +} diff --git a/apps/restconf/restconf_stream.h b/apps/restconf/restconf_stream.h new file mode 100644 index 00000000..7fd5ac4a --- /dev/null +++ b/apps/restconf/restconf_stream.h @@ -0,0 +1,44 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + */ + +#ifndef _RESTCONF_STREAM_H_ +#define _RESTCONF_STREAM_H_ + +/* + * Prototypes + */ +int api_stream(clicon_handle h, FCGX_Request *r); + +#endif /* _RESTCONF_STREAM_H_ */ diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index 7b7d52d8..894cf7c7 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -174,7 +174,6 @@ text_connect(void) xh = (xmldb_handle)th; done: return xh; - } /*! Disconnect from to a datastore plugin and deallocate handle @@ -191,6 +190,7 @@ text_disconnect(xmldb_handle xh) size_t klen; int i; + clicon_debug(1, "%s", __FUNCTION__); if (th){ if (th->th_dbdir) free(th->th_dbdir); diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index f65bc9dc..063345e3 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -62,5 +62,6 @@ int netconf_operation_failed(cbuf *cb, char *type, char *message); int netconf_operation_failed_xml(cxobj **xret, char *type, char *message); int netconf_malformed_message(cbuf *cb, char *message); int netconf_malformed_message_xml(cxobj **xret, char *message); +int netconf_trymerge(cxobj *x, yang_spec *yspec, cxobj **xret); #endif /* _CLIXON_NETCONF_LIB_H */ diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index a7cf2f9e..d59e88c9 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -60,6 +60,8 @@ #include "clixon_yang.h" #include "clixon_log.h" #include "clixon_xml.h" +#include "clixon_options.h" +#include "clixon_xml_map.h" #include "clixon_netconf_lib.h" /*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A @@ -934,3 +936,37 @@ netconf_malformed_message_xml(cxobj **xret, done: return retval; } + +/*! Help function: merge - check yang - if error make netconf errmsg + * @param[in] x XML tree + * @param[in] yspec Yang spec + * @param[in,out] xret Existing XML tree, merge x into this + * @retval -1 Error (fatal) + * @retval 0 OK + * @retval 1 Statedata callback failed + */ +int +netconf_trymerge(cxobj *x, + yang_spec *yspec, + cxobj **xret) +{ + int retval = -1; + char *reason = NULL; + cxobj *xc; + + if (xml_merge(*xret, x, yspec, &reason) < 0) + goto done; + if (reason){ + while ((xc = xml_child_i(*xret, 0)) != NULL) + xml_purge(xc); + if (netconf_operation_failed_xml(xret, "rpc", reason)< 0) + goto done; + retval = 1; + goto done; + } + retval = 0; + done: + if (reason) + free(reason); + return retval; +} diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index e29c7c30..4dfe4f96 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -230,6 +230,7 @@ xmldb_disconnect(clicon_handle h) xmldb_handle xh; struct xmldb_api *xa; + clicon_debug(1, "%s", __FUNCTION__); if ((xa = clicon_xmldb_api_get(h)) == NULL){ clicon_err(OE_DB, 0, "No xmldb plugin"); goto done; diff --git a/test/test_event.sh b/test/test_event.sh index 789dd793..de7c0689 100755 --- a/test/test_event.sh +++ b/test/test_event.sh @@ -22,6 +22,9 @@ cat < $cfg /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME + true + true + true EOF @@ -83,30 +86,37 @@ sleep 1 # get the stream list using netconf new "netconf event stream discovery RFC5277 Sec 3.2.5" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONFdefault NETCONF event streamfalseCLICONClicon logsfalse]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'CLICONClicon logsfalseNETCONFdefault NETCONF event streamfalse]]>]]>' new "netconf event stream discovery RFC8040 Sec 6.2" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONFdefault NETCONF event streamfalseCLICONClicon logsfalse]]>]]>' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'CLICONClicon logsfalsexmlhttps://example.com/stream/CLICONNETCONFdefault NETCONF event streamfalsexmlhttps://example.com/stream/NETCONF]]>]]>' new "restconf event stream discovery RFC8040 Sec 6.2" -expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"streams": {"stream": \[{"name": "CLICON","description": "Clicon logs","replay-support": false},{ "name": "NETCONF","description": "default NETCONF event stream","replay-support": false}\]}}' +expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"streams": {"stream": \[{"name": "CLICON","description": "Clicon logs","replay-support": false,"access": \[{"encoding": "xml","location": "https://example.com/stream/CLICON"}\]},{ "name": "NETCONF","description": "default NETCONF event stream","replay-support": false,"access": \[{"encoding": "xml","location": "https://example.com/stream/NETCONF"}\]}\]}' -#new "netconf subscription" +new "restconf subscribe RFC8040 Sec 6.3, get location" +expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=NETCONF/access=xml/location" 0 '{"location": "https://example.com/stream/NETCONF"}' + +new "restconf monitor event stream RFC8040 Sec 6.3" +#expectfn "curl -s -X GET http://localhost/stream/NETCONF" 0 '' + +#new "netconf subscription" NOTYET #expectwait "$clixon_netconf -qf $cfg -y $fyang" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 new "Kill restconf daemon" sudo pkill -u www-data clixon_restconf new "Kill backend" -# Check if still alive -pid=`pgrep clixon_backend` -if [ -z "$pid" ]; then - err "backend already dead" -fi # kill backend sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err "kill backend" fi +# Check if still alive +pid=`pgrep clixon_backend` +if [ -n "$pid" ]; then + sudo kill $pid +fi + rm -rf $dir diff --git a/test/test_restconf.sh b/test/test_restconf.sh index c82948ac..3e10acd6 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -25,6 +25,7 @@ cat < $cfg 1 /usr/local/var/$APPNAME /usr/local/lib/xmldb/text.so + true EOF @@ -136,7 +137,7 @@ if [ -z "$match" ]; then fi new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895" -expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"module": [{"name": "ietf-routing","revision": "2014-10-26"}]} +expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"module": [{"name": "ietf-routing","revision": "2014-10-26","namespace": "urn:ietf:params:xml:ns:yang:ietf-routing"}]} ' new "restconf options. RFC 8040 4.1" diff --git a/test/test_yang.sh b/test/test_yang.sh index 94f56dc1..b70dc527 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -21,6 +21,7 @@ cat < $cfg 1 /usr/local/var/$APPNAME /usr/local/lib/xmldb/text.so + true EOF @@ -118,7 +119,7 @@ new "cli not defined extension" #expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found" new "netconf schema resource, RFC 7895" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'ietf-yang-library2016-06-21' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'ietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-types' new "netconf edit config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "125one]]>]]>" "^]]>]]>$" diff --git a/yang/clixon-config@2018-04-30.yang b/yang/clixon-config@2018-04-30.yang index c629a3c7..f0f7ce68 100644 --- a/yang/clixon-config@2018-04-30.yang +++ b/yang/clixon-config@2018-04-30.yang @@ -38,9 +38,9 @@ module clixon-config { ***** END LICENSE BLOCK *****"; - revision 2018-02-12 { + revision 2018-09-30 { description - "Added pretty print for datastore"; + "Aligned to Clixon 3.8.0"; } typedef startup_mode{ description @@ -349,5 +349,26 @@ module clixon-config { type string; description "RFC8341 NACM external configuration file"; } + leaf CLICON_MODULE_LIBRARY_RFC7895 { + type boolean; + default false; + description "Enable RFC 7895 YANG Module library support as state + data. Ifenabled, module info will appear when doing + netconf get or restconf GET"; + } + leaf CLICON_STREAM_DISCOVERY_RFC5277 { + type boolean; + default false; + description "Enable event stream discovery as described in RFC 5277 + sections 3.2. If enabled, available streams will appear + when doing netconf get or restconf GET"; + } + leaf CLICON_STREAM_DISCOVERY_RFC8040 { + type boolean; + default false; + description "Enable event stream discovery as described in RFC 5277 + sections 3.2. If enabled, available streams will appear + when doing netconf get or restconf GET"; + } } } diff --git a/yang/ietf-netconf-notification@2008-07-01.yang b/yang/ietf-netconf-notification@2008-07-01.yang index 1e98e455..0b78ac3b 100644 --- a/yang/ietf-netconf-notification@2008-07-01.yang +++ b/yang/ietf-netconf-notification@2008-07-01.yang @@ -1,4 +1,4 @@ -module ietf-restconf-monitoring { +module ietf-netconf-notification { namespace "urn:ietf:params:xml:ns:netconf:notification:1:0"; prefix "rcmon";