* 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:
parent
f37fdb954f
commit
0631be19cb
16 changed files with 502 additions and 34 deletions
|
|
@ -5,6 +5,9 @@
|
|||
### Major New features
|
||||
|
||||
### 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
|
||||
* This aligns to clixon_cli and clixon_backend
|
||||
* Application command option -S to clixon_netconf is obsolete. Use `clixon_netconf -l s` instead.
|
||||
|
|
|
|||
|
|
@ -99,20 +99,10 @@ client_subscription_add(struct client_entry *ce,
|
|||
return su;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*! Delete stream subscription from client subscription list */
|
||||
static int
|
||||
client_subscription_delete(struct client_entry *ce,
|
||||
struct client_subscription *su0)
|
||||
struct client_subscription *su0)
|
||||
{
|
||||
struct client_subscription *su;
|
||||
struct client_subscription **su_prev;
|
||||
|
|
@ -134,7 +124,8 @@ client_subscription_delete(struct client_entry *ce,
|
|||
|
||||
#ifdef notused /* xxx */
|
||||
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;
|
||||
|
||||
|
|
@ -146,6 +137,18 @@ client_subscription_find(struct client_entry *ce, char *stream)
|
|||
}
|
||||
#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
|
||||
* Close down everything wrt clients (eg sockets, subscriptions)
|
||||
* Finally actually remove client struct in handle
|
||||
|
|
@ -258,6 +261,79 @@ from_client_get_config(clicon_handle h,
|
|||
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
|
||||
*
|
||||
* @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 */
|
||||
assert(xret);
|
||||
|
||||
#if 1 /* get netconf state data */
|
||||
if ((ret = client_get_streams(h, selector, &xret)) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
clicon_err_reset();
|
||||
if ((ret = clixon_plugin_statedata(h, selector, &xret)) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -756,6 +756,11 @@ main(int argc,
|
|||
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){
|
||||
clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n");
|
||||
goto done;
|
||||
|
|
@ -765,10 +770,11 @@ main(int argc,
|
|||
/* Connect to plugin to get a handle */
|
||||
if (xmldb_connect(h) < 0)
|
||||
goto done;
|
||||
/* Parse db spec file */
|
||||
/* 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;
|
||||
/* Set options: database dir and yangspec (could be hidden in connect?)*/
|
||||
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ struct backend_handle {
|
|||
int bh_magic; /* magic (HDR)*/
|
||||
clicon_hash_t *bh_copt; /* clicon option list (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 ------ */
|
||||
struct client_entry *bh_ce_list; /* The client list */
|
||||
int bh_ce_nr; /* Number of clients, just increment */
|
||||
|
|
|
|||
|
|
@ -76,14 +76,14 @@
|
|||
* @see struct clicon_handle, struct backend_handle
|
||||
*/
|
||||
struct cli_handle {
|
||||
int cl_magic; /* magic (HDR)*/
|
||||
clicon_hash_t *cl_copt; /* clicon option list (HDR) */
|
||||
clicon_hash_t *cl_data; /* internal clicon data (HDR) */
|
||||
int cl_magic; /* magic (HDR)*/
|
||||
clicon_hash_t *cl_copt; /* clicon option list (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 ------ */
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
This implementation uses FastCGI, see http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html.
|
||||
|
||||
Define nginx config file: /etc/nginx/sites-available/default
|
||||
```
|
||||
server {
|
||||
...
|
||||
location /restconf {
|
||||
location / {
|
||||
root /usr/share/nginx/html/restconf;
|
||||
fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
|
||||
include fastcgi_params;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
#include <clixon/clixon_queue.h>
|
||||
#include <clixon/clixon_hash.h>
|
||||
#include <clixon/clixon_handle.h>
|
||||
#include <clixon/clixon_stream.h>
|
||||
#include <clixon/clixon_yang.h>
|
||||
#include <clixon/clixon_yang_type.h>
|
||||
#include <clixon/clixon_event.h>
|
||||
|
|
|
|||
|
|
@ -74,4 +74,9 @@ clicon_hash_t *clicon_options(clicon_handle h);
|
|||
/* Return internal clicon data (hash-array) given a handle.*/
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ int yang_mandatory(yang_stmt *ys);
|
|||
int yang_config(yang_stmt *ys);
|
||||
yang_spec *yang_spec_netconf(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);
|
||||
int yang_key_match(yang_node *yn, char *name);
|
||||
|
||||
|
|
|
|||
|
|
@ -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_proto.c clixon_proto_client.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 \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@
|
|||
#include "clixon_log.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_stream.h"
|
||||
#include "clixon_options.h"
|
||||
|
||||
#define CLICON_MAGIC 0x99aafabe
|
||||
|
|
@ -65,9 +67,10 @@
|
|||
* @see struct backend_handle
|
||||
*/
|
||||
struct clicon_handle {
|
||||
int ch_magic; /* magic (HDR) */
|
||||
clicon_hash_t *ch_copt; /* clicon option list (HDR) */
|
||||
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
|
||||
int ch_magic; /* magic (HDR) */
|
||||
clicon_hash_t *ch_copt; /* clicon option list (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.
|
||||
|
|
@ -133,6 +136,7 @@ clicon_handle_exit(clicon_handle h)
|
|||
hash_free(copt);
|
||||
if ((data = clicon_data(h)) != NULL)
|
||||
hash_free(data);
|
||||
stream_free(clicon_stream(h));
|
||||
free(ch);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -172,3 +176,26 @@ clicon_data(clicon_handle h)
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] mainmod Name of main YANG module. Or absolute file name.
|
||||
* @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 -1 Error encountered
|
||||
* The database symbols are inserted in alphabetical order.
|
||||
|
|
@ -2388,7 +2388,9 @@ yang_config(yang_stmt *ys)
|
|||
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_netconf(clicon_handle h)
|
||||
{
|
||||
|
|
@ -2405,18 +2407,16 @@ yang_spec_netconf(clicon_handle h)
|
|||
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] f file to print to (if printspec enabled)
|
||||
* @param[in] printspec print database (YANG) specification as read from file
|
||||
*/
|
||||
yang_spec*
|
||||
yang_spec_main(clicon_handle h)
|
||||
{
|
||||
yang_spec *yspec = NULL;
|
||||
char *yang_dir;
|
||||
char *yang_module;
|
||||
char *yang_revision;
|
||||
char *yang_dir;
|
||||
|
||||
if ((yang_dir = clicon_yang_dir(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set");
|
||||
|
|
@ -2439,6 +2439,46 @@ yang_spec_main(clicon_handle h)
|
|||
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
|
||||
*
|
||||
* @param[in] ys Yang statement
|
||||
|
|
|
|||
|
|
@ -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");}
|
||||
| augment_stmt { clicon_debug(2,"body-stmt -> augment-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");}
|
||||
|
|
@ -521,7 +522,8 @@ container_substmt : when_stmt { clicon_debug(2,"container-substmt -> when-
|
|||
| reference_stmt { clicon_debug(2,"container-substmt -> reference-stmt"); }
|
||||
| typedef_stmt { clicon_debug(2,"container-substmt -> typedef-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");}
|
||||
| { 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"); }
|
||||
| grouping_stmt { clicon_debug(2,"list-substmt -> grouping-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");}
|
||||
| { 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"); }
|
||||
| reference_stmt { clicon_debug(2,"augment-substmt -> reference-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 -> "); }
|
||||
;
|
||||
|
||||
|
|
@ -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_stmt : K_TYPEDEF id_arg_str
|
||||
{ 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"); }
|
||||
| grouping_stmt { clicon_debug(2,"grouping-substmt -> grouping-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 -> "); }
|
||||
;
|
||||
|
||||
|
|
|
|||
117
test/test_event.sh
Executable file
117
test/test_event.sh
Executable 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
|
||||
|
|
@ -45,6 +45,7 @@ YANGSPECS += ietf-netconf@2011-06-01.yang
|
|||
YANGSPECS += ietf-netconf-acm@2018-02-14.yang
|
||||
YANGSPECS += ietf-inet-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
|
||||
|
||||
|
|
|
|||
149
yang/ietf-restconf-monitoring@2017-01-26.yang
Normal file
149
yang/ietf-restconf-monitoring@2017-01-26.yang
Normal 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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue