Refined netconf none semantics in tests and text datastore

This commit is contained in:
Olof hagsand 2017-05-01 12:46:09 +02:00
parent 2f30bda7d4
commit 69ff0e3891
19 changed files with 247 additions and 91 deletions

View file

@ -29,6 +29,8 @@
#
# ***** END LICENSE BLOCK *****
- Refined netconf "none" semantics in tests and text datastore
- Moved apps/dbctrl to datastore/
- Added connect/disconnect/getopt/setopt and handle to xmldb API

View file

@ -1,5 +1,4 @@
CLIXON
======
# CLIXON
CLIXON is an automatic configuration manager where you from a YANG
specification generate interactive CLI, NETCONF, RESTCONF and embedded
@ -12,6 +11,8 @@ applications such as CLICON/ROST does not run on CLIXON.
Presentations and tutorial is found on the [CLICON project
page](http://www.clicon.org)
## Installation
A typical installation is as follows:
> configure # Configure clixon to platform
@ -22,17 +23,27 @@ A typical installation is as follows:
One example applications is provided, the IETF IP YANG datamodel with generated CLI and configuration interface. It all origins from work at
[KTH](http://www.csc.kth.se/~olofh/10G_OSR)
## Dependencies
[CLIgen](http://www.cligen.se) is required for building CLIXON. If you need
to build and install CLIgen:
git clone https://github.com/olofhagsand/cligen.git
cd cligen; configure; make; make install
## Licenses
CLIXON is dual license. Either Apache License, Version 2.0 or GNU
General Public License Version 2. You choose.
See LICENSE.md for license, CHANGELOG for recent changes.
## Client code
[CLI](apps/restconf).
[Restconf](apps/restconf).
[Netconf](apps/netconf).
[Netconf](apps/netconf).

View file

@ -307,7 +307,6 @@ from_client_edit_config(clicon_handle h,
}
}
if ((xc = xpath_first(xn, "config")) != NULL){
/* XXX see from_client_xmlput() */
if (xmldb_put(h, target, operation, api_path, xc) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"

View file

@ -66,8 +66,8 @@ curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"et
3. DEBUGGING
++++++++++++
Start the restconf programs with debug flag:
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data
Start the restconf fastcgi program with debug flag:
sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-data
Look at syslog:
tail -f /var/log/syslog | grep clixon_restconf

View file

@ -296,7 +296,7 @@ api_data_edit(clicon_handle h,
goto done;
}
cprintf(cbx, "</config>");
clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_edit_config(h, "candidate",
operation,
api_path,
@ -337,7 +337,8 @@ api_data_edit(clicon_handle h,
If the data resource already exists, then the POST request MUST fail
and a "409 Conflict" status-line MUST be returned.
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation * @example
*/
int
api_data_post(clicon_handle h,
@ -359,8 +360,8 @@ api_data_post(clicon_handle h,
* @param[in] pi Offset, where to start pcvec
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] data Stream input data
* Example:
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
* @example
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
*
PUT:
if the PUT request creates a new resource,

View file

@ -652,7 +652,7 @@ kv_get(xmldb_handle xh,
}
/* Top is special case */
if (!xml_flag(xt, XML_FLAG_MARK))
if (xml_tree_prune_unmarked(xt, NULL) < 0)
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
@ -1309,7 +1309,7 @@ kv_put(xmldb_handle xh,
yang_spec *yspec;
char *dbfilename = NULL;
if ((xml_child_nr(xt)==0 || xml_body(xt)!= NULL) &&
if (xt && (xml_child_nr(xt)==0 || xml_body(xt)!= NULL) &&
api_path && strlen(api_path) && strcmp(api_path,"/"))
return xmldb_put_xkey(kh, db, op, api_path, xml_body(xt));
if ((yspec = kh->kh_yangspec) == NULL){

View file

@ -245,6 +245,32 @@ text_setopt(xmldb_handle xh,
return retval;
}
/*! Populate with spec
* @param[in] xt XML tree with some node marked
*/
int
xml_spec_populate(cxobj *x,
void *arg)
{
int retval = -1;
yang_spec *yspec = (yang_spec*)arg;
char *name;
yang_stmt *y; /* yang node */
cxobj *xp; /* xml parent */
yang_stmt *yp; /* parent yang */
name = xml_name(x);
if ((xp = xml_parent(x)) != NULL &&
(yp = xml_spec(xp)) != NULL)
y = yang_find_syntax((yang_node*)yp, xml_name(x));
else
y = yang_find_topnode(yspec, name); /* still NULL for config */
xml_spec_set(x, y);
retval = 0;
// done:
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
@ -335,20 +361,20 @@ text_get(xmldb_handle xh,
}
/* Top is special case */
if (!xml_flag(xt, XML_FLAG_MARK))
if (xml_tree_prune_unmarked(xt, NULL) < 0)
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
if (0){ /* No xml_spec(xt) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
}
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
if (xvec0 && xlen0){
@ -755,11 +781,14 @@ text_modify(cxobj *x0,
goto done;
}
/* Fall thru */
case OP_NONE: /* XXX */
case OP_MERGE:
case OP_REPLACE:
if (x0==NULL){
if ((x0 = xml_new_spec(name, x0p, y)) == NULL)
goto done;
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
if (x1bstr){ /* empty type does not have body */
if ((x0b = xml_new("body", x0)) == NULL)
goto done;
@ -767,12 +796,24 @@ text_modify(cxobj *x0,
}
}
if (x1bstr){
if ((x0b = xml_body_get(x0)) == NULL)
goto done;
if ((x0b = xml_body_get(x0)) == NULL){
if ((x0b = xml_new("body", x0)) == NULL)
goto done;
xml_type_set(x0b, CX_BODY);
}
if (xml_value_set(x0b, x1bstr) < 0)
goto done;
}
break;
case OP_DELETE:
if (x0==NULL){
clicon_err(OE_XML, 0, "Object to delete does not exist");
goto done;
}
case OP_REMOVE:
if (x0)
xml_purge(x0);
break;
default:
break;
} /* switch op */
@ -799,11 +840,15 @@ text_modify(cxobj *x0,
xml_purge(x0);
x0 = NULL;
}
case OP_NONE: /* XXX */
case OP_MERGE:
if (x0==NULL){
if ((x0 = xml_new_spec(name, x0p, y)) == NULL)
goto done;
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
}
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
@ -823,6 +868,15 @@ text_modify(cxobj *x0,
goto done;
}
break;
case OP_DELETE:
if (x0==NULL){
clicon_err(OE_XML, 0, "Object to delete does not exist");
goto done;
}
case OP_REMOVE:
if (x0)
xml_purge(x0);
break;
default:
break;
} /* CONTAINER switch op */
@ -857,7 +911,7 @@ text_modify(cxobj *x0,
* @endcode
*/
int
text_put(xmldb_handle xh,
text_put(xmldb_handle xh,
char *db,
enum operation_type op,
char *api_path,
@ -877,10 +931,12 @@ text_put(xmldb_handle xh,
cxobj *xnew = NULL;
yang_node *y = NULL;
#if 0 /* Just ignore */
if ((op==OP_DELETE || op==OP_REMOVE) && xmod){
clicon_err(OE_XML, 0, "xml tree should be NULL for REMOVE/DELETE");
goto done;
}
#endif
if (text_db2file(th, db, &dbfile) < 0)
goto done;
if (dbfile==NULL){
@ -942,6 +998,12 @@ text_put(xmldb_handle xh,
else
if (text_modify(xbase, xbasep, xmod, op, (yang_node*)y, yspec) < 0)
goto done;
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged(xt, XML_FLAG_NONE, 0, NULL) <0)
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)XML_FLAG_NONE) < 0)
goto done;
// output:
/* Print out top-level xml tree after modification to file */
if ((cb = cbuf_new()) == NULL){

View file

@ -27,3 +27,7 @@ CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
# Enabled uses "startup" configuration on boot
CLICON_USE_STARTUP_CONFIG 0
# XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])
CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/text.so
#CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/keyvalue.so

View file

@ -67,6 +67,7 @@ typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
#define XML_FLAG_ADD 0x02 /* Node is added (commits) or parent added rec*/
#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) or parent deleted rec */
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
/*
* Prototypes
@ -102,6 +103,7 @@ int xml_childvec_set(cxobj *x, int len);
cxobj *xml_new(char *name, cxobj *xn_parent);
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
void *xml_spec(cxobj *x);
void *xml_spec_set(cxobj *x, void *spec);
cxobj *xml_find(cxobj *xn_parent, char *name);
int xml_addsub(cxobj *xp, cxobj *xc);

View file

@ -106,7 +106,7 @@ typedef int (xmldb_delete_t)(xmldb_handle xh, char *db);
/* Type of xmldb init function */
typedef int (xmldb_init_t)(xmldb_handle xh, char *db);
/* grideye agent plugin init struct for the api */
/* plugin init struct for the api */
struct xmldb_api{
int xa_version;
int xa_magic;

View file

@ -64,7 +64,7 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
int xml_tree_prune_unmarked(cxobj *xt, int *upmark);
int xml_tree_prune_flagged(cxobj *xt, int flag, int test, int *upmark);
int xml_default(cxobj *x, void *arg);
int xml_order(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg);

View file

@ -521,12 +521,21 @@ xml_new_spec(char *name,
return x;
}
void *
xml_spec(cxobj *x)
{
return x->x_spec;
}
void *
xml_spec_set(cxobj *x,
void *spec)
{
x->x_spec = spec;
return 0;
}
/*! Find an XML node matching name among a parent's children.
*
* Get first XML node directly under x_up in the xml hierarchy with

View file

@ -63,6 +63,9 @@
#include "clixon_options.h"
#include "clixon_xml_db.h"
/* Set to log get and put requests */
#define DEBUG 0
/*! Load an xmldb storage plugin according to filename
* If init function fails (not found, wrong version, etc) print a log and dont
* add it.
@ -350,7 +353,7 @@ xmldb_get(clicon_handle h,
goto done;
}
retval = xa->xa_get_fn(xh, db, xpath, xtop, xvec, xlen);
#if 0 /* XXX DEBUG */
#if DEBUG
if (retval == 0) {
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, *xtop, 0, 0);
@ -407,7 +410,7 @@ xmldb_put(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
#if 0 /* XXX DEBUG */
#if DEBUG
{
cbuf *cb = cbuf_new();
if (xt)

View file

@ -1076,16 +1076,23 @@ xmlkeyfmt2xpath(char *xkfmt,
return retval;
}
/*! Prune everything that has not been marked
/*! Prune everything that does not pass test
* @param[in] xt XML tree with some node marked
* @param[in] flag Which flag to test for
* @param[in] test 1: test that flag is set, 0: test that flag is not set
* @param[out] upmark Set if a child (recursively) has marked set.
* The function removes all branches that does not contain a marked child
* XXX: maybe key leafs should not be purged if list is not purged?
* XXX: consider move to clicon_xml
* The function removes all branches that does not a child that pass the test
* Purge all nodes that dont have MARK flag set recursively.
* Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
* @code
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
* @endcode
*/
int
xml_tree_prune_unmarked(cxobj *xt,
int *upmark)
xml_tree_prune_flagged(cxobj *xt,
int flag,
int test,
int *upmark)
{
int retval = -1;
int submark;
@ -1097,12 +1104,12 @@ xml_tree_prune_unmarked(cxobj *xt,
x = NULL;
xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK)){
if (xml_flag(x, flag) == test?flag:0){
mark++;
xprev = x;
continue; /* mark and stop here */
}
if (xml_tree_prune_unmarked(x, &submark) < 0)
if (xml_tree_prune_flagged(x, flag, test, &submark) < 0)
goto done;
if (submark)
mark++;

View file

@ -358,6 +358,13 @@ xpath_parse(char *xpath,
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
}
#if 1
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
#else
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, NULL, &xpnext);
#endif
#if 1 /* Problems with .[userid=1321] */
else if (strncmp(s,".", strlen("."))==0)
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
@ -368,13 +375,7 @@ xpath_parse(char *xpath,
else if (strncmp(s,"self::", strlen("self::"))==0)
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
#if 1
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
#else
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, NULL, &xpnext);
#endif
else if (strncmp(s,"parent::", strlen("parent::"))==0)
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
@ -1076,7 +1077,7 @@ int
main(int argc, char **argv)
{
int i;
cxobj **xv;
cxobj **xv
cxobj *x;
cxobj *xn;
size_t xlen = 0;

View file

@ -26,6 +26,8 @@ sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "cli tests"
new "cli configure top"
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""

View file

@ -20,14 +20,44 @@ sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "netconf tests"
new "netconf get empty config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc message-id=\"101\"><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply message-id=\"101\"><data/></rpc-reply>]]>]]>$"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Add subtree eth0 using none which should not change anything"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces><interface><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check nothing added"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Add subtree eth0 using none and create which should add eth0"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check eth0 added using xpath"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth0]"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><name>eth0</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "Re-create same eth0 which should generate error"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "Delete eth0 using none config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Check deleted eth0"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config><interfaces/></config></data></rpc-reply>]]>]]>$'
new "Re-Delete eth0 using none should generate error"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "netconf edit config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get config xpath"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/interfaces/interface[name=eth1]/enabled\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf get config xpath parent"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled/../.."/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><name>eth0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf validate missing type"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
@ -48,23 +78,11 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config replace"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get replaced config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf edit config create"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config create 2nd"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "netconf edit config delete"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get delete config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"

View file

@ -24,32 +24,40 @@ sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c
sleep 1
new "restconf tests"
new "restconf options"
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
new "restconf get empty config"
expectfn "curl -sG http://localhost/restconf/data" "^null $"
new "restconf put config"
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth1","type":"eth","enabled":"true"},{"name":"eth0","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
new "restconf get config"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}}
$'
new "restconf head"
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
new "restconf POST config"
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
new "restconf get empty config"
expectfn "curl -sG http://localhost/restconf/data" "^null $"
new "restconf DELETE config"
#
new "Add subtree eth0,eth1 using POST"
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth0","type":"eth","enabled":"true"},{"name":"eth1","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
new "Check eth0 added"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth0","type": "eth","enabled": "true"},{ "name": "eth1","type": "eth","enabled": "true"}\]}}
$'
new "Re-post eth0 which should generate error"
expectfn 'curl -sX POST -d {"interfaces":{"interface":{"name":"eth0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' "Not Found"
new "delete eth0"
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
new "restconf get config"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
new "Check deleted eth0"
expectfn 'curl -sG http://localhost/restconf/data' '{"interfaces": {"interface": {"name": "eth1","type": "eth","enabled": "true"}}}
$'
new "Re-Delete eth0 using none should generate error"
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "Not Found"
return
new "restconf PATCH config"
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""

View file

@ -47,15 +47,18 @@ db='<config><x><y><a>1</a><b>2</b><c>first-entry</c></y><y><a>1</a><b>3</b><c>se
run(){
name=$1
dir=/tmp/$name
if [ ! -d $dir ]; then
mkdir $dir
fi
rm -rf $dir/*
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip"
echo "conf:$conf"
# echo "conf:$conf"
new "datastore $name init"
expectfn "$datastore $conf init" ""
# Whole tree operations
new "datastore $name put all replace"
expectfn "$datastore $conf put replace / $db" ""
@ -89,31 +92,55 @@ run(){
new "datastore $name put top create"
expectfn "$datastore $conf put create / <config><x/></config>" "" # error
return
# Single key operations
# leaf
new "datastore $name put all delete"
expectfn "$datastore $conf delete" ""
new "datastore $name init"
expectfn "$datastore $conf init" ""
new "datastore $name create leaf"
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
new "datastore $name put top"
expectfn "$datastore $conf put replace / $db"
new "datastore $name create leaf"
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
new "datastore $name put del"
new "datastore $name delete leaf"
expectfn "$datastore $conf put delete /x/y=1,3"
new "datastore $name replace leaf"
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
new "datastore $name remove leaf"
expectfn "$datastore $conf put remove /x/g"
new "datastore $name remove leaf"
expectfn "$datastore $conf put remove /x/y=1,3/c"
new "datastore $name delete leaf"
expectfn "$datastore $conf put delete /x/g"
return
new "datastore $name get empty"
expectfn "$datastore $conf get /" "^<config/>$"
new "datastore $name merge leaf"
expectfn "$datastore $conf put merge /x/g <g>nalle</g>"
new "datastore $name put top"
expectfn "$datastore $conf put replace / <config><x><y><a>foo</a><b>bar</b><c>fie</c></y></x></config>" ""
new "datastore $name replace leaf"
expectfn "$datastore $conf put replace /x/g <g>nalle</g>"
new "datastore $name get config"
expectfn "$datastore $conf get /" "^<config><x><y><a>foo</a><b>bar</b><c>fie</c></y></x></config>$"
new "datastore $name merge leaf"
expectfn "$datastore $conf put merge /x/y=1,3/c <c>newentry</c>"
new "datastore $name put delete"
expectfn "$datastore $conf put delete / <config/>" ""
new "datastore $name replace leaf"
expectfn "$datastore $conf put replace /x/y=1,3/c <c>newentry</c>"
new "datastore $name create leaf"
expectfn "$datastore $conf put create /x/h <h><j>aaa</j></h>"
new "datastore $name create leaf"
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
#leaf-list
new "datastore $name get deleted"
expectfn "$datastore $conf get /" "^<config/>$"
rm -rf $dir
}