mem leak
This commit is contained in:
parent
fd91bb2933
commit
265ed859de
8 changed files with 137 additions and 39 deletions
11
README.md
11
README.md
|
|
@ -88,9 +88,14 @@ used to generate an interactive CLI client as well as provide
|
|||
[Restconf](apps/restconf/README.md) clients.
|
||||
|
||||
The [YANG RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) is implemented with the following exceptions:
|
||||
- object-references
|
||||
- if-feature
|
||||
- unique
|
||||
- conformance: feature, if-feature, deviation
|
||||
- identy, base, identityref
|
||||
- list features: min/max-elements, unique, ordered-by
|
||||
|
||||
There are also new features in YANG 1.1 [YANG RFC
|
||||
7950](https://www.rfc-editor.org/rfc/rfc7950.txt) most of which are
|
||||
not implemented.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
|
||||
xoutput=xpath_first(*xret, "/");
|
||||
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xoutput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
|
|
|
|||
|
|
@ -264,7 +264,19 @@ test(FCGX_Request *r,
|
|||
printparam(r, "SERVER_NAME", dbg);
|
||||
printparam(r, "HTTP_COOKIE", dbg);
|
||||
printparam(r, "HTTPS", dbg);
|
||||
|
||||
printparam(r, "HTTP_ACCEPT", dbg);
|
||||
printparam(r, "HTTP_CONTENT_TYPE", dbg);
|
||||
#if 0 /* For debug */
|
||||
clicon_debug(1, "All environment vars:");
|
||||
{
|
||||
extern char **environ;
|
||||
int i;
|
||||
for (i = 0; environ[i] != NULL; i++){
|
||||
clicon_debug(1, "%s", environ[i]);
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "End environment vars:");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ api_operations(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Process a FastCGI request
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
|
|
@ -189,12 +188,10 @@ request_process(clicon_handle h,
|
|||
clicon_debug(1, "DATA=%s", data);
|
||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||
goto done;
|
||||
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
test(r, 1);
|
||||
/* If present, check credentials */
|
||||
|
|
@ -392,7 +389,7 @@ main(int argc,
|
|||
if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){
|
||||
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0 ||
|
||||
strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)-1) == 0)
|
||||
request_process(h, r);
|
||||
request_process(h, r); /* This is the function */
|
||||
else{
|
||||
clicon_debug(1, "top-level not found");
|
||||
notfound(r);
|
||||
|
|
|
|||
|
|
@ -161,8 +161,13 @@ api_data_get_gen(clicon_handle h,
|
|||
cbuf *cbj = NULL;;
|
||||
int code;
|
||||
const char *reason_phrase;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((path = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
|
|
@ -213,15 +218,22 @@ api_data_get_gen(clicon_handle h,
|
|||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (head)
|
||||
goto ok;
|
||||
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
|
||||
vec = xml_childvec_get(xret);
|
||||
|
||||
clicon_debug(1, "%s xretnr:%d", __FUNCTION__, xml_child_nr(xret));
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cbx, xret, 0, 1) < 0) /* Dont print top object? */
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
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");
|
||||
|
|
@ -276,8 +288,8 @@ api_data_head(clicon_handle h,
|
|||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang.data+xml
|
||||
* Content-Type: application/yang.data+json
|
||||
* Content-Type: application/yang-data+xml
|
||||
* Content-Type: application/yang-data+json
|
||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
|
|
@ -340,10 +352,15 @@ api_data_post(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
|
|
@ -356,8 +373,14 @@ api_data_post(clicon_handle h,
|
|||
xbot = xtop;
|
||||
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){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -448,10 +471,15 @@ api_data_put(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
|
|
@ -464,8 +492,14 @@ api_data_put(clicon_handle h,
|
|||
xbot = xtop;
|
||||
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){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -642,6 +676,7 @@ api_operation_post(clicon_handle h,
|
|||
yang_stmt *yrpc = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *yinput;
|
||||
yang_stmt *youtput;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
|
|
@ -649,10 +684,23 @@ api_operation_post(clicon_handle h,
|
|||
cxobj *xbot = NULL;
|
||||
yang_node *y = NULL;
|
||||
cxobj *xinput;
|
||||
cxobj *xoutput;
|
||||
cxobj *x;
|
||||
cxobj **vec = NULL;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default return JSON */
|
||||
|
||||
clicon_debug(1, "%s json:\"%s\"", __FUNCTION__, data);
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
clicon_debug(1, "%s accept:\"%s\" content-type:\"%s\"",
|
||||
__FUNCTION__, media_accept, media_content_type);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
|
|
@ -677,8 +725,14 @@ api_operation_post(clicon_handle h,
|
|||
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){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -705,22 +759,44 @@ api_operation_post(clicon_handle h,
|
|||
goto done;
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
xoutput=xpath_first(xret, "/");
|
||||
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL &&
|
||||
xoutput){
|
||||
xml_name_set(xoutput, "output");
|
||||
clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
cbuf_reset(cbx);
|
||||
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xoutput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_yang_validate_add(xoutput, NULL) < 0)
|
||||
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, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
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");
|
||||
|
||||
if (xoutput){
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cbx, xoutput, 0, 1) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml2json_cbuf(cbx, xoutput, 1) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s xoutput:%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 (xtop)
|
||||
xml_free(xtop);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cbx)
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
* goto err;
|
||||
* cbuf_free(cb);
|
||||
* @endcode
|
||||
* See also xml2json
|
||||
* @see clicon_xml2cbuf
|
||||
*/
|
||||
int
|
||||
xml2json_cbuf(cbuf *cb,
|
||||
|
|
@ -442,7 +442,7 @@ xml2json_cbuf(cbuf *cb,
|
|||
|
||||
/*! Translate a vector of xml objects to JSON CLigen buffer.
|
||||
* This is done by adding a top pseudo-object, and add the vector as subs,
|
||||
* and then not pritning the top pseudo-.object using the 'flat' option.
|
||||
* and then not printing the top pseudo-object using the 'flat' option.
|
||||
* @param[out] cb Cligen buffer to write to
|
||||
* @param[in] vec Vector of xml objecst
|
||||
* @param[in] veclen Length of vector
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ expectfn(){
|
|||
return
|
||||
fi
|
||||
# grep extended grep
|
||||
match=`echo "$ret" | grep -EZo "$expect"`
|
||||
match=`echo $ret | grep -EZo "$expect"`
|
||||
# echo "ret:\"$ret\""
|
||||
# echo "expect:\"$expect\""
|
||||
# echo "match:\"$match\""
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ new "restconf options"
|
|||
expectfn "curl -i -sS -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
||||
|
||||
new "restconf head"
|
||||
expectfn "curl -sS -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
||||
expectfn "curl -sS -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
|
||||
#Content-Type: application/yang-data+json"
|
||||
|
||||
new "restconf get empty config"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "^null
$"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "null"
|
||||
|
||||
new "restconf Add subtree to datastore using POST"
|
||||
expectfn 'curl -sS -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' ""
|
||||
|
|
@ -46,7 +47,7 @@ new "restconf delete interfaces"
|
|||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces' ""
|
||||
|
||||
new "restconf Check empty config"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "^null
$"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "null"
|
||||
|
||||
new "restconf Add interfaces subtree eth/0/0 using POST"
|
||||
expectfn 'curl -sS -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' ""
|
||||
|
|
@ -69,7 +70,7 @@ new "restconf delete eth/0/0"
|
|||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||
|
||||
new "Check deleted eth/0/0"
|
||||
expectfn 'curl -sS -G http://localhost/restconf/data' "^null
$"
|
||||
expectfn 'curl -sS -G http://localhost/restconf/data' "null"
|
||||
|
||||
new "restconf Re-Delete eth/0/0 using none should generate error"
|
||||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "Not Found"
|
||||
|
|
@ -81,8 +82,15 @@ new "restconf get subtree"
|
|||
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}}
|
||||
$'
|
||||
|
||||
new "restconf rpc using POST (wrong output encoding)"
|
||||
expectfn 'curl -sS -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '{"rpc-reply": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}}
$'
|
||||
new "restconf rpc using POST json"
|
||||
expectfn 'curl -sS -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '{ "output": { "route": { "address-family": "ipv4", "next-hop": { "next-hop-list": "2.3.4.5" } } } } '
|
||||
|
||||
new "restconf rpc using POST xml"
|
||||
ret=$(curl -sS -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)
|
||||
expect="<output> <route> <address-family>ipv4</address-family> <next-hop> <next-hop-list>2.3.4.5</next-hop-li t> </next-hop> </route> </output> "
|
||||
match=`echo $ret | grep -EZo "$expect"`
|
||||
echo -n "ret: "
|
||||
echo $ret
|
||||
|
||||
new "Kill restconf daemon"
|
||||
sudo pkill -u www-data clixon_restconf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue