RESTCONF PUT/POST -d {} media is enforced
This commit is contained in:
parent
aa653d0831
commit
aa14f8ac2c
35 changed files with 933 additions and 640 deletions
|
|
@ -38,6 +38,15 @@
|
|||
#ifndef _CLIXON_RESTCONF_H_
|
||||
#define _CLIXON_RESTCONF_H_
|
||||
|
||||
/*
|
||||
* Types (also in restconf_lib.h)
|
||||
*/
|
||||
enum restconf_media{
|
||||
YANG_DATA_JSON, /* "application/yang-data+json" (default for RESTCONF) */
|
||||
YANG_DATA_XML /* "application/yang-data+xml" */
|
||||
};
|
||||
typedef enum restconf_media restconf_media;
|
||||
|
||||
/*
|
||||
* Prototypes (also in restconf_lib.h)
|
||||
*/
|
||||
|
|
@ -55,7 +64,7 @@ int restconf_test(FCGX_Request *r, int dbg);
|
|||
cbuf *readdata(FCGX_Request *r);
|
||||
int get_user_cookie(char *cookiestr, char *attribute, char **val);
|
||||
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
|
||||
int pretty, int use_xml, int code);
|
||||
int pretty, restconf_media media, int code);
|
||||
|
||||
|
||||
#endif /* _CLIXON_RESTCONF_H_ */
|
||||
|
|
|
|||
|
|
@ -139,6 +139,14 @@ static const map_str2int http_reason_phrase_map[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* See RFC 8040
|
||||
*/
|
||||
static const map_str2int http_media_map[] = {
|
||||
{"application/yang-data+xml", YANG_DATA_XML},
|
||||
{"application/yang-data+json", YANG_DATA_JSON},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
int
|
||||
restconf_err2code(char *tag)
|
||||
{
|
||||
|
|
@ -151,11 +159,23 @@ restconf_code2reason(int code)
|
|||
return clicon_int2str(http_reason_phrase_map, code);
|
||||
}
|
||||
|
||||
const restconf_media
|
||||
restconf_media_str2int(char *media)
|
||||
{
|
||||
return clicon_str2int(http_media_map, media);
|
||||
}
|
||||
|
||||
const char *
|
||||
restconf_media_int2str(restconf_media media)
|
||||
{
|
||||
return clicon_int2str(http_media_map, media);
|
||||
}
|
||||
|
||||
/*! HTTP error 400
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
badrequest(FCGX_Request *r)
|
||||
restconf_badrequest(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -173,7 +193,7 @@ badrequest(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
unauthorized(FCGX_Request *r)
|
||||
restconf_unauthorized(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -190,7 +210,7 @@ unauthorized(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
forbidden(FCGX_Request *r)
|
||||
restconf_forbidden(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -207,7 +227,7 @@ forbidden(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
notfound(FCGX_Request *r)
|
||||
restconf_notfound(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -227,7 +247,7 @@ notfound(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
notacceptable(FCGX_Request *r)
|
||||
restconf_notacceptable(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -247,7 +267,7 @@ notacceptable(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
conflict(FCGX_Request *r)
|
||||
restconf_conflict(FCGX_Request *r)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_FPrintF(r->out, "Status: 409\r\n"); /* 409 Conflict */
|
||||
|
|
@ -256,11 +276,25 @@ conflict(FCGX_Request *r)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 409
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
restconf_unsupported_media(FCGX_Request *r)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_SetExitStatus(415, r->out);
|
||||
FCGX_FPrintF(r->out, "Status: 415 Unsupported Media Type\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(r->out, "<h1>Unsupported Media Type</h1>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! HTTP error 500
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
internal_server_error(FCGX_Request *r)
|
||||
restconf_internal_server_error(FCGX_Request *r)
|
||||
{
|
||||
char *path;
|
||||
|
||||
|
|
@ -276,7 +310,7 @@ internal_server_error(FCGX_Request *r)
|
|||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
int
|
||||
notimplemented(FCGX_Request *r)
|
||||
restconf_notimplemented(FCGX_Request *r)
|
||||
{
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
FCGX_FPrintF(r->out, "Status: 501\r\n");
|
||||
|
|
@ -285,7 +319,6 @@ notimplemented(FCGX_Request *r)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
|
|
@ -396,16 +429,16 @@ get_user_cookie(char *cookiestr,
|
|||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] xerr XML error message from backend
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
|
||||
* otherwise use this code
|
||||
* @param[in] media Output media
|
||||
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
|
||||
* otherwise use this code
|
||||
*/
|
||||
int
|
||||
api_return_err(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
cxobj *xerr,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
restconf_media media,
|
||||
int code0)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -419,7 +452,7 @@ api_return_err(clicon_handle h,
|
|||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){
|
||||
notfound(r);
|
||||
restconf_notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
tagstr = xml_body(xtag);
|
||||
|
|
@ -433,20 +466,14 @@ api_return_err(clicon_handle h,
|
|||
reason_phrase="";
|
||||
if (xml_name_set(xerr, "error") < 0)
|
||||
goto done;
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cb, xerr, 2, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
FCGX_SetExitStatus(code, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
|
||||
use_xml?"xml":"json");
|
||||
|
||||
if (use_xml){
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n\r\n", restconf_media_int2str(media));
|
||||
switch (media){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cb, xerr, 2, pretty) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
if (pretty){
|
||||
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
|
||||
|
|
@ -457,8 +484,11 @@ api_return_err(clicon_handle h,
|
|||
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "</errors>\r\n");
|
||||
}
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
if (pretty){
|
||||
FCGX_FPrintF(r->out, "{\n");
|
||||
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : %s\n",
|
||||
|
|
@ -471,7 +501,7 @@ api_return_err(clicon_handle h,
|
|||
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "}\r\n");
|
||||
}
|
||||
}
|
||||
} /* switch media */
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -683,3 +713,4 @@ restconf_uripath(FCGX_Request *r)
|
|||
*q = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,26 +41,43 @@
|
|||
*/
|
||||
#define RESTCONF_API "restconf"
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
|
||||
/*! RESTCONF media types
|
||||
* @see http_media_map
|
||||
*/
|
||||
enum restconf_media{
|
||||
YANG_DATA_JSON, /* "application/yang-data+json" (default for RESTCONF) */
|
||||
YANG_DATA_XML /* "application/yang-data+xml" */
|
||||
};
|
||||
typedef enum restconf_media restconf_media;
|
||||
|
||||
/*
|
||||
* Prototypes (also in clixon_restconf.h)
|
||||
*/
|
||||
int restconf_err2code(char *tag);
|
||||
const char *restconf_code2reason(int code);
|
||||
|
||||
int badrequest(FCGX_Request *r);
|
||||
int unauthorized(FCGX_Request *r);
|
||||
int forbidden(FCGX_Request *r);
|
||||
int notfound(FCGX_Request *r);
|
||||
int notacceptable(FCGX_Request *r);
|
||||
int conflict(FCGX_Request *r);
|
||||
int internal_server_error(FCGX_Request *r);
|
||||
int notimplemented(FCGX_Request *r);
|
||||
const restconf_media restconf_media_str2int(char *media);
|
||||
const char *restconf_media_int2str(restconf_media media);
|
||||
|
||||
int restconf_badrequest(FCGX_Request *r);
|
||||
int restconf_unauthorized(FCGX_Request *r);
|
||||
int restconf_forbidden(FCGX_Request *r);
|
||||
int restconf_notfound(FCGX_Request *r);
|
||||
int restconf_notacceptable(FCGX_Request *r);
|
||||
int restconf_conflict(FCGX_Request *r);
|
||||
int restconf_unsupported_media(FCGX_Request *r);
|
||||
int restconf_internal_server_error(FCGX_Request *r);
|
||||
int restconf_notimplemented(FCGX_Request *r);
|
||||
|
||||
int restconf_test(FCGX_Request *r, int dbg);
|
||||
cbuf *readdata(FCGX_Request *r);
|
||||
int get_user_cookie(char *cookiestr, char *attribute, char **val);
|
||||
int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr,
|
||||
int pretty, int use_xml, int code);
|
||||
int pretty, enum restconf_media media, int code);
|
||||
int http_location(FCGX_Request *r, cxobj *xobj);
|
||||
int restconf_terminate(clicon_handle h);
|
||||
int restconf_insert_attributes(cxobj *xdata, cvec *qvec);
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@
|
|||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input daat
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
* @param[in] media_in Input media
|
||||
* @param[in] media_out Output media
|
||||
*/
|
||||
static int
|
||||
api_data(clicon_handle h,
|
||||
|
|
@ -113,8 +113,8 @@ api_data(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
|
|
@ -125,19 +125,19 @@ api_data(clicon_handle h,
|
|||
if (strcmp(request_method, "OPTIONS")==0)
|
||||
retval = api_data_options(h, r);
|
||||
else if (strcmp(request_method, "HEAD")==0)
|
||||
retval = api_data_head(h, r, pcvec, pi, qvec, pretty, use_xml);
|
||||
retval = api_data_head(h, r, pcvec, pi, qvec, pretty, media_out);
|
||||
else if (strcmp(request_method, "GET")==0)
|
||||
retval = api_data_get(h, r, pcvec, pi, qvec, pretty, use_xml);
|
||||
retval = api_data_get(h, r, pcvec, pi, qvec, pretty, media_out);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml);
|
||||
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, media_in, media_out);
|
||||
else if (strcmp(request_method, "PUT")==0)
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml);
|
||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, media_in, media_out);
|
||||
else if (strcmp(request_method, "PATCH")==0)
|
||||
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data);
|
||||
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data, pretty, media_in, media_out);
|
||||
else if (strcmp(request_method, "DELETE")==0)
|
||||
retval = api_data_delete(h, r, api_path, pi, pretty, use_xml);
|
||||
retval = api_data_delete(h, r, api_path, pi, pretty, media_out);
|
||||
else
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -150,7 +150,8 @@ api_data(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
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
* @param[in] media_in Input media media
|
||||
* @param[in] media_out Output media
|
||||
*/
|
||||
static int
|
||||
api_operations(clicon_handle h,
|
||||
|
|
@ -161,8 +162,8 @@ api_operations(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
|
|
@ -171,12 +172,12 @@ api_operations(clicon_handle h,
|
|||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
if (strcmp(request_method, "GET")==0)
|
||||
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml);
|
||||
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, media_out);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
retval = api_operations_post(h, r, path, pcvec, pi, qvec, data,
|
||||
pretty, use_xml, parse_xml);
|
||||
pretty, media_in, media_out);
|
||||
else
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -211,15 +212,15 @@ api_well_known(clicon_handle h,
|
|||
* See RFC8040 3.3
|
||||
*/
|
||||
static int
|
||||
api_root(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
api_root(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
int pretty;
|
||||
yang_stmt *yspec;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
|
|
@ -227,14 +228,10 @@ api_root(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
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++;
|
||||
clicon_debug(1, "%s use-xml:%d media-accept:%s", __FUNCTION__, use_xml, media_accept);
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
|
||||
if (xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>", NULL, &xt) < 0)
|
||||
|
|
@ -247,13 +244,16 @@ api_root(clicon_handle h,
|
|||
}
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if (use_xml){
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
FCGX_FPrintF(r->out, "%s", cb?cbuf_get(cb):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
retval = 0;
|
||||
|
|
@ -270,24 +270,20 @@ api_root(clicon_handle h,
|
|||
*/
|
||||
static int
|
||||
api_yang_library_version(clicon_handle h,
|
||||
FCGX_Request *r)
|
||||
FCGX_Request *r,
|
||||
int pretty,
|
||||
restconf_media media_out)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cxobj *xt = NULL;
|
||||
cbuf *cb = NULL;
|
||||
int pretty;
|
||||
char *ietf_yang_library_revision = "2016-06-21"; /* XXX */
|
||||
|
||||
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, "Cache-Control: no-cache\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (xml_parse_va(&xt, NULL, "<yang-library-version>%s</yang-library-version>", ietf_yang_library_revision) < 0)
|
||||
goto done;
|
||||
|
|
@ -296,13 +292,15 @@ api_yang_library_version(clicon_handle h,
|
|||
if ((cb = cbuf_new()) == NULL){
|
||||
goto done;
|
||||
}
|
||||
if (use_xml){
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cb, xt, 0, pretty) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cb, xt, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
|
||||
FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):"");
|
||||
|
|
@ -335,11 +333,10 @@ api_restconf(clicon_handle h,
|
|||
cbuf *cb = NULL;
|
||||
char *data;
|
||||
int authenticated = 0;
|
||||
char *media_accept;
|
||||
char *media_content_type;
|
||||
char *media_str = NULL;
|
||||
restconf_media media_in = YANG_DATA_JSON; /* XXX defaults should be set in methods, here is too generic */
|
||||
restconf_media media_out = YANG_DATA_JSON;
|
||||
int pretty;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
|
@ -348,37 +345,61 @@ api_restconf(clicon_handle h,
|
|||
path = restconf_uripath(r);
|
||||
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
/* get xml/json in put and output */
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (media_accept && strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (media_content_type &&
|
||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
/* Get media for input (Content-Type)
|
||||
* This is for methods that have input, such as PUT/POST, etc
|
||||
*/
|
||||
if ((media_str = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp)) == NULL){
|
||||
retval = restconf_unsupported_media(r);
|
||||
goto done;
|
||||
}
|
||||
else if ((media_in = restconf_media_str2int(media_str)) == -1){
|
||||
clicon_debug(1, "%s Content_Type: %s (unsupported)", __FUNCTION__, media_str);
|
||||
retval = restconf_unsupported_media(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s CONTENT_TYPE: %s %s", __FUNCTION__, media_str, restconf_media_int2str(media_in));
|
||||
/* Get media for output (proactive negotiation) RFC7231 by using
|
||||
* Accept:. This is for methods that have output, such as GET,
|
||||
* operation POST, etc
|
||||
* If accept is * default is yang-json
|
||||
*/
|
||||
if ((media_str = FCGX_GetParam("HTTP_ACCEPT", r->envp)) == NULL){
|
||||
// retval = restconf_unsupported_media(r);
|
||||
// goto done;
|
||||
}
|
||||
else if ((media_out = restconf_media_str2int(media_str)) == -1){
|
||||
if (strcmp(media_str, "*/*") == 0) /* catch-all */
|
||||
media_out = YANG_DATA_JSON;
|
||||
else{
|
||||
retval = restconf_unsupported_media(r);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "%s ACCEPT: %s %s", __FUNCTION__, media_str, restconf_media_int2str(media_out));
|
||||
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
/* Sanity check of path. Should be /restconf/ */
|
||||
if (pn < 2){
|
||||
notfound(r);
|
||||
restconf_notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
if (strlen(pvec[0]) != 0){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(pvec[1], RESTCONF_API)){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
restconf_test(r, 1);
|
||||
|
||||
if (pn == 2){
|
||||
retval = api_root(h, r);
|
||||
retval = api_root(h, r, pretty, media_out);
|
||||
goto done;
|
||||
}
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
||||
|
|
@ -410,7 +431,7 @@ api_restconf(clicon_handle h,
|
|||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -418,23 +439,23 @@ api_restconf(clicon_handle h,
|
|||
}
|
||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
if (strcmp(method, "yang-library-version")==0){
|
||||
if (api_yang_library_version(h, r) < 0)
|
||||
if (api_yang_library_version(h, r, pretty, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
||||
if (api_data(h, r, path, pcvec, 2, qvec, data,
|
||||
pretty, use_xml, parse_xml) < 0)
|
||||
pretty, media_in, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "operations") == 0){ /* rpc */
|
||||
if (api_operations(h, r, path, pcvec, 2, qvec, data,
|
||||
pretty, use_xml, parse_xml) < 0)
|
||||
pretty, media_in, media_out) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "test") == 0)
|
||||
restconf_test(r, 0);
|
||||
else
|
||||
notfound(r);
|
||||
restconf_notfound(r);
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -796,7 +817,7 @@ main(int argc,
|
|||
}
|
||||
else{
|
||||
clicon_debug(1, "top-level %s not found", path);
|
||||
notfound(r);
|
||||
restconf_notfound(r);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -224,8 +224,8 @@ match_list_keys(yang_stmt *y,
|
|||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
* @param[in] media_in Input media media
|
||||
* @param[in] media_out Output media
|
||||
|
||||
* @note restconf PUT is mapped to edit-config replace.
|
||||
* @see RFC8040 Sec 4.5 PUT
|
||||
|
|
@ -260,8 +260,8 @@ api_data_put(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
enum operation_type op;
|
||||
|
|
@ -302,13 +302,13 @@ api_data_put(clicon_handle h,
|
|||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
/* Translate api_path to xtop/xbot */
|
||||
/* Translate api_path to xml in the form of xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){
|
||||
if (api_path){ /* If URI, otherwise top data/config object */
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
goto done;
|
||||
if (ybot)
|
||||
ymodapi=ys_module(ybot);
|
||||
ymodapi = ys_module(ybot);
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -317,14 +317,15 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
if (xml_parse_string(data, yspec, &xdata0) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -332,12 +333,12 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
/* Data here cannot cannot be Yang populated since it is loosely
|
||||
* hanging without top symbols.
|
||||
* And if it is not yang populated, it cant be translated properly
|
||||
|
|
@ -351,7 +352,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -360,11 +361,11 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
} /* switch media_in */
|
||||
|
||||
/* The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
|
|
@ -376,7 +377,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -404,7 +405,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -445,7 +446,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -470,7 +471,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -494,7 +495,7 @@ api_data_put(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -509,7 +510,7 @@ api_data_put(clicon_handle h,
|
|||
/* xbot is already populated, resolve yang for added xdata too */
|
||||
if (xml_apply0(xdata, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (!parse_xml && nullspec){
|
||||
if (media_in == YANG_DATA_JSON && nullspec){
|
||||
/* json2xml decode could not be done above in json_parse,
|
||||
* need to be done here instead
|
||||
* UNLESS it is root resource, then json-parse has already done it
|
||||
|
|
@ -517,7 +518,7 @@ api_data_put(clicon_handle h,
|
|||
if ((ret = json2xml_decode(xdata, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -561,7 +562,7 @@ api_data_put(clicon_handle h,
|
|||
*/
|
||||
if (xpath_first(xe, ".[error-tag=\"data-exists\"]") == NULL ||
|
||||
op == OP_REPLACE){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -598,7 +599,7 @@ api_data_put(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -656,14 +657,13 @@ api_data_put(clicon_handle h,
|
|||
return retval;
|
||||
} /* api_data_put */
|
||||
|
||||
|
||||
/*! Generic REST DELETE method translated to edit-config
|
||||
* @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)
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] media_out Output media
|
||||
* See RFC 8040 Sec 4.7
|
||||
* Example:
|
||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
||||
|
|
@ -675,7 +675,7 @@ api_data_delete(clicon_handle h,
|
|||
char *api_path,
|
||||
int pi,
|
||||
int pretty,
|
||||
int use_xml)
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -716,7 +716,7 @@ api_data_delete(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -744,7 +744,7 @@ api_data_delete(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -766,7 +766,7 @@ api_data_delete(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ int api_data_options(clicon_handle h, FCGX_Request *r);
|
|||
int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data,
|
||||
int pretty, int use_xml, int parse_xml);
|
||||
int pretty,
|
||||
restconf_media media_in, restconf_media media_out);
|
||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi,
|
||||
int pretty, int use_xml);
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_H_ */
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] media_out Output media
|
||||
* @param[in] head If 1 is HEAD, otherwise GET
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
|
|
@ -96,7 +96,7 @@ api_data_get2(clicon_handle h,
|
|||
int pi,
|
||||
cvec *qvec,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
restconf_media media_out,
|
||||
int head)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -131,7 +131,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -148,7 +148,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -167,7 +167,7 @@ api_data_get2(clicon_handle h,
|
|||
#endif
|
||||
/* Check if error return */
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -176,28 +176,20 @@ api_data_get2(clicon_handle h,
|
|||
goto done;
|
||||
if (head){
|
||||
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, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
goto ok;
|
||||
}
|
||||
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
|
||||
if (use_xml){
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cbx, xret, 0, pretty) < 0) /* Dont print top object? */
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
#if 0
|
||||
if (debug){
|
||||
cbuf *ccc=cbuf_new();
|
||||
if (clicon_xml2cbuf(ccc, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s xret: %s",
|
||||
__FUNCTION__, cbuf_get(ccc));
|
||||
cbuf_free(ccc);
|
||||
}
|
||||
#endif
|
||||
if (xml2json_cbuf(cbx, xret, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cbx, xret, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
|
@ -208,7 +200,7 @@ api_data_get2(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -221,11 +213,12 @@ api_data_get2(clicon_handle h,
|
|||
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
||||
goto done;
|
||||
/* override invalid-value default 400 with 404 */
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml, 404) < 0)
|
||||
if (api_return_err(h, r, xerr, pretty, media_out, 404) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (use_xml){
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
for (i=0; i<xlen; i++){
|
||||
char *prefix, *namespace2; /* Same as namespace? */
|
||||
x = xvec[i];
|
||||
|
|
@ -240,19 +233,20 @@ api_data_get2(clicon_handle h,
|
|||
if (clicon_xml2cbuf(cbx, x, 0, pretty) < 0) /* Dont print top object? */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
/* In: <x xmlns="urn:example:clixon">0</x>
|
||||
* Out: {"example:x": {"0"}}
|
||||
*/
|
||||
if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
|
@ -282,7 +276,7 @@ api_data_get2(clicon_handle h,
|
|||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] media_out Output media
|
||||
*
|
||||
* 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
|
||||
|
|
@ -296,9 +290,9 @@ api_data_head(clicon_handle h,
|
|||
int pi,
|
||||
cvec *qvec,
|
||||
int pretty,
|
||||
int use_xml)
|
||||
restconf_media media_out)
|
||||
{
|
||||
return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 1);
|
||||
return api_data_get2(h, r, pcvec, pi, qvec, pretty, media_out, 1);
|
||||
}
|
||||
|
||||
/*! REST GET method
|
||||
|
|
@ -309,7 +303,7 @@ api_data_head(clicon_handle h,
|
|||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] media_out Output media
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
|
|
@ -333,9 +327,9 @@ api_data_get(clicon_handle h,
|
|||
int pi,
|
||||
cvec *qvec,
|
||||
int pretty,
|
||||
int use_xml)
|
||||
restconf_media media_out)
|
||||
{
|
||||
return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 0);
|
||||
return api_data_get2(h, r, pcvec, pi, qvec, pretty, media_out, 0);
|
||||
}
|
||||
|
||||
/*! GET restconf/operations resource
|
||||
|
|
@ -347,7 +341,7 @@ api_data_get(clicon_handle h,
|
|||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] media_out Output media
|
||||
*
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/operations
|
||||
|
|
@ -372,7 +366,7 @@ api_operations_get(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml)
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -387,10 +381,14 @@ api_operations_get(clicon_handle h,
|
|||
yspec = clicon_dbspec_yang(h);
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (use_xml)
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cbx, "<operations>");
|
||||
else
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
cprintf(cbx, "{\"operations\": {");
|
||||
break;
|
||||
}
|
||||
ymod = NULL;
|
||||
i = 0;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL) {
|
||||
|
|
@ -399,21 +397,28 @@ api_operations_get(clicon_handle h,
|
|||
while ((yc = yn_each(ymod, yc)) != NULL) {
|
||||
if (yang_keyword_get(yc) != Y_RPC)
|
||||
continue;
|
||||
if (use_xml)
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cbx, "<%s xmlns=\"%s\"/>", yang_argument_get(yc), namespace);
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (i++)
|
||||
cprintf(cbx, ",");
|
||||
cprintf(cbx, "\"%s:%s\": null", yang_argument_get(ymod), yang_argument_get(yc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (use_xml)
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
cprintf(cbx, "</operations>");
|
||||
else
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
cprintf(cbx, "}}");
|
||||
break;
|
||||
}
|
||||
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, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
* Restconf method implementation for operations get and data get and head
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _RESTCONF_METHODS_GET_H_
|
||||
#define _RESTCONF_METHODS_GET_H_
|
||||
|
||||
|
|
@ -42,12 +41,12 @@
|
|||
* Prototypes
|
||||
*/
|
||||
int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||
cvec *qvec, int pretty, int use_xml);
|
||||
cvec *qvec, int pretty, restconf_media media_out);
|
||||
int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||
cvec *qvec, int pretty, int use_xml);
|
||||
cvec *qvec, int pretty, restconf_media media_out);
|
||||
int api_operations_get(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data,
|
||||
int pretty, int use_xml);
|
||||
int pretty, restconf_media media_out);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_GET_H_ */
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ Mapping netconf error-tag -> status code
|
|||
#include "restconf_methods_patch.h"
|
||||
|
||||
|
||||
/*! Generic REST PATCH method
|
||||
/*! Generic REST PATCH method for plain patch
|
||||
* @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)
|
||||
|
|
@ -133,19 +133,117 @@ Mapping netconf error-tag -> status code
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
* Netconf: <edit-config> (nc:operation="merge")
|
||||
* See RFC8040 Sec 4.6
|
||||
* See RFC8040 Sec 4.6.1
|
||||
*/
|
||||
int
|
||||
api_data_patch(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *api_path,
|
||||
char *api_path0,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
char *data,
|
||||
int pretty,
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
notimplemented(r);
|
||||
return 0;
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
// yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
|
||||
cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
|
||||
int i;
|
||||
char *api_path;
|
||||
cxobj *xtop = NULL; /* top of api-path */
|
||||
cxobj *xbot = NULL; /* bottom of api-path */
|
||||
yang_stmt *ybot = NULL; /* yang of xbot */
|
||||
cxobj *xerr = NULL; /* malloced must be freed */
|
||||
cxobj *xe;
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0);
|
||||
clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
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 xml in the form of xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){ /* If URI, otherwise top data/config object */
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
goto done;
|
||||
// if (ybot)
|
||||
// ymodapi = ys_module(ybot);
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
/* Parse input data as json or xml into xml */
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
if (xml_parse_string(data, yspec, &xdata0) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
/* Data here cannot cannot be Yang populated since it is loosely
|
||||
* hanging without top symbols.
|
||||
* And if it is not yang populated, it cant be translated properly
|
||||
* from JSON to XML.
|
||||
* Therefore, yang population is done later after addsub below
|
||||
*/
|
||||
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
} /* switch media_in */
|
||||
restconf_notimplemented(r);
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
*/
|
||||
int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data);
|
||||
cvec *qvec, char *data, int pretty,
|
||||
restconf_media media_in, restconf_media media_out);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_PATCH_H_ */
|
||||
|
|
|
|||
|
|
@ -73,9 +73,8 @@
|
|||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
|
||||
* @param[in] media_in Input media
|
||||
* @param[in] media_out Output media
|
||||
* restconf POST is mapped to edit-config create.
|
||||
* @see RFC8040 Sec 4.4.1
|
||||
|
||||
|
|
@ -107,8 +106,8 @@ api_data_post(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
enum operation_type op = OP_CREATE;
|
||||
|
|
@ -158,7 +157,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -173,7 +172,8 @@ api_data_post(clicon_handle h,
|
|||
}
|
||||
#endif
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
if (xml_parse_string(data, NULL, &xdata0) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -181,12 +181,12 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
/* Data here cannot cannot (always) be Yang populated since it is
|
||||
* loosely hanging without top symbols.
|
||||
* And if it is not yang populated, it cant be translated properly
|
||||
|
|
@ -203,7 +203,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -212,11 +212,12 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} /* switch media_in */
|
||||
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
*/
|
||||
|
|
@ -227,7 +228,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -249,7 +250,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -270,7 +271,7 @@ api_data_post(clicon_handle h,
|
|||
nullspec = (xml_spec(xdata) == NULL);
|
||||
if (xml_apply0(xdata, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (!parse_xml && nullspec){
|
||||
if (media_in == YANG_DATA_JSON && nullspec){
|
||||
/* json2xml decode may not have been done above in json_parse,
|
||||
need to be done here instead
|
||||
UNLESS it is a root resource, then json-parse has already done it
|
||||
|
|
@ -278,7 +279,7 @@ api_data_post(clicon_handle h,
|
|||
if ((ret = json2xml_decode(xdata, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -318,7 +319,7 @@ api_data_post(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -340,7 +341,7 @@ api_data_post(clicon_handle h,
|
|||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) /* Use original xe */
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) /* Use original xe */
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -400,8 +401,8 @@ api_data_post(clicon_handle h,
|
|||
* @param[in] yrpc Yang rpc spec
|
||||
* @param[in] xrpc XML pointer to rpc method
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
* @param[in] media_in Input media
|
||||
* @param[in] media_out Output media
|
||||
* @retval 1 OK
|
||||
* @retval 0 Fail, Error message sent
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
|
|
@ -422,8 +423,8 @@ api_operations_post_input(clicon_handle h,
|
|||
yang_stmt *yrpc,
|
||||
cxobj *xrpc,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xdata = NULL;
|
||||
|
|
@ -440,7 +441,8 @@ api_operations_post_input(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
if (xml_parse_string(data, yspec, &xdata) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -448,12 +450,12 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else { /* JSON */
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -461,7 +463,7 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -470,11 +472,12 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} /* switch media_in */
|
||||
xml_name_set(xdata, "data");
|
||||
/* Here xdata is:
|
||||
* <data><input xmlns="urn:example:clixon">...</input></data>
|
||||
|
|
@ -504,7 +507,7 @@ api_operations_post_input(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -539,7 +542,7 @@ api_operations_post_input(clicon_handle h,
|
|||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] youtput Yang rpc output specification
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
|
||||
* @param[in] media_out Output media
|
||||
* @param[out] xoutputp Restconf JSON/XML output
|
||||
* @retval 1 OK
|
||||
* @retval 0 Fail, Error message sent
|
||||
|
|
@ -554,7 +557,7 @@ api_operations_post_output(clicon_handle h,
|
|||
yang_stmt *youtput,
|
||||
char *namespace,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
restconf_media media_out,
|
||||
cxobj **xoutputp)
|
||||
|
||||
{
|
||||
|
|
@ -578,7 +581,7 @@ api_operations_post_output(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -616,7 +619,7 @@ api_operations_post_output(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -673,8 +676,8 @@ api_operations_post_output(clicon_handle h,
|
|||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
* @param[in] media_in Input media media
|
||||
* @param[in] media_out Output media
|
||||
* See RFC 8040 Sec 3.6 / 4.4.2
|
||||
* @note We map post to edit-config create.
|
||||
* POST {+restconf}/operations/<operation>
|
||||
|
|
@ -705,8 +708,8 @@ api_operations_post(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
restconf_media media_in,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -749,7 +752,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -768,7 +771,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -779,7 +782,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -808,7 +811,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -822,7 +825,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_debug(1, "%s : 4. Parse input data: %s", __FUNCTION__, data);
|
||||
if (data && strlen(data)){
|
||||
if ((ret = api_operations_post_input(h, r, data, yspec, yrpc, xbot,
|
||||
pretty, use_xml, parse_xml)) < 0)
|
||||
pretty, media_in, media_out)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
|
|
@ -849,7 +852,7 @@ api_operations_post(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto ok;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -879,7 +882,7 @@ api_operations_post(clicon_handle h,
|
|||
goto done;
|
||||
/* Local error: return it and quit */
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -888,7 +891,7 @@ api_operations_post(clicon_handle h,
|
|||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -907,24 +910,27 @@ api_operations_post(clicon_handle h,
|
|||
#endif
|
||||
youtput = yang_find(yrpc, Y_OUTPUT, NULL);
|
||||
if ((ret = api_operations_post_output(h, r, xret, yspec, youtput, namespace,
|
||||
pretty, use_xml, &xoutput)) < 0)
|
||||
pretty, media_out, &xoutput)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
/* xoutput should now look: <output xmlns="uri"><x>0</x></output> */
|
||||
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, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
cbuf_reset(cbret);
|
||||
if (use_xml){
|
||||
switch (media_out){
|
||||
case YANG_DATA_XML:
|
||||
if (clicon_xml2cbuf(cbret, xoutput, 0, pretty) < 0)
|
||||
goto done;
|
||||
/* xoutput should now look: <output xmlns="uri"><x>0</x></output> */
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
if (xml2json_cbuf(cbret, xoutput, pretty) < 0)
|
||||
goto done;
|
||||
/* xoutput should now look: {"example:output": {"x":0,"y":42}} */
|
||||
break;
|
||||
}
|
||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cbret));
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
|
|
|||
|
|
@ -44,11 +44,16 @@
|
|||
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||
cvec *pcvec, int pi,
|
||||
cvec *qvec, char *data,
|
||||
int pretty, int use_xml, int parse_xml);
|
||||
int pretty,
|
||||
restconf_media media_in,
|
||||
restconf_media media_out);
|
||||
|
||||
int api_operations_post(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data,
|
||||
int pretty, int use_xml, int parse_xml);
|
||||
int pretty,
|
||||
restconf_media media_in,
|
||||
restconf_media media_out);
|
||||
|
||||
|
||||
#endif /* _RESTCONF_METHODS_POST_H_ */
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ restconf_stream(clicon_handle h,
|
|||
char *name,
|
||||
cvec *qvec,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
restconf_media media_out,
|
||||
int *sp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -269,7 +269,7 @@ restconf_stream(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -355,7 +355,7 @@ api_stream(clicon_handle h,
|
|||
char *data;
|
||||
int authenticated = 0;
|
||||
int pretty;
|
||||
int use_xml = 1; /* default */
|
||||
restconf_media media_out = YANG_DATA_XML; /* XXX default */
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
|
@ -374,20 +374,20 @@ api_stream(clicon_handle h,
|
|||
goto done;
|
||||
/* Sanity check of path. Should be /stream/<name> */
|
||||
if (pn != 3){
|
||||
notfound(r);
|
||||
restconf_notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
if (strlen(pvec[0]) != 0){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(pvec[1], streampath)){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
retval = restconf_notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
||||
|
|
@ -418,14 +418,14 @@ api_stream(clicon_handle h,
|
|||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0)
|
||||
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
goto ok;
|
||||
}
|
||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
if (restconf_stream(h, r, method, qvec, pretty, use_xml, &s) < 0)
|
||||
if (restconf_stream(h, r, method, qvec, pretty, media_out, &s) < 0)
|
||||
goto done;
|
||||
if (s != -1){
|
||||
#ifdef STREAM_FORK
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue