From 861300d6c07ba9749657832485d4f7f74e6da197 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 21 Dec 2018 14:44:59 +0100 Subject: [PATCH] netconf error handling and test summary script --- CHANGELOG.md | 3 +- apps/netconf/clixon_netconf.h | 1 + apps/netconf/netconf_lib.c | 37 ++++- apps/netconf/netconf_lib.h | 1 + apps/netconf/netconf_main.c | 9 +- apps/netconf/netconf_rpc.c | 8 +- apps/restconf/restconf_lib.c | 2 + apps/restconf/restconf_methods.c | 11 +- example/example.yang | 55 ++++++ lib/clixon/clixon_netconf_lib.h | 3 + lib/src/clixon_netconf_lib.c | 277 ++++++++++++++++--------------- lib/src/clixon_xml_map.c | 18 +- test/README.md | 17 +- test/all.sh | 40 ++++- test/lib.sh | 12 +- test/test_nacm.sh | 2 +- test/test_nacm_ext.sh | 2 +- test/test_nacm_protocol.sh | 2 +- test/test_perf.sh | 2 +- test/test_restconf.sh | 4 +- test/test_restconf2.sh | 2 +- test/test_rpc.sh | 8 +- test/test_stream.sh | 4 +- 23 files changed, 331 insertions(+), 189 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0035a93d..ce4188a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/apps/netconf/clixon_netconf.h b/apps/netconf/clixon_netconf.h index e2fbb5bb..9fed0a1a 100644 --- a/apps/netconf/clixon_netconf.h +++ b/apps/netconf/clixon_netconf.h @@ -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, diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index fc24810b..50723de4 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -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; +} diff --git a/apps/netconf/netconf_lib.h b/apps/netconf/netconf_lib.h index f2dc73e1..6d2de111 100644 --- a/apps/netconf/netconf_lib.h +++ b/apps/netconf/netconf_lib.h @@ -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_ */ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index cc4c9710..dc57126e 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -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; } diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 64001137..4d9b354a 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -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; } } diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index be214fbb..062dff65 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -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, " \n", cbuf_get(cb)); diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index b8beb068..a11d55c1 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -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) diff --git a/example/example.yang b/example/example.yang index 57f58fcb..749b10ff 100644 --- a/example/example.yang +++ b/example/example.yang @@ -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 { diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index b22e697b..8f1b9da0 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -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); diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 6c284fc6..8db94c4a 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -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, "%s" + "%s" + "%s" + "error", + type, tag, element) < 0) + goto done; + if (message && xml_parse_va(&xerr, NULL, "%s", + 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. @@ -321,32 +360,34 @@ netconf_missing_element(cbuf *cb, char *element, char *message) { - int retval = -1; - char *encstr = NULL; + int retval = -1; + cxobj *xret = NULL; - if (cprintf(cb, "" - "%s" - "missing-element" - "%s" - "error", - type, element) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + if (netconf_element_xml_common(&xret, type, "missing-element", element, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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 @@ -364,32 +405,26 @@ netconf_bad_element(cbuf *cb, char *element, char *message) { - int retval = -1; - char *encstr = NULL; + int retval = -1; + cxobj *xret = NULL; - if (cprintf(cb, "" - "%s" - "bad-element" - "%s" - "error", - type, element) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + if (netconf_element_xml_common(&xret, type, "bad-element", element, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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 @@ -406,32 +441,26 @@ netconf_unknown_element(cbuf *cb, char *element, char *message) { - int retval = -1; - char *encstr = NULL; + int retval = -1; + cxobj *xret = NULL; - if (cprintf(cb, "" - "%s" - "unknown-element" - "%s" - "error", - type, element) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + if (netconf_element_xml_common(&xret, type, "unknown-element", element, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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,53 +505,48 @@ 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, char *type, char *message) { - int retval = -1; - char *encstr = NULL; - - if (cprintf(cb, "" - "%s" - "access-denied" - "error", - type) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + int retval = -1; + cxobj *xret = NULL; + + if (netconf_access_denied_xml(&xret, type, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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,37 +819,25 @@ 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, char *type, char *message) { - int retval = -1; - char *encstr = NULL; + int retval = -1; + cxobj *xret = NULL; - if (cprintf(cb, "" - "%s" - "operation-failed" - "error", - type) <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") < 0) - goto err; + if (netconf_operation_failed_xml(&xret, type, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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" - "malformed-message" - "error") <0) - goto err; - if (message){ - if (xml_chardata_encode(&encstr, "%s", message) < 0) - goto done; - if (cprintf(cb, "%s", encstr) < 0) - goto err; - } - if (cprintf(cb, "") <0) - goto err; + if (netconf_malformed_message_xml(&xret, message) < 0) + goto done; + if (clicon_xml2cbuf(cb, xret, 0, 0) < 0) + goto done; 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,10 +919,17 @@ 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, - char *message) + char *message) { int retval =-1; cxobj *xerr; diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 2263bde2..2644f494 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -398,6 +398,8 @@ validate_identityref(cxobj *xt, * in [RFC6241]. If output parameters are returned, they are encoded as * child elements to the 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"); - goto done; + 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, diff --git a/test/README.md b/test/README.md index 7ae292bb..59832663 100644 --- a/test/README.md +++ b/test/README.md @@ -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 -- 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. +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. 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. +``` + + diff --git a/test/all.sh b/test/all.sh index d0e92410..79a0309f 100755 --- a/test/all.sh +++ b/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" - ./$test - errcode=$? + 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" - exit $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 -echo OK +if [ $err -eq 0 ]; then + echo OK +else + echo -e "\e[31mError" + echo -ne "\e[0m" +fi + + diff --git a/test/lib.sh b/test/lib.sh index 2a66d2d5..3d82e32d 100755 --- a/test/lib.sh +++ b/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 - } diff --git a/test/test_nacm.sh b/test/test_nacm.sh index afd1c29d..389ae010 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -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 diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index 2701b2da..9bdee51d 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -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 diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index aebaa843..963218e5 100755 --- a/test/test_nacm_protocol.sh +++ b/test/test_nacm_protocol.sh @@ -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 diff --git a/test/test_perf.sh b/test/test_perf.sh index 66c7a5b1..4d9d6dfc 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -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 diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 03d20bd9..1874a0b3 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -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"}}} ' diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index ee820082..1cf7188f 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -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 diff --git a/test/test_rpc.sh b/test/test_rpc.sh index b1b04251..f28a4df9 100755 --- a/test/test_rpc.sh +++ b/test/test_rpc.sh @@ -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 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable$' +expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable]]>]]>$' # edit-config? diff --git a/test/test_stream.sh b/test/test_stream.sh index 19ad9802..87932744 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -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" 'NONEXIST]]>]]>' '^applicationinvalid-valueerrorNo such stream]]>]]>$' $NCWAIT new "netconf EXAMPLE subscription with wrong date" -expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLEkallekaka]]>]]>' '^applicationbad-elementstartTimeerrorExpected timestamp]]>]]>$' 0 +expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLEkallekaka]]>]]>' '^applicationbad-elementstartTimeerrorInvalid time: kallekaka]]>]]>$' 0 #new "netconf EXAMPLE subscription with replay" #NOW=$(date +"%Y-%m-%dT%H:%M:%S")