(Work in progress) Restconf error handling for get and edit operations
This commit is contained in:
parent
0a11445963
commit
859d424ea3
9 changed files with 165 additions and 119 deletions
|
|
@ -4,7 +4,13 @@
|
||||||
|
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
|
* (Work in progress) Restconf error handling for get and edit operations
|
||||||
|
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
* Add username to rpc calls to prepare for authorization for backend:
|
||||||
|
clicon_rpc_config_get(h, db, xpath, xt) --> clicon_rpc_config_get(h, db, xpath, username, xt)
|
||||||
|
clicon_rpc_get(h, xpath, xt) --> clicon_rpc_get(h, xpath, username, xt)
|
||||||
|
|
||||||
* Experimental: Added CLICON_TRANSACTION_MOD configurqation option. If set,
|
* Experimental: Added CLICON_TRANSACTION_MOD configurqation option. If set,
|
||||||
modifications in validation and commit callbacks are written back
|
modifications in validation and commit callbacks are written back
|
||||||
into the datastore.
|
into the datastore.
|
||||||
|
|
@ -31,7 +37,7 @@ enables saved files to be used as datastore without any editing. Thanks Matt.
|
||||||
## 3.5.0 (12 February 2018)
|
## 3.5.0 (12 February 2018)
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
* Major Restconf feature update to compy to RFC 8040. Thanks Stephen Jones for getting right.
|
* Major Restconf feature update to comply to RFC 8040. Thanks Stephen Jones for getting right.
|
||||||
* GET: Always return object referenced (and nothing else). ie, GET /restconf/data/X returns X.
|
* GET: Always return object referenced (and nothing else). ie, GET /restconf/data/X returns X.
|
||||||
* GET Added support for the following resources: Well-known, top-level resource, and yang library version,
|
* GET Added support for the following resources: Well-known, top-level resource, and yang library version,
|
||||||
* GET Single element JSON lists use {list:[element]}, not {list:element}.
|
* GET Single element JSON lists use {list:[element]}, not {list:element}.
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,6 @@ yang2cli_var_sub(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else{ /* Cligen does not have 'max' keyword in range so need to find actual
|
else{ /* Cligen does not have 'max' keyword in range so need to find actual
|
||||||
max value of type if yang range expression is 0..max
|
max value of type if yang range expression is 0..max
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -144,47 +144,62 @@ api_data_options(clicon_handle h,
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] r Fastcgi request handle
|
||||||
* @param[in] xerr XML error message from backend
|
* @param[in] xerr XML error message from backend
|
||||||
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
|
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_data_get_err(clicon_handle h,
|
api_return_err(clicon_handle h,
|
||||||
FCGX_Request *r,
|
FCGX_Request *r,
|
||||||
cxobj *xerr)
|
cxobj *xerr,
|
||||||
|
int pretty,
|
||||||
|
int use_xml)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cbj = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xtag;
|
cxobj *xtag;
|
||||||
int code;
|
int code;
|
||||||
const char *reason_phrase;
|
const char *reason_phrase;
|
||||||
|
|
||||||
if ((cbj = cbuf_new()) == NULL)
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xtag = xpath_first(xerr, "/error-tag")) == NULL){
|
if ((xtag = xpath_first(xerr, "error-tag")) == NULL){
|
||||||
notfound(r); /* bad reply? */
|
notfound(r); /* bad reply? */
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
code = restconf_err2code(xml_body(xtag));
|
code = restconf_err2code(xml_body(xtag));
|
||||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||||
reason_phrase="";
|
reason_phrase="";
|
||||||
clicon_debug(1, "%s code:%d reason phrase:%s",
|
|
||||||
__FUNCTION__, code, reason_phrase);
|
|
||||||
|
|
||||||
if (xml_name_set(xerr, "error") < 0)
|
if (xml_name_set(xerr, "error") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml2json_cbuf(cbj, xerr, 1) < 0)
|
if (use_xml){
|
||||||
|
if (clicon_xml2cbuf(cb, xerr, 2, pretty) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
|
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
|
||||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+json\r\n\r\n");
|
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
use_xml?"xml":"json");
|
||||||
FCGX_FPrintF(r->out, "{\r\n");
|
if (use_xml){
|
||||||
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : {\r\n");
|
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">%s", cbuf_get(cb), pretty?"\r\n":"");
|
||||||
FCGX_FPrintF(r->out, " %s", cbuf_get(cbj));
|
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
|
||||||
FCGX_FPrintF(r->out, " }\r\n");
|
FCGX_FPrintF(r->out, " </errors>\r\n");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
FCGX_FPrintF(r->out, "{%s", pretty?"\r\n":"");
|
||||||
|
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : {%s", pretty?"\r\n":"");
|
||||||
|
FCGX_FPrintF(r->out, " %s", cbuf_get(cb));
|
||||||
|
FCGX_FPrintF(r->out, " }%s", pretty?"\r\n":"");
|
||||||
FCGX_FPrintF(r->out, "}\r\n");
|
FCGX_FPrintF(r->out, "}\r\n");
|
||||||
|
}
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cbj)
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
cbuf_free(cbj);
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,9 +284,9 @@ api_data_get2(clicon_handle h,
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Check if error return */
|
/* Check if error return XXX this needs more work */
|
||||||
if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){
|
if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){
|
||||||
if (api_data_get_err(h, r, xerr) < 0)
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -425,6 +440,7 @@ api_data_post(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
enum operation_type op = OP_CREATE;
|
enum operation_type op = OP_CREATE;
|
||||||
|
int pretty;
|
||||||
int i;
|
int i;
|
||||||
cxobj *xdata = NULL;
|
cxobj *xdata = NULL;
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
|
|
@ -437,10 +453,18 @@ api_data_post(clicon_handle h,
|
||||||
cxobj *xu;
|
cxobj *xu;
|
||||||
char *media_content_type;
|
char *media_content_type;
|
||||||
int parse_xml = 0; /* By default expect and parse JSON */
|
int parse_xml = 0; /* By default expect and parse JSON */
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
char *media_accept;
|
||||||
|
int use_xml = 0; /* By default use JSON */
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||||
__FUNCTION__,
|
__FUNCTION__,
|
||||||
api_path, data);
|
api_path, data);
|
||||||
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
|
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||||
|
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||||
|
use_xml++;
|
||||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||||
if (media_content_type &&
|
if (media_content_type &&
|
||||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||||
|
|
@ -499,14 +523,19 @@ api_data_post(clicon_handle h,
|
||||||
/* Create text buffer for transfer to backend */
|
/* Create text buffer for transfer to backend */
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
||||||
|
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
cprintf(cbx, "</edit-config></rpc>");
|
||||||
clicon_debug(1, "%s xml: %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_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||||
OP_NONE,
|
goto done;
|
||||||
cbuf_get(cbx)) < 0){
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
conflict(r);
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
goto ok;
|
goto done;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
/* Assume this is validation failed since commit includes validate */
|
/* Assume this is validation failed since commit includes validate */
|
||||||
if (clicon_rpc_commit(h) < 0){
|
if (clicon_rpc_commit(h) < 0){
|
||||||
|
|
@ -522,6 +551,8 @@ api_data_post(clicon_handle h,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
if (xtop)
|
if (xtop)
|
||||||
xml_free(xtop);
|
xml_free(xtop);
|
||||||
if (xdata)
|
if (xdata)
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ clicon_rpc_netconf_xml(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generate clicon error function call from Netconf error message
|
/*! Generate and log clicon error function call from Netconf error message
|
||||||
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -308,7 +308,7 @@ clicon_rpc_get_config(clicon_handle h,
|
||||||
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
||||||
* @param[in] xml XML string. Ex: <config><a>..</a><b>...</b></config>
|
* @param[in] xml XML string. Ex: <config><a>..</a><b>...</b></config>
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error and logged to syslog
|
||||||
* @note xml arg need to have <config> as top element
|
* @note xml arg need to have <config> as top element
|
||||||
* @code
|
* @code
|
||||||
* if (clicon_rpc_edit_config(h, "running", OP_MERGE,
|
* if (clicon_rpc_edit_config(h, "running", OP_MERGE,
|
||||||
|
|
@ -361,6 +361,8 @@ clicon_rpc_edit_config(clicon_handle h,
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db1 src database, eg "running"
|
* @param[in] db1 src database, eg "running"
|
||||||
* @param[in] db2 dst database, eg "startup"
|
* @param[in] db2 dst database, eg "startup"
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
* @code
|
* @code
|
||||||
* if (clicon_rpc_copy_config(h, "running", "startup") < 0)
|
* if (clicon_rpc_copy_config(h, "running", "startup") < 0)
|
||||||
* err;
|
* err;
|
||||||
|
|
@ -396,6 +398,8 @@ clicon_rpc_copy_config(clicon_handle h,
|
||||||
/*! Send a request to backend to delete a config database
|
/*! Send a request to backend to delete a config database
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db database, eg "running"
|
* @param[in] db database, eg "running"
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
* @code
|
* @code
|
||||||
* if (clicon_rpc_delete_config(h, "startup") < 0)
|
* if (clicon_rpc_delete_config(h, "startup") < 0)
|
||||||
* err;
|
* err;
|
||||||
|
|
@ -430,6 +434,8 @@ clicon_rpc_delete_config(clicon_handle h,
|
||||||
/*! Lock a database
|
/*! Lock a database
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db database, eg "running"
|
* @param[in] db database, eg "running"
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_lock(clicon_handle h,
|
clicon_rpc_lock(clicon_handle h,
|
||||||
|
|
@ -460,6 +466,8 @@ clicon_rpc_lock(clicon_handle h,
|
||||||
/*! Unlock a database
|
/*! Unlock a database
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db database, eg "running"
|
* @param[in] db database, eg "running"
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_unlock(clicon_handle h,
|
clicon_rpc_unlock(clicon_handle h,
|
||||||
|
|
@ -557,6 +565,8 @@ clicon_rpc_get(clicon_handle h,
|
||||||
|
|
||||||
/*! Close a (user) session
|
/*! Close a (user) session
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_close_session(clicon_handle h)
|
clicon_rpc_close_session(clicon_handle h)
|
||||||
|
|
@ -586,6 +596,8 @@ clicon_rpc_close_session(clicon_handle h)
|
||||||
/*! Kill other user sessions
|
/*! Kill other user sessions
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] session_id Session id of other user session
|
* @param[in] session_id Session id of other user session
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_kill_session(clicon_handle h,
|
clicon_rpc_kill_session(clicon_handle h,
|
||||||
|
|
@ -617,6 +629,7 @@ clicon_rpc_kill_session(clicon_handle h,
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_validate(clicon_handle h,
|
clicon_rpc_validate(clicon_handle h,
|
||||||
|
|
@ -647,6 +660,7 @@ clicon_rpc_validate(clicon_handle h,
|
||||||
/*! Commit changes send a commit request to backend daemon
|
/*! Commit changes send a commit request to backend daemon
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_commit(clicon_handle h)
|
clicon_rpc_commit(clicon_handle h)
|
||||||
|
|
@ -676,6 +690,7 @@ clicon_rpc_commit(clicon_handle h)
|
||||||
/*! Discard all changes in candidate / revert to running
|
/*! Discard all changes in candidate / revert to running
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_discard_changes(clicon_handle h)
|
clicon_rpc_discard_changes(clicon_handle h)
|
||||||
|
|
@ -707,6 +722,9 @@ clicon_rpc_discard_changes(clicon_handle h)
|
||||||
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
|
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
|
||||||
* @param{in] filter message filter, eg xpath for xml notifications
|
* @param{in] filter message filter, eg xpath for xml notifications
|
||||||
* @param[out] s0 socket returned where notification mesages will appear
|
* @param[out] s0 socket returned where notification mesages will appear
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
|
|
||||||
* @note When using netconf create-subsrciption,status and format is not supported
|
* @note When using netconf create-subsrciption,status and format is not supported
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -744,6 +762,8 @@ clicon_rpc_create_subscription(clicon_handle h,
|
||||||
/*! Send a debug request to backend server
|
/*! Send a debug request to backend server
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] level Debug level
|
* @param[in] level Debug level
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error and logged to syslog
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_debug(clicon_handle h,
|
clicon_rpc_debug(clicon_handle h,
|
||||||
|
|
|
||||||
73
test/clixon
73
test/clixon
|
|
@ -1,73 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Top-level cron scripts. Add this to (for example) /etc/cron.daily
|
|
||||||
|
|
||||||
err(){
|
|
||||||
testname=$1
|
|
||||||
errcode=$2
|
|
||||||
echo "Error in [$testname]"
|
|
||||||
logger "CLIXON: Error in [$testname]"
|
|
||||||
exit $errcode
|
|
||||||
}
|
|
||||||
|
|
||||||
# cd to working dir
|
|
||||||
cd /var/tmp
|
|
||||||
if [ $# -ne 0 ]; then
|
|
||||||
err "usage: $0" 0
|
|
||||||
fi
|
|
||||||
rm -rf cligen
|
|
||||||
rm -rf clixon
|
|
||||||
git clone https://github.com/olofhagsand/cligen.git
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "git clone cligen" 1
|
|
||||||
fi
|
|
||||||
cd cligen
|
|
||||||
CFLAGS=-Werror ./configure
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "configure" 2
|
|
||||||
fi
|
|
||||||
make
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make" 3
|
|
||||||
fi
|
|
||||||
cd ..
|
|
||||||
git clone https://github.com/clicon/clixon.git
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "git clone clixon" 1
|
|
||||||
fi
|
|
||||||
cd clixon
|
|
||||||
CFLAGS=-Werror ./configure --with-cligen=../cligen
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "configure" 2
|
|
||||||
fi
|
|
||||||
make
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make" 3
|
|
||||||
fi
|
|
||||||
sudo make install
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make install" 4
|
|
||||||
fi
|
|
||||||
sudo make install-include
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make install include" 5
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cd example
|
|
||||||
make
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make example" 6
|
|
||||||
fi
|
|
||||||
sudo make install
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err "make install example" 7
|
|
||||||
fi
|
|
||||||
cd ../test
|
|
||||||
#./all.sh
|
|
||||||
(cd /home/olof/src/clixon/test; ./all.sh)
|
|
||||||
errcode=$?
|
|
||||||
if [ $errcode -ne 0 ]; then
|
|
||||||
err "test" $errcode
|
|
||||||
fi
|
|
||||||
cd ../..
|
|
||||||
rm -rf clixon cligen
|
|
||||||
logger "CLIXON: tests OK"
|
|
||||||
|
|
@ -128,7 +128,6 @@ new "restconf get empty config + state json"
|
||||||
expectfn "curl -sSG http://localhost/restconf/data" "{\"data\": $state}"
|
expectfn "curl -sSG http://localhost/restconf/data" "{\"data\": $state}"
|
||||||
|
|
||||||
new "restconf get empty config + state xml"
|
new "restconf get empty config + state xml"
|
||||||
# Cant get shell macros to work, inline matching from lib.sh
|
|
||||||
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)
|
||||||
expect="<data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data>"
|
expect="<data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data>"
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
|
@ -161,8 +160,19 @@ if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
new "restconf GET datastore"
|
||||||
|
expectfn "curl -s -X GET http://localhost/restconf/data" "data"
|
||||||
|
|
||||||
new "restconf Add subtree to datastore using POST"
|
new "restconf Add subtree to datastore using POST"
|
||||||
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' ""
|
ret=$(curl -s -i -X POST -H "Accept: application/yang-data+json" -d '{"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}}' http://localhost/restconf/data)
|
||||||
|
expect="HTTP/1.1 200 OK"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "restconf Re-add subtree which should give error"
|
||||||
|
expectfn 'curl -s -i -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' '{"error-tag": "operation-failed"'
|
||||||
|
|
||||||
new "restconf Check interfaces eth/0/0 added"
|
new "restconf Check interfaces eth/0/0 added"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}
|
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}
|
||||||
|
|
@ -182,7 +192,7 @@ expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface
|
||||||
$'
|
$'
|
||||||
|
|
||||||
new "restconf Re-post eth/0/0 which should generate error"
|
new "restconf Re-post eth/0/0 which should generate error"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces' "Data resource already exists"
|
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces' 'Object to create already exists'
|
||||||
|
|
||||||
new "Add leaf description using POST"
|
new "Add leaf description using POST"
|
||||||
expectfn 'curl -s -X POST -d {"description":"The-first-interface"} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
expectfn 'curl -s -X POST -d {"description":"The-first-interface"} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,10 @@ new "restconf POST interface"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' ""
|
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' ""
|
||||||
|
|
||||||
new "restconf POST again"
|
new "restconf POST again"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' "Data resource already exis"
|
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' "Object to create already exists"
|
||||||
|
|
||||||
new "restconf POST from top"
|
new "restconf POST from top"
|
||||||
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"TEST","type":"eth0"}}} http://localhost/restconf/data' "Data resource already exists"
|
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"TEST","type":"eth0"}}} http://localhost/restconf/data' "Object to create already exists"
|
||||||
|
|
||||||
new "restconf DELETE"
|
new "restconf DELETE"
|
||||||
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' ""
|
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' ""
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
# include err() and new() functions and creates $dir
|
# include err() and new() functions and creates $dir
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
fyang=$dir/type.yang
|
|
||||||
cfg=$dir/conf_yang.xml
|
cfg=$dir/conf_yang.xml
|
||||||
|
fyang=$dir/type.yang
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > $cfg
|
cat <<EOF > $cfg
|
||||||
<config>
|
<config>
|
||||||
|
|
@ -70,6 +72,57 @@ module example{
|
||||||
enum down;
|
enum down;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
leaf length1 {
|
||||||
|
type string {
|
||||||
|
length "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* leaf length2 {
|
||||||
|
type string {
|
||||||
|
length "max";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf length3 {
|
||||||
|
type string {
|
||||||
|
length "min";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
leaf length4 {
|
||||||
|
type string {
|
||||||
|
length "4..4000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* leaf length5 {
|
||||||
|
type string {
|
||||||
|
length "min..max";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
leaf num1 {
|
||||||
|
type int32 {
|
||||||
|
range "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* leaf num2 {
|
||||||
|
type int32 {
|
||||||
|
range "min";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf num3 {
|
||||||
|
type int32 {
|
||||||
|
range "max";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
leaf num4 {
|
||||||
|
type int32 {
|
||||||
|
range "4..4000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* leaf num5 {
|
||||||
|
type int32 {
|
||||||
|
range "min..max";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ module clixon-config {
|
||||||
type boolean;
|
type boolean;
|
||||||
default false;
|
default false;
|
||||||
description "If set, modifications in validation and commit
|
description "If set, modifications in validation and commit
|
||||||
callbacks will be saved into running";
|
callbacks are written back into the datastore";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue