* All uses of `api_path2xpath_cvv()` should be replaced by `api_path2xpath()`
  * `api_path2xpath()` added an `xerr` argument.
This commit is contained in:
Olof hagsand 2020-03-10 22:37:16 +01:00
parent 0e94937ccf
commit adbb683329
20 changed files with 191 additions and 153 deletions

View file

@ -39,6 +39,9 @@ Expected: Early March 2020
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml) [search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)
### API changes on existing features (you may need to change your code) ### API changes on existing features (you may need to change your code)
* C-API:
* All uses of `api_path2xpath_cvv()` should be replaced by `api_path2xpath()`
* `api_path2xpath()` added an `xerr` argument.
* Empty values in JSON has changed to comply to RFC 7951 * Empty values in JSON has changed to comply to RFC 7951
* empty values of yang type `empty` are encoded as: `{"x":[null]}` * empty values of yang type `empty` are encoded as: `{"x":[null]}`
* empty string values are encoded as: `{"x":""}` (changed from `null` in 4.0 and `[null]` in 4.3) * empty string values are encoded as: `{"x":""}` (changed from `null` in 4.0 and `[null]` in 4.3)

View file

@ -152,7 +152,7 @@ expand_dbvar(void *h,
*/ */
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
goto done; goto done;
if (api_path2xpath(api_path, yspec, &xpath, &nsc) < 0) if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
goto done; goto done;
/* Get configuration */ /* Get configuration */
@ -714,7 +714,7 @@ cli_show_auto1(clicon_handle h,
} }
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
goto done; goto done;
if (api_path2xpath(api_path, yspec, &xpath, &nsc) < 0) if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
goto done; goto done;
/* XXX Kludge to overcome a trailing / in show, that I cannot add to /* XXX Kludge to overcome a trailing / in show, that I cannot add to
* yang2api_path_fmt_1 where it should belong. * yang2api_path_fmt_1 where it should belong.

View file

@ -708,7 +708,7 @@ restconf_insert_attributes(cxobj *xdata,
if (xml_prefix_set(xa, "yang") < 0) if (xml_prefix_set(xa, "yang") < 0)
goto done; goto done;
xml_type_set(xa, CX_ATTR); xml_type_set(xa, CX_ATTR);
if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc)) < 0) if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc, NULL)) < 0)
goto done; goto done;
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");

View file

@ -124,11 +124,11 @@ api_data(clicon_handle h,
if (strcmp(request_method, "OPTIONS")==0) if (strcmp(request_method, "OPTIONS")==0)
retval = api_data_options(h, r); retval = api_data_options(h, r);
else if (strcmp(request_method, "HEAD")==0) else if (strcmp(request_method, "HEAD")==0)
retval = api_data_head(h, r, pcvec, pi, qvec, pretty, media_out); retval = api_data_head(h, r, api_path, pcvec, pi, qvec, pretty, media_out);
else if (strcmp(request_method, "GET")==0) else if (strcmp(request_method, "GET")==0)
retval = api_data_get(h, r, pcvec, pi, qvec, pretty, media_out); retval = api_data_get(h, r, api_path, pcvec, pi, qvec, pretty, media_out);
else if (strcmp(request_method, "POST")==0) else if (strcmp(request_method, "POST")==0)
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, media_out); retval = api_data_post(h, r, api_path, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "PUT")==0) else if (strcmp(request_method, "PUT")==0)
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, media_out); retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "PATCH")==0) else if (strcmp(request_method, "PATCH")==0)
@ -169,9 +169,9 @@ api_operations(clicon_handle h,
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
if (strcmp(request_method, "GET")==0) if (strcmp(request_method, "GET")==0)
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, media_out); retval = api_operations_get(h, r, path, pi, qvec, data, pretty, media_out);
else if (strcmp(request_method, "POST")==0) else if (strcmp(request_method, "POST")==0)
retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, retval = api_operations_post(h, r, path, pi, qvec, data,
pretty, media_out); pretty, media_out);
else else
retval = restconf_notfound(r); retval = restconf_notfound(r);

View file

@ -261,9 +261,9 @@ api_data_write(clicon_handle h,
int ret; int ret;
char *namespace = NULL; char *namespace = NULL;
char *dname; char *dname;
cbuf *cbpath = NULL;
cvec *nsc = NULL; cvec *nsc = NULL;
enum yang_bind yb; enum yang_bind yb;
char *xpath = NULL;
clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0); clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0);
clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data); clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data);
@ -272,17 +272,14 @@ api_data_write(clicon_handle h,
goto done; goto done;
} }
api_path=api_path0; api_path=api_path0;
/* strip /... from start */
for (i=0; i<pi; i++) for (i=0; i<pi; i++)
api_path = index(api_path+1, '/'); api_path = index(api_path+1, '/');
/* Check if object exists in backend. if (api_path){
* Translate api-path to xpath */ /* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
if ((cbpath = cbuf_new()) == NULL) if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
goto done; goto done;
cprintf(cbpath, "/"); if (ret == 0){ /* validation failed */
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done; goto done;
@ -291,9 +288,11 @@ api_data_write(clicon_handle h,
goto done; goto done;
goto ok; goto ok;
} }
}
xret = NULL; xret = NULL;
if (clicon_rpc_get_config(h, clicon_nacm_recovery_user(h), if (clicon_rpc_get_config(h, clicon_nacm_recovery_user(h),
"candidate", cbuf_get(cbpath), nsc, &xret) < 0){ "candidate", xpath, nsc, &xret) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done; goto done;
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
@ -672,10 +671,10 @@ api_data_write(clicon_handle h,
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xpath)
free(xpath);
if (nsc) if (nsc)
xml_nsctx_free(nsc); xml_nsctx_free(nsc);
if (cbpath)
cbuf_free(cbpath);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (xerr) if (xerr)

View file

@ -66,6 +66,7 @@
* According to restconf * According to restconf
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[in] r Fastcgi request handle * @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts * @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] qvec Vector of query string (QUERY_STRING)
@ -92,7 +93,8 @@
static int static int
api_data_get2(clicon_handle h, api_data_get2(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
cvec *pcvec, char *api_path,
cvec *pcvec, /* XXX remove? */
int pi, int pi,
cvec *qvec, cvec *qvec,
int pretty, int pretty,
@ -100,7 +102,6 @@ api_data_get2(clicon_handle h,
int head) int head)
{ {
int retval = -1; int retval = -1;
cbuf *cbpath = NULL;
char *xpath = NULL; char *xpath = NULL;
cbuf *cbx = NULL; cbuf *cbx = NULL;
yang_stmt *yspec; yang_stmt *yspec;
@ -117,12 +118,41 @@ api_data_get2(clicon_handle h,
char *attr; /* attribute value string */ char *attr; /* attribute value string */
netconf_content content = CONTENT_ALL; netconf_content content = CONTENT_ALL;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */ int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
cxobj *xtop = NULL;
cxobj *xbot = NULL;
yang_stmt *y = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done; goto done;
} }
/* strip /... from start */
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
if (api_path){
if ((xtop = xml_new("top", NULL, NULL)) == NULL)
goto done;
/* Translate api-path to xml, but to validate the api-path, note: strict=1
* xtop and xbot unnecessary fir this function but neede by function
*/
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
goto done;
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
if (ret != 0 &&
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, NULL, "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;
}
}
/* Check for content attribute */ /* Check for content attribute */
if ((attr = cvec_find_str(qvec, "content")) != NULL){ if ((attr = cvec_find_str(qvec, "content")) != NULL){
clicon_debug(1, "%s content=%s", __FUNCTION__, attr); clicon_debug(1, "%s content=%s", __FUNCTION__, attr);
@ -162,25 +192,7 @@ api_data_get2(clicon_handle h,
} }
} }
} }
if ((cbpath = cbuf_new()) == NULL)
goto done;
cprintf(cbpath, "/");
/* We know "data" is element pi-1.
* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
*/
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xerr, NULL, "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;
}
xpath = cbuf_get(cbpath);
clicon_debug(1, "%s path:%s", __FUNCTION__, xpath); clicon_debug(1, "%s path:%s", __FUNCTION__, xpath);
switch (content){ switch (content){
case CONTENT_CONFIG: case CONTENT_CONFIG:
@ -306,12 +318,14 @@ api_data_get2(clicon_handle h,
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xpath)
free(xpath);
if (nsc) if (nsc)
xml_nsctx_free(nsc); xml_nsctx_free(nsc);
if (xtop)
xml_free(xtop);
if (cbx) if (cbx)
cbuf_free(cbx); cbuf_free(cbx);
if (cbpath)
cbuf_free(cbpath);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (xerr) if (xerr)
@ -324,6 +338,7 @@ api_data_get2(clicon_handle h,
/*! REST HEAD method /*! REST HEAD method
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[in] r Fastcgi request handle * @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts * @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] qvec Vector of query string (QUERY_STRING)
@ -338,19 +353,21 @@ api_data_get2(clicon_handle h,
int int
api_data_head(clicon_handle h, api_data_head(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
char *api_path,
cvec *pcvec, cvec *pcvec,
int pi, int pi,
cvec *qvec, cvec *qvec,
int pretty, int pretty,
restconf_media media_out) restconf_media media_out)
{ {
return api_data_get2(h, r, pcvec, pi, qvec, pretty, media_out, 1); return api_data_get2(h, r, api_path, pcvec, pi, qvec, pretty, media_out, 1);
} }
/*! REST GET method /*! REST GET method
* According to restconf * According to restconf
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[in] r Fastcgi request handle * @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts * @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] qvec Vector of query string (QUERY_STRING)
@ -375,13 +392,14 @@ api_data_head(clicon_handle h,
int int
api_data_get(clicon_handle h, api_data_get(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
char *api_path,
cvec *pcvec, cvec *pcvec,
int pi, int pi,
cvec *qvec, cvec *qvec,
int pretty, int pretty,
restconf_media media_out) restconf_media media_out)
{ {
return api_data_get2(h, r, pcvec, pi, qvec, pretty, media_out, 0); return api_data_get2(h, r, api_path, pcvec, pi, qvec, pretty, media_out, 0);
} }
/*! GET restconf/operations resource /*! GET restconf/operations resource
@ -413,7 +431,6 @@ int
api_operations_get(clicon_handle h, api_operations_get(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
char *path, char *path,
cvec *pcvec,
int pi, int pi,
cvec *qvec, cvec *qvec,
char *data, char *data,

View file

@ -40,13 +40,12 @@
/* /*
* Prototypes * Prototypes
*/ */
int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, int api_data_head(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi,
cvec *qvec, int pretty, restconf_media media_out); cvec *qvec, int pretty, restconf_media media_out);
int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, int api_data_get(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi,
cvec *qvec, int pretty, restconf_media media_out); cvec *qvec, int pretty, restconf_media media_out);
int api_operations_get(clicon_handle h, FCGX_Request *r, int api_operations_get(clicon_handle h, FCGX_Request *r,
char *path, char *api_path, int pi, cvec *qvec, char *data,
cvec *pcvec, int pi, cvec *qvec, char *data,
int pretty, restconf_media media_out); int pretty, restconf_media media_out);
#endif /* _RESTCONF_METHODS_GET_H_ */ #endif /* _RESTCONF_METHODS_GET_H_ */

View file

@ -100,7 +100,6 @@ int
api_data_post(clicon_handle h, api_data_post(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
char *api_path, char *api_path,
cvec *pcvec,
int pi, int pi,
cvec *qvec, cvec *qvec,
char *data, char *data,
@ -145,6 +144,7 @@ api_data_post(clicon_handle h,
/* Translate api_path to xtop/xbot */ /* Translate api_path to xtop/xbot */
xbot = xtop; xbot = xtop;
if (api_path){ if (api_path){
/* Translate api-path to xml, side-effect: validate the api-path, note: strict=1 */
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
goto done; goto done;
if (ret == 0){ /* validation failed */ if (ret == 0){ /* validation failed */
@ -609,7 +609,7 @@ api_operations_post_output(clicon_handle h,
* (1) Does not handle <ok/> properly * (1) Does not handle <ok/> properly
* (2) Uncertain how validation errors should be logged/handled * (2) Uncertain how validation errors should be logged/handled
*/ */
if (youtput!=NULL){ if (youtput != NULL){
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
#if 0 #if 0
if (xml_spec_populate(xoutput, yspec, NULL) < 0) if (xml_spec_populate(xoutput, yspec, NULL) < 0)
@ -675,9 +675,7 @@ api_operations_post_output(clicon_handle h,
/*! REST operation POST method /*! REST operation POST method
* @param[in] h CLIXON handle * @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle * @param[in] r Fastcgi request handle
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft]) * @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @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] qvec Vector of query string (QUERY_STRING)
* @param[in] data Stream input data * @param[in] data Stream input data
* @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] pretty Set to 1 for pretty-printed xml/json output
@ -706,8 +704,7 @@ api_operations_post_output(clicon_handle h,
int int
api_operations_post(clicon_handle h, api_operations_post(clicon_handle h,
FCGX_Request *r, FCGX_Request *r,
char *path, char *api_path,
cvec *pcvec,
int pi, int pi,
cvec *qvec, cvec *qvec,
char *data, char *data,
@ -716,7 +713,7 @@ api_operations_post(clicon_handle h,
{ {
int retval = -1; int retval = -1;
int i; int i;
char *oppath = path; char *oppath = api_path;
yang_stmt *yspec; yang_stmt *yspec;
yang_stmt *youtput = NULL; yang_stmt *youtput = NULL;
yang_stmt *yrpc = NULL; yang_stmt *yrpc = NULL;
@ -736,7 +733,7 @@ api_operations_post(clicon_handle h,
yang_stmt *ys = NULL; yang_stmt *ys = NULL;
char *namespace = NULL; char *namespace = NULL;
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path); clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, api_path);
/* 1. Initialize */ /* 1. Initialize */
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");

View file

@ -42,14 +42,13 @@
* Prototypes * Prototypes
*/ */
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path, int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
cvec *pcvec, int pi, int pi,
cvec *qvec, char *data, cvec *qvec, char *data,
int pretty, int pretty,
restconf_media media_out); restconf_media media_out);
int api_operations_post(clicon_handle h, FCGX_Request *r, int api_operations_post(clicon_handle h, FCGX_Request *r, char *api_path,
char *path, int pi, cvec *qvec, char *data,
cvec *pcvec, int pi, cvec *qvec, char *data,
int pretty, restconf_media media_out); int pretty, restconf_media media_out);

View file

@ -28,6 +28,8 @@ module clixon-example {
base if:interface-type; base if:interface-type;
} }
/* Translation function example - See also example_cli */ /* Translation function example - See also example_cli */
container translate{
description "dont have lists directly under top since restconf cant address list directly";
list translate{ list translate{
key k; key k;
leaf k{ leaf k{
@ -37,6 +39,7 @@ module clixon-example {
type string; type string;
} }
} }
}
/* State data (not config) for the example application*/ /* State data (not config) for the example application*/
container state { container state {
config false; config false;

View file

@ -79,8 +79,7 @@ int xml_yang_root(cxobj *x, cxobj **xr);
int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt); int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt);
int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path); int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path);
int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath); int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath);
int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, cvec **nsc, cxobj **xerr); int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc, cxobj **xerr);
int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc);
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop, int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
yang_class nodeclass, int strict, yang_class nodeclass, int strict,
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr); cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);

View file

@ -610,7 +610,7 @@ api_path_fmt2xpath(char *api_path_fmt,
* @see api_path2xml For api-path to xml tree * @see api_path2xml For api-path to xml tree
* @see api_path2xpath Using strings as parameters * @see api_path2xpath Using strings as parameters
*/ */
int static int
api_path2xpath_cvv(cvec *api_path, api_path2xpath_cvv(cvec *api_path,
int offset, int offset,
yang_stmt *yspec, yang_stmt *yspec,
@ -655,7 +655,7 @@ api_path2xpath_cvv(cvec *api_path,
/* top-node must have prefix */ /* top-node must have prefix */
if (i == offset && prefix == NULL){ if (i == offset && prefix == NULL){
cprintf(cberr, "'%s': Expected prefix:name", nodeid); cprintf(cberr, "'%s': Expected prefix:name", nodeid);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done; goto done;
goto fail; goto fail;
} }
@ -663,7 +663,7 @@ api_path2xpath_cvv(cvec *api_path,
if (prefix){ /* if prefix -> get module + change namespace */ if (prefix){ /* if prefix -> get module + change namespace */
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){ if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
cprintf(cberr, "No such yang module: %s", prefix); cprintf(cberr, "No such yang module: %s", prefix);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done; goto done;
goto fail; goto fail;
} }
@ -674,7 +674,7 @@ api_path2xpath_cvv(cvec *api_path,
else else
y = yang_find_datanode(y, name); y = yang_find_datanode(y, name);
if (y == NULL){ if (y == NULL){
if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) if (xerr && netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
goto done; goto done;
goto fail; goto fail;
} }
@ -784,6 +784,7 @@ api_path2xpath_cvv(cvec *api_path,
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[out] xpath xpath (use free() to deallocate) * @param[out] xpath xpath (use free() to deallocate)
* @param[out] nsc Namespace context of xpath (free w xml_nsctx_free) * @param[out] nsc Namespace context of xpath (free w xml_nsctx_free)
* @param[out] xerr Netconf error message
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf called * @retval 0 Invalid api_path or associated XML, netconf called
* @retval -1 Fatal error, clicon_err called * @retval -1 Fatal error, clicon_err called
@ -798,33 +799,32 @@ api_path2xpath_cvv(cvec *api_path,
* cvec_free(nsc); * cvec_free(nsc);
* @endcode * @endcode
* *
* @see api_path2xml_cvv which uses other parameter formats
*/ */
int int
api_path2xpath(char *api_path, api_path2xpath(char *api_path,
yang_stmt *yspec, yang_stmt *yspec,
char **xpathp, char **xpathp,
cvec **nsc) cvec **nsc,
cxobj **xerr)
{ {
int retval = -1; int retval = -1;
cvec *cvv = NULL; /* api-path vector */ cvec *cvv = NULL; /* api-path vector */
cbuf *xpath = NULL; /* xpath as cbuf (sub-function uses that) */ cbuf *xpath = NULL; /* xpath as cbuf (sub-function uses that) */
cxobj *xerr = NULL; /* ignored */ int ret;
if (api_path == NULL){
clicon_err(OE_XML, EINVAL, "api_path is NULL");
goto done;
}
/* Split api-path into cligen variable vector */ /* Split api-path into cligen variable vector */
if (str2cvec(api_path, '/', '=', &cvv) < 0) if (str2cvec(api_path, '/', '=', &cvv) < 0)
goto done; goto done;
if ((xpath = cbuf_new()) == NULL) if ((xpath = cbuf_new()) == NULL)
goto done; goto done;
if ((retval = api_path2xpath_cvv(cvv, 0, yspec, xpath, nsc, &xerr)) < 0){ if ((ret = api_path2xpath_cvv(cvv, 0, yspec, xpath, nsc, xerr)) < 0)
clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
} if (ret == 0)
if (retval == 0){
/* XXX: xerr ignored */
clicon_err(OE_XML, EINVAL, "xml does not adhere to yang");
goto fail; goto fail;
}
/* prepare output xpath parameter */ /* prepare output xpath parameter */
if (xpathp) if (xpathp)
if ((*xpathp = strdup(cbuf_get(xpath))) == NULL){ if ((*xpathp = strdup(cbuf_get(xpath))) == NULL){
@ -833,8 +833,6 @@ api_path2xpath(char *api_path,
} }
retval = 1; retval = 1;
done: done:
if (xerr)
xml_free(xerr);
if (cvv) if (cvv)
cvec_free(cvv); cvec_free(cvv);
if (xpath) if (xpath)
@ -851,8 +849,8 @@ api_path2xpath(char *api_path,
* @param[in] x0 Xpath tree so far * @param[in] x0 Xpath tree so far
* @param[in] y0 Yang spec for x0 * @param[in] y0 Yang spec for x0
* @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[out] xpathp Resulting xml tree * @param[out] xbotp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp * @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0) * @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf error * @retval 0 Invalid api_path or associated XML, netconf error
@ -869,8 +867,8 @@ api_path2xml_vec(char **vec,
yang_stmt *y0, yang_stmt *y0,
yang_class nodeclass, yang_class nodeclass,
int strict, int strict,
cxobj **xpathp, cxobj **xbotp,
yang_stmt **ypathp, yang_stmt **ybotp,
cxobj **xerr) cxobj **xerr)
{ {
int retval = -1; int retval = -1;
@ -895,10 +893,10 @@ api_path2xml_vec(char **vec,
cbuf *cberr = NULL; cbuf *cberr = NULL;
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){ if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
if (xpathp) if (xbotp)
*xpathp = x0; *xbotp = x0;
if (ypathp) if (ybotp)
*ypathp = y0; *ybotp = y0;
goto ok; goto ok;
} /* E.g "x=1,2" -> nodeid:x restval=1,2 */ } /* E.g "x=1,2" -> nodeid:x restval=1,2 */
if ((cberr = cbuf_new()) == NULL){ if ((cberr = cbuf_new()) == NULL){
@ -1034,7 +1032,7 @@ api_path2xml_vec(char **vec,
if ((retval = api_path2xml_vec(vec+1, nvec-1, if ((retval = api_path2xml_vec(vec+1, nvec-1,
x, y, x, y,
nodeclass, strict, nodeclass, strict,
xpathp, ypathp, xerr)) < 1) xbotp, ybotp, xerr)) < 1)
goto done; goto done;
ok: ok:
retval = 1; /* OK */ retval = 1; /* OK */
@ -1096,6 +1094,10 @@ api_path2xml(char *api_path,
cbuf *cberr = NULL; cbuf *cberr = NULL;
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
if (xtop == NULL){
clicon_err(OE_XML, EINVAL, "xtop is NULL");
goto done;
}
if ((cberr = cbuf_new()) == NULL){ if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;

View file

@ -125,14 +125,16 @@ fi
new "waiting" new "waiting"
wait_backend wait_backend
new "kill old restconf daemon" if [ $RC -ne 0 ]; then
sudo pkill -u $wwwuser -f clixon_restconf new "kill old restconf daemon"
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)" new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a start_restconf -f $cfg -- -a
new "waiting" new "waiting"
wait_restconf wait_restconf
fi
new "auth get" new "auth get"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}} ' expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}} '
@ -182,8 +184,10 @@ expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data
new "guest edit nacm" new "guest edit nacm"
expecteq "$(curl -u guest:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' expecteq "$(curl -u guest:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
new "Kill restconf daemon" if [ $RC -ne 0 ]; then
stop_restconf new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -eq 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE

View file

@ -145,14 +145,16 @@ fi
new "waiting" new "waiting"
wait_backend wait_backend
new "kill old restconf daemon" if [ $RC -ne 0 ]; then
sudo pkill -u $wwwuser -f clixon_restconf new "kill old restconf daemon"
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable http basic auth)" new "start restconf daemon (-a is enable http basic auth)"
start_restconf -f $cfg -- -a start_restconf -f $cfg -- -a
new "waiting" new "waiting"
wait_restconf wait_restconf
fi
new "auth get" new "auth get"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:state":{"op":["42","41","43"]}}} expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:state":{"op":["42","41","43"]}}}
@ -209,8 +211,10 @@ expectfn "$clixon_cli -1 -U wilma -l o -f $cfg rpc ipv4" 255 "access-denied defa
new "cli rpc as guest" new "cli rpc as guest"
expectfn "$clixon_cli -1 -U guest -l o -f $cfg rpc ipv4" 255 "access-denied access denied" expectfn "$clixon_cli -1 -U guest -l o -f $cfg rpc ipv4" 255 "access-denied access denied"
new "Kill restconf daemon" if [ $RC -ne 0 ]; then
stop_restconf new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -eq 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE

View file

@ -119,10 +119,11 @@ RULES=$(cat <<EOF
</nacm> </nacm>
<x xmlns="urn:example:nacm">42</x> <x xmlns="urn:example:nacm">42</x>
<translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate> <translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate></translate>
<translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate> <translate xmlns="urn:example:clixon"><translate><k>key43</k><value>val43</value></translate></translate>
EOF EOF
) )
# NOTE use of translate^ has nothing to do with the CLI translate semantics
new "test params: -f $cfg -- -s" new "test params: -f $cfg -- -s"
@ -164,14 +165,14 @@ expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+
#----READ access #----READ access
#user:admin #user:admin
new "admin read ok" new "admin read ok"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
' '
new "admin read netconf ok" new "admin read netconf ok"
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon" /></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon" /></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
new "admin read element ok" new "admin read element ok"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value":"val42"} expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
' '
new "admin read other module OK" new "admin read other module OK"
@ -193,14 +194,14 @@ fi
#user:limit #user:limit
new "limit read ok" new "limit read ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
' '
new "limit read netconf ok" new "limit read netconf ok"
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
new "limit read element ok" new "limit read element ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value":"val42"} expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
' '
new "limit read other module fail" new "limit read other module fail"
@ -211,7 +212,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-e
' '
new "limit read top ok (part)" new "limit read top ok (part)"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}],"clixon-example:state":{"op":["42","41","43"]}}} expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]},"clixon-example:state":{"op":["42","41","43"]}}}
' '
#user:guest #user:guest
@ -223,7 +224,7 @@ new "guest read netconf fail"
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$'
new "guest read element fail" new "guest read element fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
new "guest read other module fail" new "guest read other module fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
@ -262,7 +263,7 @@ new "admin set read-default permit"
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 "" expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 ""
new "limit read ok" new "limit read ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
' '
new "limit read other module ok" new "limit read other module ok"

View file

@ -232,7 +232,7 @@ new "update list permit"
expecteq "$(curl -u guest:bar -sS -H 'Content-Type: application/yang-data+xml' -X PUT http://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 '' expecteq "$(curl -u guest:bar -sS -H 'Content-Type: application/yang-data+xml' -X PUT http://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 ''
new "read list check" new "read list check"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:a)" 0 '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]} expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:a=key42)" 0 '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]}
' '
new "delete list deny" new "delete list deny"
@ -243,10 +243,10 @@ expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data/nacm-
#----- default deny (clixon-example limit and guest have default access) #----- default deny (clixon-example limit and guest have default access)
new "default create list deny" new "default create list deny"
expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate":[{"k":"key42","value":"val42"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
new "create list permit" new "create list permit"
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 '' expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 ''
new "default update list deny" new "default update list deny"
expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val99"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val99"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '

View file

@ -130,7 +130,10 @@ expecteq "$(curl -sS -X GET http://localhost/restconf/data/clixon-example:state)
' '
new "restconf get empty config + state json with wrong module name" new "restconf get empty config + state json with wrong module name"
expectpart "$(curl -siSG http://localhost/restconf/data/badmodule:state)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}' expectpart "$(curl -siSG http://localhost/restconf/data/badmodule:state)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"badmodule"},"error-severity":"error","error-message":"No such yang module prefix"}}}'
#'HTTP/1.1 404 Not Found'
#'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}'
new "restconf get empty config + state xml" new "restconf get empty config + state xml"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/clixon-example:state) ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/clixon-example:state)

View file

@ -141,14 +141,20 @@ new "restconf POST initial tree"
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data)" 0 '' expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data)" 0 ''
new "restconf GET initial datastore" new "restconf GET initial datastore"
expecteq "$(curl -s -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/example:a)" 0 "$XML expecteq "$(curl -s -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/example:a=0)" 0 "$XML
" "
new "restconf GET non-qualified list"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a)" 0 'HTTP/1.1 400 Bad Request' "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}"
new "restconf GET non-qualified list subelements"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/k)" 0 'HTTP/1.1 400 Bad Request' "^{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}"
new "restconf GET non-existent container body" new "restconf GET non-existent container body"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/c)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a=0/c)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "restconf GET invalid (no yang) container body" new "restconf GET invalid (no yang) container body"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a=0/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}'
new "restconf GET invalid (no yang) element" new "restconf GET invalid (no yang) element"
expectpart "$(curl -si -X GET http://localhost/restconf/data/example:xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' expectpart "$(curl -si -X GET http://localhost/restconf/data/example:xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}'

View file

@ -99,6 +99,9 @@ new "restconf GET whole list entry"
expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/)" 0 '{"list:c":{"a":[{"b":"x","c":"y","nonkey":"0"}]}} expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/)" 0 '{"list:c":{"a":[{"b":"x","c":"y","nonkey":"0"}]}}
' '
new "restconf GET list entry itself (should fail)"
expectpart "$(curl -si -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected '
new "restconf GET list entry" new "restconf GET list entry"
expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y)" 0 '{"list:a":[{"b":"x","c":"y","nonkey":"0"}]} expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y)" 0 '{"list:a":[{"b":"x","c":"y","nonkey":"0"}]}
' '
@ -106,9 +109,7 @@ expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localh
new "restconf PUT add whole list entry XML" new "restconf PUT add whole list entry XML"
expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xx</b><c>xy</c><nonkey>0</nonkey></a>' http://localhost/restconf/data/list:c/a=xx,xy)" 0 '' expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xx</b><c>xy</c><nonkey>0</nonkey></a>' http://localhost/restconf/data/list:c/a=xx,xy)" 0 ''
new "restconf GET list entry two XXX shouldnt be allowed" expectpart "$(curl -si -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a/b)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected '
expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a/b)" 0 '{"list:b":["x","xx"]}
'
new "restconf PUT change whole list entry (same keys)" new "restconf PUT change whole list entry (same keys)"
expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 '' expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 ''

View file

@ -309,7 +309,8 @@ module clixon-config {
description description
"If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2: "If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2:
The 'key' statement, which MUST be present if the list represents configuration. The 'key' statement, which MUST be present if the list represents configuration.
Some yang specs seem not to fulfil this."; Some yang specs seem not to fulfil this. However, if you reset this, there may
be follow-up errors due to code that assumes a configuration list has keys";
} }
leaf CLICON_BACKEND_DIR { leaf CLICON_BACKEND_DIR {
type string; type string;