Restconf: get well-known, top-level resource, yang library version, put whole datastore,
check for different keys in put lists.
This commit is contained in:
parent
1ee3f7e67e
commit
26667b2c2f
11 changed files with 759 additions and 91 deletions
|
|
@ -2,16 +2,15 @@
|
|||
### Features
|
||||
|
||||
Clixon restconf is a daemon based on FASTCGI. Instructions are available to
|
||||
run with NGINX.
|
||||
The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE.
|
||||
and is based on draft-ietf-netconf-restconf-13.
|
||||
There is currently (2017) a [RFC 8040: RESTCONF Protocol](https://tools.ietf.org/html/rfc8040), many of those features are _not_ implemented,
|
||||
including:
|
||||
run with NGINX.
|
||||
The implementatation is based on [RFC 8040: RESTCONF Protocol](https://tools.ietf.org/html/rfc8040).
|
||||
The following featires are supported:
|
||||
- OPTIONS, HEAD, GET, POST, PUT, DELETE
|
||||
The following are not implemented
|
||||
- PATCH
|
||||
- query parameters (section 4.9)
|
||||
- notifications (sec 6)
|
||||
- GET /restconf/ (sec 3.3)
|
||||
- GET /restconf/yang-library-version (sec 3.3.3)
|
||||
- only rudimentary error reporting exists (sec 7)
|
||||
- schema resource
|
||||
|
||||
### Installation using Nginx
|
||||
|
||||
|
|
|
|||
|
|
@ -73,9 +73,14 @@
|
|||
/* Command line options to be passed to getopt(3) */
|
||||
#define RESTCONF_OPTS "hDf:p:y:"
|
||||
|
||||
/* Should be discovered via "/.well-known/host-meta"
|
||||
resource ([RFC6415]) */
|
||||
#define RESTCONF_API_ROOT "/restconf/"
|
||||
/* RESTCONF enables deployments to specify where the RESTCONF API is
|
||||
located. The client discovers this by getting the "/.well-known/host-meta"
|
||||
resource
|
||||
*/
|
||||
#define RESTCONF_WELL_KNOWN "/.well-known/host-meta"
|
||||
|
||||
#define RESTCONF_API "restconf"
|
||||
#define RESTCONF_API_ROOT "/restconf"
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE, etc
|
||||
* @param[in] h CLIXON handle
|
||||
|
|
@ -117,6 +122,7 @@ api_data(clicon_handle h,
|
|||
retval = api_data_delete(h, r, api_path, pi);
|
||||
else
|
||||
retval = notfound(r);
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -144,18 +150,118 @@ api_operations(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, "POST")==0)
|
||||
if (strcmp(request_method, "GET")==0)
|
||||
retval = api_operation_get(h, r, path, pcvec, pi, qvec, data);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data);
|
||||
else
|
||||
retval = notfound(r);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Retrieve the Top-Level API Resource
|
||||
* @note Only returns null for operations and data,...
|
||||
*/
|
||||
static int
|
||||
api_root(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
{
|
||||
int retval = -1;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
int pretty;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
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++;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (xml_parse_string("<restconf><data></data><operations></operations><yang-library-version>2016-06-21</yang-library-version></restconf>", NULL, &xt) < 0)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
FCGX_FPrintF(r->out, "%s", cb?cbuf_get(cb):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* See https://tools.ietf.org/html/rfc7895
|
||||
*/
|
||||
static int
|
||||
api_yang_library_version(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
{
|
||||
int retval = -1;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
int pretty;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
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++;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (xml_parse_string("<yang-library-version>2016-06-21</yang-library-version>", NULL, &xt) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "%s\r\n", cb?cbuf_get(cb):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Process a FastCGI request
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
static int
|
||||
request_process(clicon_handle h,
|
||||
api_restconf(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -176,7 +282,28 @@ request_process(clicon_handle h,
|
|||
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Sanity check of path. Should be /restconf/ */
|
||||
if (pn < 2){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (strlen(pvec[0]) != 0){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(pvec[1], RESTCONF_API)){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (pn == 2){
|
||||
retval = api_root(h, r);
|
||||
goto done;
|
||||
}
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "method=%s", method);
|
||||
if (str2cvec(query, '&', '=', &qvec) < 0)
|
||||
goto done;
|
||||
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||
|
|
@ -188,10 +315,7 @@ request_process(clicon_handle h,
|
|||
clicon_debug(1, "DATA=%s", data);
|
||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||
goto done;
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
test(r, 1);
|
||||
/* If present, check credentials */
|
||||
|
|
@ -202,8 +326,9 @@ request_process(clicon_handle h,
|
|||
if (auth == 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s credentials ok 2", __FUNCTION__);
|
||||
|
||||
if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
||||
if (strcmp(method, "yang-library-version")==0)
|
||||
retval = api_yang_library_version(h, r);
|
||||
else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
||||
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
||||
else if (strcmp(method, "operations") == 0) /* rpc */
|
||||
retval = api_operations(h, r, path, pcvec, 2, qvec, data);
|
||||
|
|
@ -226,6 +351,24 @@ request_process(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Process a FastCGI request
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
static int
|
||||
api_well_known(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/xrd+xml\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\r\n");
|
||||
FCGX_FPrintF(r->out, " <Link rel='restconf' href='/restconf'/>\r\n");
|
||||
FCGX_FPrintF(r->out, "</XRD>\r\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
restconf_terminate(clicon_handle h)
|
||||
{
|
||||
|
|
@ -384,13 +527,17 @@ main(int argc,
|
|||
}
|
||||
clicon_debug(1, "------------");
|
||||
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); /* This is the function */
|
||||
clicon_debug(1, "path:%s", path);
|
||||
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0)
|
||||
api_restconf(h, r); /* This is the function */
|
||||
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
|
||||
api_well_known(h, r); /* This is the function */
|
||||
}
|
||||
else{
|
||||
clicon_debug(1, "top-level not found");
|
||||
clicon_debug(1, "top-level %s not found", path);
|
||||
notfound(r);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
clicon_debug(1, "NULL URI");
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ api_data_get(clicon_handle h,
|
|||
return api_data_get2(h, r, pcvec, pi, qvec, 0);
|
||||
}
|
||||
|
||||
/*! REST POST method
|
||||
/*! Generic REST POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||
|
|
@ -387,7 +387,7 @@ api_data_get(clicon_handle h,
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @note We map post to edit-config create.
|
||||
* @note restconf POST is mapped to edit-config create.
|
||||
POST:
|
||||
target resource type is datastore --> create a top-level resource
|
||||
target resource type is data resource --> create child resource
|
||||
|
|
@ -414,12 +414,12 @@ api_data_post(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
enum operation_type op = OP_CREATE;
|
||||
int retval = -1;
|
||||
enum operation_type op = OP_CREATE;
|
||||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
cxobj *x;
|
||||
yang_node *y = NULL;
|
||||
|
|
@ -444,8 +444,8 @@ api_data_post(clicon_handle h,
|
|||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
/* xbot is resulting xml tree on exit */
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json or xml into xml */
|
||||
|
|
@ -456,29 +456,35 @@ api_data_post(clicon_handle h,
|
|||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
/* Add xdata to xbot */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xdata, x, CX_ELMNT)) != NULL) {
|
||||
if ((xa = xml_new("operation", x, NULL)) == 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;
|
||||
/* The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
*/
|
||||
if (xml_child_nr(xdata) != 1){
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
x = xml_child_i(xdata,0);
|
||||
/* Add operation (create/replace) as attribute */
|
||||
if ((xa = xml_new("operation", x, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
goto done;
|
||||
/* Replace xbot with x, ie bottom of api-path with data */
|
||||
if (xml_addsub(xbot, x) < 0)
|
||||
goto done;
|
||||
/* Create text buffer for transfer to backend */
|
||||
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));
|
||||
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); /* XXX */
|
||||
conflict(r);
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -492,7 +498,6 @@ api_data_post(clicon_handle h,
|
|||
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");
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -505,6 +510,58 @@ api_data_post(clicon_handle h,
|
|||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
} /* api_data_post */
|
||||
|
||||
|
||||
/*! Check matching keys
|
||||
*
|
||||
* @param[in] y Yang statement, should be list or leaf-list
|
||||
* @param[in] xdata XML data tree
|
||||
* @param[in] xapipath XML api-path tree
|
||||
* @retval 0 Yes, keys match
|
||||
* @retval -1 No keys do not match
|
||||
* If the target resource represents a YANG leaf-list, then the PUT
|
||||
* method MUST NOT change the value of the leaf-list instance.
|
||||
*
|
||||
* If the target resource represents a YANG list instance, then the key
|
||||
* leaf values, in message-body representation, MUST be the same as the
|
||||
* key leaf values in the request URI. The PUT method MUST NOT be used
|
||||
* to change the key leaf values for a data resource instance.
|
||||
*/
|
||||
static int
|
||||
match_list_keys(yang_stmt *y,
|
||||
cxobj *xdata,
|
||||
cxobj *xapipath)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
char *keyname;
|
||||
cxobj *xkeya; /* xml key object in api-path */
|
||||
cxobj *xkeyd; /* xml key object in data */
|
||||
char *keya;
|
||||
char *keyd;
|
||||
|
||||
if (y->ys_keyword != Y_LIST &&y->ys_keyword != Y_LEAF_LIST)
|
||||
return -1;
|
||||
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((xkeya = xml_find(xapipath, keyname)) == NULL)
|
||||
goto done; /* No key in api-path */
|
||||
|
||||
keya = xml_body(xkeya);
|
||||
if ((xkeyd = xml_find(xdata, keyname)) == NULL)
|
||||
goto done; /* No key in data */
|
||||
keyd = xml_body(xkeyd);
|
||||
if (strcmp(keya, keyd) != 0)
|
||||
goto done; /* keys dont match */
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST PUT method
|
||||
|
|
@ -515,6 +572,7 @@ api_data_post(clicon_handle h,
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @note restconf PUT is mapped to edit-config replace.
|
||||
* @example
|
||||
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||
*
|
||||
|
|
@ -528,7 +586,7 @@ api_data_post(clicon_handle h,
|
|||
int
|
||||
api_data_put(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
char *api_path0,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
|
|
@ -539,19 +597,19 @@ api_data_put(clicon_handle h,
|
|||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *x;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
cxobj *xtop = NULL;
|
||||
cxobj *xp;
|
||||
cxobj *xparent;
|
||||
cxobj *x;
|
||||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
char *api_path;
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
__FUNCTION__, api_path0, data);
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (media_content_type &&
|
||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
|
|
@ -560,11 +618,13 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
api_path=api_path0;
|
||||
for (i=0; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
|
|
@ -579,22 +639,48 @@ api_data_put(clicon_handle h,
|
|||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
/* The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
*/
|
||||
if (xml_child_nr(xdata) != 1){
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
x = xml_child_i(xdata,0);
|
||||
/* Add operation (create/replace) as attribute */
|
||||
if ((xa = xml_new("operation", x, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
goto done;
|
||||
/* XXX Special case path=/restconf/data xml_name(x) == data */
|
||||
/* Replace xbot with x */
|
||||
xp = xml_parent(xbot);
|
||||
xml_purge(xbot);
|
||||
if (xml_addsub(xp, x) < 0)
|
||||
goto done;
|
||||
#if 1 /* This is different from POST */
|
||||
/* Replace xparent with x, ie bottom of api-path with data */
|
||||
if (api_path==NULL && strcmp(xml_name(x),"data")==0){
|
||||
if (xml_addsub(NULL, x) < 0)
|
||||
goto done;
|
||||
xtop = x;
|
||||
xml_name_set(xtop, "config");
|
||||
}
|
||||
else {
|
||||
/* Check same symbol in api-path as data */
|
||||
if (strcmp(xml_name(x), xml_name(xbot))){
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
/* If list or leaf-list, api-path keys must match data keys */
|
||||
if (y && (y->yn_keyword == Y_LIST ||y->yn_keyword == Y_LEAF_LIST)){
|
||||
if (match_list_keys((yang_stmt*)y, x, xbot) < 0){
|
||||
badrequest(r);
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
xparent = xml_parent(xbot);
|
||||
xml_purge(xbot);
|
||||
if (xml_addsub(xparent, x) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
/* Create text buffer for transfer to backend */
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||
|
|
@ -628,8 +714,7 @@ api_data_put(clicon_handle h,
|
|||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
|
||||
}
|
||||
} /* api_data_put */
|
||||
|
||||
/*! Generic REST PATCH method
|
||||
* @param[in] h CLIXON handle
|
||||
|
|
@ -724,6 +809,20 @@ api_data_delete(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! NYI
|
||||
*/
|
||||
int
|
||||
api_operation_get(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! REST operation POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@ int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
|||
cvec *qvec, char *data);
|
||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi);
|
||||
|
||||
int api_operation_get(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data);
|
||||
|
||||
int api_operation_post(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue