Replacing remaining badrequest() with proper restconf error msg.

This commit is contained in:
Olof hagsand 2018-04-22 20:13:54 +02:00
parent 3ed1c98556
commit 1913407e52
14 changed files with 153 additions and 75 deletions

View file

@ -424,19 +424,28 @@ api_data_post(clicon_handle h,
/* Parse input data as json or xml into xml */
if (parse_xml){
if (xml_parse_string(data, NULL, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
}
else if (json_parse_str(data, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
/* 4.4.1: The message-body MUST contain exactly one instance of the
* expected data resource.
*/
if (xml_child_nr(xdata) != 1){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
x = xml_child_i(xdata,0);
@ -628,19 +637,28 @@ api_data_put(clicon_handle h,
/* Parse input data as json or xml into xml */
if (parse_xml){
if (xml_parse_string(data, NULL, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
}
else if (json_parse_str(data, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
/* The message-body MUST contain exactly one instance of the
* expected data resource.
*/
if (xml_child_nr(xdata) != 1){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
x = xml_child_i(xdata,0);
@ -662,13 +680,19 @@ api_data_put(clicon_handle h,
else {
/* Check same symbol in api-path as data */
if (strcmp(xml_name(x), xml_name(xbot))){
badrequest(r);
if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
/* If list or leaf-list, api-path keys must match data keys */
if (y && (y->yn_keyword == Y_LIST ||y->yn_keyword == Y_LEAF_LIST)){
if (match_list_keys((yang_stmt*)y, x, xbot) < 0){
badrequest(r);
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
}
@ -1055,12 +1079,18 @@ api_operations_post(clicon_handle h,
/* Parse input data as json or xml into xml */
if (parse_xml){
if (xml_parse_string(data, NULL, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
}
else if (json_parse_str(data, &xdata) < 0){
badrequest(r);
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL);

View file

@ -743,7 +743,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl ../datastore
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl ../datastore ../datastore/text
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

View file

@ -743,7 +743,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/backend/ ../apps/restconf/ ../apps/netconf ../apps/dbctrl ../datastore
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/backend/ ../apps/restconf/ ../apps/netconf ../apps/dbctrl ../datastore ../datastore/text
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

View file

@ -93,27 +93,22 @@ Routing notification
The example includes a restonf, netconf, CLI and two backend plugins.
Each plugin is initiated with an API struct followed by a plugin init function.
The content of the API struct is different depending on what kind of plugin it is. Some fields are
meaningful only for some plugins.
The plugin init function may also include registering RPC functions.
The content of the API struct is different depending on what kind of plugin it is.
The plugin init function may also include registering RPC functions, see below is for a backend.
```
static clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init,
plugin_start,
plugin_exit,
NULL, /* cli prompt N/A for backend */
NULL, /* cli suspend N/A for backend */
NULL, /* cli interrupt N/A for backend */
NULL, /* auth N/A for backend */
plugin_reset,
plugin_statedata,
transaction_begin,
transaction_validate,
transaction_complete,
transaction_commit,
transaction_end,
transaction_abort
.ca_reset=plugin_reset,/* reset */
.ca_statedata=plugin_statedata, /* statedata */
.ca_trans_begin=NULL, /* trans begin */
.ca_trans_validate=transaction_validate,/* trans validate */
.ca_trans_complete=NULL, /* trans complete */
.ca_trans_commit=transaction_commit, /* trans commit */
.ca_trans_end=NULL, /* trans end */
.ca_trans_abort=NULL /* trans abort */
};
clixon_plugin_api *

View file

@ -260,13 +260,13 @@ plugin_start(clicon_handle h,
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
"example", /* name */ /*--- Common fields. ---*/
clixon_plugin_init, /* init */
plugin_start, /* start */
NULL, /* exit */
.ca_reset=plugin_reset,/* reset */ /*--- Backend plugin only ---*/
.ca_statedata=plugin_statedata, /* statedata */
.ca_trans_begin=NULL, /* trans begin */
"example", /* name */
clixon_plugin_init, /* init */
plugin_start, /* start */
NULL, /* exit */
.ca_reset=plugin_reset, /* reset */
.ca_statedata=plugin_statedata, /* statedata */
.ca_trans_begin=NULL, /* trans begin */
.ca_trans_validate=transaction_validate,/* trans validate */
.ca_trans_complete=NULL, /* trans complete */
.ca_trans_commit=transaction_commit, /* trans commit */

View file

@ -61,5 +61,6 @@ int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
int netconf_operation_failed(cbuf *cb, char *type, char *message);
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
int netconf_malformed_message(cbuf *cb, char *message);
int netconf_malformed_message_xml(cxobj **xret, char *message);
#endif /* _CLIXON_NETCONF_LIB_H */

View file

@ -49,10 +49,16 @@
*/
/*! Controls how keywords a generated in CLI syntax / prints from object model
* Example syntax a.b[] $!x $y:
* NONE: a b <x> <y>;
* VARS: a b <x> y <y>;
* ALL: a b x <x> y <y>;
* Example YANG:
* list a {a.b[] $!x $y:
* list a {
* key x;
* leaf x;
* leaf y;
* }
* NONE: a <x> <y>;
* VARS: a <x> y <y>;
* ALL: a x <x> y <y>;
*/
enum genmodel_type{
GT_ERR =-1, /* Error */

View file

@ -31,6 +31,9 @@
***** END LICENSE BLOCK *****
* Yang functions
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/
#ifndef _CLIXON_YANG_H_

View file

@ -526,22 +526,25 @@ netconf_access_denied_xml(cxobj **xret,
char *message)
{
int retval =-1;
cbuf *cbret = NULL;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
cxobj *xerr;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
if (netconf_access_denied(cbret, type, message) < 0)
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_rootchild(*xret, 0, xret) < 0)
if (xml_parse_va(&xerr, NULL, "<error-tag>access-denied</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}
@ -835,22 +838,25 @@ netconf_operation_failed_xml(cxobj **xret,
char *message)
{
int retval =-1;
cbuf *cbret = NULL;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
cxobj *xerr;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
if (netconf_operation_failed(cbret, type, message) < 0)
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_rootchild(*xret, 0, xret) < 0)
if (xml_parse_va(&xerr, NULL, "<error-tag>operation-failed</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}
@ -892,3 +898,39 @@ netconf_malformed_message(cbuf *cb,
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
*
* A message could not be handled because it failed to be parsed correctly.
* For example, the message is not well-formed XML or it uses an
* invalid character set.
* @param[out] xret Error XML tree
* @param[in] message Error message
* @note New in :base:1.1
*/
int
netconf_malformed_message_xml(cxobj **xret,
char *message)
{
int retval =-1;
cxobj *xerr;
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
goto done;
}
else if (xml_name_set(*xret, "rpc-reply") < 0)
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
if (xml_parse_va(&xerr, NULL, "<error-tag>malformed-message</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>") < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
message) < 0)
goto done;
retval = 0;
done:
return retval;
}

View file

@ -34,8 +34,9 @@
***** END LICENSE BLOCK *****
* Database specification parser cli syntax
* (Cloned from cligen parser)
* Yang parser. Hopefully useful but not complete
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/
#ifndef _CLIXON_YANG_PARSE_H_
#define _CLIXON_YANG_PARSE_H_

View file

@ -1,8 +1,4 @@
/*
* Yang 1.0 parser according to RFC6020.
* It is hopefully useful but not complete
* RFC7950 defines Yang version 1.1
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
@ -34,8 +30,9 @@
***** END LICENSE BLOCK *****
* Database specification parser cli syntax
* (Cloned from cligen parser)
* Yang parser. Hopefully useful but not complete
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/
%{

View file

@ -1,7 +1,4 @@
/*
* Yang 1.0 parser according to RFC6020.
* It is hopefully useful but not complete
* RFC7950 defines Yang version 1.1
*
***** BEGIN LICENSE BLOCK *****
@ -34,6 +31,9 @@
***** END LICENSE BLOCK *****
* Yang parser. Hopefully useful but not complete
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/

View file

@ -137,7 +137,7 @@ expectfn "curl -s -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json"
new2 "restconf empty rpc"
expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" ''
expecteq "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" ""
new2 "restconf get empty config + state json"
expecteq "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}
@ -213,11 +213,11 @@ expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces":
new2 "restconf Re-post eth/0/0 which should generate error"
expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} '
new2 "Add leaf description using POST"
new "Add leaf description using POST"
expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
new "Add nothing using POST"
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "data is in some way badly formed"
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:'
new2 "restconf Check description added"
expecteq "$(curl -s -G http://localhost/restconf/data)" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}
@ -232,7 +232,7 @@ expectfn 'curl -s -G http://localhost/restconf/data' $state
new2 "restconf Re-Delete eth/0/0 using none should generate error"
expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-missing","error-type": "application","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} '
new2 "restconf Add subtree eth/0/0 using PUT"
new "restconf Add subtree eth/0/0 using PUT"
expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
new2 "restconf get subtree"
@ -243,9 +243,12 @@ new2 "restconf rpc using POST json"
expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}}
'
# Cant get this to work due to quoting
#new2 "restconf rpc using POST wrong JSON"
#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} '
new2 "restconf rpc using POST json w/o mandatory element"
expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} '
new2 "restconf rpc non-existing rpc w/o namespace"
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} '

View file

@ -134,7 +134,7 @@ new "restconf PUT add interface"
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' ""
new "restconf PUT change key error"
expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' "Bad request"
expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "api-path keys do not match data keys"}}}}'
new "Kill restconf daemon"
sudo pkill -u www-data clixon_restconf