Event stream discovery support

Added config options CLICON_MODULE_LIBRARY_RFC7895, CLICON_STREAM_DISCOVERY_RFC5277, LICON_STREAM_DISCOVERY_RFC804
This commit is contained in:
Olof hagsand 2018-09-23 17:45:22 +02:00
parent 74fc0800ae
commit 07542269ec
19 changed files with 345 additions and 164 deletions

3
.gitignore vendored
View file

@ -8,7 +8,6 @@ Makefile
apps/Makefile apps/Makefile
apps/*/Makefile apps/*/Makefile
docker/Makefile docker/Makefile
docker/*/Makefile
etc/Makefile etc/Makefile
example/Makefile example/Makefile
lib/Makefile lib/Makefile
@ -37,8 +36,6 @@ docker/netconf/Dockerfile
etc/clixonrc etc/clixonrc
example/*.conf
include/clixon_config.h include/clixon_config.h
lib/src/build.c lib/src/build.c

View file

@ -5,15 +5,18 @@
### Major New features ### Major New features
### API changes on existing features (you may need to change your code) ### API changes on existing features (you may need to change your code)
* Limited support of RFC 7895 YANG Module Library to list modules: * YANG Module Library support
* That is, limited support of: ietf-yang-library.yang * According to RFC 7895 and implemented by ietf-yang-library.yang
* For example: `<module><name>example</name><revision/></module><module><name>ietf-restconf-monitoring</name><revision>2017-01-26</revision></module>...` * Supported: module, name, revision, namespace
* Notification event stream enhancements * Not supported: notification, deviation, module-set-id, etc.
* Yang 1.1 notification support * Enabled by default, disable by resetting CLICON_MODULE_LIBRARY_RFC7895
* Event stream discovery support according to RFC 5277 Sec 3.2.5.1 * Yang 1.1 notification support (RFC 7950: Sec 7.16)
* That is, support of ietf-restconf-monitoring.yang (mimics schema in 3.2.5.1) * Event stream discovery support according to RFC 5277 for netconf
* Event stream discovery support according to RFC 8040 (restconf) * Implemented by ietf-netconf-notification.yang
* That is, support of 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 <level> as command-line option instead of just -D * clixon_restconf and clixon_netconf now take -D <level> as command-line option instead of just -D
* This aligns to clixon_cli and clixon_backend * This aligns to clixon_cli and clixon_backend
* Application command option -S to clixon_netconf is obsolete. Use `clixon_netconf -l s` instead. * Application command option -S to clixon_netconf is obsolete. Use `clixon_netconf -l s` instead.

View file

@ -122,21 +122,6 @@ client_subscription_delete(struct client_entry *ce,
return 0; 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 * static struct client_entry *
ce_find_bypid(struct client_entry *ce_list, ce_find_bypid(struct client_entry *ce_list,
int pid) int pid)
@ -261,146 +246,138 @@ from_client_get_config(clicon_handle h,
return retval; 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 -1 Error (fatal)
* @retval 0 OK * @retval 0 OK
* @retval 1 Statedata callback failed * @retval 1 Statedata callback failed
*/ */
static int static int
client_get_streams(clicon_handle h, client_get_streams(clicon_handle h,
yang_spec *yspec,
char *xpath, char *xpath,
cxobj **xtop) char *module,
char *top,
cxobj **xret)
{ {
int retval = -1; int retval = -1;
yang_stmt *ystream = NULL; /* yang stream module */
yang_stmt *yns = NULL; /* yang namespace */
cxobj *x = NULL; cxobj *x = NULL;
cxobj *xc; cbuf *cb = NULL;
char *reason = NULL;
yang_spec *yspec;
cbuf *cb;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((ystream = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, 0, "%s yang module not found", module);
goto done; goto done;
} }
if (*xtop==NULL){ if ((yns = yang_find((yang_node*)ystream, Y_NAMESPACE, NULL)) == NULL){
clicon_err(OE_CFG, ENOENT, "XML tree expected"); clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
goto done; goto done;
} }
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "clicon buffer"); clicon_err(OE_UNIX, 0, "clicon buffer");
goto done; goto done;
} }
cprintf(cb,"<restconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring\">"); cprintf(cb,"<%s xmlns=\"%s\">", top, yns->ys_argument);
if (stream_get_xml(h, cb) < 0) if (stream_get_xml(h, strcmp(top,"restconf-state")==0, cb) < 0)
goto done; goto done;
cprintf(cb,"</restconf-state>"); cprintf(cb,"</%s>", top);
cprintf(cb,"<netconf xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">");
if (stream_get_xml(h, cb) < 0)
goto done;
cprintf(cb,"</netconf>");
/* XXX. yspec */ if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0)
goto done; goto done;
retval = 1; retval = 1;
goto done; goto done;
} }
if (xml_merge(*xtop, x, yspec, &reason) < 0) retval = netconf_trymerge(x, yspec, xret);
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;
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (reason)
free(reason);
if (x) if (x)
xml_free(x); xml_free(x);
return retval; return retval;
} }
/*! Get modules state according to RFC 7895 /*! 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 -1 Error (fatal)
* @retval 0 OK * @retval 0 OK
* @retval 1 Statedata callback failed * @retval 1 Statedata callback failed
*/ */
static int static int
client_get_modules(clicon_handle h, client_get_modules(clicon_handle h,
yang_spec *yspec,
char *xpath, char *xpath,
cxobj **xtop) char *module,
cxobj **xret)
{ {
int retval = -1; int retval = -1;
cxobj *x = NULL; cxobj *x = NULL;
cxobj *xc; cbuf *cb = NULL;
char *reason = NULL; yang_stmt *ylib = NULL; /* ietf-yang-library */
yang_spec *yspec; yang_stmt *yns = NULL; /* namespace */
cbuf *cb; yang_stmt *ymod; /* generic module */
yang_stmt *ymod = NULL; yang_stmt *ys;
yang_stmt *yrev;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, 0, "%s not found", module);
goto done; goto done;
} }
if (*xtop==NULL){ if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){
clicon_err(OE_CFG, ENOENT, "XML tree expected"); clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
goto done; goto done;
} }
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "clicon buffer"); clicon_err(OE_UNIX, 0, "clicon buffer");
goto done; goto done;
} }
cprintf(cb,"<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"); cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
cprintf(cb,"<module-set-id>1</module-set-id>"); /* NYI */ cprintf(cb,"<module-set-id>1</module-set-id>"); /* NYI */
ymod = NULL;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
cprintf(cb,"<module>"); cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument); cprintf(cb,"<name>%s</name>", ymod->ys_argument);
if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL) if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", yrev->ys_argument); cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
else else
cprintf(cb,"<revision></revision>"); cprintf(cb,"<revision></revision>");
if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
else
cprintf(cb,"<namespace></namespace>");
cprintf(cb,"</module>"); cprintf(cb,"</module>");
} }
cprintf(cb,"</modules-state>"); cprintf(cb,"</modules-state>");
/* XXX. yspec */ if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0)
goto done; goto done;
retval = 1; retval = 1;
goto done; goto done;
} }
if (xml_merge(*xtop, x, yspec, &reason) < 0) retval = netconf_trymerge(x, yspec, xret);
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;
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (reason)
free(reason);
if (x) if (x)
xml_free(x); xml_free(x);
return retval; return retval;
} }
/*! Get system state-data, including streams and plugins /*! 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 -1 Error (fatal)
* @retval 0 OK * @retval 0 OK
* @retval 1 Statedata callback failed * @retval 1 Statedata callback failed
@ -414,14 +391,23 @@ client_statedata(clicon_handle h,
cxobj **xvec = NULL; cxobj **xvec = NULL;
size_t xlen; size_t xlen;
int i; int i;
yang_spec *yspec;
if ((retval = client_get_streams(h, xpath, xret)) != 0) if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done; goto done;
if ((retval = client_get_modules(h, xpath, xret)) != 0) }
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
(retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
goto done; goto done;
if ((retval = clixon_plugin_statedata(h, xpath, xret)) != 0) 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; goto done;
#if 1
/* Code complex to filter out anything that is outside of xpath */ /* Code complex to filter out anything that is outside of xpath */
if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done; goto done;
@ -441,7 +427,6 @@ client_statedata(clicon_handle h,
/* reset flag */ /* reset flag */
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done; goto done;
#endif
retval = 0; retval = 0;
done: done:
if (xvec) if (xvec)

View file

@ -773,11 +773,15 @@ main(int argc,
/* Read and parse application yang specification */ /* Read and parse application yang specification */
if (yang_spec_main(h) == NULL) if (yang_spec_main(h) == NULL)
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) /* 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; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0)
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0)
goto done; goto done;
/* Set options: database dir and yangspec (could be hidden in connect?)*/ /* Set options: database dir and yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0) if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)

View file

@ -112,6 +112,7 @@ clixon_plugin_reset(clicon_handle h,
* This is internal system call, plugin is invoked (does not call) this function * This is internal system call, plugin is invoked (does not call) this function
* Backend plugins can register * Backend plugins can register
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] yspec Yang spec
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in,out] xtop State XML tree is merged with existing tree. * @param[in,out] xtop State XML tree is merged with existing tree.
* @retval -1 Error * @retval -1 Error
@ -121,25 +122,16 @@ clixon_plugin_reset(clicon_handle h,
*/ */
int int
clixon_plugin_statedata(clicon_handle h, clixon_plugin_statedata(clicon_handle h,
yang_spec *yspec,
char *xpath, char *xpath,
cxobj **xtop) cxobj **xret)
{ {
int retval = -1; int retval = -1;
int ret;
cxobj *x = NULL; cxobj *x = NULL;
yang_spec *yspec;
cxobj *xc;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */ 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) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_statedata) == NULL) if ((fn = cp->cp_api.ca_statedata) == NULL)
continue; continue;
@ -149,27 +141,17 @@ clixon_plugin_statedata(clicon_handle h,
retval = 1; retval = 1;
goto done; /* Dont quit here on user callbacks */ 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; 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){ if (x){
xml_free(x); xml_free(x);
x = NULL; x = NULL;
} }
} }
ok:
retval = 0; retval = 0;
done: done:
if (reason)
free(reason);
if (x) if (x)
xml_free(x); xml_free(x);
return retval; return retval;

View file

@ -71,7 +71,7 @@ int backend_plugin_initiate(clicon_handle h);
int clixon_plugin_reset(clicon_handle h, char *db); 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); transaction_data_t * transaction_new(void);
int transaction_free(transaction_data_t *); int transaction_free(transaction_data_t *);

View file

@ -75,6 +75,7 @@ APPL = clixon_restconf
# Not accessible from plugin # Not accessible from plugin
APPSRC = restconf_main.c APPSRC = restconf_main.c
APPSRC += restconf_methods.c APPSRC += restconf_methods.c
APPSRC += restconf_stream.c
APPOBJ = $(APPSRC:.c=.o) APPOBJ = $(APPSRC:.c=.o)
# Accessible from plugin # Accessible from plugin

View file

@ -73,11 +73,12 @@
/* clicon */ /* clicon */
#include <clixon/clixon.h> #include <clixon/clixon.h>
#include <fcgi_stdio.h> /* Need to be after clixon_xml-h due to attribute format */ #include <fcgi_stdio.h> /* Need to be after clixon_xml.h due to attribute format */
/* restconf */ /* restconf */
#include "restconf_lib.h" #include "restconf_lib.h"
#include "restconf_methods.h" #include "restconf_methods.h"
#include "restconf_stream.h"
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:y:a:u:" #define RESTCONF_OPTS "hD:f:l:p:y:a:u:"
@ -90,6 +91,7 @@
#define RESTCONF_API "restconf" #define RESTCONF_API "restconf"
#define RESTCONF_API_ROOT "/restconf" #define RESTCONF_API_ROOT "/restconf"
#define RESTCONF_STREAM_ROOT "/stream"
#define RESTCONF_LOGFILE "/www-data/clixon_restconf.log" #define RESTCONF_LOGFILE "/www-data/clixon_restconf.log"
@ -622,12 +624,16 @@ main(int argc,
if (yang_spec_main(h) == NULL) if (yang_spec_main(h) == NULL)
goto done; goto done;
/* Add system modules */ /* Add system modules */
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0)
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0) if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0)
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0) if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
yang_spec_append(h, CLIXON_DATADIR, "ietf-yang-library", NULL)< 0)
goto done; goto done;
/* Call start function in all plugins before we go interactive /* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start Pass all args after the standard options to plugin_start
*/ */
@ -663,6 +669,9 @@ main(int argc,
clicon_debug(1, "path: %s", path); clicon_debug(1, "path: %s", path);
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0) if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0)
api_restconf(h, r); /* This is the function */ 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) { else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
api_well_known(h, r); /* */ api_well_known(h, r); /* */
} }

