* Notification event streams enhancements

* Yang 1.1 notification support
  * Event stream discovery support according to RFC 5277 Sec 3.2.5.1 (netconf) and RFC 8040 (restconf)
This commit is contained in:
Olof hagsand 2018-09-18 22:49:29 +02:00
parent f37fdb954f
commit 0631be19cb
16 changed files with 502 additions and 34 deletions

View file

@ -5,6 +5,9 @@
### 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)
* Notification event streams enhancements
* Yang 1.1 notification support
* Event stream discovery support according to RFC 5277 Sec 3.2.5.1 (netconf) and RFC 8040 (restconf)
* 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

@ -99,20 +99,10 @@ client_subscription_add(struct client_entry *ce,
return su; return su;
} }
static struct client_entry * /*! Delete stream subscription from client subscription list */
ce_find_bypid(struct client_entry *ce_list, int pid)
{
struct client_entry *ce;
for (ce = ce_list; ce; ce = ce->ce_next)
if (ce->ce_pid == pid)
return ce;
return NULL;
}
static int static int
client_subscription_delete(struct client_entry *ce, client_subscription_delete(struct client_entry *ce,
struct client_subscription *su0) struct client_subscription *su0)
{ {
struct client_subscription *su; struct client_subscription *su;
struct client_subscription **su_prev; struct client_subscription **su_prev;
@ -134,7 +124,8 @@ client_subscription_delete(struct client_entry *ce,
#ifdef notused /* xxx */ #ifdef notused /* xxx */
static struct client_subscription * static struct client_subscription *
client_subscription_find(struct client_entry *ce, char *stream) client_subscription_find(struct client_entry *ce,
char *stream)
{ {
struct client_subscription *su = NULL; struct client_subscription *su = NULL;
@ -146,6 +137,18 @@ client_subscription_find(struct client_entry *ce, char *stream)
} }
#endif #endif
static struct client_entry *
ce_find_bypid(struct client_entry *ce_list,
int pid)
{
struct client_entry *ce;
for (ce = ce_list; ce; ce = ce->ce_next)
if (ce->ce_pid == pid)
return ce;
return NULL;
}
/*! Remove client entry state /*! Remove client entry state
* Close down everything wrt clients (eg sockets, subscriptions) * Close down everything wrt clients (eg sockets, subscriptions)
* Finally actually remove client struct in handle * Finally actually remove client struct in handle
@ -258,6 +261,79 @@ from_client_get_config(clicon_handle h,
return retval; return retval;
} }
static int
client_get_streams(clicon_handle h,
char *xpath,
cxobj **xtop)
{
int retval = -1;
cxobj *x = NULL;
cxobj *xc;
char *reason = NULL;
yang_spec *yspec;
cbuf *cb;
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;
}
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)
goto done;
cprintf(cb,"</restconf-state>");
/* XXX. yspec */
if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0)
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;
goto ok;
}
#ifdef notyet
/* Code complex to filter out anything that is outside of xpath */
if (xpath_vec(*xtop, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and
* then filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL){
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
/* Remove everything that is not marked */
if (!xml_flag(*xtop, XML_FLAG_MARK))
if (xml_tree_prune_flagged_sub(*xtop, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
/* reset flag */
if (xml_apply(*xtop, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
#endif
ok:
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (reason)
free(reason);
if (x)
xml_free(x);
return retval;
}
/*! Internal message: get /*! Internal message: get
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
@ -288,6 +364,11 @@ from_client_get(clicon_handle h,
} }
/* Get state data from plugins as defined by plugin_statedata(), if any */ /* Get state data from plugins as defined by plugin_statedata(), if any */
assert(xret); assert(xret);
#if 1 /* get netconf state data */
if ((ret = client_get_streams(h, selector, &xret)) < 0)
goto done;
#endif
clicon_err_reset(); clicon_err_reset();
if ((ret = clixon_plugin_statedata(h, selector, &xret)) < 0) if ((ret = clixon_plugin_statedata(h, selector, &xret)) < 0)
goto done; goto done;

View file

@ -756,6 +756,11 @@ main(int argc,
return -1; return -1;
} }
if (stream_register(h, "NETCONF", "default NETCONF event stream") < 0)
goto done;
if (stream_register(h, "CLICON", "Clicon logs") < 0)
goto done;
if ((xmldb_plugin = clicon_xmldb_plugin(h)) == NULL){ if ((xmldb_plugin = clicon_xmldb_plugin(h)) == NULL){
clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n"); clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n");
goto done; goto done;
@ -765,10 +770,11 @@ main(int argc,
/* Connect to plugin to get a handle */ /* Connect to plugin to get a handle */
if (xmldb_connect(h) < 0) if (xmldb_connect(h) < 0)
goto done; goto done;
/* Parse db spec file */ /* 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)
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)
goto done; goto done;

View file

@ -86,6 +86,8 @@ struct backend_handle {
int bh_magic; /* magic (HDR)*/ int bh_magic; /* magic (HDR)*/
clicon_hash_t *bh_copt; /* clicon option list (HDR) */ clicon_hash_t *bh_copt; /* clicon option list (HDR) */
clicon_hash_t *bh_data; /* internal clicon data (HDR) */ clicon_hash_t *bh_data; /* internal clicon data (HDR) */
event_stream_t *ch_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */
struct client_entry *bh_ce_list; /* The client list */ struct client_entry *bh_ce_list; /* The client list */
int bh_ce_nr; /* Number of clients, just increment */ int bh_ce_nr; /* Number of clients, just increment */

View file

@ -76,14 +76,14 @@
* @see struct clicon_handle, struct backend_handle * @see struct clicon_handle, struct backend_handle
*/ */
struct cli_handle { struct cli_handle {
int cl_magic; /* magic (HDR)*/ int cl_magic; /* magic (HDR)*/
clicon_hash_t *cl_copt; /* clicon option list (HDR) */ clicon_hash_t *cl_copt; /* clicon option list (HDR) */
clicon_hash_t *cl_data; /* internal clicon data (HDR) */ clicon_hash_t *cl_data; /* internal clicon data (HDR) */
event_stream_t *cl_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */
cligen_handle cl_cligen; /* cligen handle */
cli_syntax_t *cl_stx; /* syntax structure */
cligen_handle cl_cligen; /* cligen handle */
cli_syntax_t *cl_stx; /* syntax structure */
}; };
/*! Return a clicon handle for other CLICON API calls /*! Return a clicon handle for other CLICON API calls

View file

@ -7,11 +7,13 @@ Ensure www-data is member of the CLICON_SOCK_GROUP (default clicon). If not, add
sudo usermod -a -G clicon www-data sudo usermod -a -G clicon www-data
``` ```
This implementation uses FastCGI, see http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html.
Define nginx config file: /etc/nginx/sites-available/default Define nginx config file: /etc/nginx/sites-available/default
``` ```
server { server {
... ...
location /restconf { location / {
root /usr/share/nginx/html/restconf; root /usr/share/nginx/html/restconf;
fastcgi_pass unix:/www-data/fastcgi_restconf.sock; fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
include fastcgi_params; include fastcgi_params;

View file

@ -68,6 +68,7 @@
#include <clixon/clixon_queue.h> #include <clixon/clixon_queue.h>
#include <clixon/clixon_hash.h> #include <clixon/clixon_hash.h>
#include <clixon/clixon_handle.h> #include <clixon/clixon_handle.h>
#include <clixon/clixon_stream.h>
#include <clixon/clixon_yang.h> #include <clixon/clixon_yang.h>
#include <clixon/clixon_yang_type.h> #include <clixon/clixon_yang_type.h>
#include <clixon/clixon_event.h> #include <clixon/clixon_event.h>

View file

@ -74,4 +74,9 @@ clicon_hash_t *clicon_options(clicon_handle h);
/* Return internal clicon data (hash-array) given a handle.*/ /* Return internal clicon data (hash-array) given a handle.*/
clicon_hash_t *clicon_data(clicon_handle h); clicon_hash_t *clicon_data(clicon_handle h);
/* Return internal stream hash-array given a handle.*/
struct event_stream *clicon_stream(clicon_handle h);
struct event_stream;
int clicon_stream_append(clicon_handle h, struct event_stream *es);
#endif /* _CLIXON_HANDLE_H_ */ #endif /* _CLIXON_HANDLE_H_ */

View file

@ -274,6 +274,7 @@ int yang_mandatory(yang_stmt *ys);
int yang_config(yang_stmt *ys); int yang_config(yang_stmt *ys);
yang_spec *yang_spec_netconf(clicon_handle h); yang_spec *yang_spec_netconf(clicon_handle h);
yang_spec *yang_spec_main(clicon_handle h); yang_spec *yang_spec_main(clicon_handle h);
int yang_spec_append(clicon_handle h, char *yang_dir, char *yang_module, char *yang_revision);
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi); cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
int yang_key_match(yang_node *yn, char *name); int yang_key_match(yang_node *yn, char *name);

View file

@ -73,7 +73,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \ clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
clixon_xml_db.c clixon_netconf_lib.c clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \

View file

@ -53,6 +53,8 @@
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_stream.h"
#include "clixon_options.h" #include "clixon_options.h"
#define CLICON_MAGIC 0x99aafabe #define CLICON_MAGIC 0x99aafabe
@ -65,9 +67,10 @@
* @see struct backend_handle * @see struct backend_handle
*/ */
struct clicon_handle { struct clicon_handle {
int ch_magic; /* magic (HDR) */ int ch_magic; /* magic (HDR) */
clicon_hash_t *ch_copt; /* clicon option list (HDR) */ clicon_hash_t *ch_copt; /* clicon option list (HDR) */
clicon_hash_t *ch_data; /* internal clicon data (HDR) */ clicon_hash_t *ch_data; /* internal clicon data (HDR) Unclear why two? */
event_stream_t *ch_stream; /* notification streams, see clixon_stream.[ch] */
}; };
/*! Internal call to allocate a CLICON handle. /*! Internal call to allocate a CLICON handle.
@ -133,6 +136,7 @@ clicon_handle_exit(clicon_handle h)
hash_free(copt); hash_free(copt);
if ((data = clicon_data(h)) != NULL) if ((data = clicon_data(h)) != NULL)
hash_free(data); hash_free(data);
stream_free(clicon_stream(h));
free(ch); free(ch);
return 0; return 0;
} }
@ -172,3 +176,26 @@ clicon_data(clicon_handle h)
return ch->ch_data; return ch->ch_data;
} }
/*! Return stream hash-array given a clicon handle.
* @param[in] h Clicon handle
*/
event_stream_t *
clicon_stream(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
return ch->ch_stream;
}
int
clicon_stream_append(clicon_handle h,
event_stream_t *es)
{
struct clicon_handle *ch = handle(h);
event_stream_t **ep;
for (ep = &ch->ch_stream; (*ep); ep=&(*ep)->es_next);
*ep = es;
return 0;
}

View file

@ -1914,7 +1914,7 @@ ys_schemanode_check(yang_stmt *ys,
* @param[in] yang_dir Directory where all YANG module files reside (except mainfile) * @param[in] yang_dir Directory where all YANG module files reside (except mainfile)
* @param[in] mainmod Name of main YANG module. Or absolute file name. * @param[in] mainmod Name of main YANG module. Or absolute file name.
* @param[in] revision Main module revision date string or NULL * @param[in] revision Main module revision date string or NULL
* @param[out] ysp Yang specification. Should ave been created by caller using yspec_new * @param[out] ysp Yang specification. Should have been created by caller using yspec_new
* @retval 0 Everything OK * @retval 0 Everything OK
* @retval -1 Error encountered * @retval -1 Error encountered
* The database symbols are inserted in alphabetical order. * The database symbols are inserted in alphabetical order.
@ -2388,7 +2388,9 @@ yang_config(yang_stmt *ys)
return 1; return 1;
} }
/*! Parse netconf yang spec, used by netconf client and as internal protocol */ /*! Parse netconf yang spec, used by netconf client and as internal protocol
* @note not used yet - unfinnisshed code
*/
yang_spec * yang_spec *
yang_spec_netconf(clicon_handle h) yang_spec_netconf(clicon_handle h)
{ {
@ -2405,18 +2407,16 @@ yang_spec_netconf(clicon_handle h)
return yspec; return yspec;
} }
/*! Read, parse and save application yang specification as option /*! Parse yang specification and its dependencies recursively and return
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] f file to print to (if printspec enabled)
* @param[in] printspec print database (YANG) specification as read from file
*/ */
yang_spec* yang_spec*
yang_spec_main(clicon_handle h) yang_spec_main(clicon_handle h)
{ {
yang_spec *yspec = NULL; yang_spec *yspec = NULL;
char *yang_dir;
char *yang_module; char *yang_module;
char *yang_revision; char *yang_revision;
char *yang_dir;
if ((yang_dir = clicon_yang_dir(h)) == NULL){ if ((yang_dir = clicon_yang_dir(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set"); clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set");
@ -2439,6 +2439,46 @@ yang_spec_main(clicon_handle h)
return yspec; return yspec;
} }
/*! Parse yang specification, its dependencies, and append to default yang spec
* yang_spec_main should have been called first.
* @param[in] h clicon handle
* @param[in] yang_dir Directory, either system-wide or application-specific
* @param[in] yang_module Name of module
* @param[in] yang_revision Revision, or NULL
* @see yang_spec_main
*/
int
yang_spec_append(clicon_handle h,
char *yang_dir,
char *yang_module,
char *yang_revision)
{
int retval = -1;
yang_spec *yspec;
yang_spec *yspec0;
yang_stmt *ym = NULL; /* module */
if ((yspec0 = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "yang spec not found");
goto done;
}
if ((yspec = yspec_new()) == NULL)
goto done;
if (yang_parse(h, yang_dir, yang_module, yang_revision, yspec) < 0){
yspec_free(yspec); yspec = NULL;
goto done;
}
while ((ym = yn_each((yang_node*)yspec, ym)) != NULL)
if (yn_insert((yang_node*)yspec0, ym) < 0)
goto done;
yspec->yp_len = 0;
retval = 0;
done:
if (yspec)
yspec_free(yspec);
return retval;
}
/*! Given a yang node, translate the argument string to a cv vector /*! Given a yang node, translate the argument string to a cv vector
* *
* @param[in] ys Yang statement * @param[in] ys Yang statement

View file

@ -485,6 +485,7 @@ body_stmt : extension_stmt { clicon_debug(2,"body-stmt -> extension-st
| data_def_stmt { clicon_debug(2,"body-stmt -> data-def-stmt");} | data_def_stmt { clicon_debug(2,"body-stmt -> data-def-stmt");}
| augment_stmt { clicon_debug(2,"body-stmt -> augment-stmt");} | augment_stmt { clicon_debug(2,"body-stmt -> augment-stmt");}
| rpc_stmt { clicon_debug(2,"body-stmt -> rpc-stmt");} | rpc_stmt { clicon_debug(2,"body-stmt -> rpc-stmt");}
| notification_stmt { clicon_debug(2,"body-stmt -> notification-stmt");}
; ;
data_def_stmt : container_stmt { clicon_debug(2,"data-def-stmt -> container-stmt");} data_def_stmt : container_stmt { clicon_debug(2,"data-def-stmt -> container-stmt");}
@ -521,7 +522,8 @@ container_substmt : when_stmt { clicon_debug(2,"container-substmt -> when-
| reference_stmt { clicon_debug(2,"container-substmt -> reference-stmt"); } | reference_stmt { clicon_debug(2,"container-substmt -> reference-stmt"); }
| typedef_stmt { clicon_debug(2,"container-substmt -> typedef-stmt"); } | typedef_stmt { clicon_debug(2,"container-substmt -> typedef-stmt"); }
| grouping_stmt { clicon_debug(2,"container-substmt -> grouping-stmt"); } | grouping_stmt { clicon_debug(2,"container-substmt -> grouping-stmt"); }
| data_def_stmt { clicon_debug(2,"container-substmt -> data-def-stmt");} | data_def_stmt { clicon_debug(2,"container-substmt -> data-def-stmt");}
| notification_stmt { clicon_debug(2,"container-substmt -> notification-stmt");}
| unknown_stmt { clicon_debug(2,"container-substmt -> unknown-stmt");} | unknown_stmt { clicon_debug(2,"container-substmt -> unknown-stmt");}
| { clicon_debug(2,"container-substmt ->");} | { clicon_debug(2,"container-substmt ->");}
; ;
@ -619,6 +621,7 @@ list_substmt : when_stmt { clicon_debug(2,"list-substmt -> when-stmt
| typedef_stmt { clicon_debug(2,"list-substmt -> typedef-stmt"); } | typedef_stmt { clicon_debug(2,"list-substmt -> typedef-stmt"); }
| grouping_stmt { clicon_debug(2,"list-substmt -> grouping-stmt"); } | grouping_stmt { clicon_debug(2,"list-substmt -> grouping-stmt"); }
| data_def_stmt { clicon_debug(2,"list-substmt -> data-def-stmt"); } | data_def_stmt { clicon_debug(2,"list-substmt -> data-def-stmt"); }
| notification_stmt { clicon_debug(2,"list-substmt -> notification-stmt"); }
| unknown_stmt { clicon_debug(2,"list-substmt -> unknown-stmt");} | unknown_stmt { clicon_debug(2,"list-substmt -> unknown-stmt");}
| { clicon_debug(2,"list-substmt -> "); } | { clicon_debug(2,"list-substmt -> "); }
; ;
@ -792,7 +795,8 @@ augment_substmt : when_stmt { clicon_debug(2,"augment-substmt -> when-s
| description_stmt { clicon_debug(2,"augment-substmt -> description-stmt"); } | description_stmt { clicon_debug(2,"augment-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"augment-substmt -> reference-stmt"); } | reference_stmt { clicon_debug(2,"augment-substmt -> reference-stmt"); }
| data_def_stmt { clicon_debug(2,"augment-substmt -> data-def-stmt"); } | data_def_stmt { clicon_debug(2,"augment-substmt -> data-def-stmt"); }
| case_stmt { clicon_debug(2,"augment-substmt -> case-stmt");} | case_stmt { clicon_debug(2,"augment-substmt -> case-stmt");}
| notification_stmt { clicon_debug(2,"augment-substmt -> notification-stmt");}
| { clicon_debug(2,"augment-substmt -> "); } | { clicon_debug(2,"augment-substmt -> "); }
; ;
@ -875,6 +879,34 @@ output_stmt : K_OUTPUT /* XXX reuse input-substatements since they are same */
; ;
/* notification */
notification_stmt : K_NOTIFICATION id_arg_str ';'
{ if (ysp_add(_yy, Y_NOTIFICATION, $2, NULL) == NULL) _YYERROR("46");
clicon_debug(2,"notification-stmt -> NOTIFICATION id-arg-str ;"); }
| K_NOTIFICATION id_arg_str
{ if (ysp_add_push(_yy, Y_NOTIFICATION, $2) == NULL) _YYERROR("47"); }
'{' notification_substmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("48");
clicon_debug(2,"notification-stmt -> NOTIFICATION id-arg-str { notification-substmts }"); }
;
notification_substmts : notification_substmts notification_substmt
{ clicon_debug(2,"notification-substmts -> notification-substmts notification-substmt"); }
| notification_substmt
{ clicon_debug(2,"notification-substmts -> notification-substmt"); }
;
notification_substmt : if_feature_stmt { clicon_debug(2,"notification-substmt -> if-feature-stmt"); }
| must_stmt { clicon_debug(2,"notification-substmt -> must-stmt"); }
| status_stmt { clicon_debug(2,"notification-substmt -> status-stmt"); }
| description_stmt { clicon_debug(2,"notification-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"notification-substmt -> reference-stmt"); }
| typedef_stmt { clicon_debug(2,"notification-substmt -> typedef-stmt"); }
| grouping_stmt { clicon_debug(2,"notification-substmt -> grouping-stmt"); }
| data_def_stmt { clicon_debug(2,"notification-substmt -> data-def-stmt"); }
| { clicon_debug(2,"notification-substmt -> "); }
;
/* Typedef */ /* Typedef */
typedef_stmt : K_TYPEDEF id_arg_str typedef_stmt : K_TYPEDEF id_arg_str
{ if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("46"); } { if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("46"); }
@ -964,6 +996,7 @@ grouping_substmt : status_stmt { clicon_debug(2,"grouping-substmt -> st
| typedef_stmt { clicon_debug(2,"grouping-substmt -> typedef-stmt"); } | typedef_stmt { clicon_debug(2,"grouping-substmt -> typedef-stmt"); }
| grouping_stmt { clicon_debug(2,"grouping-substmt -> grouping-stmt"); } | grouping_stmt { clicon_debug(2,"grouping-substmt -> grouping-stmt"); }
| data_def_stmt { clicon_debug(2,"grouping-substmt -> data-def-stmt"); } | data_def_stmt { clicon_debug(2,"grouping-substmt -> data-def-stmt"); }
| notification_stmt { clicon_debug(2,"grouping-substmt -> notification-stmt"); }
| { clicon_debug(2,"grouping-substmt -> "); } | { clicon_debug(2,"grouping-substmt -> "); }
; ;

117
test/test_event.sh Executable file
View file

@ -0,0 +1,117 @@
#!/bin/bash
# Restconf basic functionality
# Assume http server setup, such as nginx described in apps/restconf/README.md
APPNAME=example
# include err() and new() functions and creates $dir
. ./lib.sh
cfg=$dir/conf.xml
fyang=$dir/restconf.yang
xml=$dir/xml.xml
# <CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
cat <<EOF > $cfg
<config>
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>$fyang</CLICON_YANG_MODULE_MAIN>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>$dir/restconf.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
</config>
EOF
# RFC5277 NETCONF Event Notifications
cat <<EOF > $fyang
module example {
namespace "http://example.com/event/1.0";
prefix ex;
organization "Example, Inc.";
contact "support at example.com";
description "Example Notification Data Model Module.";
revision "2016-07-07" {
description "Initial version.";
reference "example.com document 2-9976.";
}
notification event {
description "Example notification event.";
leaf event-class {
type string;
description "Event class identifier.";
}
container reporting-entity {
description "Event specific information.";
leaf card {
type string;
description "Line card identifier.";
}
}
leaf severity {
type string;
description "Event severity description.";
}
}
}
EOF
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg -y $fyang"
sudo $clixon_backend -s init -f $cfg -y $fyang -D 1
if [ $? -ne 0 ]; then
err
fi
new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
new "start restconf daemon"
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg # -D 1
sleep 1
new "restconf tests"
# get the stream list using netconf
new "netconf event stream discovery RFC5277 Sec 3.2.5"
echo "$clixon_netconf -qf $cfg -y $fyang"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" selector="netconf/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="subtree"><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams/></netconf></filter></get></rpc>]]>]]>' '<rpc-reply><data><netconf><streams><stream><name>NETCONF</name>'
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><get><filter type="subtree"><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams/></netconf></filter></get></rpc>]]>]]>' '<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><data><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams><stream><name>NETCONF</name>'
# get the stream list using restconf
new "restconf GET datastore"
#expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
new "netconf subscription"
#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"
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
rm -rf $dir

View file

@ -45,6 +45,7 @@ YANGSPECS += ietf-netconf@2011-06-01.yang
YANGSPECS += ietf-netconf-acm@2018-02-14.yang YANGSPECS += ietf-netconf-acm@2018-02-14.yang
YANGSPECS += ietf-inet-types@2013-07-15.yang YANGSPECS += ietf-inet-types@2013-07-15.yang
YANGSPECS += ietf-yang-types@2013-07-15.yang YANGSPECS += ietf-yang-types@2013-07-15.yang
YANGSPECS += ietf-restconf-monitoring@2017-01-26.yang
APPNAME = clixon # subdir ehere these files are installed APPNAME = clixon # subdir ehere these files are installed

View file

@ -0,0 +1,149 @@
module ietf-restconf-monitoring {
namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring";
prefix "rcmon";
import ietf-yang-types { prefix yang; }
import ietf-inet-types { prefix inet; }
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
Author: Andy Bierman
<mailto:andy@yumaworks.com>
Author: Martin Bjorklund
<mailto:mbj@tail-f.com>
Author: Kent Watsen
<mailto:kwatsen@juniper.net>";
description
"This module contains monitoring information for the
RESTCONF protocol.
Copyright (c) 2017 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8040; see
the RFC itself for full legal notices.";
revision 2017-01-26 {
description
"Initial revision.";
reference
"RFC 8040: RESTCONF Protocol.";
}
container restconf-state {
config false;
description
"Contains RESTCONF protocol monitoring information.";
container capabilities {
description
"Contains a list of protocol capability URIs.";
leaf-list capability {
type inet:uri;
description
"A RESTCONF protocol capability URI.";
}
}
container streams {
description
"Container representing the notification event streams
supported by the server.";
reference
"RFC 5277, Section 3.4, <streams> element.";
list stream {
key name;
description
"Each entry describes an event stream supported by
the server.";
leaf name {
type string;
description
"The stream name.";
reference
"RFC 5277, Section 3.4, <name> element.";
}
leaf description {
type string;
description
"Description of stream content.";
reference
"RFC 5277, Section 3.4, <description> element.";
}
leaf replay-support {
type boolean;
default false;
description
"Indicates if replay buffer is supported for this stream.
If 'true', then the server MUST support the 'start-time'
and 'stop-time' query parameters for this stream.";
reference
"RFC 5277, Section 3.4, <replaySupport> element.";
}
leaf replay-log-creation-time {
when "../replay-support" {
description
"Only present if notification replay is supported.";
}
type yang:date-and-time;
description
"Indicates the time the replay log for this stream
was created.";
reference
"RFC 5277, Section 3.4, <replayLogCreationTime>
element.";
}
list access {
key encoding;
min-elements 1;
description
"The server will create an entry in this list for each
encoding format that is supported for this stream.
The media type 'text/event-stream' is expected
for all event streams. This list identifies the
subtypes supported for this stream.";
leaf encoding {
type string;
description
"This is the secondary encoding format within the
'text/event-stream' encoding used by all streams.
The type 'xml' is supported for XML encoding.
The type 'json' is supported for JSON encoding.";
}
leaf location {
type inet:uri;
mandatory true;
description
"Contains a URL that represents the entry point
for establishing notification delivery via
server-sent events.";
}
}
}
}
}
}