* Changed so that 400 Bad Request are for invalid api-path or unknown yang elements, 404 Not Found for valid xml when object not found.
This commit is contained in:
parent
6e41592aec
commit
8cdb0bb062
13 changed files with 189 additions and 107 deletions
|
|
@ -29,6 +29,7 @@
|
|||
See function reference how to make a call.
|
||||
* RESTCONF error reporting
|
||||
* Invalid api-path syntax (eg non-matching yang) error changed from 412 operation-failed to 400 Bad request invalid-value, or unknown-element.
|
||||
* Changed so that `400 Bad Request` are for invalid api-path or unknown yang elements, `404 Not Found` for valid xml when object not found.
|
||||
* Typical installation should now add a `clicon` user (as well as group)
|
||||
* New clixon-config@2019-09-11.yang revision
|
||||
* Added: CLICON_BACKEND_USER: drop of privileges to user,
|
||||
|
|
|
|||
|
|
@ -245,6 +245,8 @@ cli_dbxml(clicon_handle h,
|
|||
yang_stmt *y = NULL; /* yang spec of xpath */
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xa; /* attribute */
|
||||
cxobj *xerr = NULL;
|
||||
int ret;
|
||||
|
||||
if (cvec_len(argv) != 1){
|
||||
clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string");
|
||||
|
|
@ -262,8 +264,14 @@ cli_dbxml(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y) < 1)
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_rpc_generate_error("Modify datastore", xerr);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
|
|
@ -289,6 +297,8 @@ cli_dbxml(clicon_handle h,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (api_path)
|
||||
|
|
@ -683,7 +693,7 @@ compare_dbs(clicon_handle h,
|
|||
{
|
||||
cxobj *xc1 = NULL; /* running xml */
|
||||
cxobj *xc2 = NULL; /* candidate xml */
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
int retval = -1;
|
||||
int astext;
|
||||
|
||||
|
|
@ -715,7 +725,6 @@ compare_dbs(clicon_handle h,
|
|||
xml_free(xc1);
|
||||
if (xc2)
|
||||
xml_free(xc2);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ expand_dbvar(void *h,
|
|||
cxobj *xt = NULL;
|
||||
char *xpath = NULL;
|
||||
cxobj **xvec = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xe; /* direct ptr */
|
||||
cxobj *xerr = NULL; /* free */
|
||||
size_t xlen = 0;
|
||||
cxobj *x;
|
||||
char *bodystr;
|
||||
|
|
@ -118,6 +119,7 @@ expand_dbvar(void *h,
|
|||
char *xpathcur;
|
||||
char *reason = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int ret;
|
||||
|
||||
if (argv == NULL || cvec_len(argv) != 2){
|
||||
clicon_err(OE_PLUGIN, 0, "requires arguments: <db> <xmlkeyfmt>");
|
||||
|
|
@ -155,8 +157,8 @@ expand_dbvar(void *h,
|
|||
/* Get configuration */
|
||||
if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
if ((xe = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xe);
|
||||
goto ok;
|
||||
}
|
||||
xcur = xt; /* default top-of-tree */
|
||||
|
|
@ -169,8 +171,14 @@ expand_dbvar(void *h,
|
|||
* xpath2xml would have worked!!
|
||||
* XXX: but y is just the first in this list, there could be other y:s?
|
||||
*/
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y) < 1)
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_rpc_generate_error("Expand datastore symbol", xerr);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (y==NULL)
|
||||
goto ok;
|
||||
|
||||
|
|
@ -242,6 +250,8 @@ expand_dbvar(void *h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (reason)
|
||||
|
|
|
|||
|
|
@ -210,3 +210,8 @@ but you need to ensure /www-data/fastcgi_restconf.sock has the following access:
|
|||
```
|
||||
rwxr-xr-x 1 www-data www-data 0 sep 22 11:46 /www-data/fastcgi_restconf.sock
|
||||
```
|
||||
|
||||
You can set debug level of the backend via restconf:
|
||||
```
|
||||
url -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-lib:input":{"level":1}}' http://localhost/restconf/operations/clixon-lib:debug
|
||||
```
|
||||
|
|
@ -338,14 +338,9 @@ api_data_write(clicon_handle h,
|
|||
/* Translate api_path to xml in the form of xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){ /* If URI, otherwise top data/config object */
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ybot)
|
||||
ymodapi = ys_module(ybot);
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
@ -354,6 +349,8 @@ api_data_write(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (ybot)
|
||||
ymodapi = ys_module(ybot);
|
||||
}
|
||||
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
||||
* expected data resource. (tested again below)
|
||||
|
|
@ -841,12 +838,9 @@ api_data_delete(clicon_handle h,
|
|||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -172,7 +172,6 @@ api_data_get2(clicon_handle h,
|
|||
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ api_data_post(clicon_handle h,
|
|||
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
|
||||
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ydata;
|
||||
cxobj *xa;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xretcom = NULL; /* return from commit */
|
||||
|
|
@ -144,14 +145,9 @@ api_data_post(clicon_handle h,
|
|||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ybot)
|
||||
ymodapi = ys_module(ybot);
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
@ -160,6 +156,8 @@ api_data_post(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (ybot)
|
||||
ymodapi = ys_module(ybot);
|
||||
}
|
||||
#if 1
|
||||
if (debug){
|
||||
|
|
@ -250,30 +248,9 @@ api_data_post(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
xdata = xml_child_i(xdata0,0);
|
||||
|
||||
/* If the api-path (above) defines a module, then xdata must have a prefix
|
||||
* and it match the module defined in api-path.
|
||||
* In a POST, maybe there are cornercases where xdata (which is a child) and
|
||||
* xbot (which is the parent) may have non-matching namespaces?
|
||||
* This does not apply if api-path is / (no module)
|
||||
*/
|
||||
xdata = xml_child_i(xdata0, 0);
|
||||
if (ys_module_by_xml(yspec, xdata, &ymoddata) < 0)
|
||||
goto done;
|
||||
if (ymoddata && ymodapi){
|
||||
if (ymoddata != ymodapi){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add operation (create/replace) as attribute */
|
||||
if ((xa = xml_new("operation", xdata, NULL)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -289,6 +266,34 @@ api_data_post(clicon_handle h,
|
|||
nullspec = (xml_spec(xdata) == NULL);
|
||||
if (xml_apply0(xdata, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
/* ybot is parent of spec(parent(data))) */
|
||||
if (ymoddata && (ydata = xml_spec(xdata)) != NULL){
|
||||
if (ys_real_module(ydata) != ymoddata){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
||||
}
|
||||
/* If URI points out an object, then data's parent should be that object
|
||||
*/
|
||||
if (ybot && yang_parent_get(ydata) != ybot){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
if (media_in == YANG_DATA_JSON && nullspec){
|
||||
/* json2xml decode may not have been done above in json_parse,
|
||||
need to be done here instead
|
||||
|
|
@ -821,12 +826,9 @@ api_operations_post(clicon_handle h,
|
|||
goto done;
|
||||
/* Here xtop is: <rpc username="foo"/> */
|
||||
}
|
||||
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y)) < 0)
|
||||
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,8 @@ int xml_spec_populate(cxobj *x, void *arg);
|
|||
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);
|
||||
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||
yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp);
|
||||
yang_class nodeclass, int strict,
|
||||
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
|
||||
|
||||
int xml2xpath(cxobj *x, char **xpath);
|
||||
int xml2api_path_1(cxobj *x, cbuf *cb);
|
||||
|
|
|
|||
|
|
@ -2467,6 +2467,10 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
/* Initialize namespace context */
|
||||
if ((nsc = xml_nsctx_init(NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
for (i=offset; i<cvec_len(api_path); i++){
|
||||
cv = cvec_i(api_path, i);
|
||||
nodeid = cv_name_get(cv);
|
||||
|
|
@ -2477,10 +2481,6 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
__FUNCTION__, i, prefix?prefix:"", name);
|
||||
/* top-node must have prefix */
|
||||
if (i == offset && prefix == NULL){
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "'%s': Expected prefix:name", nodeid);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
|
|
@ -2489,10 +2489,6 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
ymod = NULL;
|
||||
if (prefix){ /* if prefix -> get module + change namespace */
|
||||
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "No such yang module: %s", prefix);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
|
|
@ -2514,7 +2510,6 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
* note different from api-path prefix
|
||||
*/
|
||||
if (xml_nsctx_get_prefix(nsc, namespace, &xprefix) == 0){
|
||||
|
||||
xprefix = yang_find_myprefix(y);
|
||||
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
|
||||
/* not found, add it to nsc */
|
||||
|
|
@ -2595,7 +2590,7 @@ api_path2xpath_cvv(cvec *api_path,
|
|||
}
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cberr != NULL)
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (valvec)
|
||||
free(valvec);
|
||||
|
|
@ -2685,11 +2680,12 @@ api_path2xpath(char *api_path,
|
|||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||
* @param[out] xpathp Resulting xml tree
|
||||
* @param[out] ypathp Yang spec matching xpathp
|
||||
* @param[out] xerr Netconf error message (if retval=0)
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||
* @retval 0 Invalid api_path or associated XML, netconf error
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
*
|
||||
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||
* @note both retval -1 set clicon_err, retval 0 set xerr netconf xml
|
||||
* @see api_path2xpath For api-path to xml xpath translation
|
||||
* @see api_path2xml
|
||||
*/
|
||||
|
|
@ -2701,7 +2697,8 @@ api_path2xml_vec(char **vec,
|
|||
yang_class nodeclass,
|
||||
int strict,
|
||||
cxobj **xpathp,
|
||||
yang_stmt **ypathp)
|
||||
yang_stmt **ypathp,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
char *nodeid;
|
||||
|
|
@ -2722,6 +2719,7 @@ api_path2xml_vec(char **vec,
|
|||
yang_stmt *ymod;
|
||||
yang_stmt *ykey;
|
||||
char *namespace = NULL;
|
||||
cbuf *cberr = NULL;
|
||||
|
||||
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
||||
if (xpathp)
|
||||
|
|
@ -2730,6 +2728,11 @@ api_path2xml_vec(char **vec,
|
|||
*ypathp = y0;
|
||||
goto ok;
|
||||
} /* E.g "x=1,2" -> nodeid:x restval=1,2 */
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* restval is RFC 3896 encoded */
|
||||
if ((restval_enc = index(nodeid, '=')) != NULL){
|
||||
*restval_enc = '\0';
|
||||
|
|
@ -2742,11 +2745,15 @@ api_path2xml_vec(char **vec,
|
|||
goto done;
|
||||
if (y0->ys_keyword == Y_SPEC){ /* top-node */
|
||||
if (prefix == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "api-path element '%s', expected prefix:name", nodeid);
|
||||
cprintf(cberr, "api-path element '%s', expected prefix:name", nodeid);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix);
|
||||
cprintf(cberr, "No such yang module prefix");
|
||||
if (netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
namespace = yang_find_mynamespace(ymod);
|
||||
|
|
@ -2756,12 +2763,15 @@ api_path2xml_vec(char **vec,
|
|||
yang_find_schemanode(y0, name):
|
||||
yang_find_datanode(y0, name);
|
||||
if (y == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "api-path name: '%s', no such yang element", name);
|
||||
if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (prefix && namespace == NULL){
|
||||
if ((ymod = yang_find_module_by_name(ys_spec(y0), prefix)) == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix);
|
||||
cprintf(cberr, "api-path element prefix: '%s', no such yang module", prefix);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
namespace = yang_find_mynamespace(ymod);
|
||||
|
|
@ -2789,7 +2799,9 @@ api_path2xml_vec(char **vec,
|
|||
}
|
||||
if (restval==NULL){
|
||||
if (strict){
|
||||
clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
|
||||
cprintf(cberr, "malformed key =%s, expected '=restval'", nodeid);
|
||||
if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
|
@ -2800,7 +2812,9 @@ api_path2xml_vec(char **vec,
|
|||
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
||||
goto done;
|
||||
if ((nvalvec != cvec_len(cvk)) && strict){
|
||||
clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
|
||||
cprintf(cberr, "List key %s length mismatch", name);
|
||||
if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
|
@ -2814,9 +2828,11 @@ api_path2xml_vec(char **vec,
|
|||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((ykey = yang_find(y, Y_LEAF, keyname)) == NULL){
|
||||
clicon_err(OE_XML, 0, "List statement \"%s\" has no key leaf \"%s\"",
|
||||
cprintf(cberr, "List statement \"%s\" has no key leaf \"%s\"",
|
||||
yang_argument_get(y), keyname);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((xn = xml_new(keyname, x, ykey)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -2845,11 +2861,14 @@ api_path2xml_vec(char **vec,
|
|||
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||
x, y,
|
||||
nodeclass, strict,
|
||||
xpathp, ypathp)) < 1)
|
||||
xpathp, ypathp, xerr)) < 1)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 1; /* OK */
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (name)
|
||||
|
|
@ -2871,10 +2890,11 @@ api_path2xml_vec(char **vec,
|
|||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||
* @param[out] xbotp Resulting xml tree (end of xpath)
|
||||
* @param[out] ybotp Yang spec matching xbotp
|
||||
* @param[out] xerr Netconf error message (if retval=0)
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||
* @retval 0 Invalid api_path or associated XML, netconf error
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||
* @note both retval -1 set clicon_err, retval 0 set xerr netconf xml
|
||||
* @example
|
||||
* api_path: /subif-entry=foo/subid
|
||||
* xtop[in] <config/>
|
||||
|
|
@ -2893,17 +2913,24 @@ api_path2xml(char *api_path,
|
|||
yang_class nodeclass,
|
||||
int strict,
|
||||
cxobj **xbotp,
|
||||
yang_stmt **ybotp)
|
||||
yang_stmt **ybotp,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
cxobj *xroot;
|
||||
cbuf *cberr = NULL;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (*api_path!='/'){
|
||||
clicon_err(OE_XML, EINVAL, "Invalid api-path: %s (must start with '/')",
|
||||
api_path);
|
||||
cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL)
|
||||
|
|
@ -2912,13 +2939,15 @@ api_path2xml(char *api_path,
|
|||
if (nvec > 1 && !strlen(vec[nvec-1]))
|
||||
nvec--;
|
||||
if (nvec < 1){
|
||||
clicon_err(OE_XML, EINVAL, "Malformed api-path: %s", api_path);
|
||||
cprintf(cberr, "Malformed api-path: %s: too short)", api_path);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
nvec--; /* NULL-terminated */
|
||||
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||
xtop, yspec, nodeclass, strict,
|
||||
xbotp, ybotp)) < 1)
|
||||
xbotp, ybotp, xerr)) < 1)
|
||||
goto done;
|
||||
xml_yang_root(*xbotp, &xroot);
|
||||
if (xmlns_assign(xroot) < 0)
|
||||
|
|
@ -2926,6 +2955,8 @@ api_path2xml(char *api_path,
|
|||
// ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (vec)
|
||||
free(vec);
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ module ietf-interfaces {
|
|||
EOF
|
||||
|
||||
# From rfc7950 sec 7.17
|
||||
# Note "when" is not present
|
||||
# This is the main module where the augment exists
|
||||
cat <<EOF > $fyang
|
||||
module example-augment {
|
||||
|
|
@ -117,7 +116,12 @@ module example-augment {
|
|||
}
|
||||
}
|
||||
augment "/if:interfaces/if:interface" {
|
||||
/* when 'derived-from-or-self(if:type, "mymod:some-new-iftype")'; */
|
||||
when 'derived-from-or-self(if:type, "mymod:some-new-iftype")';
|
||||
container ospf { /* moved from test_restconf_err (two-level augment) */
|
||||
leaf reference-bandwidth {
|
||||
type uint32;
|
||||
}
|
||||
}
|
||||
leaf mandatory-leaf {
|
||||
mandatory true;
|
||||
type string;
|
||||
|
|
@ -214,6 +218,38 @@ expectpart "$(curl -s -i -X GET http://localhost/restconf/data/ietf-interfaces:i
|
|||
new "restconf get augment xml"
|
||||
expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>'
|
||||
|
||||
|
||||
#<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
|
||||
XML=$(cat <<EOF
|
||||
<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
EOF
|
||||
)
|
||||
|
||||
# Test for multi-module path where an augment stretches across modules
|
||||
new "restconf PUT augment multi-namespace path e1 (whole path)"
|
||||
expectpart "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1 -d "$XML")" 0 ''
|
||||
|
||||
XML=$(cat <<EOF
|
||||
<ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf>
|
||||
EOF
|
||||
)
|
||||
|
||||
new "restconf POST augment multi-namespace path e2 (middle path)"
|
||||
expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 ''
|
||||
|
||||
new "restconf GET augment multi-namespace top"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
|
||||
new "restconf GET augment multi-namespace level 1"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080}\]}'
|
||||
|
||||
new "restconf GET augment multi-namespace cross"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}'
|
||||
|
||||
new "restconf GET augment multi-namespace cross level 2"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example-augment:reference-bandwidth":23}'
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ expectpart "$(curl -s -i -X DELETE http://localhost/restconf/data/example:crypt
|
|||
|
||||
# 2. set identity in other module with restconf , read it with restconf and netconf
|
||||
new "restconf add POST instead of PUT (should fail)"
|
||||
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}'
|
||||
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Data is not prefixed with matching namespace"}}}'
|
||||
|
||||
new "restconf add other (des) identity using POST"
|
||||
expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 201 Created' 'Location: http://localhost/restconf/data/example:crypto'
|
||||
|
|
|
|||
|
|
@ -119,6 +119,9 @@ expectpart "$(curl -si -X POST -H "Accept: application/yang-data+json" -d {\"cli
|
|||
new "restconf empty rpc with extra args (should fail)"
|
||||
expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error"}}}
'
|
||||
|
||||
new "restconf debug rpc"
|
||||
expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-lib:input\":{\"level\":0}} http://localhost/restconf/operations/clixon-lib:debug)" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
new "restconf get empty config + state json"
|
||||
expecteq "$(curl -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["42","41","43"]}}
|
||||
'
|
||||
|
|
@ -264,7 +267,7 @@ if [ -z "$match" ]; then
|
|||
fi
|
||||
|
||||
new "restconf Add subtree without key (expected error)"
|
||||
expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key, expected '"'"'=restval'"'"'"}}}
'
|
||||
expectpart "$(curl -is -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface)" 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 =interface, expected'
|
||||
|
||||
new "restconf Add subtree with too many keys (expected error)"
|
||||
expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}}
'
|
||||
|
|
|
|||
|
|
@ -103,13 +103,6 @@ module example{
|
|||
}
|
||||
}
|
||||
}
|
||||
augment "/aug:route-config/aug:dynamic" {
|
||||
container ospf {
|
||||
leaf reference-bandwidth {
|
||||
type uint32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
|
@ -158,11 +151,9 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/xxx)" 0 '
|
|||
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"}}}'
|
||||
|
||||
if false; then
|
||||
new "restconf POST non-existent (no yang) element"
|
||||
# should be invalid element
|
||||
expectpart "$(curl -is -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data/example:a=23/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Unknown element: '
|
||||
fi
|
||||
expectpart "$(curl -is -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data/example:a=23/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"}}}'
|
||||
|
||||
# Test for multi-module path where an augment stretches across modules
|
||||
new "restconf POST augment multi-namespace path"
|
||||
|
|
@ -181,8 +172,8 @@ new "restconf GET augment multi-namespace cross level 2"
|
|||
expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/example:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example:reference-bandwidth":23}'
|
||||
|
||||
# XXX actually no such element
|
||||
#new "restconf GET augment multi-namespace, no 2nd module in api-path, fail"
|
||||
#expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"rpc-error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}}'
|
||||
new "restconf GET augment multi-namespace, no 2nd module in api-path, fail"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"rpc-error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}}'
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue