* Limited support of RFC 7895 YANG Module Library to list modules:

* That is, limited support of: ietf-yang-library.yang
  * For example: `<module><name>example</name><revision/></module><module><name>ietf-restconf-monitoring</name><revision>2017-01-26</revision></module>...`
* 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
This commit is contained in:
Olof hagsand 2018-09-22 16:30:58 +02:00
parent 0631be19cb
commit 74fc0800ae
18 changed files with 921 additions and 120 deletions

View file

@ -5,14 +5,23 @@
### 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 * Limited support of RFC 7895 YANG Module Library to list modules:
* That is, limited support of: ietf-yang-library.yang
* For example: `<module><name>example</name><revision/></module><module><name>ietf-restconf-monitoring</name><revision>2017-01-26</revision></module>...`
* Notification event stream enhancements
* Yang 1.1 notification support * 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 <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.
### Minor changes ### 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<file>. * Unified log handling for all clicon applications using -l e|o|s|f<file>.
* The options stand for e:stderr, o:stdout, s: syslog, f:file * The options stand for e:stderr, o:stdout, s: syslog, f:file
* Added file logging (`-l f` or `-l f<file>`) for cases where neither syslog nor stderr is useful. * Added file logging (`-l f` or `-l f<file>`) for cases where neither syslog nor stderr is useful.

View file

