- removed api_path extension from internal netconf

- Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
This commit is contained in:
Olof hagsand 2017-05-30 21:57:06 +02:00
parent 683376610c
commit 3453dae0db
36 changed files with 1048 additions and 1307 deletions

View file

@ -256,8 +256,6 @@ from_client_get_config(clicon_handle h,
* @param[in] xe Netconf request xml tree
* @param[in] mypid Process/session id of calling client
* @param[out] cbret Return xml value cligen buffer
* CLIXON addition:
* <filter type="restconf" select="/data/profile=a" />
*/
static int
from_client_edit_config(clicon_handle h,
@ -270,10 +268,8 @@ from_client_edit_config(clicon_handle h,
cbuf *cb = NULL;
cxobj *xret = NULL;
cxobj *xc;
cxobj *xfilter;
cxobj *x;
enum operation_type operation = OP_MERGE;
char *api_path = NULL;
int piddb;
if ((target = netconf_db_find(xn, "target")) == NULL){
@ -293,9 +289,6 @@ from_client_edit_config(clicon_handle h,
piddb);
goto ok;
}
/* ie <filter type="restconf" select=<api-path> /> */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
api_path = xml_find_value(xfilter, "select");
if ((x = xpath_first(xn, "default-operation")) != NULL){
if (xml_operation(xml_body(x), &operation) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
@ -307,7 +300,7 @@ from_client_edit_config(clicon_handle h,
}
}
if ((xc = xpath_first(xn, "config")) != NULL){
if (xmldb_put(h, target, operation, api_path, xc) < 0){
if (xmldb_put(h, target, operation, xc) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>protocol</error-type>"

View file

@ -194,7 +194,7 @@ rundb_main(clicon_handle h,
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if (xmldb_put(h, "tmp", OP_MERGE, NULL, xn) < 0)
if (xmldb_put(h, "tmp", OP_MERGE, xn) < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;

View file

@ -207,38 +207,64 @@ cli_dbxml(clicon_handle h,
{
int retval = -1;
char *str = NULL;
char *xkfmt; /* xml key format */
char *xk = NULL; /* xml key */
char *api_path_fmt; /* xml key format */
char *api_path = NULL; /* xml key */
cg_var *cval;
int len;
cg_var *arg;
cbuf *cb = NULL;
yang_spec *yspec;
cxobj *xbot = NULL; /* xpath, NULL if datastore */
yang_node *y = NULL; /* yang spec of xpath */
cxobj *xtop = NULL; /* xpath root */
cxobj *xa; /* attribute */
cxobj *xb; /* body */
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be xml key format string", __FUNCTION__);
goto done;
}
arg = cvec_i(argv, 0);
xkfmt = cv_string_get(arg);
if (xmlkeyfmt2key(xkfmt, cvv, &xk) < 0)
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
len = cvec_len(cvv);
if (len > 1){
cval = cvec_i(cvv, len-1);
if ((str = cv2str_dup(cval)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
arg = cvec_i(argv, 0);
api_path_fmt = cv_string_get(arg);
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
goto done;
/* Create config top-of-tree */
if ((xtop = xml_new("config", NULL)) == NULL)
goto done;
xbot = xtop;
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
goto done;
if ((xa = xml_new("operation", xbot)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
if (y->yn_keyword != Y_LIST){
len = cvec_len(cvv);
if (len > 1){
cval = cvec_i(cvv, len-1);
if ((str = cv2str_dup(cval)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if ((xb = xml_new("body", xbot)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, str) < 0)
goto done;
}
}
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (str)
cprintf(cb, "<config>%s</config>", str);
else
cprintf(cb, "<config/>");
if (clicon_rpc_edit_config(h, "candidate", op, xk, cbuf_get(cb)) < 0)
if (clicon_xml2cbuf(cb, xtop, 0, 0) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate", OP_NONE, cbuf_get(cb)) < 0)
goto done;
if (clicon_autocommit(h)) {
if (clicon_rpc_commit(h) < 0)
@ -250,8 +276,10 @@ cli_dbxml(clicon_handle h,
cbuf_free(cb);
if (str)
free(str);
if (xk)
free(xk);
if (api_path)
free(api_path);
if (xtop)
xml_free(xtop);
return retval;
}
@ -287,6 +315,31 @@ int cli_mergev(clicon_handle h, cvec *vars, cvec *argv)
return cli_merge(h, vars, argv);
}
int
cli_create(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = -1;
if (cli_dbxml(h, cvv, argv, OP_CREATE) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*!
* @see cli_del
*/
int
cli_remove(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = -1;
if (cli_dbxml(h, cvv, argv, OP_REMOVE) < 0)
goto done;
retval = 0;
done:
return retval;
}
int
cli_del(clicon_handle h, cvec *cvv, cvec *argv)
@ -704,7 +757,6 @@ load_config_file(clicon_handle h,
}
if (clicon_rpc_edit_config(h, "candidate",
replace?OP_REPLACE:OP_MERGE,
"",
cbuf_get(cbxml)) < 0)
goto done;
cbuf_free(cbxml);
@ -1135,7 +1187,7 @@ cli_copy_config(clicon_handle h,
cbuf_reset(cb);
/* create xml copy tree and merge it with database configuration */
clicon_xml2cbuf(cb, x2, 0, 0);
if (clicon_rpc_edit_config(h, db, OP_MERGE, NULL, cbuf_get(cb)) < 0)
if (clicon_rpc_edit_config(h, db, OP_MERGE, cbuf_get(cb)) < 0)
goto done;
retval = 0;
done:

View file

@ -64,7 +64,7 @@
#include "cli_generate.h"
/* This is the default callback function. But this is typically overwritten */
#define GENERATE_CALLBACK "cli_set"
#define GENERATE_CALLBACK "overwrite_me"
/* variable expand function */
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
@ -115,13 +115,12 @@ cli_expand_var_generate(clicon_handle h,
enum cv_type cvtype,
cbuf *cb0,
int options,
uint8_t fraction_digits
)
uint8_t fraction_digits)
{
int retval = -1;
char *xkfmt = NULL;
char *api_path_fmt = NULL;
if (yang2xmlkeyfmt(ys, 1, &xkfmt) < 0)
if (yang2api_path_fmt(ys, 1, &api_path_fmt) < 0)
goto done;
cprintf(cb0, "|<%s:%s", ys->ys_argument,
cv_type2str(cvtype));
@ -129,15 +128,15 @@ cli_expand_var_generate(clicon_handle h,
cprintf(cb0, " fraction-digits:%u", fraction_digits);
cprintf(cb0, " %s(\"candidate\",\"%s\")>",
GENERATE_EXPAND_XMLDB,
xkfmt);
api_path_fmt);
retval = 0;
done:
if (xkfmt)
free(xkfmt);
if (api_path_fmt)
free(api_path_fmt);
return retval;
}
/*! Create callback with xmlkey format string as argument
/*! Create callback with api_path format string as argument
* @param[in] h clicon handle
* @param[in] ys yang_stmt of the node at hand
* @param[in] cb0 The string where the result format string is inserted.
@ -149,15 +148,16 @@ cli_callback_generate(clicon_handle h,
cbuf *cb0)
{
int retval = -1;
char *xkfmt = NULL;
char *api_path_fmt = NULL;
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0)
goto done;
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK,
api_path_fmt);
retval = 0;
done:
if (xkfmt)
free(xkfmt);
if (api_path_fmt)
free(api_path_fmt);
return retval;
}
@ -166,8 +166,7 @@ static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
enum genmodel_type gt,
int level);
/*
* Check for completion (of already existent values), ranges (eg range[min:max]) and
/*! Check for completion (of already existent values), ranges (eg range[min:max]) and
* patterns, (eg regexp:"[0.9]*").
*/
static int
@ -403,7 +402,6 @@ yang2cli_leaf(clicon_handle h,
return retval;
}
static int
yang2cli_container(clicon_handle h,
yang_stmt *ys,

View file

@ -99,10 +99,10 @@ expand_dbvar(void *h,
cvec *helptexts)
{
int retval = -1;
char *xkfmt;
char *api_path;
char *dbstr;
cxobj *xt = NULL;
char *xkpath = NULL;
char *xpath = NULL;
cxobj **xvec = NULL;
size_t xlen = 0;
cxobj *x;
@ -129,23 +129,24 @@ expand_dbvar(void *h,
goto done;
}
if ((cv = cvec_i(argv, 1)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <xkfmt>");
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <api_path>");
goto done;
}
xkfmt = cv_string_get(cv);
/* xkfmt = /interface/%s/address/%s
api_path = cv_string_get(cv);
/* api_path = /interface/%s/address/%s
--> ^/interface/eth0/address/.*$
--> /interface/[name=eth0]/address
*/
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
if (api_path_fmt2xpath(api_path, cvv, &xpath) < 0)
goto done;
/* XXX read whole configuration, why not send xpath? */
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
goto done;
/* One round to detect duplicates
* XXX The code below would benefit from some cleanup
*/
j = 0;
if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0)
if (xpath_vec(xt, xpath, &xvec, &xlen) < 0)
goto done;
for (i = 0; i < xlen; i++) {
char *str;
@ -190,8 +191,8 @@ expand_dbvar(void *h,
free(xvec);
if (xt)
xml_free(xt);
if (xkpath)
free(xkpath);
if (xpath)
free(xpath);
return retval;
}
int

View file

@ -82,6 +82,9 @@ int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
int cli_merge(clicon_handle h, cvec *vars, cvec *argv);
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
int cli_create(clicon_handle h, cvec *vars, cvec *argv);
int cli_remove(clicon_handle h, cvec *vars, cvec *argv);
int cli_del(clicon_handle h, cvec *vars, cvec *argv);
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);

View file

@ -67,8 +67,7 @@ curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"et
Start the restconf fastcgi program with debug flag:
```
sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-
data
sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-data
```
Look at syslog:
```

View file

@ -69,11 +69,12 @@ notfound(FCGX_Request *r)
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
FCGX_FPrintF(r->out, "Status: 404\r\n"); /* 404 not found */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Clixon Not Found</h1>\n");
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
path);
return 0;
}
int
badrequest(FCGX_Request *r)
{
@ -89,6 +90,16 @@ badrequest(FCGX_Request *r)
return 0;
}
int
conflict(FCGX_Request *r)
{
clicon_debug(1, "%s", __FUNCTION__);
FCGX_FPrintF(r->out, "Status: 409\r\n"); /* 409 Conflict */
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
FCGX_FPrintF(r->out, "<h1>Data resource already exists</h1>\n");
return 0;
}
/*! Specialization of clicon_debug with xml tree */
int
clicon_debug_xml(int dbglevel,

View file

@ -45,6 +45,7 @@
*/
int notfound(FCGX_Request *r);
int badrequest(FCGX_Request *r);
int conflict(FCGX_Request *r);
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);

View file

@ -141,7 +141,7 @@ request_process(clicon_handle h,
int auth = 0;
clicon_debug(1, "%s", __FUNCTION__);
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
path = FCGX_GetParam("REQUEST_URI", r->envp);
query = FCGX_GetParam("QUERY_STRING", r->envp);
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
goto done;
@ -359,7 +359,7 @@ main(int argc,
goto done;
}
clicon_debug(1, "------------");
if ((path = FCGX_GetParam("DOCUMENT_URI", r->envp)) != NULL){
if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0 ||
strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)-1) == 0)
request_process(h, r);

View file

@ -34,8 +34,7 @@
*/
/*
* See draft-ietf-netconf-restconf-13.txt [draft]
* See draft-ietf-netconf-restconf-17.txt [draft]
* See rfc8040
* sudo apt-get install libfcgi-dev
* gcc -o fastcgi fastcgi.c -lfcgi
@ -120,7 +119,7 @@ Mapping netconf error-tag -> status code
#include "restconf_methods.h"
/*! REST OPTIONS method
* According to restconf (Sec 3.5.1.1 in [draft])
* According to restconf
* @param[in] h Clixon handle
* @param[in] r Fastcgi request handle
* @code
@ -227,7 +226,7 @@ api_data_head(clicon_handle h,
}
/*! REST GET method
* According to restconf (Sec 3.5.1.1 in [draft])
* According to restconf
* @param[in] h Clixon handle
* @param[in] r Fastcgi request handle
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
@ -259,77 +258,23 @@ api_data_get(clicon_handle h,
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 parse fail: %s", __FUNCTION__, data);
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 xml: %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] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @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
* @note We map post to edit-config create.
POST:
target resource type is datastore --> create a top-level resource
target resource type is data resource --> create child resource
The message-body MUST contain exactly one instance of the
expected data resource. The data model for the child tree is the
subtree, as defined by YANG for the child resource.
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
@ -338,7 +283,6 @@ api_data_edit(clicon_handle h,
If the data resource already exists, then the POST request MUST fail
and a "409 Conflict" status-line MUST be returned.
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation * @example
*/
int
api_data_post(clicon_handle h,
@ -349,13 +293,91 @@ api_data_post(clicon_handle h,
cvec *qvec,
char *data)
{
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_CREATE);
enum operation_type op = OP_CREATE;
int retval = -1;
int i;
cxobj *xdata = NULL;
cxobj *xtop = NULL; /* xpath root */
cbuf *cbx = NULL;
cxobj *xbot = NULL;
cxobj *x;
yang_node *y = NULL;
yang_spec *yspec;
cxobj *xa;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
api_path, data);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Create config top-of-tree */
if ((xtop = xml_new("config", NULL)) == NULL)
goto done;
xbot = xtop;
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
goto done;
/* Parse input data as json into xml */
if (json_parse_str(data, &xdata) < 0){
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
goto done;
}
/* Add xdata to xbot */
x = NULL;
while ((x = xml_child_each(xdata, x, CX_ELMNT)) != NULL) {
if ((xa = xml_new("operation", x)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
if (xml_addsub(xbot, x) < 0)
goto done;
}
xdata = NULL;
if ((cbx = cbuf_new()) == NULL)
goto done;
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
goto done;
clicon_debug(1, "%s xml: %s",__FUNCTION__, cbuf_get(cbx));
if (clicon_rpc_edit_config(h, "candidate",
OP_NONE,
cbuf_get(cbx)) < 0){
// notfound(r); /* XXX */
conflict(r);
goto done;
}
if (clicon_rpc_validate(h, "candidate") < 0){
if (clicon_rpc_discard_changes(h) < 0)
goto done;
badrequest(r);
retval = 0;
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");
// XXX api_path can be null FCGX_FPrintF(r->out, "Location: %s\r\n", api_path);
FCGX_FPrintF(r->out, "\r\n");
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xtop)
xml_free(xtop);
if (xdata)
xml_free(xdata);
if (cbx)
cbuf_free(cbx);
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] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @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)
@ -379,14 +401,96 @@ api_data_put(clicon_handle h,
cvec *qvec,
char *data)
{
/* XXX: OP_CREATE? */
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_REPLACE);
int retval = -1;
enum operation_type op = OP_REPLACE;
int i;
cxobj *xdata = NULL;
cbuf *cbx = NULL;
cxobj *x;
cxobj *xbot = NULL;
cxobj *xtop;
cxobj *xp;
yang_node *y = NULL;
yang_spec *yspec;
cxobj *xa;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
api_path, data);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Create config top-of-tree */
if ((xtop = xml_new("config", NULL)) == NULL)
goto done;
xbot = xtop;
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
goto done;
/* Parse input data as json into xml */
if (json_parse_str(data, &xdata) < 0){
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
goto done;
}
if (xml_child_nr(xdata) != 1){
badrequest(r);
retval = 0;
goto done;
}
x = xml_child_i(xdata,0);
if ((xa = xml_new("operation", x)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
/* Replace xbot with x */
xp = xml_parent(xbot);
xml_purge(xbot);
if (xml_addsub(xp, x) < 0)
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
goto done;
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_edit_config(h, "candidate",
OP_NONE,
cbuf_get(cbx)) < 0){
notfound(r);
goto done;
}
if (clicon_rpc_validate(h, "candidate") < 0){
if (clicon_rpc_discard_changes(h) < 0)
goto done;
badrequest(r);
retval = 0;
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 (xtop)
xml_free(xtop);
if (xdata)
xml_free(xdata);
if (cbx)
cbuf_free(cbx);
return retval;
}
/*! 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] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @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)
@ -402,13 +506,15 @@ api_data_patch(clicon_handle h,
cvec *qvec,
char *data)
{
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE);
badrequest(r);
// return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE);
return 0;
}
/*! 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] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @param[in] pi Offset, where path starts
* Example:
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
@ -422,14 +528,39 @@ api_data_delete(clicon_handle h,
{
int retval = -1;
int i;
cxobj *xtop = NULL; /* xpath root */
cxobj *xbot = NULL;
cxobj *xa;
cbuf *cbx = NULL;
yang_node *y = NULL;
yang_spec *yspec;
enum operation_type op = OP_DELETE;
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Create config top-of-tree */
if ((xtop = xml_new("config", NULL)) == NULL)
goto done;
xbot = xtop;
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
goto done;
if ((xa = xml_new("operation", xbot)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate",
OP_DELETE,
api_path,
"<config/>") < 0){
OP_NONE,
cbuf_get(cbx)) < 0){
notfound(r);
goto done;
}
@ -440,6 +571,10 @@ api_data_delete(clicon_handle h,
FCGX_FPrintF(r->out, "\r\n");
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
if (xtop)
xml_free(xtop);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
}