Invalid api-path syntax error changed from 412 operation-failed to 404 invalid-value.

This commit is contained in:
Olof hagsand 2019-09-21 11:26:03 +02:00
parent 8c18f2a86d
commit 22307e0a9e
7 changed files with 66 additions and 17 deletions

View file

@ -10,6 +10,17 @@
* If dropped temporary, you can restore privileges with `restore_priv()` * If dropped temporary, you can restore privileges with `restore_priv()`
### API changes on existing features (you may need to change your code) ### API changes on existing features (you may need to change your code)
* RESTCONF error reporting
* Invalid api-path syntax error changed from 412 operation-failed to 404 invalid-value. For example, change from
```
HTTP/1.1 412 Precondition Failed
{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"No such yang module: badmodule"}}}
```
to:
```
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"}}}
```
* Typical installation should now add a `clicon` user (as well as group) * Typical installation should now add a `clicon` user (as well as group)
* New clixon-config@2019-09-11.yang revision * New clixon-config@2019-09-11.yang revision
* Added: CLICON_BACKEND_USER: drop of privileges to user, * Added: CLICON_BACKEND_USER: drop of privileges to user,

View file

@ -67,8 +67,8 @@
*/ */
static const map_str2int netconf_restconf_map[] = { static const map_str2int netconf_restconf_map[] = {
{"in-use", 409}, {"in-use", 409},
{"invalid-value", 400},
{"invalid-value", 404}, {"invalid-value", 404},
{"invalid-value", 400},
{"invalid-value", 406}, {"invalid-value", 406},
{"too-big", 413}, /* request */ {"too-big", 413}, /* request */
{"too-big", 400}, /* response */ {"too-big", 400}, /* response */

View file

@ -280,12 +280,9 @@ api_data_write(clicon_handle h,
if ((cbpath = cbuf_new()) == NULL) if ((cbpath = cbuf_new()) == NULL)
goto done; goto done;
cprintf(cbpath, "/"); cprintf(cbpath, "/");
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace)) < 0) if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace, &xerr)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
clicon_err_reset();
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ if ((xe = xpath_first(xerr, "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;

View file

@ -166,8 +166,18 @@ api_data_get2(clicon_handle h,
goto done; goto done;
cprintf(cbpath, "/"); cprintf(cbpath, "/");
/* We know "data" is element pi-1 */ /* We know "data" is element pi-1 */
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace)) < 0) if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace, &xerr)) < 0)
goto done; 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;
}
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
if (ret == 0){ if (ret == 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;

View file

@ -70,7 +70,7 @@ int xml_sanity(cxobj *x, void *arg);
int xml_non_config_data(cxobj *xt, void *arg); int xml_non_config_data(cxobj *xt, void *arg);
int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_stmt *yspec); int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_stmt *yspec);
int xml_spec_populate(cxobj *x, void *arg); int xml_spec_populate(cxobj *x, void *arg);
int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, char **namespace); int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, char **namespace, cxobj **xerr);
int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, char **namespace); int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, char **namespace);
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, cxobj **xpathp, yang_stmt **ypathp); yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp);

View file

@ -2382,11 +2382,12 @@ xml_spec_populate(cxobj *x,
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in,out] xpath The xpath as cbuf (must be created and may have content) * @param[in,out] xpath The xpath as cbuf (must be created and may have content)
* @param[out] namespace Namespace of xpath (direct pointer don't free) * @param[out] namespace Namespace of xpath (direct pointer don't free)
* @param[out] xerr Netconf error message
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err called * @retval 0 Invalid api_path or associated XML, netconf error xml set
* @retval -1 Fatal error, clicon_err called * @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 sets netconf xml msg
* @note Not proper namespace translation from api-path 2 xpath * @note Not proper namespace translation from api-path 2 xpath
* It works like this: * It works like this:
* Assume origin incoming path is * Assume origin incoming path is
@ -2413,7 +2414,8 @@ api_path2xpath_cvv(cvec *api_path,
int offset, int offset,
yang_stmt *yspec, yang_stmt *yspec,
cbuf *xpath, cbuf *xpath,
char **namespace) char **namespace,
cxobj **xerr)
{ {
int retval = -1; int retval = -1;
int i; int i;
@ -2429,6 +2431,7 @@ api_path2xpath_cvv(cvec *api_path,
char **valvec = NULL; char **valvec = NULL;
int vi; int vi;
int nvalvec; int nvalvec;
cbuf *cberr = NULL;
for (i=offset; i<cvec_len(api_path); i++){ for (i=offset; i<cvec_len(api_path); i++){
cv = cvec_i(api_path, i); cv = cvec_i(api_path, i);
@ -2438,11 +2441,23 @@ api_path2xpath_cvv(cvec *api_path,
clicon_debug(1, "%s [%d] cvname: %s:%s", __FUNCTION__, i, prefix?prefix:"", name); clicon_debug(1, "%s [%d] cvname: %s:%s", __FUNCTION__, i, prefix?prefix:"", name);
if (i == offset){ /* top-node */ if (i == offset){ /* top-node */
if (prefix == NULL){ if (prefix == NULL){
clicon_err(OE_XML, EINVAL, "'%s': Expected prefix:name", nodeid); 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;
goto fail; goto fail;
} }
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){ if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
clicon_err(OE_YANG, ENOENT, "No such yang module: %s", prefix); 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;
goto fail; goto fail;
} }
y = yang_find_datanode(ymod, name); y = yang_find_datanode(ymod, name);
@ -2450,7 +2465,13 @@ 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){
clicon_err(OE_YANG, errno, "Unknown element: '%s'", name); if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Unknown element: '%s'", name);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail; goto fail;
} }
/* Check if has value, means '=' */ /* Check if has value, means '=' */
@ -2506,6 +2527,8 @@ api_path2xpath_cvv(cvec *api_path,
*namespace = yang_find_mynamespace(ymod); *namespace = yang_find_mynamespace(ymod);
retval = 1; /* OK */ retval = 1; /* OK */
done: done:
if (cberr != NULL)
cbuf_free(cberr);
if (valvec) if (valvec)
free(valvec); free(valvec);
if (prefix) if (prefix)
@ -2524,7 +2547,7 @@ api_path2xpath_cvv(cvec *api_path,
* @param[out] xpath xpath (use free() to deallocate) * @param[out] xpath xpath (use free() to deallocate)
* @param[out] namespace Namespace of xpath (direct pointer don't free) * @param[out] namespace Namespace of xpath (direct pointer don't free)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err 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
* @code * @code
* char *xpath = NULL; * char *xpath = NULL;
@ -2546,16 +2569,22 @@ api_path2xpath(char *api_path,
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 */
/* 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, namespace)) < 0) if ((retval = api_path2xpath_cvv(cvv, 0, yspec, xpath, namespace, &xerr)) < 0){
clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
if (retval == 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){
@ -2564,6 +2593,8 @@ 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)

View file

@ -124,7 +124,7 @@ 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"
expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"No such yang module: badmodule"}}} ' 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"}}}'
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)