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 ***** # ***** END LICENSE BLOCK *****
- Refined netconf "none" semantics in tests and text datastore
- Moved apps/dbctrl to datastore/ - Moved apps/dbctrl to datastore/
- Added connect/disconnect/getopt/setopt and handle to xmldb API - 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 CLIXON is an automatic configuration manager where you from a YANG
specification generate interactive CLI, NETCONF, RESTCONF and embedded 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 Presentations and tutorial is found on the [CLICON project
page](http://www.clicon.org) page](http://www.clicon.org)
## Installation
A typical installation is as follows: A typical installation is as follows:
> configure # Configure clixon to platform > 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 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) [KTH](http://www.csc.kth.se/~olofh/10G_OSR)
## Dependencies
[CLIgen](http://www.cligen.se) is required for building CLIXON. If you need [CLIgen](http://www.cligen.se) is required for building CLIXON. If you need
to build and install CLIgen: to build and install CLIgen:
git clone https://github.com/olofhagsand/cligen.git git clone https://github.com/olofhagsand/cligen.git
cd cligen; configure; make; make install cd cligen; configure; make; make install
## Licenses
CLIXON is dual license. Either Apache License, Version 2.0 or GNU CLIXON is dual license. Either Apache License, Version 2.0 or GNU
General Public License Version 2. You choose. General Public License Version 2. You choose.
See LICENSE.md for license, CHANGELOG for recent changes. 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){ if ((xc = xpath_first(xn, "config")) != NULL){
/* XXX see from_client_xmlput() */
if (xmldb_put(h, target, operation, api_path, xc) < 0){ if (xmldb_put(h, target, operation, api_path, xc) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>" cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>" "<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 3. DEBUGGING
++++++++++++ ++++++++++++
Start the restconf programs with debug flag: Start the restconf fastcgi program with debug flag:
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-data
Look at syslog: Look at syslog:
tail -f /var/log/syslog | grep clixon_restconf tail -f /var/log/syslog | grep clixon_restconf

View file

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

View file

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

View file

@ -245,6 +245,32 @@ text_setopt(xmldb_handle xh,
return retval; 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 /*! 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 * The function returns a minimal tree that includes all sub-trees that match
* xpath. * xpath.
@ -335,12 +361,12 @@ text_get(xmldb_handle xh,
} }
/* Top is special case */ /* Top is special case */
if (!xml_flag(xt, XML_FLAG_MARK)) 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; goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done; goto done;
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
if (0){ /* No xml_spec(xt) */ goto done;
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done; goto done;
/* XXX does not work for top-level */ /* XXX does not work for top-level */
@ -348,7 +374,7 @@ text_get(xmldb_handle xh,
goto done; goto done;
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done; goto done;
}
if (debug>1) if (debug>1)
clicon_xml2file(stderr, xt, 0, 1); clicon_xml2file(stderr, xt, 0, 1);
if (xvec0 && xlen0){ if (xvec0 && xlen0){
@ -755,11 +781,14 @@ text_modify(cxobj *x0,
goto done; goto done;
} }
/* Fall thru */ /* Fall thru */
case OP_NONE: /* XXX */
case OP_MERGE: case OP_MERGE:
case OP_REPLACE: case OP_REPLACE:
if (x0==NULL){ if (x0==NULL){
if ((x0 = xml_new_spec(name, x0p, y)) == NULL) if ((x0 = xml_new_spec(name, x0p, y)) == NULL)
goto done; 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 (x1bstr){ /* empty type does not have body */
if ((x0b = xml_new("body", x0)) == NULL) if ((x0b = xml_new("body", x0)) == NULL)
goto done; goto done;
@ -767,12 +796,24 @@ text_modify(cxobj *x0,
} }
} }
if (x1bstr){ if (x1bstr){
if ((x0b = xml_body_get(x0)) == NULL) if ((x0b = xml_body_get(x0)) == NULL){
if ((x0b = xml_new("body", x0)) == NULL)
goto done; goto done;
xml_type_set(x0b, CX_BODY);
}
if (xml_value_set(x0b, x1bstr) < 0) if (xml_value_set(x0b, x1bstr) < 0)
goto done; goto done;
} }
break; 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: default:
break; break;
} /* switch op */ } /* switch op */
@ -799,11 +840,15 @@ text_modify(cxobj *x0,
xml_purge(x0); xml_purge(x0);
x0 = NULL; x0 = NULL;
} }
case OP_NONE: /* XXX */
case OP_MERGE: case OP_MERGE:
if (x0==NULL){ if (x0==NULL){
if ((x0 = xml_new_spec(name, x0p, y)) == NULL) if ((x0 = xml_new_spec(name, x0p, y)) == NULL)
goto done; goto done;
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
} }
/* Loop through children of the modification tree */ /* Loop through children of the modification tree */
x1c = NULL; x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
@ -823,6 +868,15 @@ text_modify(cxobj *x0,
goto done; goto done;
} }
break; 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: default:
break; break;
} /* CONTAINER switch op */ } /* CONTAINER switch op */
@ -877,10 +931,12 @@ text_put(xmldb_handle xh,
cxobj *xnew = NULL; cxobj *xnew = NULL;
yang_node *y = NULL; yang_node *y = NULL;
#if 0 /* Just ignore */
if ((op==OP_DELETE || op==OP_REMOVE) && xmod){ if ((op==OP_DELETE || op==OP_REMOVE) && xmod){
clicon_err(OE_XML, 0, "xml tree should be NULL for REMOVE/DELETE"); clicon_err(OE_XML, 0, "xml tree should be NULL for REMOVE/DELETE");
goto done; goto done;
} }
#endif
if (text_db2file(th, db, &dbfile) < 0) if (text_db2file(th, db, &dbfile) < 0)
goto done; goto done;
if (dbfile==NULL){ if (dbfile==NULL){
@ -942,6 +998,12 @@ text_put(xmldb_handle xh,
else else
if (text_modify(xbase, xbasep, xmod, op, (yang_node*)y, yspec) < 0) if (text_modify(xbase, xbasep, xmod, op, (yang_node*)y, yspec) < 0)
goto done; 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: // output:
/* Print out top-level xml tree after modification to file */ /* Print out top-level xml tree after modification to file */
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){

View file

@ -27,3 +27,7 @@ CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
# Enabled uses "startup" configuration on boot # Enabled uses "startup" configuration on boot
CLICON_USE_STARTUP_CONFIG 0 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_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_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_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
/* /*
* Prototypes * Prototypes
@ -102,6 +103,7 @@ int xml_childvec_set(cxobj *x, int len);
cxobj *xml_new(char *name, cxobj *xn_parent); cxobj *xml_new(char *name, cxobj *xn_parent);
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec); cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
void *xml_spec(cxobj *x); void *xml_spec(cxobj *x);
void *xml_spec_set(cxobj *x, void *spec);
cxobj *xml_find(cxobj *xn_parent, char *name); cxobj *xml_find(cxobj *xn_parent, char *name);
int xml_addsub(cxobj *xp, cxobj *xc); 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 */ /* Type of xmldb init function */
typedef int (xmldb_init_t)(xmldb_handle xh, char *db); 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{ struct xmldb_api{
int xa_version; int xa_version;
int xa_magic; 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 yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2xpath(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_default(cxobj *x, void *arg);
int xml_order(cxobj *x, void *arg); int xml_order(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg); int xml_sanity(cxobj *x, void *arg);

View file

@ -521,12 +521,21 @@ xml_new_spec(char *name,
return x; return x;
} }
void * void *
xml_spec(cxobj *x) xml_spec(cxobj *x)
{ {
return x->x_spec; 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. /*! Find an XML node matching name among a parent's children.
* *
* Get first XML node directly under x_up in the xml hierarchy with * 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_options.h"
#include "clixon_xml_db.h" #include "clixon_xml_db.h"
/* Set to log get and put requests */
#define DEBUG 0
/*! Load an xmldb storage plugin according to filename /*! Load an xmldb storage plugin according to filename
* If init function fails (not found, wrong version, etc) print a log and dont * If init function fails (not found, wrong version, etc) print a log and dont
* add it. * add it.
@ -350,7 +353,7 @@ xmldb_get(clicon_handle h,
goto done; goto done;
} }
retval = xa->xa_get_fn(xh, db, xpath, xtop, xvec, xlen); retval = xa->xa_get_fn(xh, db, xpath, xtop, xvec, xlen);
#if 0 /* XXX DEBUG */ #if DEBUG
if (retval == 0) { if (retval == 0) {
cbuf *cb = cbuf_new(); cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, *xtop, 0, 0); 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"); clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done; goto done;
} }
#if 0 /* XXX DEBUG */ #if DEBUG
{ {
cbuf *cb = cbuf_new(); cbuf *cb = cbuf_new();
if (xt) if (xt)

View file

@ -1076,15 +1076,22 @@ xmlkeyfmt2xpath(char *xkfmt,
return retval; 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] 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. * @param[out] upmark Set if a child (recursively) has marked set.
* The function removes all branches that does not contain a marked child * The function removes all branches that does not a child that pass the test
* XXX: maybe key leafs should not be purged if list is not purged? * Purge all nodes that dont have MARK flag set recursively.
* XXX: consider move to clicon_xml * 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 int
xml_tree_prune_unmarked(cxobj *xt, xml_tree_prune_flagged(cxobj *xt,
int flag,
int test,
int *upmark) int *upmark)
{ {
int retval = -1; int retval = -1;
@ -1097,12 +1104,12 @@ xml_tree_prune_unmarked(cxobj *xt,
x = NULL; x = NULL;
xprev = x = NULL; xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != 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++; mark++;
xprev = x; xprev = x;
continue; /* mark and stop here */ 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; goto done;
if (submark) if (submark)
mark++; mark++;

View file

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

View file

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

View file

@ -20,14 +20,44 @@ sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "netconf tests"
new "netconf get empty config" 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" 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>]]>]]>$" 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" 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" new "netconf validate missing type"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
@ -53,18 +83,6 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate
new "netconf get replaced config" 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>]]>]]>$" 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" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" 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 sleep 1
new "restconf tests"
new "restconf options" new "restconf options"
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE" 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" new "restconf head"
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json" expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
new "restconf POST config" new "restconf get empty config"
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" 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' "" expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
new "restconf get config" new "Check deleted eth0"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}} 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" new "restconf PATCH config"
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" 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(){ run(){
name=$1 name=$1
dir=/tmp/$name dir=/tmp/$name
if [ ! -d $dir ]; then if [ ! -d $dir ]; then
mkdir $dir mkdir $dir
fi fi
rm -rf $dir/* rm -rf $dir/*
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip" 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" new "datastore $name init"
expectfn "$datastore $conf init" "" expectfn "$datastore $conf init" ""
# Whole tree operations
new "datastore $name put all replace" new "datastore $name put all replace"
expectfn "$datastore $conf put replace / $db" "" expectfn "$datastore $conf put replace / $db" ""
@ -89,31 +92,55 @@ run(){
new "datastore $name put top create" new "datastore $name put top create"
expectfn "$datastore $conf put create / <config><x/></config>" "" # error 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" new "datastore $name create leaf"
expectfn "$datastore $conf put replace / $db" 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" expectfn "$datastore $conf put delete /x/g"
return new "datastore $name merge leaf"
new "datastore $name get empty" expectfn "$datastore $conf put merge /x/g <g>nalle</g>"
expectfn "$datastore $conf get /" "^<config/>$"
new "datastore $name put top" new "datastore $name replace leaf"
expectfn "$datastore $conf put replace / <config><x><y><a>foo</a><b>bar</b><c>fie</c></y></x></config>" "" expectfn "$datastore $conf put replace /x/g <g>nalle</g>"
new "datastore $name get config" new "datastore $name merge leaf"
expectfn "$datastore $conf get /" "^<config><x><y><a>foo</a><b>bar</b><c>fie</c></y></x></config>$" expectfn "$datastore $conf put merge /x/y=1,3/c <c>newentry</c>"
new "datastore $name put delete" new "datastore $name replace leaf"
expectfn "$datastore $conf put delete / <config/>" "" 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 rm -rf $dir
} }