netconf error handling and test summary script
This commit is contained in:
parent
f872c7e295
commit
861300d6c0
23 changed files with 331 additions and 189 deletions
|
|
@ -20,7 +20,8 @@
|
|||
* Openconfig yang specs parsed: https://github.com/openconfig/public
|
||||
* Improved "unknown" handling
|
||||
* More precise Yang validation and better error messages
|
||||
* For Example, adding bad-, missing-, or unknown-element error messages, etc instead of operation-failed
|
||||
* Example: adding bad-, missing-, or unknown-element error messages, etc instead of operation-failed, bad-element instead of "yang node not found", etc.
|
||||
*
|
||||
* Yang load file configure options changed
|
||||
* `CLICON_YANG_DIR` is changed from a single directory to a path of directories
|
||||
* Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
* (Duplicated. Also in netconf_*.h)
|
||||
*/
|
||||
int netconf_output(int s, cbuf *xf, char *msg);
|
||||
int netconf_output_encap(int s, cbuf *xf, char *msg);
|
||||
|
||||
int netconf_xpath(cxobj *xsearch,
|
||||
cxobj *xfilter,
|
||||
|
|
|
|||
|
|
@ -182,9 +182,9 @@ netconf_get_target(cxobj *xn,
|
|||
* @param[in] s
|
||||
* @param[in] cb Cligen buffer that contains the XML message
|
||||
* @param[in] msg Only for debug
|
||||
* @note Assumes "cb" contains valid XML, ie encoding is correct. This is done
|
||||
* if it is output by a xml render routine (xml_print et al), but NOT
|
||||
* otherwise.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see netconf_output_encap for function with encapsulation
|
||||
*/
|
||||
int
|
||||
netconf_output(int s,
|
||||
|
|
@ -216,3 +216,34 @@ netconf_output(int s,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Encapsulate and send outgoing netconf packet as cbuf on socket
|
||||
* @param[in] s
|
||||
* @param[in] cb Cligen buffer that contains the XML message
|
||||
* @param[in] msg Only for debug
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note Assumes "cb" contains valid XML
|
||||
* @see netconf_output without encapsulation
|
||||
*/
|
||||
int
|
||||
netconf_output_encap(int s,
|
||||
cbuf *cb,
|
||||
char *msg)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb1 = NULL;
|
||||
|
||||
if ((cb1 = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
add_preamble(cb1);
|
||||
cprintf(cb1, "%s", cbuf_get(cb));
|
||||
add_postamble(cb1);
|
||||
retval = netconf_output(s, cb1, msg);
|
||||
done:
|
||||
if (cb1)
|
||||
cbuf_free(cb1);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,5 +75,6 @@ int add_error_preamble(cbuf *xf, char *reason);
|
|||
char *netconf_get_target(cxobj *xn, char *path);
|
||||
int add_error_postamble(cbuf *xf);
|
||||
int netconf_output(int s, cbuf *xf, char *msg);
|
||||
int netconf_output_encap(int s, cbuf *cb, char *msg);
|
||||
|
||||
#endif /* _NETCONF_LIB_H_ */
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ process_incoming_packet(clicon_handle h,
|
|||
free(str0);
|
||||
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
|
||||
goto done;
|
||||
netconf_output(1, cbret, "rpc-error");
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto done;
|
||||
}
|
||||
free(str0);
|
||||
|
|
@ -121,7 +121,7 @@ process_incoming_packet(clicon_handle h,
|
|||
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output(1, cbret, "rpc-error");
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
|
|
@ -157,11 +157,8 @@ process_incoming_packet(clicon_handle h,
|
|||
if (xml_addsub(xc, xa2) < 0)
|
||||
goto done;
|
||||
}
|
||||
add_preamble(cbret);
|
||||
|
||||
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
|
||||
add_postamble(cbret);
|
||||
if (netconf_output(1, cbret, "rpc-reply") < 0){
|
||||
if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
|
||||
cbuf_free(cbret);
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -776,12 +776,10 @@ netconf_notification_cb(int s,
|
|||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
add_preamble(cb); /* Make it well-formed netconf xml */
|
||||
if (clicon_xml2cbuf(cb, xn, 0, 0) < 0)
|
||||
goto done;
|
||||
add_postamble(cb);
|
||||
/* Send it to listening client on stdout */
|
||||
if (netconf_output(1, cb, "notification") < 0){
|
||||
if (netconf_output_encap(1, cb, "notification") < 0){
|
||||
cbuf_free(cb);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -925,13 +923,13 @@ netconf_application_rpc(clicon_handle h,
|
|||
if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output(1, cbret, "rpc-error");
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output(1, cbret, "rpc-error");
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,10 +450,12 @@ api_return_err(clicon_handle h,
|
|||
else
|
||||
if (xml2json_cbuf(cb, xerr, pretty) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
|
||||
FCGX_SetExitStatus(code, r->out); /* Created */
|
||||
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
|
||||
use_xml?"xml":"json");
|
||||
|
||||
if (use_xml){
|
||||
if (pretty){
|
||||
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
|
||||
|
|
|
|||
|
|
@ -1118,15 +1118,8 @@ api_operations_post(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (yrpc == NULL){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
|
||||
clicon_debug(1, "%s rpc %s not found", __FUNCTION__, id);
|
||||
if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ module example {
|
|||
}
|
||||
/* Translation function example - See also example_cli */
|
||||
list translate{
|
||||
key k;
|
||||
leaf k{
|
||||
type string;
|
||||
}
|
||||
leaf value{
|
||||
type string;
|
||||
}
|
||||
|
|
@ -73,6 +77,57 @@ module example {
|
|||
}
|
||||
}
|
||||
}
|
||||
rpc empty {
|
||||
description "Smallest possible RPC with no input or output";
|
||||
}
|
||||
rpc example {
|
||||
description "Some example input/output for testing RFC7950 7.14.
|
||||
RPC simply echoes the input for debugging.";
|
||||
input {
|
||||
leaf x {
|
||||
description
|
||||
"If a leaf in the input tree has a 'mandatory' statement with
|
||||
the value 'true', the leaf MUST be present in an RPC invocation.";
|
||||
type string;
|
||||
mandatory true;
|
||||
}
|
||||
leaf y {
|
||||
description
|
||||
"If a leaf in the input tree has a 'mandatory' statement with the
|
||||
value 'true', the leaf MUST be present in an RPC invocation.";
|
||||
type string;
|
||||
default "42";
|
||||
}
|
||||
leaf-list z {
|
||||
description
|
||||
"If a leaf-list in the input tree has one or more default
|
||||
values, the server MUST use these values (XXX not supported)";
|
||||
type string;
|
||||
}
|
||||
leaf w {
|
||||
description
|
||||
"If any node has a 'when' statement that would evaluate to
|
||||
'false',then this node MUST NOT be present in the input tree.
|
||||
(XXX not supported)";
|
||||
type string;
|
||||
when "/translate/k=5/value='w'";
|
||||
}
|
||||
}
|
||||
output {
|
||||
leaf x {
|
||||
type string;
|
||||
}
|
||||
leaf y {
|
||||
type string;
|
||||
}
|
||||
leaf z {
|
||||
type string;
|
||||
}
|
||||
leaf w {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc debug {
|
||||
description "Set debug level of backend. XXX should be in clixon-config";
|
||||
input {
|
||||
|
|
|
|||
|
|
@ -47,8 +47,11 @@ int netconf_missing_attribute(cbuf *cb, char *type, char *info, char *message);
|
|||
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
|
||||
int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message);
|
||||
int netconf_missing_element(cbuf *cb, char *type, char *element, char *message);
|
||||
int netconf_missing_element_xml(cxobj **xret, char *type, char *element, char *message);
|
||||
int netconf_bad_element(cbuf *cb, char *type, char *info, char *element);
|
||||
int netconf_bad_element_xml(cxobj **xret, char *type, char *info, char *element);
|
||||
int netconf_unknown_element(cbuf *cb, char *type, char *element, char *message);
|
||||
int netconf_unknown_element_xml(cxobj **xret, char *type, char *element, char *message);
|
||||
int netconf_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
|
||||
int netconf_access_denied(cbuf *cb, char *type, char *message);
|
||||
int netconf_access_denied_xml(cxobj **xret, char *type, char *message);
|
||||
|
|
|
|||
|
|
@ -307,6 +307,45 @@ netconf_unknown_attribute(cbuf *cb,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Common Netconf element XML tree according to RFC 6241 App A
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] tag Error tag
|
||||
* @param[in] element bad-element xml
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
static int
|
||||
netconf_element_xml_common(cxobj **xret,
|
||||
char *type,
|
||||
char *tag,
|
||||
char *element,
|
||||
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-type>%s</error-type>"
|
||||
"<error-tag>%s</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, tag, element) < 0)
|
||||
goto done;
|
||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
message) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* An expected element is missing.
|
||||
|
|
@ -322,31 +361,33 @@ netconf_missing_element(cbuf *cb,
|
|||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>missing-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_element_xml_common(&xret, type, "missing-element", element, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] element bad-element xml
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
int
|
||||
netconf_missing_element_xml(cxobj **xret,
|
||||
char *type,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
return netconf_element_xml_common(xret, type, "missing-element", element, message);
|
||||
}
|
||||
|
||||
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
|
||||
|
|
@ -365,31 +406,25 @@ netconf_bad_element(cbuf *cb,
|
|||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>bad-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_element_xml_common(&xret, type, "bad-element", element, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
int
|
||||
netconf_bad_element_xml(cxobj **xret,
|
||||
char *type,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
return netconf_element_xml_common(xret, type, "bad-element", element, message);
|
||||
}
|
||||
|
||||
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
||||
|
|
@ -407,31 +442,25 @@ netconf_unknown_element(cbuf *cb,
|
|||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>unknown-element</error-tag>"
|
||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type, element) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_element_xml_common(&xret, type, "unknown-element", element, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
int
|
||||
netconf_unknown_element_xml(cxobj **xret,
|
||||
char *type,
|
||||
char *element,
|
||||
char *message)
|
||||
{
|
||||
return netconf_element_xml_common(xret, type, "unknown-element", element, message);
|
||||
}
|
||||
|
||||
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
|
||||
|
|
@ -476,13 +505,14 @@ netconf_unknown_namespace(cbuf *cb,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
||||
/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
|
||||
*
|
||||
* Access to the requested protocol operation or data model is denied because
|
||||
* authorization failed.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @see netconf_access_denied_xml Same but returns XML tree
|
||||
*/
|
||||
int
|
||||
netconf_access_denied(cbuf *cb,
|
||||
|
|
@ -490,39 +520,33 @@ netconf_access_denied(cbuf *cb,
|
|||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>access-denied</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_access_denied_xml(&xret, type, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* Access to the requested protocol operation or data model is denied because
|
||||
* authorization failed.
|
||||
* @param[out] xret Error XML tree
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
* if (netconf_access_denied_xml(&xret, "protocol", "Unauthorized") < 0)
|
||||
* err;
|
||||
* xml_free(xret);
|
||||
* @endcode
|
||||
* @see netconf_access_denied Same but returns cligen buffer
|
||||
*/
|
||||
int
|
||||
netconf_access_denied_xml(cxobj **xret,
|
||||
|
|
@ -795,6 +819,7 @@ netconf_operation_not_supported(cbuf *cb,
|
|||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @see netconf_operation_failed_xml Same but returns XML tree
|
||||
*/
|
||||
int
|
||||
netconf_operation_failed(cbuf *cb,
|
||||
|
|
@ -802,30 +827,17 @@ netconf_operation_failed(cbuf *cb,
|
|||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>%s</error-type>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-severity>error</error-severity>",
|
||||
type) <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_operation_failed_xml(&xret, type, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") < 0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
||||
|
|
@ -835,6 +847,13 @@ netconf_operation_failed(cbuf *cb,
|
|||
* @param[out] xret Error XML tree
|
||||
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
* if (netconf_operation_failed_xml(&xret, "protocol", "Unauthorized") < 0)
|
||||
* err;
|
||||
* xml_free(xret);
|
||||
* @endcode
|
||||
* @see netconf_operation_failed Same but returns cligen buffer
|
||||
*/
|
||||
int
|
||||
netconf_operation_failed_xml(cxobj **xret,
|
||||
|
|
@ -872,35 +891,24 @@ netconf_operation_failed_xml(cxobj **xret,
|
|||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] message Error message
|
||||
* @note New in :base:1.1
|
||||
* @see netconf_malformed_message_xml Same but returns XML tree
|
||||
*/
|
||||
int
|
||||
netconf_malformed_message(cbuf *cb,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>rpc</error-type>"
|
||||
"<error-tag>malformed-message</error-tag>"
|
||||
"<error-severity>error</error-severity>") <0)
|
||||
goto err;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
if (netconf_malformed_message_xml(&xret, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
|
||||
|
|
@ -911,6 +919,13 @@ netconf_malformed_message(cbuf *cb,
|
|||
* @param[out] xret Error XML tree
|
||||
* @param[in] message Error message
|
||||
* @note New in :base:1.1
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
* if (netconf_malformed_message_xml(&xret, "Unauthorized") < 0)
|
||||
* err;
|
||||
* xml_free(xret);
|
||||
* @endcode
|
||||
* @see netconf_malformed_message Same but returns cligen buffer
|
||||
*/
|
||||
int
|
||||
netconf_malformed_message_xml(cxobj **xret,
|
||||
|
|
|
|||
|
|
@ -398,6 +398,8 @@ validate_identityref(cxobj *xt,
|
|||
* in [RFC6241]. If output parameters are returned, they are encoded as
|
||||
* child elements to the <rpc-reply> element defined in [RFC6241], in
|
||||
* the same order as they are defined within the "output" statement.
|
||||
* @see xml_yang_validate_all
|
||||
* @note Should need a variant accepting cxobj **xret
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_rpc(cxobj *xrpc,
|
||||
|
|
@ -454,6 +456,7 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
|||
* fail;
|
||||
* @endcode
|
||||
* @see xml_yang_validate_all
|
||||
* @note Should need a variant accepting cxobj **xret
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_add(cxobj *xt,
|
||||
|
|
@ -503,15 +506,14 @@ xml_yang_validate_add(cxobj *xt,
|
|||
* needs to be reparsed when concrete type is selected
|
||||
*/
|
||||
if ((body = xml_body(xt)) != NULL){
|
||||
if (cv_parse(body, cv) <0){
|
||||
clicon_err(OE_UNIX, errno, "cv_parse");
|
||||
if (cv_parse1(body, cv, &reason) != 1){
|
||||
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((ys_cv_validate(cv, ys, &reason)) != 1){
|
||||
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
|
||||
goto done;
|
||||
if (reason)
|
||||
free(reason);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
|
@ -531,6 +533,8 @@ xml_yang_validate_add(cxobj *xt,
|
|||
done:
|
||||
if (cv)
|
||||
cv_free(cv);
|
||||
if (reason)
|
||||
free(reason);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
@ -544,7 +548,6 @@ xml_yang_validate_add(cxobj *xt,
|
|||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
* @see xml_yang_validate_add
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* cbuf *cbret = cbuf_new();
|
||||
|
|
@ -553,6 +556,9 @@ xml_yang_validate_add(cxobj *xt,
|
|||
* if (ret == 0)
|
||||
* fail;
|
||||
* @endcode
|
||||
* @see xml_yang_validate_add
|
||||
* @see xml_yang_validate_rpc
|
||||
* @note Should need a variant accepting cxobj **xret
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_all(cxobj *xt,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Clixon tests
|
||||
|
||||
This directory contains testing code for clixon and the example
|
||||
routing application. Assumes setup of http daemon as describe under apps/restonf
|
||||
application. Assumes setup of http daemon as describe under apps/restonf
|
||||
- clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script
|
||||
- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically.
|
||||
- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically. By default the script will exit on first error. Run as `all.sh summary` to continue and print a summary on all tests.
|
||||
- test_nacm.sh Auth tests using internal NACM
|
||||
- test_nacm_ext.sh Auth tests using external NACM (separate file)
|
||||
- test_cli.sh CLI tests
|
||||
|
|
@ -13,3 +13,14 @@ routing application. Assumes setup of http daemon as describe under apps/restonf
|
|||
- test_leafref.sh Yang leafref tests
|
||||
- test_datastore.sh Datastore tests
|
||||
|
||||
Example runs:
|
||||
```
|
||||
> run.sh
|
||||
# Runs through all tests matching 'test_*.sh' in the directory. Prints test output
|
||||
# and stops on first error
|
||||
|
||||
> run.sh summary
|
||||
# Same as above but continues after errors and does not print test output.
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
32
test/all.sh
32
test/all.sh
|
|
@ -1,17 +1,43 @@
|
|||
#!/bin/bash
|
||||
# Run, eg as:
|
||||
# ./run.sh 2>&1 | tee test.log
|
||||
# ./all.sh 2>&1 | tee test.log # break on first test
|
||||
# ./all.sh summary # to run all tests and print
|
||||
|
||||
summary=0
|
||||
if [ $# -gt 0 ]; then
|
||||
summary=1
|
||||
fi
|
||||
if [ $# -gt 1 ]; then
|
||||
echo "usage: $0 [summary] # pipe to dev/null and continue on error"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
||||
err=0
|
||||
for test in test*.sh; do
|
||||
echo "Running $test"
|
||||
if [ $summary -ne 0 ]; then
|
||||
./$test > /dev/null 2>&1
|
||||
errcode=$?
|
||||
else
|
||||
./$test
|
||||
errcode=$?
|
||||
fi
|
||||
if [ $errcode -ne 0 ]; then
|
||||
echo "Error in $test errcode=$errcode"
|
||||
err=1
|
||||
echo -e "\e[31mError in $test errcode=$errcode"
|
||||
echo -ne "\e[0m"
|
||||
if [ $summary -eq 0 ]; then
|
||||
exit $errcode
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ $err -eq 0 ]; then
|
||||
echo OK
|
||||
else
|
||||
echo -e "\e[31mError"
|
||||
echo -ne "\e[0m"
|
||||
fi
|
||||
|
||||
|
||||
|
|
|
|||
12
test/lib.sh
12
test/lib.sh
|
|
@ -38,7 +38,9 @@ if [ ! -d $dir ]; then
|
|||
fi
|
||||
rm -rf $dir/*
|
||||
|
||||
# error and exit, arg is optional extra errmsg
|
||||
# error and exit,
|
||||
# arg1: expected
|
||||
# arg2: errmsg[optional]
|
||||
err(){
|
||||
echo -e "\e[31m\nError in Test$testnr [$testname]:"
|
||||
if [ $# -gt 0 ]; then
|
||||
|
|
@ -53,7 +55,7 @@ err(){
|
|||
echo "$expect"| od -t c > $dir/clixon-expect
|
||||
diff $dir/clixon-expect $dir/clixon-ret
|
||||
|
||||
exit $testnr
|
||||
exit -1 #$testnr
|
||||
}
|
||||
|
||||
# Increment test number and print a nice string
|
||||
|
|
@ -221,8 +223,9 @@ expectwait(){
|
|||
done
|
||||
# cat /tmp/flag
|
||||
if [ $(cat /tmp/flag) != "ok" ]; then
|
||||
cat /tmp/flag
|
||||
exit
|
||||
# err "ok" $(cat /tmp/flag)
|
||||
# cat /tmp/flag
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -250,5 +253,4 @@ expectmatch(){
|
|||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
|||
|
||||
sleep 1
|
||||
new "start restconf daemon (-a is enable basic authentication)"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ new "kill old restconf daemon"
|
|||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon (-a is enable http basic auth)"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
|||
|
||||
sleep 1
|
||||
new "start restconf daemon (-a is enable basic authentication)"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ new "kill old restconf daemon"
|
|||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
|
|||
|
|
@ -282,10 +282,10 @@ new2 "restconf rpc using POST json without mandatory element"
|
|||
expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}}
'
|
||||
|
||||
new2 "restconf rpc non-existing rpc without namespace"
|
||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang node not found"}}}
'
|
||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||
|
||||
new2 "restconf rpc non-existing rpc"
|
||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang node not found"}}}
'
|
||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||
|
||||
new2 "restconf rpc missing name"
|
||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}}
'
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ new "kill old restconf daemon"
|
|||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ new "kill old restconf daemon"
|
|||
sudo pkill -u www-data clixon_restconf
|
||||
|
||||
new "start restconf daemon"
|
||||
sudo su -c "$clixon_restconf -f $cfg" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -D $DBG" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ new "rpc tests"
|
|||
|
||||
# 1.First some positive tests vary media types
|
||||
#
|
||||
new2 "restconf example rpc json/json default - no headers"
|
||||
new2 "restconf example rpc json/json default - no http media headers"
|
||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}}
|
||||
'
|
||||
|
||||
|
|
@ -93,13 +93,13 @@ new2 "restconf add extra"
|
|||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
|
||||
|
||||
new2 "restconf wrong method"
|
||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang node not found"}}}
'
|
||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||
|
||||
new2 "restconf edit-config missing mandatory"
|
||||
expecteq "$(curl -s -X POST -d '{"input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
||||
|
||||
new "netconf kill-session missing session-id mandatory"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
||||
# edit-config?
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ new "kill old restconf daemon"
|
|||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon"
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
|
||||
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
|
||||
|
||||
sleep $RCWAIT
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ new "netconf NONEXIST subscription"
|
|||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
|
||||
|
||||
new "netconf EXAMPLE subscription with wrong date"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Expected timestamp</error-message></rpc-error></rpc-reply>]]>]]>$' 0
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Invalid time: kallekaka</error-message></rpc-error></rpc-reply>]]>]]>$' 0
|
||||
|
||||
#new "netconf EXAMPLE subscription with replay"
|
||||
#NOW=$(date +"%Y-%m-%dT%H:%M:%S")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue