restconf fcgi refactoring and generalizing restconf tests for evhtp

This commit is contained in:
Olof hagsand 2020-06-19 15:19:03 +02:00
parent ee29452d24
commit 126cc209f4
15 changed files with 173 additions and 620 deletions

View file

@ -148,12 +148,18 @@ restconf_reply_send(void *req0,
htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL);
#endif
/* If body, add a content-length header */
if (cb != NULL && cbuf_len(cb))
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
/* create evbuffer* : bufferevent_write_buffer/ drain,
ie send everything , except body */
evhtp_send_reply_start(req, req->status);
/* Write a body if cbuf is nonzero */
if (cb != NULL && cbuf_len(cb)){
/* Suboptimal, copy from cbuf to evbuffer */
if ((eb = evbuffer_new()) == NULL){
clicon_err(OE_CFG, errno, "evbuffer_new");

View file

@ -102,8 +102,6 @@ restconf_badrequest(clicon_handle h,
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
goto done;
cprintf(cb, "The requested URL %s or data is in some way badly formed.\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 400, cb) < 0)
goto done;
retval = 0;
@ -144,9 +142,6 @@ restconf_unauthorized(clicon_handle h,
goto done;
cprintf(cb, "<error-tag>access-denied</error-tag>\n");
cprintf(cb, "The requested URL %s was unauthorized.\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 400, cb) < 0)
goto done;
retval = 0;
@ -184,8 +179,6 @@ restconf_forbidden(clicon_handle h,
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
goto done;
cprintf(cb, "The requested URL %s was forbidden.\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 403, cb) < 0)
goto done;
retval = 0;
@ -224,8 +217,6 @@ restconf_notfound(clicon_handle h,
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
goto done;
cprintf(cb, "The requested URL %s was not found on this server.\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 404, cb) < 0)
goto done;
retval = 0;
@ -283,8 +274,6 @@ restconf_notacceptable(clicon_handle h,
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
goto done;
cprintf(cb, "The target resource does not have a current representation that would be acceptable to the user agent.\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 406, cb) < 0)
goto done;
retval = 0;
@ -353,8 +342,6 @@ restconf_internal_server_error(clicon_handle h,
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
goto done;
cprintf(cb, "Internal server error when accessing %s</h1>\n", path);
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)=
goto done;
if (restconf_reply_send(req, 500, cb) < 0)
goto done;
retval = 0;
@ -479,8 +466,6 @@ api_return_err(clicon_handle h,
goto done;
break;
} /* switch media */
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, code, cb) < 0)
goto done;
// ok:

View file

@ -138,11 +138,41 @@ evhtp_method2str(enum htp_method m)
case htp_method_DELETE:
return "DELETE";
break;
case htp_method_MKCOL:
return "MKCOL";
break;
case htp_method_COPY:
return "COPY";
break;
case htp_method_MOVE:
return "MOVE";
break;
case htp_method_OPTIONS:
return "OPTIONS";
break;
case htp_method_PROPFIND:
return "PROPFIND";
break;
case htp_method_PROPPATCH:
return "PROPPATCH";
break;
case htp_method_LOCK:
return "LOCK";
break;
case htp_method_UNLOCK:
return "UNLOCK";
break;
case htp_method_TRACE:
return "TRACE";
break;
case htp_method_CONNECT:
return "CONNECT";
break;
case htp_method_PATCH:
return "PATCH";
break;
default:
return "XXX";
return "UNKNOWN";
break;
}
}
@ -221,7 +251,6 @@ evhtp_params_set(clicon_handle h,
clicon_err(OE_CFG, errno, "evhtp_kvs_for_each");
goto done;
}
if (clixon_restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0)
goto done;
if (clixon_restconf_param_set(h, "REQUEST_URI", path->full) < 0)
@ -327,6 +356,7 @@ cx_path_wellknown(evhtp_request_t *req,
{
clicon_handle h = arg;
clicon_debug(1, "------------");
/* input debug */
if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, print_header, h);
@ -356,6 +386,7 @@ cx_path_restconf(evhtp_request_t *req,
clicon_handle h = arg;
cvec *qvec = NULL;
clicon_debug(1, "------------");
/* input debug */
if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, print_header, h);

View file

@ -107,7 +107,6 @@ fcgi_params_set(clicon_handle h,
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
if (clixon_strsplit(envp[i], '=', &param, &val) < 0)
goto done;
clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val);
if (clixon_restconf_param_set(h, param, val) < 0)
goto done;
if (param){
@ -142,7 +141,6 @@ fcgi_params_clear(clicon_handle h,
for (i = 0; envp[i] != NULL; i++){ /* on the form <param>=<value> */
if (clixon_strsplit(envp[i], '=', &param, NULL) < 0)
goto done;
clicon_debug(1, "%s param:%s", __FUNCTION__, param);
if (clixon_restconf_param_del(h, param) < 0)
goto done;
if (param){
@ -152,371 +150,9 @@ fcgi_params_clear(clicon_handle h,
}
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
return retval;
}
/*! Print all FCGI headers
* @param[in] req Fastcgi request handle
* @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https
*/
static int
fcgi_print_headers(FCGX_Request *req)
{
char **environ = req->envp;
int i;
clicon_debug(1, "All environment vars:");
for (i = 0; environ[i] != NULL; i++)
clicon_debug(1, "%s", environ[i]);
clicon_debug(1, "End environment vars");
return 0;
}
/*! Generic REST method, GET, PUT, DELETE, etc
* @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where to start pcvec
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] pretty Set to 1 for pretty-printed xml/json output
* @param[in] media_in Input media
* @param[in] media_out Output media
*/
static int
api_data(clicon_handle h,
FCGX_Request *req,
char *api_path,
cvec *pcvec,
int pi,
cvec *qvec,
char *data,
int pretty,
restconf_media media_out)
{
int retval = -1;
char *request_method;
clicon_debug(1, "%s", __FUNCTION__);
request_method = clixon_restconf_param_get(h, "REQUEST_METHOD");
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
if (strcmp(request_method, "OPTIONS")==0)
retval = api_data_options(h, req);
else if (strcmp(request_method, "HEAD")==0)
retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out);
else if (strcmp(request_method, "GET")==0)
retval = api_data_get(h, req, api_path, pcvec, pi, qvec, pretty, media_out);
else if (strcmp(request_method, "POST")==0)
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "PUT")==0)
retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "PATCH")==0)
retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "DELETE")==0)
retval = api_data_delete(h, req, api_path, pi, pretty, media_out);
else
retval = restconf_notfound(h, req);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
}
/*! Operations REST method, POST
* @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where to start pcvec
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] data Stream input data
* @param[in] media_out Output media
*/
static int
api_operations(clicon_handle h,
FCGX_Request *r,
char *path,
cvec *pcvec,
int pi,
cvec *qvec,
char *data,
int pretty,
restconf_media media_out)
{
int retval = -1;
char *request_method;
clicon_debug(1, "%s", __FUNCTION__);
request_method = clixon_restconf_param_get(h, "REQUEST_METHOD");
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
if (strcmp(request_method, "GET")==0)
retval = api_operations_get(h, r, path, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "POST")==0)
retval = api_operations_post(h, r, path, pi, qvec, data,
pretty, media_out);
else
retval = restconf_notfound(h, r);
return retval;
}
/*! Retrieve the Top-Level API Resource
* @param[in] h Clicon handle
* @param[in] r Fastcgi request handle
* @note Only returns null for operations and data,...
* See RFC8040 3.3
* XXX doesnt check method
*/
static int
api_fcgi_root(clicon_handle h,
FCGX_Request *r,
int pretty,
restconf_media media_out)
{
int retval = -1;
cxobj *xt = NULL;
cbuf *cb = NULL;
yang_stmt *yspec;
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Status: 200 OK\r\n");
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
FCGX_FPrintF(r->out, "\r\n");
if (clixon_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>",
YB_MODULE, yspec, &xt, NULL) < 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;
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
goto done;
break;
case YANG_DATA_JSON:
if (xml2json_cbuf(cb, xt, pretty) < 0)
goto done;
break;
default:
break;
}
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 pretty,
restconf_media media_out)
{
int retval = -1;
cxobj *xt = NULL;
cbuf *cb = NULL;
char *ietf_yang_library_revision = "2016-06-21"; /* XXX */
clicon_debug(1, "%s", __FUNCTION__);
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
FCGX_FPrintF(r->out, "\r\n");
if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL,
"<yang-library-version>%s</yang-library-version>",
ietf_yang_library_revision) < 0)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
goto done;
}
switch (media_out){
case YANG_DATA_XML:
if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0)
goto done;
break;
case YANG_DATA_JSON:
if (xml2json_cbuf(cb, xt, pretty) < 0)
goto done;
break;
default:
break;
}
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):"");
FCGX_FPrintF(r->out, "\n\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
api_fcgi_restconf(clicon_handle h,
FCGX_Request *req)
{
int retval = -1;
char *path;
char *query = NULL;
char *api_resource;
char **pvec = NULL;
int pn;
cvec *qvec = NULL;
cvec *pcvec = NULL; /* for rest api */
cbuf *cb = NULL;
char *indata = NULL;
int authenticated = 0;
char *media_str = NULL;
restconf_media media_out = YANG_DATA_JSON;
int pretty;
cxobj *xret = NULL;
cxobj *xerr;
clicon_debug(1, "%s", __FUNCTION__);
path = restconf_uripath(h);
query = clixon_restconf_param_get(h, "QUERY_STRING");
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
/* 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 = clixon_restconf_param_get(h, "HTTP_ACCEPT")) == NULL){
// retval = restconf_unsupported_media(r);
// goto done;
}
else if ((int)(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(req);
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){
restconf_notfound(h, req);
goto ok;
}
if (strlen(pvec[0]) != 0){
retval = restconf_notfound(h, req);
goto done;
}
if (strcmp(pvec[1], RESTCONF_API)){
retval = restconf_notfound(h, req);
goto done;
}
fcgi_print_headers(req);
if (pn == 2){
retval = api_fcgi_root(h, req, pretty, media_out);
goto done;
}
if ((api_resource = pvec[2]) == NULL){
retval = restconf_notfound(h, req);
goto done;
}
clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource);
if (query != NULL && strlen(query))
if (str2cvec(query, '&', '=', &qvec) < 0)
goto done;
if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */
goto done;
/* indata */
if ((cb = restconf_get_indata(req)) == NULL)
goto done;
indata = cbuf_get(cb);
clicon_debug(1, "%s DATA=%s", __FUNCTION__, indata);
/* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5
*/
if ((authenticated = clixon_plugin_auth_all(h, req)) < 0)
goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
/* If set but no user, set a dummy user */
if (authenticated){
if (clicon_username_get(h) == NULL)
clicon_username_set(h, "none");
}
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, req, 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 (strcmp(api_resource, "yang-library-version")==0){
if (api_yang_library_version(h, req, pretty, media_out) < 0)
goto done;
}
else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */
if (api_data(h, req, path, pcvec, 2, qvec, indata,
pretty, media_out) < 0)
goto done;
}
else if (strcmp(api_resource, "operations") == 0){ /* rpc */
if (api_operations(h, req, path, pcvec, 2, qvec, indata,
pretty, media_out) < 0)
goto done;
}
else
restconf_notfound(h, req);
ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pvec)
free(pvec);
if (qvec)
cvec_free(qvec);
if (pcvec)
cvec_free(pcvec);
if (cb)
cbuf_free(cb);
if (xret)
xml_free(xret);
return retval;
}
/* Need global variable to for signal handler XXX */
static clicon_handle _CLICON_HANDLE = NULL;
@ -868,14 +504,21 @@ main(int argc,
*/
if (fcgi_params_set(h, req->envp) < 0)
goto done;
/* Debug_ print headers */
if (clicon_debug_get())
fcgi_print_headers(req);
if ((path = clixon_restconf_param_get(h, "REQUEST_URI")) != NULL){
clicon_debug(1, "path: %s", path);
if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0)
api_fcgi_restconf(h, req); /* This is the function */
if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0){
char *query = NULL;
cvec *qvec = NULL;
query = clixon_restconf_param_get(h, "QUERY_STRING");
if (query != NULL && strlen(query))
if (str2cvec(query, '&', '=', &qvec) < 0)
goto done;
api_root_restconf(h, req, qvec); /* This is the function */
if (qvec){
cvec_free(qvec);
qvec = NULL;
}
}
else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) {
api_stream(h, req, stream_path, &finish);
}

