restconf post/put/head/patch, memtests
This commit is contained in:
parent
eec5896797
commit
19813a4d9c
17 changed files with 782 additions and 365 deletions
|
|
@ -85,7 +85,6 @@ client_subscription_add(struct client_entry *ce,
|
|||
{
|
||||
struct client_subscription *su = NULL;
|
||||
|
||||
fprintf(stderr, "%s stream:%s filter:%s\n", __FUNCTION__, stream, filter);
|
||||
if ((su = malloc(sizeof(*su))) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "malloc");
|
||||
goto done;
|
||||
|
|
@ -118,7 +117,6 @@ client_subscription_delete(struct client_entry *ce,
|
|||
struct client_subscription *su;
|
||||
struct client_subscription **su_prev;
|
||||
|
||||
fprintf(stderr, "%s stream:%s\n", __FUNCTION__, su0->su_stream);
|
||||
su_prev = &ce->ce_subscription; /* this points to stack and is not real backpointer */
|
||||
for (su = *su_prev; su; su = su->su_next){
|
||||
if (su == su0){
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ backend_netconf_register_callback(clicon_handle h,
|
|||
memset (nr, 0, sizeof (*nr));
|
||||
nr->nr_callback = cb;
|
||||
nr->nr_arg = arg;
|
||||
nr->nr_tag = strdup(tag); /* strdup */
|
||||
nr->nr_tag = strdup(tag); /* XXX strdup memleak */
|
||||
INSQ(nr, deps);
|
||||
return 0;
|
||||
catch:
|
||||
|
|
|
|||
|
|
@ -614,8 +614,6 @@ show_confv_as_command(clicon_handle h,
|
|||
enum genmodel_type gt;
|
||||
int retval = -1;
|
||||
|
||||
if ((xt = xml_new("tmp", NULL)) == NULL)
|
||||
goto done;
|
||||
if (show_confv_as(h, cvv, argv, &xt) < 0)
|
||||
goto done;
|
||||
xc = NULL; /* Dont print xt itself */
|
||||
|
|
|
|||
|
|
@ -178,7 +178,8 @@ netconf_input_cb(int s,
|
|||
unsigned char buf[BUFSIZ];
|
||||
int i;
|
||||
int len;
|
||||
static cbuf *cb; /* XXX: should use ce state? */
|
||||
// static cbuf *cb; /* XXX: should use ce state? */
|
||||
cbuf *cb=NULL; /* XXX: should use ce state? */
|
||||
int xml_state = 0;
|
||||
int retval = -1;
|
||||
|
||||
|
|
@ -221,7 +222,8 @@ netconf_input_cb(int s,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
// cbuf_free(cb);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cc_closed)
|
||||
retval = -1;
|
||||
return retval;
|
||||
|
|
@ -257,9 +259,11 @@ netconf_terminate(clicon_handle h)
|
|||
{
|
||||
yang_spec *yspec;
|
||||
|
||||
|
||||
clicon_rpc_close_session(h);
|
||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||
yspec_free(yspec);
|
||||
event_exit();
|
||||
clicon_handle_exit(h);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ CPPFLAGS = @CPPFLAGS@
|
|||
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
SRC = restconf_lib.c
|
||||
SRC += restconf_methods.c
|
||||
|
||||
OBJS = $(SRC:.c=.o)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,39 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,36 @@
|
|||
/*
|
||||
*
|
||||
* $COPYRIGHTSTATEMENT$
|
||||
*
|
||||
* $LICENSE$
|
||||
* This is backend headend sender code, ie communication with a pmagent
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _RESTCONF_LIB_H_
|
||||
|
|
|
|||
|
|
@ -66,7 +66,9 @@
|
|||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
/* restconf */
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_methods.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hDf:p:"
|
||||
|
|
@ -75,301 +77,6 @@
|
|||
resource ([RFC6415]) */
|
||||
#define RESTCONF_API_ROOT "/restconf/"
|
||||
|
||||
/*! REST OPTIONS method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] head Set if HEAD request instead of GET
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
*/
|
||||
static int
|
||||
api_data_options(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
int head)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "GET, HEAD, OPTIONS, PUT, POST, DELETE\r\n");
|
||||
retval = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST GET method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||
* I would like to support both xml and json.
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang.data+xml
|
||||
* Content-Type: application/yang.data+json
|
||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
* "400 Bad Request" status-line MUST be returned by the server.
|
||||
*/
|
||||
static int
|
||||
api_data_get(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv;
|
||||
char *val;
|
||||
char *v;
|
||||
int i;
|
||||
cbuf *path = NULL;
|
||||
cbuf *path1 = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj **vec = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *y;
|
||||
yang_stmt *ykey;
|
||||
char *name;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((path = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
||||
goto done;
|
||||
cv = NULL;
|
||||
cprintf(path1, "/");
|
||||
/* translate eg a/b=c -> a/[b=c] */
|
||||
for (i=pi; i<cvec_len(pcvec); i++){
|
||||
cv = cvec_i(pcvec, i);
|
||||
name = cv_name_get(cv);
|
||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||
if (i == pi){
|
||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Check if has value, means '=' */
|
||||
if (cv2str(cv, NULL, 0) > 0){
|
||||
if ((val = cv2str_dup(cv)) == NULL)
|
||||
goto done;
|
||||
v = val;
|
||||
/* XXX sync with yang */
|
||||
while((v=index(v, ',')) != NULL){
|
||||
*v = '\0';
|
||||
v++;
|
||||
}
|
||||
/* Find keys */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
cprintf(path, "/%s", name);
|
||||
v = val;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
||||
v += strlen(v)+1;
|
||||
}
|
||||
if (val)
|
||||
free(val);
|
||||
}
|
||||
else{
|
||||
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
||||
cprintf(path1, "/%s", name);
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
|
||||
vec = xml_childvec_get(xret);
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (path)
|
||||
cbuf_free(path);
|
||||
if (path1)
|
||||
cbuf_free(path1);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST DELETE method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pi Offset, where path starts
|
||||
* Example:
|
||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
||||
*/
|
||||
static int
|
||||
api_data_delete(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
int pi)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||
for (i=0; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
if (clicon_rpc_edit_config(h, "candidate",
|
||||
OP_REMOVE,
|
||||
api_path,
|
||||
"<config/>") < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_commit(h) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(201, r->out);
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST PUT method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input data
|
||||
* @param[in] post POST instead of PUT
|
||||
* 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,
|
||||
a "201 Created" status-line is returned. If an existing resource is
|
||||
modified, a "204 No Content" status-line is returned.
|
||||
|
||||
POST:
|
||||
If the POST method succeeds, a "201 Created" status-line is returned
|
||||
and there is no response message-body. A "Location" header
|
||||
identifying the child resource that was created MUST be present in
|
||||
the response in this case.
|
||||
|
||||
If the data resource already exists, then the POST request MUST fail
|
||||
and a "409 Conflict" status-line MUST be returned.
|
||||
*/
|
||||
static int
|
||||
api_data_put(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data,
|
||||
int post)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *x;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s json:%s",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
for (i=0; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json fail", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<config>");
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xdata, x, -1)) != NULL) {
|
||||
if (clicon_xml2cbuf(cbx, x, 0, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbx, "</config>");
|
||||
clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
|
||||
if (clicon_rpc_edit_config(h, "candidate",
|
||||
OP_MERGE,
|
||||
api_path,
|
||||
cbuf_get(cbx)) < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (clicon_rpc_commit(h) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
|
|
@ -393,14 +100,17 @@ api_data(clicon_handle h,
|
|||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
if (strcmp(request_method, "OPTIONS")==0)
|
||||
retval = api_data_options(h, r, pcvec, pi, qvec, 0);
|
||||
retval = api_data_options(h, r);
|
||||
else if (strcmp(request_method, "HEAD")==0)
|
||||
retval = api_data_head(h, r, pcvec, pi, qvec);
|
||||
else if (strcmp(request_method, "GET")==0)
|
||||
retval = api_data_get(h, r, pcvec, pi, qvec);
|
||||
else if (strcmp(request_method, "PUT")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 0);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 1);
|
||||
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data);
|
||||
else if (strcmp(request_method, "PUT")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data);
|
||||
else if (strcmp(request_method, "DELETE")==0)
|
||||
retval = api_data_delete(h, r, api_path, pi);
|
||||
else
|
||||
|
|
@ -470,7 +180,7 @@ request_process(clicon_handle h,
|
|||
else
|
||||
retval = notfound(r);
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
clicon_debug(1, "%s retval:%d K", __FUNCTION__, retval);
|
||||
if (dvec)
|
||||
cvec_free(dvec);
|
||||
if (qvec)
|
||||
|
|
@ -488,6 +198,7 @@ restconf_terminate(clicon_handle h)
|
|||
{
|
||||
yang_spec *yspec;
|
||||
|
||||
clicon_debug(0, "%s", __FUNCTION__);
|
||||
clicon_rpc_close_session(h);
|
||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||
yspec_free(yspec);
|
||||
|
|
@ -495,6 +206,27 @@ restconf_terminate(clicon_handle h)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Need global variable to for signal handler */
|
||||
static clicon_handle _CLICON_HANDLE = NULL;
|
||||
|
||||
/*! Signall terminates process
|
||||
*/
|
||||
static void
|
||||
restconf_sig_term(int arg)
|
||||
{
|
||||
static int i=0;
|
||||
|
||||
if (i++ == 0)
|
||||
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
||||
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
||||
else
|
||||
exit(-1);
|
||||
if (_CLICON_HANDLE)
|
||||
restconf_terminate(_CLICON_HANDLE);
|
||||
clicon_exit_set(); /* checked in event_loop() */
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*! Usage help routine
|
||||
* @param[in] argv0 command line
|
||||
* @param[in] h Clicon handle
|
||||
|
|
@ -538,7 +270,7 @@ main(int argc,
|
|||
/* Create handle */
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
goto done;
|
||||
|
||||
_CLICON_HANDLE = h; /* for termination handling */
|
||||
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
|
|
@ -566,6 +298,15 @@ main(int argc,
|
|||
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_SYSLOG);
|
||||
clicon_debug_init(debug, NULL);
|
||||
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
|
||||
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
|
||||
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||
goto done;
|
||||
}
|
||||
if (set_signal(SIGINT, restconf_sig_term, NULL) < 0){
|
||||
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Find and read configfile */
|
||||
if (clicon_options_main(h) < 0)
|
||||
|
|
|
|||
513
apps/restconf/restconf_methods.c
Normal file
513
apps/restconf/restconf_methods.c
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* See draft-ietf-netconf-restconf-13.txt [draft]
|
||||
* See draft-ietf-netconf-restconf-17.txt [draft]
|
||||
|
||||
* sudo apt-get install libfcgi-dev
|
||||
* gcc -o fastcgi fastcgi.c -lfcgi
|
||||
|
||||
* sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data
|
||||
|
||||
* This is the interface:
|
||||
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
|
||||
* api/test
|
||||
+----------------------------+--------------------------------------+
|
||||
| 100 Continue | POST accepted, 201 should follow |
|
||||
| 200 OK | Success with response message-body |
|
||||
| 201 Created | POST to create a resource success |
|
||||
| 204 No Content | Success without response message- |
|
||||
| | body |
|
||||
| 304 Not Modified | Conditional operation not done |
|
||||
| 400 Bad Request | Invalid request message |
|
||||
| 401 Unauthorized | Client cannot be authenticated |
|
||||
| 403 Forbidden | Access to resource denied |
|
||||
| 404 Not Found | Resource target or resource node not |
|
||||
| | found |
|
||||
| 405 Method Not Allowed | Method not allowed for target |
|
||||
| | resource |
|
||||
| 409 Conflict | Resource or lock in use |
|
||||
| 412 Precondition Failed | Conditional method is false |
|
||||
| 413 Request Entity Too | too-big error |
|
||||
| Large | |
|
||||
| 414 Request-URI Too Large | too-big error |
|
||||
| 415 Unsupported Media Type | non RESTCONF media type |
|
||||
| 500 Internal Server Error | operation-failed |
|
||||
| 501 Not Implemented | unknown-operation |
|
||||
| 503 Service Unavailable | Recoverable server error |
|
||||
+----------------------------+--------------------------------------+
|
||||
Mapping netconf error-tag -> status code
|
||||
+-------------------------+-------------+
|
||||
| <error‑tag> | status code |
|
||||
+-------------------------+-------------+
|
||||
| in-use | 409 |
|
||||
| invalid-value | 400 |
|
||||
| too-big | 413 |
|
||||
| missing-attribute | 400 |
|
||||
| bad-attribute | 400 |
|
||||
| unknown-attribute | 400 |
|
||||
| bad-element | 400 |
|
||||
| unknown-element | 400 |
|
||||
| unknown-namespace | 400 |
|
||||
| access-denied | 403 |
|
||||
| lock-denied | 409 |
|
||||
| resource-denied | 409 |
|
||||
| rollback-failed | 500 |
|
||||
| data-exists | 409 |
|
||||
| data-missing | 409 |
|
||||
| operation-not-supported | 501 |
|
||||
| operation-failed | 500 |
|
||||
| partial-operation | 500 |
|
||||
| malformed-message | 400 |
|
||||
+-------------------------+-------------+
|
||||
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <fcgi_stdio.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "restconf_lib.h"
|
||||
#include "restconf_methods.h"
|
||||
|
||||
/*! REST OPTIONS method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* Minimal support:
|
||||
* 200 OK
|
||||
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
||||
*/
|
||||
int
|
||||
api_data_options(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generic GET (both HEAD and GET)
|
||||
*/
|
||||
static int
|
||||
api_data_get_gen(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
int head)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv;
|
||||
char *val;
|
||||
char *v;
|
||||
int i;
|
||||
cbuf *path = NULL;
|
||||
cbuf *path1 = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj **vec = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *y;
|
||||
yang_stmt *ykey;
|
||||
char *name;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((path = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
||||
goto done;
|
||||
cv = NULL;
|
||||
cprintf(path1, "/");
|
||||
/* translate eg a/b=c -> a/[b=c] */
|
||||
for (i=pi; i<cvec_len(pcvec); i++){
|
||||
cv = cvec_i(pcvec, i);
|
||||
name = cv_name_get(cv);
|
||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||
if (i == pi){
|
||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Check if has value, means '=' */
|
||||
if (cv2str(cv, NULL, 0) > 0){
|
||||
if ((val = cv2str_dup(cv)) == NULL)
|
||||
goto done;
|
||||
v = val;
|
||||
/* XXX sync with yang */
|
||||
while((v=index(v, ',')) != NULL){
|
||||
*v = '\0';
|
||||
v++;
|
||||
}
|
||||
/* Find keys */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
cprintf(path, "/%s", name);
|
||||
v = val;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
||||
v += strlen(v)+1;
|
||||
}
|
||||
if (val)
|
||||
free(val);
|
||||
}
|
||||
else{
|
||||
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
||||
cprintf(path1, "/%s", name);
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
{
|
||||
cbuf *cb = cbuf_new();
|
||||
clicon_xml2cbuf(cb, xret, 0, 0);
|
||||
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
|
||||
cbuf_free(cb);
|
||||
}
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (head)
|
||||
goto ok;
|
||||
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
|
||||
vec = xml_childvec_get(xret);
|
||||
clicon_debug(1, "%s xretnr:%d", __FUNCTION__, xml_child_nr(xret));
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (path)
|
||||
cbuf_free(path);
|
||||
if (path1)
|
||||
cbuf_free(path1);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! REST HEAD method
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
The HEAD method is sent by the client to retrieve just the header fields
|
||||
that would be returned for the comparable GET method, without the
|
||||
response message-body.
|
||||
* Relation to netconf: none
|
||||
*/
|
||||
int
|
||||
api_data_head(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec)
|
||||
{
|
||||
return api_data_get_gen(h, r, pcvec, pi, qvec, 1);
|
||||
}
|
||||
|
||||
/*! REST GET method
|
||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||
* I would like to support both xml and json.
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang.data+xml
|
||||
* Content-Type: application/yang.data+json
|
||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
* "400 Bad Request" status-line MUST be returned by the server.
|
||||
* Netconf: <get-config>, <get>
|
||||
*/
|
||||
int
|
||||
api_data_get(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec)
|
||||
{
|
||||
return api_data_get_gen(h, r, pcvec, pi, qvec, 0);
|
||||
}
|
||||
|
||||
/*! Generic edit-config method: PUT/POST/PATCH
|
||||
*/
|
||||
static int
|
||||
api_data_edit(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data,
|
||||
enum operation_type operation)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *x;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s json:%s",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
for (i=0; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json fail", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<config>");
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xdata, x, -1)) != NULL) {
|
||||
if (clicon_xml2cbuf(cbx, x, 0, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbx, "</config>");
|
||||
clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
|
||||
if (clicon_rpc_edit_config(h, "candidate",
|
||||
operation,
|
||||
api_path,
|
||||
cbuf_get(cbx)) < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (clicon_rpc_commit(h) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! REST POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
POST:
|
||||
If the POST method succeeds, a "201 Created" status-line is returned
|
||||
and there is no response message-body. A "Location" header
|
||||
identifying the child resource that was created MUST be present in
|
||||
the response in this case.
|
||||
|
||||
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
|
||||
*/
|
||||
int
|
||||
api_data_post(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_CREATE);
|
||||
}
|
||||
|
||||
/*! Generic REST PUT method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @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
|
||||
*
|
||||
PUT:
|
||||
if the PUT request creates a new resource,
|
||||
a "201 Created" status-line is returned. If an existing resource is
|
||||
modified, a "204 No Content" status-line is returned.
|
||||
|
||||
* Netconf: <edit-config> (nc:operation="create/replace")
|
||||
*/
|
||||
int
|
||||
api_data_put(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
/* XXX: OP_CREATE? */
|
||||
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_REPLACE);
|
||||
}
|
||||
|
||||
/*! Generic REST PATCH method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* Netconf: <edit-config> (nc:operation="merge")
|
||||
*/
|
||||
int
|
||||
api_data_patch(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE);
|
||||
}
|
||||
|
||||
/*! Generic REST DELETE method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pi Offset, where path starts
|
||||
* Example:
|
||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
||||
* Netconf: <edit-config> (nc:operation="delete")
|
||||
*/
|
||||
int
|
||||
api_data_delete(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
int pi)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||
for (i=0; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
if (clicon_rpc_edit_config(h, "candidate",
|
||||
OP_DELETE,
|
||||
api_path,
|
||||
"<config/>") < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_commit(h) < 0)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(201, r->out);
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
63
apps/restconf/restconf_methods.h
Normal file
63
apps/restconf/restconf_methods.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _RESTCONF_METHODS_H_
|
||||
#define _RESTCONF_METHODS_H_
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int api_data_options(clicon_handle h, FCGX_Request *r);
|
||||
int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||
cvec *qvec);
|
||||
int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||
cvec *qvec);
|
||||
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data);
|
||||
int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data);
|
||||
int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data);
|
||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_H_ */
|
||||
|
|
@ -62,7 +62,7 @@ int
|
|||
transaction_validate(clicon_handle h,
|
||||
transaction_data td)
|
||||
{
|
||||
transaction_print(stderr, td);
|
||||
// transaction_print(stderr, td);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -73,16 +73,18 @@ transaction_commit(clicon_handle h,
|
|||
transaction_data td)
|
||||
{
|
||||
cxobj *target = transaction_target(td); /* wanted XML tree */
|
||||
cxobj **vec;
|
||||
cxobj **vec = NULL;
|
||||
int i;
|
||||
size_t len;
|
||||
|
||||
/* Get all added i/fs */
|
||||
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
||||
return -1;
|
||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||
xml_print(stdout, vec[i]); /* Print the added interface */
|
||||
|
||||
if (debug)
|
||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||
xml_print(stdout, vec[i]); /* Print the added interface */
|
||||
if (vec)
|
||||
free(vec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ clicon_rpc_msg(clicon_handle h,
|
|||
goto done;
|
||||
break;
|
||||
}
|
||||
clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);
|
||||
if (retdata &&
|
||||
clicon_xml_parse_str(retdata, &xret) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -1067,7 +1067,6 @@ xmldb_get(clicon_handle h,
|
|||
goto done;
|
||||
if (debug)
|
||||
clicon_xml2file(stdout, xt, 0, 1);
|
||||
|
||||
*xtop = xt;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -921,8 +921,8 @@ xpath_each(cxobj *cxtop,
|
|||
|
||||
/*! A restricted xpath that returns a vector of matches
|
||||
*
|
||||
* See xpath1() on details for subset.
|
||||
* @param[in] cxtop xml-tree where to search
|
||||
* See xpath1() on details for subset
|
||||
. * @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
|
|
@ -940,7 +940,7 @@ xpath_each(cxobj *cxtop,
|
|||
* }
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* Note that although the returned vector must be freed after use, the returned xml
|
||||
* @Note that although the returned vector must be freed after use, the returned xml
|
||||
* trees need not be.
|
||||
* @see also xpath_first, xpath_each.
|
||||
*/
|
||||
|
|
@ -982,7 +982,27 @@ xpath_vec(cxobj *cxtop,
|
|||
}
|
||||
|
||||
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
|
||||
* @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
* @retval 0 OK
|
||||
* @retval -1 error.
|
||||
* @code
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec_flag(cxtop, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
* ...
|
||||
* }
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* @Note that although the returned vector must be freed after use, the returned xml
|
||||
* trees need not be.
|
||||
* @see also xpath_vec This is a specialized version.
|
||||
*/
|
||||
int
|
||||
xpath_vec_flag(cxobj *cxtop,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
||||
# For memcheck
|
||||
#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli"
|
||||
clixon_cli=clixon_cli
|
||||
|
||||
# kill old backend (if any)
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $clixon_cf
|
||||
|
|
@ -23,65 +27,67 @@ if [ $? -ne 0 ]; then
|
|||
err
|
||||
fi
|
||||
new "cli configure top"
|
||||
expectfn "clixon_cli -1f $clixon_cf set interfaces" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
|
||||
|
||||
new "cli show configuration top"
|
||||
expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces$"
|
||||
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces$"
|
||||
|
||||
new "cli configure delete top"
|
||||
expectfn "clixon_cli -1f $clixon_cf delete interfaces" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf delete interfaces" ""
|
||||
|
||||
new "cli show configuration delete top"
|
||||
expectfn "clixon_cli -1f $clixon_cf show conf cli" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf show conf cli" ""
|
||||
|
||||
new "cli configure"
|
||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
|
||||
|
||||
new "cli show configuration"
|
||||
expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
|
||||
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
|
||||
interfaces interface enabled true$"
|
||||
|
||||
new "cli failed validate"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"
|
||||
|
||||
new "cli configure more"
|
||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
|
||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 description mydesc" ""
|
||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 description mydesc" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
|
||||
|
||||
new "cli show xpath description"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" "<description>mydesc</description>"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" "<description>mydesc</description>"
|
||||
|
||||
new "cli delete description"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o delete interfaces interface eth0 description mydesc"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o delete interfaces interface eth0 description mydesc"
|
||||
|
||||
new "cli show xpath no description"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" ""
|
||||
|
||||
new "cli success validate"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o validate" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o validate" ""
|
||||
|
||||
new "cli commit"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o commit" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o commit" ""
|
||||
|
||||
new "cli save"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o save /tmp/foo" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o save /tmp/foo" ""
|
||||
|
||||
new "cli delete all"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o delete all" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o delete all" ""
|
||||
|
||||
new "cli load"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o load /tmp/foo" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o load /tmp/foo" ""
|
||||
|
||||
new "cli check load"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0
|
||||
interfaces interface enabled true$"
|
||||
|
||||
new "cli debug"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o debug level 1" ""
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 1" ""
|
||||
# How to test this?
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 0" ""
|
||||
|
||||
new "cli downcall"
|
||||
expectfn "clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$"
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
||||
# For memcheck
|
||||
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||
clixon_netconf=clixon_netconf
|
||||
|
||||
# kill old backend (if any)
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $clixon_cf
|
||||
|
|
@ -17,61 +21,61 @@ if [ $? -ne 0 ]; then
|
|||
err
|
||||
fi
|
||||
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 "valgrind $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 "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"
|
||||
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 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>"
|
||||
|
||||
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>]]>]]>$"
|
||||
|
||||
new "netconf get empty config2"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit config patch"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
new "netconf edit config eth1"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf lock/unlock"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf lock/lock"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf lock"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "close-session"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><close-session/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><close-session/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "kill-session"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "copy startup"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get startup"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf delete startup"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf check empty startup"
|
||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf subscription"
|
||||
expectwait "clixon_netconf -qf $clixon_cf" "<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 $clixon_cf" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
# kill old backend (if any)
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $clixon_cf
|
||||
#sudo clixon_backend -zf $clixon_cf
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
|
|
@ -24,6 +24,9 @@ sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c
|
|||
|
||||
sleep 1
|
||||
|
||||
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
$"
|
||||
|
||||
|
|
@ -34,8 +37,12 @@ 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 patch config"
|
||||
expectfn 'curl -sX POST -d {"type":"type"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||
# XXX POST/PUT/PATCH
|
||||
|
||||
new "restconf delete config"
|
||||
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
|
||||
|
|
@ -45,7 +52,7 @@ expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface":
|
|||
$'
|
||||
|
||||
new "Kill restconf daemon"
|
||||
sudo pkill -u www-data clixon_restconf
|
||||
#sudo pkill -u www-data clixon_restconf
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue