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

View file

@ -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,"<restconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring\">");
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,"</restconf-state>");
cprintf(cb,"<netconf xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">");
if (stream_get_xml(h, cb) < 0)
goto done;
cprintf(cb,"</netconf>");
cprintf(cb,"</%s>", 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,"<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 */
ymod = NULL;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", yrev->ys_argument);
if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
else
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,"</modules-state>");
/* 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)

View file

@ -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;

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
* 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;

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_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 *);

View file

@ -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

View file

@ -73,11 +73,12 @@
/* clicon */
#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 */
#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); /* */
}

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