@ -261,6 +261,11 @@ from_client_get_config(clicon_handle h,
return retval; 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 static int
client_get_streams(clicon_handle h, client_get_streams(clicon_handle h,
char *xpath, char *xpath,
@ -289,9 +294,18 @@ client_get_streams(clicon_handle h,
if (stream_get_xml(h, cb) < 0) if (stream_get_xml(h, cb) < 0)
goto done; goto done;
cprintf(cb,"</restconf-state>"); cprintf(cb,"</restconf-state>");
/* XXX. yspec */ cprintf(cb,"<netconf xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">");
if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0) if (stream_get_xml(h, cb) < 0)
goto done; goto done;
cprintf(cb,"</netconf>");
/* 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) if (xml_merge(*xtop, x, yspec, &reason) < 0)
goto done; goto done;
if (reason){ if (reason){
@ -299,11 +313,117 @@ client_get_streams(clicon_handle h,
xml_purge(xc); xml_purge(xc);
if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0) if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0)
goto done; 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,"<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">");
cprintf(cb,"<module-set-id>1</module-set-id>"); /* NYI */
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", yrev->ys_argument);
else
cprintf(cb,"<revision></revision>");
cprintf(cb,"</module>");
}
cprintf(cb,"</modules-state>");
/* XXX. yspec */
if (xml_parse_string(cbuf_get(cb), NULL, &x) < 0){
if (netconf_operation_failed_xml(xtop, "protocol", clicon_err_reason)< 0)
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 */ /* 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; goto done;
/* If vectors are specified then mark the nodes found and /* 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); xml_flag_set(xvec[i], XML_FLAG_MARK);
} }
/* Remove everything that is not marked */ /* Remove everything that is not marked */
if (!xml_flag(*xtop, XML_FLAG_MARK)) if (!xml_flag(*xret, XML_FLAG_MARK))
if (xml_tree_prune_flagged_sub(*xtop, XML_FLAG_MARK, 1, NULL) < 0) if (xml_tree_prune_flagged_sub(*xret, XML_FLAG_MARK, 1, NULL) < 0)
goto done; goto done;
/* reset flag */ /* 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; goto done;
#endif #endif
ok:
retval = 0; retval = 0;
done: done:
if (cb) if (xvec)
cbuf_free(cb); free(xvec);
if (reason)
free(reason);
if (x)
xml_free(x);
return retval; return retval;
} }
@ -346,31 +461,26 @@ from_client_get(clicon_handle h,
cxobj *xe, cxobj *xe,
cbuf *cbret) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
cxobj *xfilter; cxobj *xfilter;
char *selector = "/"; char *xpath = "/";
cxobj *xret = NULL; cxobj *xret = NULL;
int ret; int ret;
cbuf *cbx = NULL; /* Assist cbuf */ cbuf *cbx = NULL; /* Assist cbuf */
if ((xfilter = xml_find(xe, "filter")) != NULL) if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((selector = xml_find_value(xfilter, "select"))==NULL) if ((xpath = xml_find_value(xfilter, "select"))==NULL)
selector="/"; xpath="/";
/* Get config */ /* 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) if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done; goto done;
goto ok; goto ok;
} }
/* 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 = client_statedata(h, xpath, &xret)) < 0)
goto done; goto done;
if (ret == 0){ /* OK */ if (ret == 0){ /* OK */
cprintf(cbret, "<rpc-reply>"); cprintf(cbret, "<rpc-reply>");

View file

@ -775,6 +775,10 @@ main(int argc,
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0) if (yang_spec_append(h, CLIXON_DATADIR, "ietf-restconf-monitoring", NULL)< 0)
goto done; goto done;
if (yang_spec_append(h, CLIXON_DATADIR, "ietf-netconf-notification", NULL)< 0)
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?)*/ /* 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

@ -125,11 +125,8 @@ clixon_plugin_statedata(clicon_handle h,
cxobj **xtop) cxobj **xtop)
{ {
int retval = -1; int retval = -1;
int i;
cxobj *x = NULL; cxobj *x = NULL;
yang_spec *yspec; yang_spec *yspec;
cxobj **xvec = NULL;
size_t xlen;
cxobj *xc; cxobj *xc;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */ plgstatedata_t *fn; /* Plugin statedata fn */
@ -168,25 +165,6 @@ clixon_plugin_statedata(clicon_handle h,
x = NULL; 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<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;
ok: ok:
retval = 0; retval = 0;
done: done:
@ -194,8 +172,6 @@ clixon_plugin_statedata(clicon_handle h,
free(reason); free(reason);
if (x) if (x)
xml_free(x); xml_free(x);
if (xvec)
free(xvec);
return retval; return retval;
} }

View file

@ -43,9 +43,6 @@
/* /*
* Types * Types
*/ */
/* subscription callback */
typedef int (*subscription_fn_t)(clicon_handle, void *filter, void *arg);
/* Notification subscription info /* Notification subscription info
* @see client_subscription in config_client.h * @see client_subscription in config_client.h
*/ */

View file

@ -68,7 +68,7 @@ curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enable
Start the restconf fastcgi program with debug flag: Start the restconf fastcgi program with debug flag:
``` ```
sudo su -c "/www-data/clixon_restconf -D 1 f /usr/local/etc/example.xml" -s /bin/sh www-data sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml" -s /bin/sh www-data
``` ```
Look at syslog: Look at syslog:
``` ```
@ -79,3 +79,13 @@ Send command:
``` ```
curl -G http://127.0.0.1/restconf/data/* curl -G http://127.0.0.1/restconf/data/*
``` ```
You can also run restconf in a debugger.
```
sudo gdb /www-data/clixon_restconf
(gdb) run -D 1 -f /usr/local/etc/example.xml
```
but you need to ensure /www-data/fastcgi_restconf.sock has the following access:
```
rwxr-xr-x 1 www-data www-data 0 sep 22 11:46 /www-data/fastcgi_restconf.sock
```

View file

@ -34,6 +34,8 @@
*/ */
/* /*
* This program should be run as user www-data
*
* See draft-ietf-netconf-restconf-13.txt [draft] * See draft-ietf-netconf-restconf-13.txt [draft]
* sudo apt-get install libfcgi-dev * sudo apt-get install libfcgi-dev
@ -46,6 +48,10 @@
* api/test * api/test
*/ */
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -615,7 +621,13 @@ main(int argc,
/* Parse yang database spec file */ /* Parse yang database spec file */
if (yang_spec_main(h) == NULL) if (yang_spec_main(h) == NULL)
goto done; 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 /* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start Pass all args after the standard options to plugin_start
*/ */

View file

@ -620,33 +620,51 @@ yang_find_schemanode(yang_node *yn,
*/ */
yang_stmt * yang_stmt *
yang_find_topnode(yang_spec *ysp, yang_find_topnode(yang_spec *ysp,
char *argument, char *nodeid,
yang_class class) yang_class class)
{ {
yang_stmt *ys = NULL; yang_stmt *ymod = NULL; /* module */
yang_stmt *yc = NULL; yang_stmt *yres = NULL; /* result */
char *prefix = NULL;
char *id = NULL;
int i; int i;
for (i=0; i<ysp->yp_len; i++){ if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
ys = ysp->yp_stmt[i]; goto done;
switch (class){ if (prefix){
case YC_NONE: if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL){
if ((yc = yang_find((yang_node*)ys, 0, argument)) != NULL) if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
return yc; goto ok;
break; goto done;
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 */
} }
} }
return NULL; else /* No prefix given - loop through and find first */
for (i=0; i<ysp->yp_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 /*! Given a yang statement, find the prefix associated to this module
@ -822,7 +840,6 @@ yarg_prefix(yang_stmt *ys)
return prefix; return prefix;
} }
/*! Split yang node identifier into prefix and identifer. /*! Split yang node identifier into prefix and identifer.
* @param[in] node-id * @param[in] node-id
* @param[out] prefix Malloced string. May be NULL. * @param[out] prefix Malloced string. May be NULL.

View file

@ -148,7 +148,7 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" "" expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get" 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" new "auth set authentication config"

View file

@ -13,6 +13,7 @@ fyang=$dir/test.yang
fyangerr=$dir/err.yang fyangerr=$dir/err.yang
nacmfile=$dir/nacmfile nacmfile=$dir/nacmfile
# Note filter out example_backend_nacm.so in CLICON_BACKEND_REGEXP below
cat <<EOF > $cfg cat <<EOF > $cfg
<config> <config>
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
@ -190,7 +191,7 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" "" expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get" 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" new "Set x to 0"

View file

@ -67,7 +67,7 @@ if [ $? -ne 0 ]; then
err err
fi fi
new "start backend -s init -f $cfg -y $fyang" 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 if [ $? -ne 0 ]; then
err err
@ -77,26 +77,21 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf sudo pkill -u www-data clixon_restconf
new "start restconf daemon" 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 sleep 1
new "restconf tests"
# get the stream list using netconf # get the stream list using netconf
new "netconf event stream discovery RFC5277 Sec 3.2.5" new "netconf event stream discovery RFC5277 Sec 3.2.5"
echo "$clixon_netconf -qf $cfg -y $fyang" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf><streams><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support></stream><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" 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>' new "netconf event stream discovery RFC8040 Sec 6.2"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state><streams><stream><name>NETCONF</name><description>default NETCONF event stream</description><replay-support>false</replay-support></stream><stream><name>CLICON</name><description>Clicon logs</description><replay-support>false</replay-support></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc 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>' 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 "netconf subscription"
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 #expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -5,7 +5,7 @@ APPNAME=example
# include err() and new() functions and creates $dir # include err() and new() functions and creates $dir
. ./lib.sh . ./lib.sh
exit 0 # NYI
cfg=$dir/conf_yang.xml cfg=$dir/conf_yang.xml
fyang=$dir/test.yang fyang=$dir/test.yang
@ -91,6 +91,8 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candid
new "minmax: empty" new "minmax: empty"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c/></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c/></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
exit # NYI
new "minmax: validate should fail" new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"

View file

@ -135,6 +135,10 @@ if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi 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" 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" 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" expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json" #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)" "" expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/example:empty)" ""
new2 "restconf get empty config + state json" 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" new "restconf get empty config + state xml"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data) ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/interfaces-state)
expect="<data><interfaces-state><interface><name>eth0</name><type>ex:eth</type><if-index>42</if-index></interface></interfaces-state></data>" expect="<interfaces-state><interface><name>eth0</name><type>ex:eth</type><if-index>42</if-index></interface></interfaces-state>"
match=`echo $ret | grep -EZo "$expect"` match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
@ -184,7 +195,7 @@ if [ -z "$match" ]; then
fi fi
new2 "restconf GET datastore" 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 # 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}\]}} 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) "" expecteq $(curl -s -X DELETE http://localhost/restconf/data/interfaces) ""
new "restconf Check empty config" 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" 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 "" 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 # 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)" "" #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" new2 "restconf Check eth/0/0 added config"
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 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" 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:' 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" 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" 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)" "" 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" 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" new2 "restconf rpc using POST json"

View file

@ -69,8 +69,8 @@ new "restconf tests"
new "restconf POST initial tree" new "restconf POST initial tree"
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 "" expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
new "restconf GET datastore" new "restconf GET datastore intial"
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 GET interface" new "restconf GET interface"
expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" 0 '{"interface": \[{"name": "local0","type": "regular"}\]}' 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 "" expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 ""
new "restconf GET null datastore" 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" new "restconf POST initial tree"
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 "" expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
new "restconf GET initial tree" 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" new "restconf DELETE whole datastore"
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 "" expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
new "restconf GET null datastore" 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" new "restconf PUT initial datastore"
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 "" expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
new "restconf GET datastore" 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" new "restconf PUT replace datastore"
expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 "" expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
new "restconf GET replaced datastore" 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" new "restconf PUT initial datastore again"
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 "" 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" new "restconf PUT change interface"
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' 0 "" expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' 0 ""
new "restconf GET datastore atm" 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" new "restconf PUT add interface"
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 "" expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 ""

View file

@ -117,6 +117,9 @@ new "cli not defined extension"
# This text yields an error, but the test cannot detect the error message yet # 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" #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 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-library</name><revision>2016-06-21</revision></module>'
new "netconf edit config" new "netconf edit config"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -140,7 +143,7 @@ new "netconf get leaf-list path"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e='hej']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e='hej']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
new "netconf get (should be some)" new "netconf get (should be some)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x>"
new "cli set leaf-list" new "cli set leaf-list"
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 "" expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""

View file

@ -46,6 +46,8 @@ 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 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 APPNAME = clixon # subdir ehere these files are installed

View file

@ -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: <http://tools.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
WG Chair: Mehmet Ersue
<mailto:mehmet.ersue@nsn.com>
WG Chair: Bert Wijnen
<mailto:bertietf@bwijnen.net>
Editor: Mark Scott
<mailto:mark.scott@ericsson.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
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 <rpc> messages received.";
}
leaf in-bad-rpcs {
type yang:zero-based-counter32;
description
"Number of messages received when an <rpc> message was expected,
that were not correct <rpc> messages. This includes XML parse
errors and errors on the rpc layer.";
}
leaf out-rpc-errors {
type yang:zero-based-counter32;
description
"Number of <rpc-reply> messages sent that contained an
<rpc-error> element.";
}
leaf out-notifications {
type yang:zero-based-counter32;
description
"Number of <notification> 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 <lock> and <partial-lock> 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 <partial-lock>
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 <get-schema> 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
<get-schema> 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 <hello> message was received. This includes <hello>
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 <hello> message with a <session-id> 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
<close-session> operation, or killed by a <kill-session>
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 <error-tag> is
'invalid-value'.
If more than one schema matches the requested parameters, the
<error-tag> is 'operation-failed', and <error-app-tag> 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.";
}
}
}
}

View file

@ -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, <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.";
}
}
}
}
}