netconf error handling and test summary script

This commit is contained in:
Olof hagsand 2018-12-21 14:44:59 +01:00
parent f872c7e295
commit 861300d6c0
23 changed files with 331 additions and 189 deletions

View file

@ -20,7 +20,8 @@
* Openconfig yang specs parsed: https://github.com/openconfig/public * Openconfig yang specs parsed: https://github.com/openconfig/public
* Improved "unknown" handling * Improved "unknown" handling
* More precise Yang validation and better error messages * 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 * Yang load file configure options changed
* `CLICON_YANG_DIR` is changed from a single directory to a path of directories * `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 * Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list

View file

@ -44,6 +44,7 @@
* (Duplicated. Also in netconf_*.h) * (Duplicated. Also in netconf_*.h)
*/ */
int netconf_output(int s, cbuf *xf, char *msg); int netconf_output(int s, cbuf *xf, char *msg);
int netconf_output_encap(int s, cbuf *xf, char *msg);
int netconf_xpath(cxobj *xsearch, int netconf_xpath(cxobj *xsearch,
cxobj *xfilter, cxobj *xfilter,

View file

@ -182,9 +182,9 @@ netconf_get_target(cxobj *xn,
* @param[in] s * @param[in] s
* @param[in] cb Cligen buffer that contains the XML message * @param[in] cb Cligen buffer that contains the XML message
* @param[in] msg Only for debug * @param[in] msg Only for debug
* @note Assumes "cb" contains valid XML, ie encoding is correct. This is done * @retval 0 OK
* if it is output by a xml render routine (xml_print et al), but NOT * @retval -1 Error
* otherwise. * @see netconf_output_encap for function with encapsulation
*/ */
int int
netconf_output(int s, netconf_output(int s,
@ -216,3 +216,34 @@ netconf_output(int s,
return retval; 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;
}

View file

@ -75,5 +75,6 @@ int add_error_preamble(cbuf *xf, char *reason);
char *netconf_get_target(cxobj *xn, char *path); char *netconf_get_target(cxobj *xn, char *path);
int add_error_postamble(cbuf *xf); int add_error_postamble(cbuf *xf);
int netconf_output(int s, cbuf *xf, char *msg); int netconf_output(int s, cbuf *xf, char *msg);
int netconf_output_encap(int s, cbuf *cb, char *msg);
#endif /* _NETCONF_LIB_H_ */ #endif /* _NETCONF_LIB_H_ */

View file

@ -112,7 +112,7 @@ process_incoming_packet(clicon_handle h,
free(str0); free(str0);
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0) if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
goto done; goto done;
netconf_output(1, cbret, "rpc-error"); netconf_output_encap(1, cbret, "rpc-error");
goto done; goto done;
} }
free(str0); free(str0);
@ -121,7 +121,7 @@ process_incoming_packet(clicon_handle h,
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0) if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
netconf_output(1, cbret, "rpc-error"); netconf_output_encap(1, cbret, "rpc-error");
goto ok; goto ok;
} }
} }
@ -157,11 +157,8 @@ process_incoming_packet(clicon_handle h,
if (xml_addsub(xc, xa2) < 0) if (xml_addsub(xc, xa2) < 0)
goto done; goto done;
} }
add_preamble(cbret);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0); clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
add_postamble(cbret); if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
if (netconf_output(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret); cbuf_free(cbret);
goto done; goto done;
} }

View file

@ -776,12 +776,10 @@ netconf_notification_cb(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new"); clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done; goto done;
} }
add_preamble(cb); /* Make it well-formed netconf xml */
if (clicon_xml2cbuf(cb, xn, 0, 0) < 0) if (clicon_xml2cbuf(cb, xn, 0, 0) < 0)
goto done; goto done;
add_postamble(cb);
/* Send it to listening client on stdout */ /* 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); cbuf_free(cb);
goto done; goto done;
} }
@ -925,13 +923,13 @@ netconf_application_rpc(clicon_handle h,
if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0) if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
netconf_output(1, cbret, "rpc-error"); netconf_output_encap(1, cbret, "rpc-error");
goto ok; goto ok;
} }
if ((ret = xml_yang_validate_add(xn, cbret)) < 0) if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
netconf_output(1, cbret, "rpc-error"); netconf_output_encap(1, cbret, "rpc-error");
goto ok; goto ok;
} }
} }

View file

@ -450,10 +450,12 @@ api_return_err(clicon_handle h,
else else
if (xml2json_cbuf(cb, xerr, pretty) < 0) if (xml2json_cbuf(cb, xerr, pretty) < 0)
goto done; goto done;
clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
FCGX_SetExitStatus(code, r->out); /* Created */ FCGX_SetExitStatus(code, r->out); /* Created */
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase); 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", FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
use_xml?"xml":"json"); use_xml?"xml":"json");
if (use_xml){ if (use_xml){
if (pretty){ if (pretty){
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb)); FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));

View file

@ -1118,15 +1118,8 @@ api_operations_post(clicon_handle h,
goto ok; goto ok;
} }
if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){ if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) clicon_debug(1, "%s rpc %s not found", __FUNCTION__, id);
goto done; if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
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)
goto done; goto done;
if ((xe = xpath_first(xerr, "rpc-error")) != NULL) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0) if (api_return_err(h, r, xe, pretty, use_xml) < 0)

View file

@ -26,6 +26,10 @@ module example {
} }
/* Translation function example - See also example_cli */ /* Translation function example - See also example_cli */
list translate{ list translate{
key k;
leaf k{
type string;
}
leaf value{ leaf value{
type string; 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 { rpc debug {
description "Set debug level of backend. XXX should be in clixon-config"; description "Set debug level of backend. XXX should be in clixon-config";
input { input {

View file

@ -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_bad_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_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(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(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(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_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
int netconf_access_denied(cbuf *cb, char *type, char *message); int netconf_access_denied(cbuf *cb, char *type, char *message);
int netconf_access_denied_xml(cxobj **xret, char *type, char *message); int netconf_access_denied_xml(cxobj **xret, char *type, char *message);

View file

@ -307,6 +307,45 @@ netconf_unknown_attribute(cbuf *cb,
goto done; 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 /*! Create Netconf missing-element error XML tree according to RFC 6241 App A
* *
* An expected element is missing. * An expected element is missing.
@ -322,31 +361,33 @@ netconf_missing_element(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_element_xml_common(&xret, type, "missing-element", element, message) < 0)
"<error-type>%s</error-type>" goto done;
"<error-tag>missing-element</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<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)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; 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 /*! Create Netconf bad-element error XML tree according to RFC 6241 App A
@ -365,31 +406,25 @@ netconf_bad_element(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_element_xml_common(&xret, type, "bad-element", element, message) < 0)
"<error-type>%s</error-type>" goto done;
"<error-tag>bad-element</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<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)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; return retval;
err: }
clicon_err(OE_XML, errno, "cprintf"); int
goto done; 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 /*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
@ -407,31 +442,25 @@ netconf_unknown_element(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_element_xml_common(&xret, type, "unknown-element", element, message) < 0)
"<error-type>%s</error-type>" goto done;
"<error-tag>unknown-element</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<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)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; return retval;
err: }
clicon_err(OE_XML, errno, "cprintf"); int
goto done; 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 /*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
@ -476,13 +505,14 @@ netconf_unknown_namespace(cbuf *cb,
goto done; 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 * Access to the requested protocol operation or data model is denied because
* authorization failed. * authorization failed.
* @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol" * @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message * @param[in] message Error message
* @see netconf_access_denied_xml Same but returns XML tree
*/ */
int int
netconf_access_denied(cbuf *cb, netconf_access_denied(cbuf *cb,
@ -490,39 +520,33 @@ netconf_access_denied(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_access_denied_xml(&xret, type, message) < 0)
"<error-type>%s</error-type>" goto done;
"<error-tag>access-denied</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
} }
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A /*! 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 * Access to the requested protocol operation or data model is denied because
* authorization failed. * 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] type Error type: "application" or "protocol"
* @param[in] message Error message * @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 int
netconf_access_denied_xml(cxobj **xret, 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[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol" * @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] message Error message * @param[in] message Error message
* @see netconf_operation_failed_xml Same but returns XML tree
*/ */
int int
netconf_operation_failed(cbuf *cb, netconf_operation_failed(cbuf *cb,
@ -802,30 +827,17 @@ netconf_operation_failed(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_operation_failed_xml(&xret, type, message) < 0)
"<error-type>%s</error-type>" goto done;
"<error-tag>operation-failed</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
} }
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A /*! 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[out] xret Error XML tree
* @param[in] type Error type: "rpc", "application" or "protocol" * @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] message Error message * @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 int
netconf_operation_failed_xml(cxobj **xret, 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[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] message Error message * @param[in] message Error message
* @note New in :base:1.1 * @note New in :base:1.1
* @see netconf_malformed_message_xml Same but returns XML tree
*/ */
int int
netconf_malformed_message(cbuf *cb, netconf_malformed_message(cbuf *cb,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; cxobj *xret = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (netconf_malformed_message_xml(&xret, message) < 0)
"<error-type>rpc</error-type>" goto done;
"<error-tag>malformed-message</error-tag>" if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
"<error-severity>error</error-severity>") <0)
goto err;
if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0)
goto done; 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; retval = 0;
done: done:
if (encstr) if (xret)
free(encstr); xml_free(xret);
return retval; return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
} }
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A /*! 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[out] xret Error XML tree
* @param[in] message Error message * @param[in] message Error message
* @note New in :base:1.1 * @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 int
netconf_malformed_message_xml(cxobj **xret, netconf_malformed_message_xml(cxobj **xret,

View file

@ -398,6 +398,8 @@ validate_identityref(cxobj *xt,
* in [RFC6241]. If output parameters are returned, they are encoded as * in [RFC6241]. If output parameters are returned, they are encoded as
* child elements to the <rpc-reply> element defined in [RFC6241], in * child elements to the <rpc-reply> element defined in [RFC6241], in
* the same order as they are defined within the "output" statement. * 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 int
xml_yang_validate_rpc(cxobj *xrpc, xml_yang_validate_rpc(cxobj *xrpc,
@ -454,6 +456,7 @@ xml_yang_validate_rpc(cxobj *xrpc,
* fail; * fail;
* @endcode * @endcode
* @see xml_yang_validate_all * @see xml_yang_validate_all
* @note Should need a variant accepting cxobj **xret
*/ */
int int
xml_yang_validate_add(cxobj *xt, 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 * needs to be reparsed when concrete type is selected
*/ */
if ((body = xml_body(xt)) != NULL){ if ((body = xml_body(xt)) != NULL){
if (cv_parse(body, cv) <0){ if (cv_parse1(body, cv, &reason) != 1){
clicon_err(OE_UNIX, errno, "cv_parse"); if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
goto done; goto done;
goto fail;
} }
if ((ys_cv_validate(cv, ys, &reason)) != 1){ if ((ys_cv_validate(cv, ys, &reason)) != 1){
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0) if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
goto done; goto done;
if (reason)
free(reason);
goto fail; goto fail;
} }
} }
@ -531,6 +533,8 @@ xml_yang_validate_add(cxobj *xt,
done: done:
if (cv) if (cv)
cv_free(cv); cv_free(cv);
if (reason)
free(reason);
return retval; return retval;
fail: fail:
retval = 0; retval = 0;
@ -544,7 +548,6 @@ xml_yang_validate_add(cxobj *xt,
* @retval 1 Validation OK * @retval 1 Validation OK
* @retval 0 Validation failed * @retval 0 Validation failed
* @retval -1 Error * @retval -1 Error
* @see xml_yang_validate_add
* @code * @code
* cxobj *x; * cxobj *x;
* cbuf *cbret = cbuf_new(); * cbuf *cbret = cbuf_new();
@ -553,6 +556,9 @@ xml_yang_validate_add(cxobj *xt,
* if (ret == 0) * if (ret == 0)
* fail; * fail;
* @endcode * @endcode
* @see xml_yang_validate_add
* @see xml_yang_validate_rpc
* @note Should need a variant accepting cxobj **xret
*/ */
int int
xml_yang_validate_all(cxobj *xt, xml_yang_validate_all(cxobj *xt,

View file

@ -1,9 +1,9 @@
# Clixon tests # Clixon tests
This directory contains testing code for clixon and the example 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 - 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.sh Auth tests using internal NACM
- test_nacm_ext.sh Auth tests using external NACM (separate file) - test_nacm_ext.sh Auth tests using external NACM (separate file)
- test_cli.sh CLI tests - 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_leafref.sh Yang leafref tests
- test_datastore.sh Datastore 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.
```

View file

@ -1,17 +1,43 @@
#!/bin/bash #!/bin/bash
# Run, eg as: # 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 # include err() and new() functions
. ./lib.sh . ./lib.sh
err=0
for test in test*.sh; do for test in test*.sh; do
echo "Running $test" echo "Running $test"
if [ $summary -ne 0 ]; then
./$test > /dev/null 2>&1
errcode=$?
else
./$test ./$test
errcode=$? errcode=$?
fi
if [ $errcode -ne 0 ]; then 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 exit $errcode
fi fi
fi
done done
echo OK if [ $err -eq 0 ]; then
echo OK
else
echo -e "\e[31mError"
echo -ne "\e[0m"
fi

View file

@ -38,7 +38,9 @@ if [ ! -d $dir ]; then
fi fi
rm -rf $dir/* rm -rf $dir/*
# error and exit, arg is optional extra errmsg # error and exit,
# arg1: expected
# arg2: errmsg[optional]
err(){ err(){
echo -e "\e[31m\nError in Test$testnr [$testname]:" echo -e "\e[31m\nError in Test$testnr [$testname]:"
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
@ -53,7 +55,7 @@ err(){
echo "$expect"| od -t c > $dir/clixon-expect echo "$expect"| od -t c > $dir/clixon-expect
diff $dir/clixon-expect $dir/clixon-ret diff $dir/clixon-expect $dir/clixon-ret
exit $testnr exit -1 #$testnr
} }
# Increment test number and print a nice string # Increment test number and print a nice string
@ -221,8 +223,9 @@ expectwait(){
done done
# cat /tmp/flag # cat /tmp/flag
if [ $(cat /tmp/flag) != "ok" ]; then if [ $(cat /tmp/flag) != "ok" ]; then
cat /tmp/flag # err "ok" $(cat /tmp/flag)
exit # cat /tmp/flag
exit -1
fi fi
} }
@ -250,5 +253,4 @@ expectmatch(){
fi fi
fi fi
fi fi
} }

View file

@ -123,7 +123,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
sleep 1 sleep 1
new "start restconf daemon (-a is enable basic authentication)" 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 sleep $RCWAIT

View file

@ -155,7 +155,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon (-a is enable http basic auth)" 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 sleep $RCWAIT

View file

@ -147,7 +147,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
sleep 1 sleep 1
new "start restconf daemon (-a is enable basic authentication)" 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 sleep $RCWAIT

View file

@ -78,7 +78,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon" 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 sleep $RCWAIT

View file

@ -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"}}} ' 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" 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" 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" 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"}}} ' 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"}}} '

View file

@ -65,7 +65,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon" 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 sleep $RCWAIT

View file

@ -46,7 +46,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf sudo pkill -u www-data clixon_restconf
new "start restconf daemon" 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 sleep $RCWAIT
@ -54,7 +54,7 @@ new "rpc tests"
# 1.First some positive tests vary media types # 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"}} 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"}}} ' 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" 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" 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"}}} ' 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" 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? # edit-config?

View file

@ -121,7 +121,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon" 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 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 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" 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" #new "netconf EXAMPLE subscription with replay"
#NOW=$(date +"%Y-%m-%dT%H:%M:%S") #NOW=$(date +"%Y-%m-%dT%H:%M:%S")