View file

@ -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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <fcntl.h>
#include <time.h>
#include <limits.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <libgen.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include <fcgi_stdio.h> /* 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;
}

View file

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

View file

@ -174,7 +174,6 @@ text_connect(void)
xh = (xmldb_handle)th; xh = (xmldb_handle)th;
done: done:
return xh; return xh;
} }
/*! Disconnect from to a datastore plugin and deallocate handle /*! Disconnect from to a datastore plugin and deallocate handle
@ -191,6 +190,7 @@ text_disconnect(xmldb_handle xh)
size_t klen; size_t klen;
int i; int i;
clicon_debug(1, "%s", __FUNCTION__);
if (th){ if (th){
if (th->th_dbdir) if (th->th_dbdir)
free(th->th_dbdir); free(th->th_dbdir);

View file

@ -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_operation_failed_xml(cxobj **xret, char *type, char *message);
int netconf_malformed_message(cbuf *cb, char *message); int netconf_malformed_message(cbuf *cb, char *message);
int netconf_malformed_message_xml(cxobj **xret, 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 */ #endif /* _CLIXON_NETCONF_LIB_H */

View file

@ -60,6 +60,8 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_xml_map.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A /*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
@ -934,3 +936,37 @@ netconf_malformed_message_xml(cxobj **xret,
done: done:
return retval; 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;
}

View file

@ -230,6 +230,7 @@ xmldb_disconnect(clicon_handle h)
xmldb_handle xh; xmldb_handle xh;
struct xmldb_api *xa; struct xmldb_api *xa;
clicon_debug(1, "%s", __FUNCTION__);
if ((xa = clicon_xmldb_api_get(h)) == NULL){ if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
goto done; goto done;

View file

@ -22,6 +22,9 @@ cat <<EOF > $cfg
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR> <CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR> <CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE> <CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
<CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277>
<CLICON_STREAM_DISCOVERY_RFC8040>true</CLICON_STREAM_DISCOVERY_RFC8040>
</config> </config>
EOF EOF
@ -83,30 +86,37 @@ sleep 1
# get the stream list using netconf # get the stream list using netconf
new "netconf event stream discovery RFC5277 Sec 3.2.5" new "netconf event stream discovery RFC5277 Sec 3.2.5"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf><streams><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support></stream><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>' expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf><streams><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support></stream><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
new "netconf event stream discovery RFC8040 Sec 6.2" new "netconf event stream discovery RFC8040 Sec 6.2"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state><streams><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support></stream><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support></stream></streams></restconf-state></data></rpc-reply>]]>]]>' expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state><streams><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support><access><encoding>xml</encoding><location>https://example.com/stream/CLICON</location></access></stream><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support><access><encoding>xml</encoding><location>https://example.com/stream/NETCONF</location></access></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
new "restconf event stream discovery RFC8040 Sec 6.2" 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" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30 #expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
new "Kill restconf daemon" new "Kill restconf daemon"
sudo pkill -u www-data clixon_restconf sudo pkill -u www-data clixon_restconf
new "Kill backend" new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend # kill backend
sudo clixon_backend -zf $cfg sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err "kill backend" err "kill backend"
fi fi
# Check if still alive
pid=`pgrep clixon_backend`
if [ -n "$pid" ]; then
sudo kill $pid
fi
rm -rf $dir rm -rf $dir

View file

@ -25,6 +25,7 @@ cat <<EOF > $cfg
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION> <CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN> <CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
</config> </config>
EOF EOF
@ -136,7 +137,7 @@ if [ -z "$match" ]; then
fi fi
new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895" 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" new "restconf options. RFC 8040 4.1"

View file

@ -21,6 +21,7 @@ cat <<EOF > $cfg
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION> <CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN> <CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
</config> </config>
EOF 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" #expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found"
new "netconf schema resource, RFC 7895" new "netconf schema resource, RFC 7895"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-library</name><revision>2016-06-21</revision></module>' expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace></module>'
new "netconf edit config" new "netconf edit config"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"

View file

@ -38,9 +38,9 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
revision 2018-02-12 { revision 2018-09-30 {
description description
"Added pretty print for datastore"; "Aligned to Clixon 3.8.0";
} }
typedef startup_mode{ typedef startup_mode{
description description
@ -349,5 +349,26 @@ module clixon-config {
type string; type string;
description "RFC8341 NACM external configuration file"; 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";
}
} }
} }

View file

@ -1,4 +1,4 @@
module ietf-restconf-monitoring { module ietf-netconf-notification {
namespace "urn:ietf:params:xml:ns:netconf:notification:1:0"; namespace "urn:ietf:params:xml:ns:netconf:notification:1:0";
prefix "rcmon"; prefix "rcmon";