restconf RPC
This commit is contained in:
parent
d0660bf611
commit
fd91bb2933
17 changed files with 371 additions and 109 deletions
|
|
@ -236,7 +236,7 @@ cli_dbxml(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot)) == NULL)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ expand_dbvar(void *h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Special case for leafref. Detect leafref via Yang-type,
|
||||
* Get Yang path element, tentatively add the new syntax to the whole
|
||||
|
|
|
|||
|
|
@ -196,27 +196,6 @@ catch:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*! Struct to carry info into and out of ys_find_rpc callback
|
||||
*/
|
||||
typedef struct {
|
||||
char *name; /* name of rpc */
|
||||
yang_stmt *yrpc; /* matching yang statement */
|
||||
} find_rpc_arg;
|
||||
|
||||
/*! Check yang rpc statement, return yang rpc statement if found
|
||||
*/
|
||||
static int
|
||||
ys_find_rpc(yang_stmt *ys,
|
||||
void *arg)
|
||||
{
|
||||
find_rpc_arg *fra = (find_rpc_arg*)arg;
|
||||
|
||||
if (strcmp(fra->name, ys->ys_argument) == 0){
|
||||
fra->yrpc = ys;
|
||||
return 1; /* handled */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! See if there is any callback registered for this tag
|
||||
*
|
||||
|
|
@ -240,8 +219,7 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
yang_stmt *yinput;
|
||||
yang_stmt *youtput;
|
||||
cxobj *xoutput;
|
||||
find_rpc_arg fra = {0,0};
|
||||
int ret;
|
||||
cbuf *cb;
|
||||
|
||||
if (deps != NULL){
|
||||
nreg = deps;
|
||||
|
|
@ -259,13 +237,17 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* create absolute path */
|
||||
cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
|
||||
/* Find yang rpc statement, return yang rpc statement if found */
|
||||
fra.name = xml_name(xn);
|
||||
if ((ret = yang_apply((yang_node*)yspec, Y_RPC, ys_find_rpc, &fra)) < 0)
|
||||
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), &yrpc) < 0)
|
||||
goto done;
|
||||
/* Check if found */
|
||||
if (ret == 1){
|
||||
yrpc = fra.yrpc;
|
||||
if (yrpc != NULL){
|
||||
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
||||
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
|
|
@ -300,6 +282,8 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
resource ([RFC6415]) */
|
||||
#define RESTCONF_API_ROOT "/restconf/"
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE
|
||||
/*! 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])
|
||||
|
|
@ -120,6 +120,38 @@ api_data(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Operations REST method, POST
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input data
|
||||
*/
|
||||
static int
|
||||
api_operations(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
|
||||
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)
|
||||
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data);
|
||||
else
|
||||
retval = notfound(r);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Process a FastCGI request
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
|
|
@ -176,6 +208,9 @@ request_process(clicon_handle h,
|
|||
|
||||
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);
|
||||
else if (strcmp(method, "test") == 0)
|
||||
retval = test(r, 0);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ api_data_post(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
|
|
@ -462,7 +462,7 @@ api_data_put(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
|
|
@ -581,7 +581,7 @@ api_data_delete(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -606,7 +606,7 @@ api_data_delete(clicon_handle h,
|
|||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbx)
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
|
|
@ -614,3 +614,116 @@ api_data_delete(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! REST operation POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @note We map post to edit-config create.
|
||||
|
||||
POST {+restconf}/operations/<operation>
|
||||
|
||||
|
||||
*/
|
||||
int
|
||||
api_operation_post(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *oppath = path;
|
||||
yang_stmt *yrpc = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *yinput;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
yang_node *y = NULL;
|
||||
cxobj *xinput;
|
||||
cxobj *x;
|
||||
cxobj **vec = NULL;
|
||||
|
||||
clicon_debug(1, "%s json:\"%s\"", __FUNCTION__, data);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<pi; i++)
|
||||
oppath = index(oppath+1, '/');
|
||||
/* Find yang rpc statement, return yang rpc statement if found */
|
||||
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
|
||||
goto done;
|
||||
if (yrpc == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
/* Create an xml message:
|
||||
* <"rpc"><operation><input-args>...
|
||||
* eg <rpc><fib-route><name>
|
||||
*/
|
||||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new("rpc", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if (data){
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
/* xdata should have format <top><input> */
|
||||
if ((xinput = xpath_first(xdata, "/input")) != NULL){
|
||||
/* Add all input under <rpc>path */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xinput, x, -1)) != NULL)
|
||||
if (xml_addsub(xbot, x) < 0)
|
||||
goto done;
|
||||
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
||||
xml_spec_set(xinput, yinput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xinput, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xinput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_yang_validate_add(xinput, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
/* Sanity check of outgoing XML */
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
vec = xml_childvec_get(xret);
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,8 @@ 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_post(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_H_ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue