diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a46eab7..4d7ce430 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,14 +5,23 @@
### Major New features
### API changes on existing features (you may need to change your code)
-* Notification event streams enhancements
+* Limited support of RFC 7895 YANG Module Library to list modules:
+ * That is, limited support of: ietf-yang-library.yang
+ * For example: `exampleietf-restconf-monitoring2017-01-26...`
+* Notification event stream enhancements
* Yang 1.1 notification support
- * Event stream discovery support according to RFC 5277 Sec 3.2.5.1 (netconf) and RFC 8040 (restconf)
+ * Event stream discovery support according to RFC 5277 Sec 3.2.5.1
+ * That is, support of ietf-restconf-monitoring.yang (mimics schema in 3.2.5.1)
+ * Event stream discovery support according to RFC 8040 (restconf)
+ * That is, support of ietf-netconf-notification.yang
* clixon_restconf and clixon_netconf now take -D as command-line option instead of just -D
* This aligns to clixon_cli and clixon_backend
* Application command option -S to clixon_netconf is obsolete. Use `clixon_netconf -l s` instead.
### Minor changes
+* Comply to RFC 8040 3.5.3.1 rule: api-identifier = [module-name ":"] identifier
+ * The "module-name" was a no-op before.
+ * This means that there was no difference between eg: GET /restconf/data/ietf-yang-library:modules-state and GET /restconf/data/XXXX:modules-state
* Unified log handling for all clicon applications using -l e|o|s|f.
* The options stand for e:stderr, o:stdout, s: syslog, f:file
* Added file logging (`-l f` or `-l f`) for cases where neither syslog nor stderr is useful.
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 71714e68..bede591f 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -261,6 +261,11 @@ from_client_get_config(clicon_handle h,
return retval;
}
+/*! Get streams state according to RFC 5277 and RCC 8040
+ * @retval -1 Error (fatal)
+ * @retval 0 OK
+ * @retval 1 Statedata callback failed
+ */
static int
client_get_streams(clicon_handle h,
char *xpath,
@@ -289,9 +294,18 @@ client_get_streams(clicon_handle h,
if (stream_get_xml(h, cb) < 0)
goto done;
cprintf(cb,"");
- /* XXX. yspec */
- if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0)
+ cprintf(cb,"");
+ if (stream_get_xml(h, cb) < 0)
goto done;
+ cprintf(cb,"");
+
+ /* XXX. yspec */
+ if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){
+ if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0)
+ goto done;
+ retval = 1;
+ goto done;
+ }
if (xml_merge(*xtop, x, yspec, &reason) < 0)
goto done;
if (reason){
@@ -299,11 +313,117 @@ client_get_streams(clicon_handle h,
xml_purge(xc);
if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0)
goto done;
- goto ok;
+ retval = 1;
+ goto done;
}
-#ifdef notyet
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ if (reason)
+ free(reason);
+ if (x)
+ xml_free(x);
+ return retval;
+}
+
+/*! Get modules state according to RFC 7895
+ * @retval -1 Error (fatal)
+ * @retval 0 OK
+ * @retval 1 Statedata callback failed
+ */
+static int
+client_get_modules(clicon_handle h,
+ char *xpath,
+ cxobj **xtop)
+{
+ int retval = -1;
+ cxobj *x = NULL;
+ cxobj *xc;
+ char *reason = NULL;
+ yang_spec *yspec;
+ cbuf *cb;
+ yang_stmt *ymod = NULL;
+ yang_stmt *yrev;
+
+ 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,"");
+ cprintf(cb,"1"); /* NYI */
+
+ while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
+ cprintf(cb,"");
+ cprintf(cb,"%s", ymod->ys_argument);
+ if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
+ cprintf(cb,"%s", yrev->ys_argument);
+ else
+ cprintf(cb,"");
+ cprintf(cb,"");
+ }
+ cprintf(cb,"");
+
+ /* XXX. yspec */
+ if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){
+ if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0)
+ 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;
+ 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
+ * @retval -1 Error (fatal)
+ * @retval 0 OK
+ * @retval 1 Statedata callback failed
+ */
+static int
+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)
+ goto done;
+#if 1
/* Code complex to filter out anything that is outside of xpath */
- if (xpath_vec(*xtop, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
+ if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and
@@ -315,22 +435,17 @@ client_get_streams(clicon_handle h,
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)
+ if (!xml_flag(*xret, XML_FLAG_MARK))
+ if (xml_tree_prune_flagged_sub(*xret, 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)
+ if (xml_apply(*xret, 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);
+ if (xvec)
+ free(xvec);
return retval;
}
@@ -346,31 +461,26 @@ from_client_get(clicon_handle h,
cxobj *xe,
cbuf *cbret)
{
- int retval = -1;
- cxobj *xfilter;
- char *selector = "/";
- cxobj *xret = NULL;
- int ret;
- cbuf *cbx = NULL; /* Assist cbuf */
+ int retval = -1;
+ cxobj *xfilter;
+ char *xpath = "/";
+ cxobj *xret = NULL;
+ int ret;
+ cbuf *cbx = NULL; /* Assist cbuf */
if ((xfilter = xml_find(xe, "filter")) != NULL)
- if ((selector = xml_find_value(xfilter, "select"))==NULL)
- selector="/";
+ if ((xpath = xml_find_value(xfilter, "select"))==NULL)
+ xpath="/";
/* Get config */
- if (xmldb_get(h, "running", selector, 0, &xret) < 0){
+ if (xmldb_get(h, "running", xpath, 0, &xret) < 0){
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
/* 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)
+ if ((ret = client_statedata(h, xpath, &xret)) < 0)
goto done;
if (ret == 0){ /* OK */
cprintf(cbret, "");
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index fd128941..0873fc1c 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -775,6 +775,10 @@ main(int argc,
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;
/* Set options: database dir and yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
goto done;
diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c
index b11e9b67..255b0f1d 100644
--- a/apps/backend/backend_plugin.c
+++ b/apps/backend/backend_plugin.c
@@ -125,11 +125,8 @@ clixon_plugin_statedata(clicon_handle h,
cxobj **xtop)
{
int retval = -1;
- int i;
cxobj *x = NULL;
yang_spec *yspec;
- cxobj **xvec = NULL;
- size_t xlen;
cxobj *xc;
clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */
@@ -168,25 +165,6 @@ clixon_plugin_statedata(clicon_handle h,
x = NULL;
}
}
- /* 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
#include
#include
@@ -615,7 +621,13 @@ main(int argc,
/* Parse yang database spec file */
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;
/* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start
*/
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index acee080c..5b053217 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -620,33 +620,51 @@ yang_find_schemanode(yang_node *yn,
*/
yang_stmt *
yang_find_topnode(yang_spec *ysp,
- char *argument,
+ char *nodeid,
yang_class class)
{
- yang_stmt *ys = NULL;
- yang_stmt *yc = NULL;
+ yang_stmt *ymod = NULL; /* module */
+ yang_stmt *yres = NULL; /* result */
+ char *prefix = NULL;
+ char *id = NULL;
int i;
- for (i=0; iyp_len; i++){
- ys = ysp->yp_stmt[i];
- switch (class){
- case YC_NONE:
- if ((yc = yang_find((yang_node*)ys, 0, argument)) != NULL)
- return yc;
- break;
- case YC_DATANODE:
- if ((yc = yang_find_datanode((yang_node*)ys, argument)) != NULL)
- return yc;
- break;
- case YC_SCHEMANODE:
- if ((yc = yang_find_schemanode((yang_node*)ys, argument)) != NULL)
- return yc;
- break;
- case YC_DATADEFINITION:
- break; /* nyi */
+ if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
+ goto done;
+ if (prefix){
+ if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL){
+ if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
+ goto ok;
+ goto done;
}
}
- return NULL;
+ else /* No prefix given - loop through and find first */
+ for (i=0; iyp_len; i++){
+ ymod = ysp->yp_stmt[i];
+ switch (class){
+ case YC_NONE:
+ if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
+ goto ok;
+ break;
+ case YC_DATANODE:
+ if ((yres = yang_find_datanode((yang_node*)ymod, id)) != NULL)
+ goto ok;
+ break;
+ case YC_SCHEMANODE:
+ if ((yres = yang_find_schemanode((yang_node*)ymod, id)) != NULL)
+ goto ok;
+ break;
+ case YC_DATADEFINITION:
+ break; /* nyi */
+ }
+ }
+ ok:
+ done:
+ if (prefix)
+ free(prefix);
+ if (id)
+ free(id);
+ return yres;
}
/*! Given a yang statement, find the prefix associated to this module
@@ -822,7 +840,6 @@ yarg_prefix(yang_stmt *ys)
return prefix;
}
-
/*! Split yang node identifier into prefix and identifer.
* @param[in] node-id
* @param[out] prefix Malloced string. May be NULL.
diff --git a/test/test_auth.sh b/test/test_auth.sh
index 6837f8e8..d54609a8 100755
--- a/test/test_auth.sh
+++ b/test/test_auth.sh
@@ -148,7 +148,7 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get"
-expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": null}
+expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/x)" 'null
'
new "auth set authentication config"
diff --git a/test/test_auth_ext.sh b/test/test_auth_ext.sh
index c1806891..55d57739 100755
--- a/test/test_auth_ext.sh
+++ b/test/test_auth_ext.sh
@@ -13,6 +13,7 @@ fyang=$dir/test.yang
fyangerr=$dir/err.yang
nacmfile=$dir/nacmfile
+# Note filter out example_backend_nacm.so in CLICON_BACKEND_REGEXP below
cat < $cfg
$cfg
@@ -190,7 +191,7 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get"
-expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/ietf-interfaces:interfaces-state)" '{"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}
'
new "Set x to 0"
diff --git a/test/test_event.sh b/test/test_event.sh
index 8bcd9c46..789dd793 100755
--- a/test/test_event.sh
+++ b/test/test_event.sh
@@ -67,7 +67,7 @@ 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
+sudo $clixon_backend -s init -f $cfg -y $fyang # -D 1
if [ $? -ne 0 ]; then
err
@@ -77,26 +77,21 @@ 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
+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 ']]>]]>' 'NETCONFdefault NETCONF event streamfalseCLICONClicon logsfalse]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONFdefault NETCONF event streamfalseCLICONClicon logsfalse]]>]]>'
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONF'
+new "netconf event stream discovery RFC8040 Sec 6.2"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONFdefault NETCONF event streamfalseCLICONClicon logsfalse]]>]]>'
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'NETCONF'
+new "restconf event stream discovery RFC8040 Sec 6.2"
+expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"streams": {"stream": \[{"name": "CLICON","description": "Clicon logs","replay-support": false},{ "name": "NETCONF","description": "default NETCONF event stream","replay-support": false}\]}}'
-# 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"
+#new "netconf subscription"
#expectwait "$clixon_netconf -qf $cfg -y $fyang" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30
new "Kill restconf daemon"
diff --git a/test/test_list.sh b/test/test_list.sh
index 25e2ee37..ed04b36a 100755
--- a/test/test_list.sh
+++ b/test/test_list.sh
@@ -5,7 +5,7 @@ APPNAME=example
# include err() and new() functions and creates $dir
. ./lib.sh
-exit 0 # NYI
+
cfg=$dir/conf_yang.xml
fyang=$dir/test.yang
@@ -91,6 +91,8 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace]]>]]>" "^]]>]]>$"
+exit # NYI
+
new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$"
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index c2fc32b1..c82948ac 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -135,6 +135,10 @@ if [ -z "$match" ]; then
err "$expect" "$ret"
fi
+new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895"
+expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"module": [{"name": "ietf-routing","revision": "2014-10-26"}]}
+
'
+
new "restconf options. RFC 8040 4.1"
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" 0 "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
@@ -142,16 +146,23 @@ new "restconf head. RFC 8040 4.2"
expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json"
-new2 "restconf empty rpc"
+new "restconf empty rpc"
expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/example:empty)" ""
new2 "restconf get empty config + state json"
-expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+expecteq "$(curl -sSG http://localhost/restconf/data/interfaces-state)" '{"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}
'
+new2 "restconf get empty config + state json with module name"
+expecteq "$(curl -sSG http://localhost/restconf/data/ietf-interfaces:interfaces-state)" '{"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}
+
'
+
+new2 "restconf get empty config + state json with wrong module name"
+expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:interfaces-state)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "No yang node found: badmodule:interfaces-state"}}}}
'
+
new "restconf get empty config + state xml"
-ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data)
-expect="eth0ex:eth42"
+ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/interfaces-state)
+expect="eth0ex:eth42"
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -184,7 +195,7 @@ if [ -z "$match" ]; then
fi
new2 "restconf GET datastore"
-expecteq "$(curl -s -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+expecteq "$(curl -s -X GET http://localhost/restconf/data/interfaces-state)" '{"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}
'
# Exact match
@@ -201,19 +212,23 @@ new "restconf Check interfaces eth/0/0 added"
expectfn "curl -s -G http://localhost/restconf/data" 0 '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "ex:eth","if-index": 42}\]}}
'
-new2 "restconf delete interfaces"
+new "restconf delete interfaces"
expecteq $(curl -s -X DELETE http://localhost/restconf/data/interfaces) ""
new "restconf Check empty config"
-expectfn "curl -sG http://localhost/restconf/data" 0 "$state"
+expectfn "curl -sG http://localhost/restconf/data/interfaces-state" 0 "$state"
new "restconf Add interfaces subtree eth/0/0 using POST"
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/interfaces' 0 ""
# XXX cant get this to work
#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" ""
-new2 "restconf Check eth/0/0 added"
-expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+new2 "restconf Check eth/0/0 added config"
+expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
+
'
+
+new2 "restconf Check eth/0/0 added state"
+expecteq "$(curl -s -G http://localhost/restconf/data/interfaces-state)" '{"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}
'
new2 "restconf Re-post eth/0/0 which should generate error"
@@ -226,7 +241,7 @@ new "Add nothing using POST"
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:'
new2 "restconf Check description added"
-expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}}
'
new "restconf delete eth/0/0"
@@ -242,7 +257,7 @@ new "restconf Add subtree eth/0/0 using PUT"
expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
new2 "restconf get subtree"
-expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "ex:eth","if-index": 42}]}}}
+expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
'
new2 "restconf rpc using POST json"
diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh
index 1065cbee..6780b6d2 100755
--- a/test/test_restconf2.sh
+++ b/test/test_restconf2.sh
@@ -69,8 +69,8 @@ new "restconf tests"
new "restconf POST initial tree"
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
-new "restconf GET datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
+new "restconf GET datastore intial"
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
new "restconf GET interface"
expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" 0 '{"interface": \[{"name": "local0","type": "regular"}\]}'
@@ -94,31 +94,31 @@ new "restconf DELETE"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": null}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
new "restconf POST initial tree"
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
new "restconf GET initial tree"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
new "restconf DELETE whole datastore"
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": null}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
new "restconf PUT initial datastore"
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
new "restconf GET datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
new "restconf PUT replace datastore"
expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
new "restconf GET replaced datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont2": {"name": "foo"}}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont2" 0 '{"cont2": {"name": "foo"}}'
new "restconf PUT initial datastore again"
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
@@ -126,9 +126,8 @@ expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type
new "restconf PUT change interface"
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' 0 ""
-
new "restconf GET datastore atm"
-expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}'
new "restconf PUT add interface"
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 ""
diff --git a/test/test_yang.sh b/test/test_yang.sh
index 3b59cfa7..94f56dc1 100755
--- a/test/test_yang.sh
+++ b/test/test_yang.sh
@@ -117,6 +117,9 @@ new "cli not defined extension"
# This text yields an error, but the test cannot detect the error message yet
#expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found"
+new "netconf schema resource, RFC 7895"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' 'ietf-yang-library2016-06-21'
+
new "netconf edit config"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "125one]]>]]>" "^]]>]]>$"
@@ -140,7 +143,7 @@ new "netconf get leaf-list path"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^hejhopp]]>]]>$"
new "netconf get (should be some)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^125one]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^125one"
new "cli set leaf-list"
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
diff --git a/yang/Makefile.in b/yang/Makefile.in
index d5c0c7ea..ae2a69b2 100644
--- a/yang/Makefile.in
+++ b/yang/Makefile.in
@@ -46,6 +46,8 @@ 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
+YANGSPECS += ietf-netconf-notification@2008-07-01.yang
+YANGSPECS += ietf-yang-library@2016-06-21.yang
APPNAME = clixon # subdir ehere these files are installed
diff --git a/yang/ietf-netconf-monitoring@2010-10-04.yang b/yang/ietf-netconf-monitoring@2010-10-04.yang
new file mode 100644
index 00000000..38a92d06
--- /dev/null
+++ b/yang/ietf-netconf-monitoring@2010-10-04.yang
@@ -0,0 +1,555 @@
+module ietf-netconf-monitoring {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+ prefix "ncm";
+
+ import ietf-yang-types { prefix yang; }
+ import ietf-inet-types { prefix inet; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ WG Chair: Mehmet Ersue
+
+
+ WG Chair: Bert Wijnen
+
+
+ Editor: Mark Scott
+
+
+ Editor: Martin Bjorklund
+ ";
+
+ description
+ "NETCONF Monitoring Module.
+ All elements in this module are read-only.
+
+ Copyright (c) 2010 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 6022; see
+ the RFC itself for full legal notices.";
+
+ revision 2010-10-04 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6022: YANG Module for NETCONF Monitoring";
+ }
+
+ typedef netconf-datastore-type {
+ type enumeration {
+ enum running;
+ enum candidate;
+ enum startup;
+ }
+ description
+ "Enumeration of possible NETCONF datastore types.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+ }
+
+ identity transport {
+ description
+ "Base identity for NETCONF transport types.";
+ }
+
+ identity netconf-ssh {
+ base transport;
+ description
+ "NETCONF over Secure Shell (SSH).";
+ reference
+ "RFC 4742: Using the NETCONF Configuration Protocol
+ over Secure SHell (SSH)";
+ }
+
+ identity netconf-soap-over-beep {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP) over
+ Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+ }
+
+ identity netconf-soap-over-https {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP)
+ over Hypertext Transfer Protocol Secure (HTTPS).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+ }
+
+ identity netconf-beep {
+ base transport;
+ description
+ "NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4744: Using the NETCONF Protocol over the
+ Blocks Extensible Exchange Protocol (BEEP)";
+ }
+
+ identity netconf-tls {
+ base transport;
+ description
+ "NETCONF over Transport Layer Security (TLS).";
+ reference
+ "RFC 5539: NETCONF over Transport Layer Security (TLS)";
+ }
+
+ identity schema-format {
+ description
+ "Base identity for data model schema languages.";
+ }
+
+ identity xsd {
+ base schema-format;
+ description
+ "W3C XML Schema Definition.";
+ reference
+ "W3C REC REC-xmlschema-1-20041028:
+ XML Schema Part 1: Structures";
+ }
+
+ identity yang {
+ base schema-format;
+ description
+ "The YANG data modeling language for NETCONF.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+ }
+
+ identity yin {
+ base schema-format;
+ description
+ "The YIN syntax for YANG.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+ }
+
+ identity rng {
+ base schema-format;
+ description
+ "Regular Language for XML Next Generation (RELAX NG).";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+ }
+
+ identity rnc {
+ base schema-format;
+ description
+ "Relax NG Compact Syntax";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+ }
+
+ grouping common-counters {
+ description
+ "Counters that exist both per session, and also globally,
+ accumulated from all sessions.";
+
+ leaf in-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of correct messages received.";
+ }
+ leaf in-bad-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of messages received when an message was expected,
+ that were not correct messages. This includes XML parse
+ errors and errors on the rpc layer.";
+ }
+ leaf out-rpc-errors {
+ type yang:zero-based-counter32;
+ description
+ "Number of messages sent that contained an
+ element.";
+ }
+ leaf out-notifications {
+ type yang:zero-based-counter32;
+ description
+ "Number of messages sent.";
+ }
+ }
+
+ container netconf-state {
+ config false;
+ description
+ "The netconf-state container is the root of the monitoring
+ data model.";
+
+ container capabilities {
+ description
+ "Contains the list of NETCONF capabilities supported by the
+ server.";
+
+ leaf-list capability {
+ type inet:uri;
+ description
+ "List of NETCONF capabilities supported by the server.";
+ }
+ }
+
+ container datastores {
+ description
+ "Contains the list of NETCONF configuration datastores.";
+
+ list datastore {
+ key name;
+ description
+ "List of NETCONF configuration datastores supported by
+ the NETCONF server and related information.";
+
+ leaf name {
+ type netconf-datastore-type;
+ description
+ "Name of the datastore associated with this list entry.";
+ }
+ container locks {
+ presence
+ "This container is present only if the datastore
+ is locked.";
+ description
+ "The NETCONF and operations allow
+ a client to lock specific resources in a datastore. The
+ NETCONF server will prevent changes to the locked
+ resources by all sessions except the one that acquired
+ the lock(s).
+
+ Monitoring information is provided for each datastore
+ entry including details such as the session that acquired
+ the lock, the type of lock (global or partial) and the
+ list of locked resources. Multiple locks per datastore
+ are supported.";
+
+ grouping lock-info {
+ description
+ "Lock related parameters, common to both global and
+ partial locks.";
+
+ leaf locked-by-session {
+ type uint32;
+ mandatory true;
+ description
+ "The session ID of the session that has locked
+ this resource. Both a global lock and a partial
+ lock MUST contain the NETCONF session-id.
+
+ If the lock is held by a session that is not managed
+ by the NETCONF server (e.g., a CLI session), a session
+ id of 0 (zero) is reported.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+ }
+ leaf locked-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The date and time of when the resource was
+ locked.";
+ }
+ }
+ choice lock-type {
+ description
+ "Indicates if a global lock or a set of partial locks
+ are set.";
+
+ container global-lock {
+ description
+ "Present if the global lock is set.";
+ uses lock-info;
+ }
+
+ list partial-lock {
+ key lock-id;
+ description
+ "List of partial locks.";
+ reference
+ "RFC 5717: Partial Lock Remote Procedure Call (RPC) for
+ NETCONF";
+
+ leaf lock-id {
+ type uint32;
+ description
+ "This is the lock id returned in the
+ response.";
+ }
+ uses lock-info;
+ leaf-list select {
+ type yang:xpath1.0;
+ min-elements 1;
+ description
+ "The xpath expression that was used to request
+ the lock. The select expression indicates the
+ original intended scope of the lock.";
+ }
+ leaf-list locked-node {
+ type instance-identifier;
+ description
+ "The list of instance-identifiers (i.e., the
+ locked nodes).
+
+ The scope of the partial lock is defined by the list
+ of locked nodes.";
+ }
+ }
+ }
+ }
+ }
+ }
+ container schemas {
+ description
+ "Contains the list of data model schemas supported by the
+ server.";
+
+ list schema {
+ key "identifier version format";
+
+ description
+ "List of data model schemas supported by the server.";
+
+ leaf identifier {
+ type string;
+ description
+ "Identifier to uniquely reference the schema. The
+ identifier is used in the operation and may
+ be used for other purposes such as file retrieval.
+
+ For modeling languages that support or require a data
+ model name (e.g., YANG module name) the identifier MUST
+ match that name. For YANG data models, the identifier is
+ the name of the module or submodule. In other cases, an
+ identifier such as a filename MAY be used instead.";
+ }
+ leaf version {
+ type string;
+ description
+ "Version of the schema supported. Multiple versions MAY be
+ supported simultaneously by a NETCONF server. Each
+ version MUST be reported individually in the schema list,
+ i.e., with same identifier, possibly different location,
+ but different version.
+
+ For YANG data models, version is the value of the most
+ recent YANG 'revision' statement in the module or
+ submodule, or the empty string if no 'revision' statement
+ is present.";
+ }
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language the schema is written
+ in (currently xsd, yang, yin, rng, or rnc).
+ For YANG data models, 'yang' format MUST be supported and
+ 'yin' format MAY also be provided.";
+ }
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ description
+ "The XML namespace defined by the data model.
+
+ For YANG data models, this is the module's namespace.
+ If the list entry describes a submodule, this field
+ contains the namespace of the module to which the
+ submodule belongs.";
+ }
+ leaf-list location {
+ type union {
+ type enumeration {
+ enum "NETCONF";
+ }
+ type inet:uri;
+ }
+ description
+ "One or more locations from which the schema can be
+ retrieved. This list SHOULD contain at least one
+ entry per schema.
+
+ A schema entry may be located on a remote file system
+ (e.g., reference to file system for ftp retrieval) or
+ retrieved directly from a server supporting the
+ operation (denoted by the value 'NETCONF').";
+ }
+ }
+ }
+ container sessions {
+ description
+ "The sessions container includes session-specific data for
+ NETCONF management sessions. The session list MUST include
+ all currently active NETCONF sessions.";
+
+ list session {
+ key session-id;
+ description
+ "All NETCONF sessions managed by the NETCONF server
+ MUST be reported in this list.";
+
+ leaf session-id {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "Unique identifier for the session. This value is the
+ NETCONF session identifier, as defined in RFC 4741.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+ }
+ leaf transport {
+ type identityref {
+ base transport;
+ }
+ mandatory true;
+ description
+ "Identifies the transport for each session, e.g.,
+ 'netconf-ssh', 'netconf-soap', etc.";
+ }
+ leaf username {
+ type string;
+ mandatory true;
+ description
+ "The username is the client identity that was authenticated
+ by the NETCONF transport protocol. The algorithm used to
+ derive the username is NETCONF transport protocol specific
+ and in addition specific to the authentication mechanism
+ used by the NETCONF transport protocol.";
+ }
+ leaf source-host {
+ type inet:host;
+ description
+ "Host identifier of the NETCONF client. The value
+ returned is implementation specific (e.g., hostname,
+ IPv4 address, IPv6 address)";
+ }
+ leaf login-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "Time at the server at which the session was established.";
+ }
+ uses common-counters {
+ description
+ "Per-session counters. Zero based with following reset
+ behaviour:
+ - at start of a session
+ - when max value is reached";
+ }
+ }
+ }
+
+ container statistics {
+ description
+ "Statistical data pertaining to the NETCONF server.";
+
+ leaf netconf-start-time {
+ type yang:date-and-time;
+ description
+ "Date and time at which the management subsystem was
+ started.";
+ }
+ leaf in-bad-hellos {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions silently dropped because an
+ invalid message was received. This includes
+ messages with a 'session-id' attribute, bad namespace, and
+ bad capability declarations.";
+ }
+ leaf in-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions started. This counter is incremented
+ when a message with a is sent.
+
+ 'in-sessions' - 'in-bad-hellos' =
+ 'number of correctly started netconf sessions'";
+ }
+ leaf dropped-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions that were abnormally terminated, e.g.,
+ due to idle timeout or transport close. This counter is not
+ incremented when a session is properly closed by a
+ operation, or killed by a
+ operation.";
+ }
+ uses common-counters {
+ description
+ "Global counters, accumulated from all sessions.
+ Zero based with following reset behaviour:
+ - re-initialization of NETCONF server
+ - when max value is reached";
+ }
+ }
+ }
+
+ rpc get-schema {
+ description
+ "This operation is used to retrieve a schema from the
+ NETCONF server.
+
+ Positive Response:
+ The NETCONF server returns the requested schema.
+
+ Negative Response:
+ If requested schema does not exist, the is
+ 'invalid-value'.
+
+ If more than one schema matches the requested parameters, the
+ is 'operation-failed', and is
+ 'data-not-unique'.";
+
+ input {
+ leaf identifier {
+ type string;
+ mandatory true;
+ description
+ "Identifier for the schema list entry.";
+ }
+ leaf version {
+ type string;
+ description
+ "Version of the schema requested. If this parameter is not
+ present, and more than one version of the schema exists on
+ the server, a 'data-not-unique' error is returned, as
+ described above.";
+ }
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language of the schema. If this
+ parameter is not present, and more than one formats of
+ the schema exists on the server, a 'data-not-unique' error
+ is returned, as described above.";
+ }
+ }
+ output {
+ anyxml data {
+ description
+ "Contains the schema content.";
+ }
+ }
+ }
+}
diff --git a/yang/ietf-netconf-notification@2008-07-01.yang b/yang/ietf-netconf-notification@2008-07-01.yang
new file mode 100644
index 00000000..1e98e455
--- /dev/null
+++ b/yang/ietf-netconf-notification@2008-07-01.yang
@@ -0,0 +1,94 @@
+module ietf-restconf-monitoring {
+ namespace "urn:ietf:params:xml:ns:netconf:notification:1:0";
+ prefix "rcmon";
+
+ import ietf-yang-types { prefix yang; }
+ import ietf-inet-types { prefix inet; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ description
+ "Note this is a translation from RFC 5277 schema in section 4 to Yang.
+ RFC 5277 is Copyright (C) The IETF Trust (2008).";
+
+ revision 2008-07-01 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 5277: NETCONF Event Notifications.";
+ }
+
+ container netconf {
+ config false;
+ description
+ "Contains NETCONF 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, 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, element.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "Description of stream content.";
+ reference
+ "RFC 5277, Section 3.4, 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, 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,
+ element.";
+ }
+
+ }
+ }
+ }
+}