View file

@ -441,6 +441,7 @@ clixon_restconf_param_set(clicon_handle h,
char *param,
char *val)
{
clicon_debug(1, "%s=%s", param, val);
return clicon_data_set(h, param, val);
}

View file

@ -65,9 +65,11 @@
#include "restconf_api.h"
#include "restconf_err.h"
#include "restconf_root.h"
#include "restconf_methods.h"
#include "restconf_methods_get.h"
#include "restconf_methods_post.h"
/*! Determine the root of the RESTCONF API
/*! Determine the root of the RESTCONF API by accessing /.well-known
* @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle)
* @see RFC8040 3.1 and RFC7320
@ -105,9 +107,6 @@ api_well_known(clicon_handle h,
cprintf(cb, " <Link rel='restconf' href='/restconf'/>\n");
cprintf(cb, "</XRD>\r\n");
/* Must be after body */
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 200, cb) < 0)
goto done;
ok:
@ -118,19 +117,19 @@ api_well_known(clicon_handle h,
return retval;
}
/*! Retrieve the Top-Level API Resource
/*! Retrieve the Top-Level API Resource /restconf/ (exact)
* @param[in] h Clicon handle
* @param[in] r Fastcgi request handle
* @note Only returns null for operations and data,...
* See RFC8040 3.3
* XXX doesnt check method
* @see api_root_restconf for accessing /restconf/ *
*/
static int
api_root(clicon_handle h,
void *req,
char *request_method,
int pretty,
restconf_media media_out)
api_root_restconf_exact(clicon_handle h,
void *req,
char *request_method,
int pretty,
restconf_media media_out)
{
int retval = -1;
@ -175,8 +174,6 @@ api_root(clicon_handle h,
default:
break;
}
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 200, cb) < 0)
goto done;
ok:
@ -199,20 +196,16 @@ api_yang_library_version(clicon_handle h,
restconf_media media_out)
{
#if 1
clicon_debug(1, "%s", __FUNCTION__);
return 0;
#else
int retval = -1;
cxobj *xt = NULL;
cbuf *cb = NULL;
char *ietf_yang_library_revision = "2016-06-21"; /* XXX */
clicon_debug(1, "%s", __FUNCTION__);
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n");
FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out));
FCGX_FPrintF(r->out, "\r\n");
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
goto done;
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL,
"<yang-library-version>%s</yang-library-version>",
ietf_yang_library_revision) < 0)
@ -220,6 +213,7 @@ api_yang_library_version(clicon_handle h,
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
switch (media_out){
@ -234,9 +228,8 @@ api_yang_library_version(clicon_handle h,
default:
break;
}
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):"");
FCGX_FPrintF(r->out, "\n\n");
if (restconf_reply_send(req, 200, cb) < 0)
goto done;
retval = 0;
done:
if (cb)
@ -244,7 +237,6 @@ api_yang_library_version(clicon_handle h,
if (xt)
xml_free(xt);
return retval;
#endif
}
/*! Generic REST method, GET, PUT, DELETE, etc
@ -269,10 +261,6 @@ api_data(clicon_handle h,
int pretty,
restconf_media media_out)
{
#if 1
clicon_debug(1, "%s", __FUNCTION__);
return 0;
#else
int retval = -1;
char *request_method;
@ -297,104 +285,6 @@ api_data(clicon_handle h,
retval = restconf_notfound(h, req);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
#endif
}
/*! Move back to restconf_methods_get
*/
static int
api_operations_get(clicon_handle h,
void *req,
char *path,
int pi,
cvec *qvec,
char *data,
int pretty,
restconf_media media_out)
{
int retval = -1;
yang_stmt *yspec;
yang_stmt *ymod; /* yang module */
yang_stmt *yc;
char *namespace;
cbuf *cb = NULL;
cxobj *xt = NULL;
int i;
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
switch (media_out){
case YANG_DATA_XML:
cprintf(cb, "<operations>");
break;
case YANG_DATA_JSON:
if (pretty)
cprintf(cb, "{\"operations\": {\n");
else
cprintf(cb, "{\"operations\":{");
break;
default:
break;
}
ymod = NULL;
i = 0;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
namespace = yang_find_mynamespace(ymod);
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
if (yang_keyword_get(yc) != Y_RPC)
continue;
switch (media_out){
case YANG_DATA_XML:
cprintf(cb, "<%s xmlns=\"%s\"/>", yang_argument_get(yc), namespace);
break;
case YANG_DATA_JSON:
if (i++){
cprintf(cb, ",");
if (pretty)
cprintf(cb, "\n\t");
}
if (pretty)
cprintf(cb, "\"%s:%s\": [null]", yang_argument_get(ymod), yang_argument_get(yc));
else
cprintf(cb, "\"%s:%s\":[null]", yang_argument_get(ymod), yang_argument_get(yc));
break;
default:
break;
}
}
}
switch (media_out){
case YANG_DATA_XML:
cprintf(cb, "</operations>");
break;
case YANG_DATA_JSON:
if (pretty)
cprintf(cb, "}\n}");
else
cprintf(cb, "}}");
break;
default:
break;
}
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
goto done;
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
if (restconf_reply_send(req, 200, cb) < 0)
goto done;
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
if (xt)
xml_free(xt);
return retval;
}
/*! Operations REST method, POST
@ -425,11 +315,9 @@ api_operations(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
if (strcmp(request_method, "GET")==0)
retval = api_operations_get(h, req, path, pi, qvec, data, pretty, media_out);
#ifdef NYI
else if (strcmp(request_method, "POST")==0)
retval = api_operations_post(h, req, path, pi, qvec, data,
pretty, media_out);
#endif
else
retval = restconf_notfound(h, req);
return retval;
@ -439,6 +327,7 @@ api_operations(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle)
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
* @see api_root_restconf_exact for accessing /restconf/ exact
*/
int
api_root_restconf(clicon_handle h,
@ -506,7 +395,7 @@ api_root_restconf(clicon_handle h,
goto done;
}
if (pn == 2){
retval = api_root(h, req, request_method, pretty, media_out);
retval = api_root_restconf_exact(h, req, request_method, pretty, media_out);
goto done;
}
if ((api_resource = pvec[2]) == NULL){
@ -546,10 +435,10 @@ api_root_restconf(clicon_handle h,
}
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
if (strcmp(api_resource, "yang-library-version")==0){
if (api_yang_library_version(h, req, pretty, media_out) < 0) /* XXX NYI */
if (api_yang_library_version(h, req, pretty, media_out) < 0)
goto done;
}
else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */ /* XXX NYI */
else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */
if (api_data(h, req, path, pcvec, 2, qvec, indata,
pretty, media_out) < 0)
goto done;