From f872c7e295422881168465e3e6c062314a27dff0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 21 Dec 2018 01:33:41 +0100 Subject: [PATCH] * More precise Yang validation and better error messages * For Example, adding bad-, missing-, or unknown-element error messages, etc instead of operation-failed * Removed delete-config support for candidate db since it is not supported in RFC6241. * Switched the order of `error-type` and `error-tag` in all netconf and restconf error messages to comply to RFC order. * Added example_rpc RPC to example backend * Renamed xml_namespace[_set]() to xml_prefix[_set]() * Some restconf error messages contained "rpc-reply" or "rpc-error" which have now been removed. * Netconf/Restconf RPC extra input arguments are ignored (https://github.com/clicon/clixon/issues/47) --- CHANGELOG.md | 15 +- README.md | 3 +- ROADMAP.md | 20 ++- apps/backend/backend_client.c | 31 ++-- apps/backend/backend_commit.c | 106 +++++++++----- apps/backend/backend_commit.h | 2 +- apps/backend/backend_main.c | 25 +++- apps/netconf/netconf_main.c | 31 ++-- apps/netconf/netconf_rpc.c | 37 +++-- apps/restconf/restconf_lib.c | 9 +- apps/restconf/restconf_methods.c | 159 +++++++++++++-------- example/example_backend.c | 49 ++++++- lib/clixon/clixon_netconf_lib.h | 6 +- lib/clixon/clixon_xml.h | 4 +- lib/clixon/clixon_xml_map.h | 7 +- lib/src/clixon_json.c | 2 +- lib/src/clixon_netconf_lib.c | 81 +++++------ lib/src/clixon_options.c | 12 +- lib/src/clixon_proto_client.c | 2 +- lib/src/clixon_xml.c | 26 ++-- lib/src/clixon_xml_map.c | 235 +++++++++++++++++++++++-------- lib/src/clixon_xml_parse.y | 16 +-- lib/src/clixon_xml_sort.c | 3 + lib/src/clixon_yang.c | 2 +- test/test_cli.sh | 6 +- test/test_feature.sh | 5 +- test/test_identity.sh | 4 +- test/test_leafref.sh | 8 +- test/test_list.sh | 2 +- test/test_nacm.sh | 12 +- test/test_nacm_ext.sh | 12 +- test/test_nacm_protocol.sh | 14 +- test/test_netconf.sh | 4 +- test/test_order.sh | 4 +- test/test_perf.sh | 2 +- test/test_restconf.sh | 57 ++++---- test/test_restconf2.sh | 10 +- test/test_rpc.sh | 125 ++++++++++++++++ test/test_stream.sh | 8 +- test/test_type.sh | 6 +- test/test_union.sh | 2 +- test/test_when_must.sh | 8 +- test/test_yang.sh | 8 +- test/test_yang_load.sh | 28 ++-- test/test_yang_namespace.sh | 2 +- 45 files changed, 806 insertions(+), 404 deletions(-) create mode 100755 test/test_rpc.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 160f62540..0035a93d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ * [Roadmap](ROADMAP.md) (Uncommitted and unprioritized) ### Major New features - * NACM extension (RFC8341) * NACM module support (RFC8341 A1+A2) * Recovery user "_nacm_recovery" added. @@ -20,6 +19,8 @@ * Support of submodule, include and belongs-to. * 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 * 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 @@ -27,18 +28,20 @@ * CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded. * Correct XML namespace handling * XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration: -``` + ``` # Wrong but accepted # Correct -``` + ``` * To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default) * XML to JSON translator support for mapping xmlns attribute to module name prefix. * Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0" * See https://github.com/clicon/clixon/issues/49 ### API changes on existing features (you may need to change your code) +* Removed delete-config support for candidate db since it is not supported in RFC6241. +* Switched the order of `error-type` and `error-tag` in all netconf and restconf error messages to comply to RFC order. * Yang parser is stricter (see above) which may break parsing of existing yang specs. * XML namespace handling is corrected (see above) * For backward compatibility set config option CLICON_XML_NS_ITERATE @@ -49,6 +52,8 @@ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h ### Minor changes +* Added example_rpc RPC to example backend +* Renamed xml_namespace[_set]() to xml_prefix[_set]() * Changed all make tags --> make TAGS * Keyvalue datastore removed (it has been disabled since 3.3.3) * Removed return value ymodp from yang parse functions (eg yang_parse()). @@ -60,8 +65,10 @@ * config", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0) goto done; goto ok; } else{ - /* yang spec may be set to anyxml by ingress yang check,...*/ + /* yang spec may be set to anyxmly by ingress yang check,...*/ if (xml_spec(xc) != NULL) xml_spec_set(xc, NULL); /* Populate XML with Yang spec (why not do this in parser?) @@ -530,7 +530,7 @@ from_client_lock(clicon_handle h, cbuf *cbx = NULL; /* Assist cbuf */ if ((db = netconf_db_find(xe, "target")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) goto done; goto ok; } @@ -589,7 +589,7 @@ from_client_unlock(clicon_handle h, cbuf *cbx = NULL; /* Assist cbuf */ if ((db = netconf_db_find(xe, "target")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) goto done; goto ok; } @@ -651,7 +651,7 @@ from_client_kill_session(clicon_handle h, if ((x = xml_find(xe, "session-id")) == NULL || (str = xml_find_value(x, "body")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0) goto done; goto ok; } @@ -709,7 +709,7 @@ from_client_copy_config(clicon_handle h, cbuf *cbx = NULL; /* Assist cbuf */ if ((source = netconf_db_find(xe, "source")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0) goto done; goto ok; } @@ -724,7 +724,7 @@ from_client_copy_config(clicon_handle h, goto ok; } if ((target = netconf_db_find(xe, "target")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) goto done; goto ok; } @@ -777,7 +777,7 @@ from_client_delete_config(clicon_handle h, if ((target = netconf_db_find(xe, "target")) == NULL|| strcmp(target, "running")==0){ - if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0) goto done; goto ok; } @@ -856,7 +856,7 @@ from_client_create_subscription(clicon_handle h, if ((x = xpath_first(xe, "//stopTime")) != NULL){ if ((stoptime = xml_find_value(x, "body")) != NULL && str2time(stoptime, &stop) < 0){ - if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0) + if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0) goto done; goto ok; } @@ -864,7 +864,7 @@ from_client_create_subscription(clicon_handle h, if ((x = xpath_first(xe, "//startTime")) != NULL){ if ((starttime = xml_find_value(x, "body")) != NULL && str2time(starttime, &start) < 0){ - if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0) + if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0) goto done; goto ok; } @@ -925,7 +925,7 @@ from_client_debug(clicon_handle h, char *valstr; if ((valstr = xml_find_body(xe, "level")) == NULL){ - if (netconf_missing_element(cbret, "application", "level", NULL) < 0) + if (netconf_missing_element(cbret, "application", "level", NULL) < 0) goto done; goto ok; } @@ -993,13 +993,10 @@ from_client_msg(clicon_handle h, /* Populate incoming XML tree with yang */ if (xml_spec_populate_rpc(h, x, yspec) < 0) goto done; - if ((ret = xml_yang_validate_rpc(x)) < 0) + if ((ret = xml_yang_validate_rpc(x, cbret)) < 0) goto done; - if (ret == 0){ - if (netconf_operation_failed(cbret, "application", "Validation failed")< 0) - goto done; + if (ret == 0) goto reply; - } xe = NULL; username = xml_find_value(x, "username"); while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) { @@ -1059,7 +1056,7 @@ from_client_msg(clicon_handle h, } else if (strcmp(rpc, "validate") == 0){ if ((db = netconf_db_find(xe, "source")) == NULL){ - if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0) + if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0) goto done; goto reply; } diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 3aa2f82d4..88ea9e99a 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -81,68 +81,86 @@ * are if code comes via XML/NETCONF. * @param[in] yspec Yang spec * @param[in] td Transaction data + * @param[out] cbret Cligen buffer containing netconf error (if retval == 0) + * @retval -1 Error + * @retval 0 Validation failed (with cbret set) + * @retval 1 Validation OK */ static int generic_validate(yang_spec *yspec, - transaction_data_t *td) + transaction_data_t *td, + cbuf *cbret) { int retval = -1; cxobj *x1; cxobj *x2; yang_stmt *ys; int i; + int ret; /* All entries */ - if (xml_apply(td->td_target, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0) + if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0) goto done; - + if (ret == 0) + goto fail; /* changed entries */ for (i=0; itd_clen; i++){ x1 = td->td_scvec[i]; /* source changed */ x2 = td->td_tcvec[i]; /* target changed */ - if (xml_yang_validate_add(x2, NULL) < 0) + /* Should this be recursive? */ + if ((ret = xml_yang_validate_add(x2, cbret)) < 0) goto done; + if (ret == 0) + goto fail; } /* deleted entries */ for (i=0; itd_dlen; i++){ x1 = td->td_dvec[i]; ys = xml_spec(x1); if (ys && yang_mandatory(ys)){ - clicon_err(OE_CFG, 0,"Removed mandatory variable: %s", - xml_name(x1)); - goto done; + if (netconf_missing_element(cbret, "protocol", xml_name(x1), "Removed mandatory variable") < 0) + goto done; + goto fail; } } /* added entries */ for (i=0; itd_alen; i++){ x2 = td->td_avec[i]; - if (xml_apply0(x2, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_add, NULL) < 0) + if ((ret = xml_yang_validate_add(x2, cbret)) < 0) goto done; + if (ret == 0) + goto fail; } - retval = 0; + // ok: + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } /*! Common code of candidate_validate and candidate_commit * @param[in] h Clicon handle * @param[in] candidate The candidate database. The wanted backend state - * @retval 0 OK - * @retval -1 Fatal error or validation fail - * @note Need to differentiate between error and validation fail + * @retval -1 Error - or validation failed (but cbret not set) + * @retval 0 Validation failed (with cbret set) + * @retval 1 Validation OK + * @note Need to differentiate between error and validation fail + * (only done for generic_validate) */ static int validate_common(clicon_handle h, char *candidate, - transaction_data_t *td) + transaction_data_t *td, + cbuf *cbret) { int retval = -1; yang_spec *yspec; int i; cxobj *xn; - + int ret; + if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; @@ -193,9 +211,12 @@ validate_common(clicon_handle h, if (plugin_transaction_begin(h, td) < 0) goto done; - /* 5. Make generic validation on all new or changed data. */ - if (generic_validate(yspec, td) < 0) + /* 5. Make generic validation on all new or changed data. + Note this is only call that uses 3-values */ + if ((ret = generic_validate(yspec, td, cbret)) < 0) goto done; + if (ret == 0) + goto fail; /* 6. Call plugin transaction validate callbacks */ if (plugin_transaction_validate(h, td) < 0) @@ -204,9 +225,12 @@ validate_common(clicon_handle h, /* 7. Call plugin transaction complete callbacks */ if (plugin_transaction_complete(h, td) < 0) goto done; - retval = 0; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } /*! Do a diff between candidate and running, then start a commit transaction @@ -216,24 +240,30 @@ validate_common(clicon_handle h, * do something more drastic? * @param[in] h Clicon handle * @param[in] candidate A candidate database, not necessarily "candidate" - * @retval 0 OK - * @retval -1 Fatal error or validation fail + * @retval -1 Error - or validation failed (but cbret not set) + * @retval 0 Validation failed (with cbret set) + * @retval 1 Validation OK * @note Need to differentiate between error and validation fail + * (only done for validate_common) */ int candidate_commit(clicon_handle h, - char *candidate) + char *candidate, + cbuf *cbret) { - int retval = -1; + int retval = -1; transaction_data_t *td = NULL; + int ret; /* 1. Start transaction */ if ((td = transaction_new()) == NULL) goto done; - /* Common steps (with validate) */ - if (validate_common(h, candidate, td) < 0) + /* Common steps (with validate). Note this is only call that uses 3-values */ + if ((ret = validate_common(h, candidate, td, cbret)) < 0) goto done; + if (ret == 0) + goto fail; /* 7. Call plugin transaction commit callbacks */ if (plugin_transaction_commit(h, td) < 0) @@ -252,21 +282,23 @@ candidate_commit(clicon_handle h, /* 9. Call plugin transaction end callbacks */ plugin_transaction_end(h, td); - /* 8. Copy running back to candidate in case end functions updated running */ if (xmldb_copy(h, "running", candidate) < 0){ /* ignore errors or signal major setback ? */ clicon_log(LOG_NOTICE, "Error in rollback, trying to continue"); goto done; } - retval = 0; + retval = 1; done: - /* In case of failure, call plugin transaction termination callbacks */ - if (retval < 0 && td) + /* In case of failure (or error), call plugin transaction termination callbacks */ + if (retval < 1 && td) plugin_transaction_abort(h, td); if (td) transaction_free(td); return retval; + fail: + retval = 0; + goto done; } /*! Commit changes from candidate to running @@ -283,6 +315,7 @@ from_client_commit(clicon_handle h, int retval = -1; int piddb; cbuf *cbx = NULL; /* Assist cbuf */ + int ret; /* Check if target locked by other client */ piddb = xmldb_islocked(h, "running"); @@ -296,9 +329,10 @@ from_client_commit(clicon_handle h, goto done; goto ok; } - if (candidate_commit(h, "candidate") < 0){ /* Assume validation fail, nofatal */ + if ((ret = candidate_commit(h, "candidate", cbret)) < 0){ /* Assume validation fail, nofatal */ clicon_debug(1, "Commit candidate failed"); - if (netconf_invalid_value(cbret, "protocol", clicon_err_reason)< 0) + if (ret < 0) + if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) goto done; goto ok; } @@ -367,6 +401,7 @@ from_client_validate(clicon_handle h, { int retval = -1; transaction_data_t *td = NULL; + int ret; if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){ if (netconf_invalid_value(cbret, "protocol", "No such database")< 0) @@ -379,11 +414,12 @@ from_client_validate(clicon_handle h, if ((td = transaction_new()) == NULL) goto done; /* Common steps (with commit) */ - if (validate_common(h, db, td) < 0){ + if ((ret = validate_common(h, db, td, cbret)) < 1){ clicon_debug(1, "Validate %s failed", db); - /* XXX: candidate_validate should have proper error handling */ - if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) - goto done; + if (ret < 0){ + if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) + goto done; + } goto ok; } /* Optionally write (potentially modified) tree back to candidate */ diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h index 6be05858a..45b0f9224 100644 --- a/apps/backend/backend_commit.h +++ b/apps/backend/backend_commit.h @@ -43,6 +43,6 @@ int from_client_validate(clicon_handle h, char *db, cbuf *cbret); int from_client_commit(clicon_handle h, int pid, cbuf *cbret); int from_client_discard_changes(clicon_handle h, int pid, cbuf *cbret); -int candidate_commit(clicon_handle h, char *db); +int candidate_commit(clicon_handle h, char *db, cbuf *cbret); #endif /* _BACKEND_COMMIT_H_ */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index ec4a99d73..8e433d855 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -386,6 +386,7 @@ startup_mode_running(clicon_handle h, char *extraxml_file) { int retval = -1; + cbuf *cbret = NULL; /* Stash original running to candidate for later commit */ if (xmldb_copy(h, "running", "candidate") < 0) @@ -405,15 +406,20 @@ startup_mode_running(clicon_handle h, /* Clear running db */ if (db_reset(h, "running") < 0) goto done; + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } /* Commit original running. Assume -1 is validate fail */ - if (candidate_commit(h, "candidate") < 0){ + if (candidate_commit(h, "candidate", cbret) < 1){ /* (1) We cannot differentiate between fatal errors and validation * failures * (2) If fatal error, we should exit * (3) If validation fails we cannot continue. How could we? * (4) Need to restore the running db since we destroyed it above */ - clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting.", __FUNCTION__); + clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting: %s.", + __FUNCTION__, cbuf_get(cbret)); /* Reinstate original */ if (xmldb_copy(h, "candidate", "running") < 0) goto done; @@ -424,6 +430,8 @@ startup_mode_running(clicon_handle h, goto done; retval = 0; done: + if (cbret) + cbuf_free(cbret); if (xmldb_delete(h, "tmp") < 0) goto done; return retval; @@ -455,6 +463,7 @@ startup_mode_startup(clicon_handle h, char *extraxml_file) { int retval = -1; + cbuf *cbret = NULL; /* Stash original running to backup */ if (xmldb_copy(h, "running", "backup") < 0) @@ -478,13 +487,19 @@ startup_mode_startup(clicon_handle h, /* Clear running db */ if (db_reset(h, "running") < 0) goto done; + /* Create return buffer (not used) */ + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } /* Commit startup */ - if (candidate_commit(h, "startup") < 0){ /* diff */ + if (candidate_commit(h, "startup", cbret) < 1){ /* diff */ /* We cannot differentiate between fatal errors and validation * failures * In both cases we copy back the original running and quit */ - clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting.", __FUNCTION__); + clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.", + __FUNCTION__, cbuf_get(cbret)); if (xmldb_copy(h, "backup", "running") < 0) goto done; goto done; @@ -494,6 +509,8 @@ startup_mode_startup(clicon_handle h, goto done; retval = 0; done: + if (cbret) + cbuf_free(cbret); if (xmldb_delete(h, "backup") < 0) goto done; if (xmldb_delete(h, "tmp") < 0) diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 55ee628b6..cc4c97105 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -83,15 +83,17 @@ static int process_incoming_packet(clicon_handle h, cbuf *cb) { - char *str; - char *str0; - cxobj *xreq = NULL; /* Request (in) */ - int isrpc = 0; /* either hello or rpc */ - cbuf *cbret = NULL; - cxobj *xret = NULL; /* Return (out) */ - cxobj *xrpc; - cxobj *xc; + int retval = -1; + char *str; + char *str0; + cxobj *xreq = NULL; /* Request (in) */ + int isrpc = 0; /* either hello or rpc */ + cbuf *cbret = NULL; + cxobj *xret = NULL; /* Return (out) */ + cxobj *xrpc; + cxobj *xc; yang_spec *yspec; + int ret; clicon_debug(1, "RECV"); clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb)); @@ -115,15 +117,12 @@ process_incoming_packet(clicon_handle h, } free(str0); if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){ - int ret; isrpc++; - if ((ret = xml_yang_validate_rpc(xrpc)) < 0) + if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0) goto done; if (ret == 0){ - if (netconf_operation_failed(cbret, "application", "Validation failed")< 0) - goto done; netconf_output(1, cbret, "rpc-error"); - goto done; + goto ok; } } else @@ -168,6 +167,8 @@ process_incoming_packet(clicon_handle h, } } } + ok: + retval = 0; done: if (xreq) xml_free(xreq); @@ -175,7 +176,7 @@ process_incoming_packet(clicon_handle h, xml_free(xret); if (cbret) cbuf_free(cbret); - return 0; + return retval; } /*! Get netconf message: detect end-of-msg @@ -229,7 +230,7 @@ netconf_input_cb(int s, /* Remove trailer */ *(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0'; if (process_incoming_packet(h, cb) < 0) - goto done; + ; //goto done; // ignore errors if (cc_closed) break; cbuf_reset(cb); diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index f53b8675a..640011373 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -876,12 +877,16 @@ netconf_application_rpc(clicon_handle h, cbuf *cb = NULL; cbuf *cbret = NULL; int ret; - + /* First check system / netconf RPC:s */ if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, 0, "cbuf_new"); goto done; } + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, 0, "cbuf_new"); + goto done; + } /* Find yang rpc statement, return yang rpc statement if found Check application RPC */ if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -917,15 +922,18 @@ netconf_application_rpc(clicon_handle h, xml_spec_set(xn, yinput); /* needed for xml_spec_populate */ if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if (xml_apply(xn, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0) + if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0) goto done; - if (xml_yang_validate_add(xn, NULL) < 0) + if (ret == 0){ + netconf_output(1, cbret, "rpc-error"); + goto ok; + } + if ((ret = xml_yang_validate_add(xn, cbret)) < 0) goto done; - } - if ((cbret = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, 0, "cbuf_new"); - goto done; + if (ret == 0){ + netconf_output(1, cbret, "rpc-error"); + goto ok; + } } /* Look for local (client-side) netconf plugins. */ if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0) @@ -943,11 +951,18 @@ netconf_application_rpc(clicon_handle h, xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if (xml_apply(xoutput, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0) + if ((ret = xml_yang_validate_all_top(xoutput, cbret)) < 0) goto done; - if (xml_yang_validate_add(xoutput, NULL) < 0) + if (ret == 0){ + clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret)); + goto ok; + } + if ((ret = xml_yang_validate_add(xoutput, cbret)) < 0) goto done; + if (ret == 0){ + clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret)); + goto ok; + } } retval = 1; /* handled by callback */ goto done; diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 75c066991..be214fbb7 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -71,11 +71,12 @@ static const map_str2int netconf_restconf_map[] = { {"missing-attribute", 400}, {"bad-attribute", 400}, {"unknown-attribute", 400}, + {"missing-element", 400}, {"bad-element", 400}, {"unknown-element", 400}, {"unknown-namespace", 400}, - {"access-denied", 401}, - {"access-denied", 403}, + {"access-denied", 401}, /* or 403 */ + {"access-denied", 403}, {"lock-denied", 409}, {"resource-denied", 409}, {"rollback-failed", 500}, @@ -436,7 +437,8 @@ api_return_err(clicon_handle h, goto ok; } tagstr = xml_body(xtag); - code = restconf_err2code(tagstr); + if ((code = restconf_err2code(tagstr)) < 0) + code = 500; /* internal server error */ if ((reason_phrase = restconf_code2reason(code)) == NULL) reason_phrase=""; if (xml_name_set(xerr, "error") < 0) @@ -448,6 +450,7 @@ api_return_err(clicon_handle h, else if (xml2json_cbuf(cb, xerr, pretty) < 0) goto done; + 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"); diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 42d4d331e..b8beb0686 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -190,7 +190,7 @@ api_data_get2(clicon_handle h, yang_spec *yspec; cxobj *xret = NULL; cxobj *xerr = NULL; /* malloced */ - cxobj *xe; + cxobj *xe = NULL; cxobj **xvec = NULL; size_t xlen; int i; @@ -206,8 +206,9 @@ api_data_get2(clicon_handle h, if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 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; } path = cbuf_get(cbpath); @@ -215,8 +216,9 @@ api_data_get2(clicon_handle h, if (clicon_rpc_get(h, path, &xret) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 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; } /* We get return via netconf which is complete tree from root @@ -434,16 +436,18 @@ api_data_post(clicon_handle h, if (xml_parse_string(data, NULL, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } } else if (json_parse_str(data, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } /* 4.4.1: The message-body MUST contain exactly one instance of the @@ -452,8 +456,9 @@ api_data_post(clicon_handle h, if (xml_child_nr(xdata) != 1){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } x = xml_child_i(xdata,0); @@ -662,16 +667,18 @@ api_data_put(clicon_handle h, if (xml_parse_string(data, NULL, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } } else if (json_parse_str(data, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } /* The message-body MUST contain exactly one instance of the @@ -680,8 +687,9 @@ api_data_put(clicon_handle h, if (xml_child_nr(xdata) != 1){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } x = xml_child_i(xdata,0); @@ -705,8 +713,9 @@ api_data_put(clicon_handle h, if (strcmp(xml_name(x), xml_name(xbot))){ if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } /* If list or leaf-list, api-path keys must match data keys */ @@ -714,8 +723,9 @@ api_data_put(clicon_handle h, if (match_list_keys((yang_stmt*)y, x, xbot) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) - goto done; + if ((xe = xpath_first(xerr, "rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } } @@ -866,7 +876,7 @@ api_data_delete(clicon_handle h, if ((xa = xml_new("operation", xbot, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); - if (xml_value_set(xa, xml_operation2str(op)) < 0) + if (xml_value_set(xa, xml_operation2str(op)) < 0) goto done; if ((cbx = cbuf_new()) == NULL) goto done; @@ -1054,7 +1064,6 @@ api_operations_post(clicon_handle h, cxobj *xdata = NULL; cxobj *xret = NULL; cxobj *xerr = NULL; /* malloced must be freed */ - cxobj *xer; /* non-malloced error */ cbuf *cbx = NULL; cxobj *xtop = NULL; /* xpath root */ cxobj *xbot = NULL; @@ -1076,13 +1085,18 @@ api_operations_post(clicon_handle h, clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, 0, "cbuf_new"); + goto done; + } for (i=0; i h ?*/ if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if (xml_apply(xbot, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0) + if ((ret = xml_yang_validate_all(xbot, cbret)) < 0) goto done; - if (xml_yang_validate_add(xbot, NULL) < 0){ - if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) + if (ret == 0){ /* validation failed */ + clicon_debug(1, "%s err: %s", __FUNCTION__, cbuf_get(cbret)); + if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0) goto done; - if (api_return_err(h, r, xerr, pretty, use_xml) < 0) + if ((xe=xpath_first(xerr, "rpc-reply/rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; + goto ok; + } + if ((ret = xml_yang_validate_add(xbot, cbret)) < 0){ + if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0) goto done; + if ((xe=xpath_first(xerr, "rpc-reply/rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; goto ok; } + if (xml_apply0(xbot, CX_ELMNT, xml_default, NULL) < 0) + goto done; } } } - if ((cbret = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, 0, "cbuf_new"); - goto done; - } + ret = 0; xe = NULL; while ((xe = xml_child_each(xtop, xe, CX_ELMNT)) != NULL) { /* Look for local (client-side) restconf plugins. */ @@ -1208,8 +1238,8 @@ api_operations_post(clicon_handle h, if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0) goto done; /* Local error: return it and quit */ - if ((xer = xpath_first(xret, "//rpc-error")) != NULL){ - if (api_return_err(h, r, xer, pretty, use_xml) < 0) + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ + if (api_return_err(h, r, xe, pretty, use_xml) < 0) goto done; goto ok; } @@ -1219,8 +1249,8 @@ api_operations_post(clicon_handle h, if (ret == 0){ /* Send to backend */ if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) goto done; - if ((xer = xpath_first(xret, "//rpc-error")) != NULL){ - if (api_return_err(h, r, xer, pretty, use_xml) < 0) + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ + if (api_return_err(h, r, xe, pretty, use_xml) < 0) goto done; goto ok; } @@ -1240,18 +1270,31 @@ api_operations_post(clicon_handle h, goto done; if ((xoutput=xpath_first(xret, "/")) != NULL){ xml_name_set(xoutput, "output"); -#if 0 - clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx)); -#endif cbuf_reset(cbx); xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; - if (xml_apply(xoutput, CX_ELMNT, - (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0) + if ((ret = xml_yang_validate_all(xoutput, cbret)) < 0) goto done; - if (xml_yang_validate_add(xoutput, NULL) < 0) + if (ret == 0){ /* validation failed */ + clicon_debug(1, "%s output validation failed", __FUNCTION__); + if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0) + goto done; + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; + goto ok; + } + if ((ret = xml_yang_validate_add(xoutput, cbret)) < 0) goto done; + if (ret == 0){ /* validation failed */ + if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0) + goto done; + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL) + if (api_return_err(h, r, xe, pretty, use_xml) < 0) + goto done; + goto ok; + } } /* Sanity check of outgoing XML */ FCGX_SetExitStatus(200, r->out); /* OK */ diff --git a/example/example_backend.c b/example/example_backend.c index 3a420a0e5..35fe20d4f 100644 --- a/example/example_backend.c +++ b/example/example_backend.c @@ -132,6 +132,7 @@ fib_route(clicon_handle h, /* Clicon handle */ cprintf(cbret, "" "ipv4" "2.3.4.5" + "static" ""); return 0; } @@ -157,16 +158,41 @@ route_count(clicon_handle h, * in [RFC6241]. */ static int -empty(clicon_handle h, /* Clicon handle */ - cxobj *xe, /* Request: */ - cbuf *cbret, /* Reply eg ... */ - void *arg, /* client_entry */ - void *regarg) /* Argument given at register */ +empty_rpc(clicon_handle h, /* Clicon handle */ + cxobj *xe, /* Request: */ + cbuf *cbret, /* Reply eg ... */ + void *arg, /* client_entry */ + void *regarg) /* Argument given at register */ { cprintf(cbret, ""); return 0; } +/*! More elaborate example RPC for testing + * The RPC returns the incoming parameters + */ +static int +example_rpc(clicon_handle h, /* Clicon handle */ + cxobj *xe, /* Request: */ + cbuf *cbret, /* Reply eg ... */ + void *arg, /* client_entry */ + void *regarg) /* Argument given at register */ +{ + int retval = -1; + cxobj *x = NULL; + + cprintf(cbret, ""); + while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) { + if (clicon_xml2cbuf(cbret, x, 0, 0) < 0) + goto done; + } + cprintf(cbret, ""); + retval = 0; + done: + return retval; +} + + /*! Called to get state data from plugin * @param[in] h Clicon handle * @param[in] xpath String with XPATH syntax. or NULL for all @@ -315,22 +341,31 @@ clixon_plugin_init(clicon_handle h) if (example_stream_timer_setup(h) < 0) goto done; - /* Register callback for routing rpc calls */ + /* Register callback for routing rpc calls + */ + if (rpc_callback_register(h, fib_route, NULL, "fib-route"/* Xml tag when callback is made */ ) < 0) goto done; + /* From ietf-routing.yang */ if (rpc_callback_register(h, route_count, NULL, "route-count"/* Xml tag when callback is made */ ) < 0) goto done; - if (rpc_callback_register(h, empty, + /* From example.yang (clicon) */ + if (rpc_callback_register(h, empty_rpc, NULL, "empty"/* Xml tag when callback is made */ ) < 0) goto done; + if (rpc_callback_register(h, example_rpc, + NULL, + "example"/* Xml tag when callback is made */ + ) < 0) + goto done; /* Return plugin API */ return &api; diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index cdf570e9e..b22e697b9 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -46,9 +46,9 @@ int netconf_too_big(cbuf *cb, char *type, char *message); 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 *info, char *message); -int netconf_bad_element(cbuf *cb, char *type, char *info, char *message); -int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message); +int netconf_missing_element(cbuf *cb, char *type, char *element, char *message); +int netconf_bad_element(cbuf *cb, char *type, char *info, char *element); +int netconf_unknown_element(cbuf *cb, 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/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 803e5948a..68f332aba 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -99,8 +99,8 @@ extern int _CLICON_XML_NS_ITERATE; char *xml_type2str(enum cxobj_type type); char *xml_name(cxobj *xn); int xml_name_set(cxobj *xn, char *name); -char *xml_namespace(cxobj *xn); -int xml_namespace_set(cxobj *xn, char *name); +char *xml_prefix(cxobj *xn); +int xml_prefix_set(cxobj *xn, char *name); int xml2ns(cxobj *x, char *localname, char **namespace); cxobj *xml_parent(cxobj *xn); int xml_parent_set(cxobj *xn, cxobj *parent); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index abd1fe50c..dba82884c 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -43,9 +43,10 @@ */ int xml2txt(FILE *f, cxobj *x, int level); int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt); -int xml_yang_validate_rpc(cxobj *xrpc); -int xml_yang_validate_add(cxobj *xt, void *arg); -int xml_yang_validate_all(cxobj *xt, void *arg); +int xml_yang_validate_rpc(cxobj *xrpc, cbuf *cbret); +int xml_yang_validate_add(cxobj *xt, cbuf *cbret); +int xml_yang_validate_all(cxobj *xt, cbuf *cbret); +int xml_yang_validate_all_top(cxobj *xt, cbuf *cbret); int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0); int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0); int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2, diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 00778c303..06aa71c95 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -315,7 +315,7 @@ xml2json1_cbuf(cbuf *cb, * Harder if x has a prefix, then that should also be translated to associated * module name */ - prefix = xml_namespace(x); + prefix = xml_prefix(x); if (xml2ns(x, prefix, &namespace) < 0) goto done; if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */ diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index ccb88275d..6c284fc6c 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -80,8 +80,8 @@ netconf_in_use(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "in-use" "%s" + "in-use" "error", type) <0) goto err; @@ -119,8 +119,8 @@ netconf_invalid_value(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "invalid-value" "%s" + "invalid-value" "error", type) <0) goto err; @@ -159,8 +159,8 @@ netconf_too_big(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "too-big" "%s" + "too-big" "error", type) <0) goto err; @@ -200,8 +200,8 @@ netconf_missing_attribute(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "missing-attribute" "%s" + "missing-attribute" "%s" "error", type, info) <0) @@ -241,8 +241,8 @@ netconf_bad_attribute(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "bad-attribute" "%s" + "bad-attribute" "%s" "error", type, info) <0) @@ -283,8 +283,8 @@ netconf_unknown_attribute(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "unknown-attribute" "%s" + "unknown-attribute" "%s" "error", type, info) <0) @@ -318,18 +318,18 @@ netconf_unknown_attribute(cbuf *cb, int netconf_missing_element(cbuf *cb, char *type, - char *info, + char *element, char *message) { int retval = -1; char *encstr = NULL; if (cprintf(cb, "" - "missing-element" "%s" - "%s" + "missing-element" + "%s" "error", - type, info) <0) + type, element) <0) goto err; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) @@ -355,24 +355,24 @@ netconf_missing_element(cbuf *cb, * pattern mismatch. * @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[in] type Error type: "application" or "protocol" - * @param[in] info bad-element xml + * @param[in] elemnt Bad element name * @param[in] message Error message */ int netconf_bad_element(cbuf *cb, char *type, - char *info, + char *element, char *message) { int retval = -1; char *encstr = NULL; if (cprintf(cb, "" - "bad-element" "%s" - "%s" + "bad-element" + "%s" "error", - type, info) <0) + type, element) <0) goto err; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) @@ -397,24 +397,24 @@ netconf_bad_element(cbuf *cb, * An unexpected element is present. * @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[in] type Error type: "application" or "protocol" - * @param[in] info bad-element xml + * @param[in] element Bad element name * @param[in] message Error message */ int netconf_unknown_element(cbuf *cb, char *type, - char *info, + char *element, char *message) { int retval = -1; char *encstr = NULL; if (cprintf(cb, "" - "unknown-element" "%s" - "%s" + "unknown-element" + "%s" "error", - type, info) <0) + type, element) <0) goto err; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) @@ -452,8 +452,8 @@ netconf_unknown_namespace(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "unknown-namespace" "%s" + "unknown-namespace" "%s" "error", type, info) <0) @@ -478,7 +478,8 @@ netconf_unknown_namespace(cbuf *cb, /*! Create Netconf access-denied error XML tree according to RFC 6241 App A * - * An expected element is missing. + * 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 @@ -492,8 +493,8 @@ netconf_access_denied(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "access-denied" "%s" + "access-denied" "error", type) <0) goto err; @@ -517,7 +518,8 @@ netconf_access_denied(cbuf *cb, /*! Create Netconf access-denied error XML tree according to RFC 6241 App A * - * An expected element is missing. + * Access to the requested protocol operation or data model is denied because + * authorization failed. * @param[out] xret Error XML tree * @param[in] type Error type: "application" or "protocol" * @param[in] message Error message @@ -538,8 +540,8 @@ netconf_access_denied_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "access-denied" - "%s" + if (xml_parse_va(&xerr, NULL, "%s" + "access-denied" "error", type) < 0) goto done; if (message && xml_parse_va(&xerr, NULL, "%s", @@ -567,8 +569,8 @@ netconf_lock_denied(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "lock-denied" "protocol" + "lock-denied" "%s" "error", info) <0) @@ -593,7 +595,7 @@ netconf_lock_denied(cbuf *cb, /*! Create Netconf resource-denied error XML tree according to RFC 6241 App A * - * An expected element is missing. + * Request could not be completed because of insufficient resources. * @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[in] type Error type: "transport, "rpc", "application", "protocol" * @param[in] message Error message @@ -607,8 +609,8 @@ netconf_resource_denied(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "resource-denied" "%s" + "resource-denied" "error", type) <0) goto err; @@ -647,8 +649,8 @@ netconf_rollback_failed(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "rollback-failed" "%s" + "rollback-failed" "error", type) <0) goto err; @@ -686,8 +688,8 @@ netconf_data_exists(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "data-exists" "application" + "data-exists" "error") <0) goto err; if (message){ @@ -724,8 +726,8 @@ netconf_data_missing(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "data-missing" "application" + "data-missing" "error") <0) goto err; if (message){ @@ -763,8 +765,8 @@ netconf_operation_not_supported(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "operation-not-supported" "%s" + "operation-not-supported" "error", type) <0) goto err; @@ -803,8 +805,8 @@ netconf_operation_failed(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "operation-failed" "%s" + "operation-failed" "error", type) <0) goto err; @@ -850,8 +852,8 @@ netconf_operation_failed_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "operation-failed" - "%s" + if (xml_parse_va(&xerr, NULL, "%s" + "operation-failed" "error", type) < 0) goto done; if (message && xml_parse_va(&xerr, NULL, "%s", @@ -879,8 +881,8 @@ netconf_malformed_message(cbuf *cb, char *encstr = NULL; if (cprintf(cb, "" - "malformed-message" "rpc" + "malformed-message" "error") <0) goto err; if (message){ @@ -925,8 +927,8 @@ netconf_malformed_message_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "malformed-message" - "rpc" + if (xml_parse_va(&xerr, NULL, "rpc" + "malformed-message" "error") < 0) goto done; if (message && xml_parse_va(&xerr, NULL, "%s", @@ -990,7 +992,6 @@ netconf_module_load(clicon_handle h) clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded"); goto done; } - /* Enable features (hardcoded here) */ if (xml_parse_string("ietf-netconf:candidate", yspec, &xc) < 0) goto done; diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 873d3aafa..45c3948bb 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -136,6 +136,8 @@ parse_configfile(clicon_handle h, char *name; char *body; clicon_hash_t *copt = clicon_options(h); + cbuf *cbret = NULL; + int ret; if (filename == NULL || !strlen(filename)){ clicon_err(OE_UNIX, 0, "Not specified"); @@ -167,8 +169,16 @@ parse_configfile(clicon_handle h, } if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0) goto done; - if (xml_apply0(xc, CX_ELMNT, xml_yang_validate_add, NULL) < 0) + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); goto done; + } + if ((ret = xml_yang_validate_add(xc, cbret)) < 0) + goto done; + if (ret == 0){ + clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret)); + goto done; + } while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) { name = xml_name(x); body = xml_body(x); diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 0cf421d2f..aa3ac4ac0 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -444,7 +444,7 @@ clicon_rpc_delete_config(clicon_handle h, char *username; username = clicon_username_get(h); - if ((msg = clicon_msg_encode("<%s/>", + if ((msg = clicon_msg_encode("<%s/>none", username?username:"", db)) == NULL) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 24bab41be..59292ba93 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -196,10 +196,9 @@ xml_name_set(cxobj *xn, /*! Get namespace of xnode * @param[in] xn xml node * @retval namespace of xml node - * XXX change to xml_localname */ char* -xml_namespace(cxobj *xn) +xml_prefix(cxobj *xn) { return xn->x_prefix; } @@ -209,11 +208,10 @@ xml_namespace(cxobj *xn) * @param[in] localname new namespace, null-terminated string, copied by function * @retval -1 on error with clicon-err set * @retval 0 OK - * XXX change to xml_localname_set */ int -xml_namespace_set(cxobj *xn, - char *localname) +xml_prefix_set(cxobj *xn, + char *localname) { if (xn->x_prefix){ free(xn->x_prefix); @@ -288,7 +286,7 @@ xmlns_check(cxobj *xn, char *xns; while ((x = xml_child_each(xn, x, CX_ATTR)) != NULL) - if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 && + if ((xns = xml_prefix(x)) && strcmp(xns, "xmlns")==0 && strcmp(xml_name(x), nsn) == 0) return xml_value(x); return NULL; @@ -311,7 +309,7 @@ xml_localname_check(cxobj *xn, yang_stmt *ys = xml_spec(xn); /* No namespace name - comply */ - if ((nsn = xml_namespace(xn)) == NULL) + if ((nsn = xml_prefix(xn)) == NULL) return 0; /* Check if NSN defined in same node */ if (xmlns_check(xn, nsn) != NULL) @@ -965,7 +963,7 @@ xml_find_type_value(cxobj *xt, char *xprefix; /* xprefix */ while ((x = xml_child_each(xt, x, type)) != NULL) { - xprefix = xml_namespace(x); + xprefix = xml_prefix(x); if (prefix) pmatch = xprefix?strcmp(prefix,xprefix)==0:0; else @@ -1121,7 +1119,7 @@ clicon_xml2file(FILE *f, if (x == NULL) goto ok; name = xml_name(x); - namespace = xml_namespace(x); + namespace = xml_prefix(x); switch(xml_type(x)){ case CX_BODY: if ((val = xml_value(x)) == NULL) /* incomplete tree */ @@ -1246,7 +1244,7 @@ clicon_xml2cbuf(cbuf *cb, char *val; name = xml_name(x); - namespace = xml_namespace(x); + namespace = xml_prefix(x); switch(xml_type(x)){ case CX_BODY: if ((val = xml_value(x)) == NULL) /* incomplete tree */ @@ -1333,10 +1331,10 @@ xmltree2cbuf(cbuf *cb, cprintf(cb, " "); if (xml_type(x) != CX_BODY) cprintf(cb, "%s", xml_type2str(xml_type(x))); - if (xml_namespace(x)==NULL) + if (xml_prefix(x)==NULL) cprintf(cb, " %s", xml_name(x)); else - cprintf(cb, " %s:%s", xml_namespace(x), xml_name(x)); + cprintf(cb, " %s:%s", xml_prefix(x), xml_name(x)); if (xml_value(x)) cprintf(cb, " value:\"%s\"", xml_value(x)); if (x->x_flags) @@ -1612,8 +1610,8 @@ xml_copy_one(cxobj *x0, if ((s = xml_name(x0))) /* malloced string */ if ((xml_name_set(x1, s)) < 0) return -1; - if ((s = xml_namespace(x0))) /* malloced string */ - if ((xml_namespace_set(x1, s)) < 0) + if ((s = xml_prefix(x0))) /* malloced string */ + if ((xml_prefix_set(x1, s)) < 0) return -1; return 0; } diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 6794b503c..2263bde27 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -87,6 +87,7 @@ #include "clixon_xpath.h" #include "clixon_log.h" #include "clixon_err.h" +#include "clixon_netconf_lib.h" #include "clixon_xml_sort.h" #include "clixon_xml_map.h" @@ -230,10 +231,15 @@ xml2cli(FILE *f, /*! Validate xml node of type leafref, ensure the value is one of that path's reference * @param[in] xt XML leaf node of type leafref * @param[in] ytype Yang type statement belonging to the XML node + * @param[out] cbret Error buffer + * @retval 1 Validation OK + * @retval 0 Validation failed + * @retval -1 Error */ static int validate_leafref(cxobj *xt, - yang_stmt *ytype) + yang_stmt *ytype, + cbuf *cbret) { int retval = -1; yang_stmt *ypath; @@ -247,8 +253,9 @@ validate_leafref(cxobj *xt, if ((leafrefbody = xml_body(xt)) == NULL) goto ok; if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){ - clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument); - goto done; + if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0) + goto done; + goto fail; } if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0) goto done; @@ -260,9 +267,9 @@ validate_leafref(cxobj *xt, break; } if (i==xlen){ - clicon_err(OE_DB, 0, "Leafref validation failed, no such leaf: %s", - leafrefbody); - goto done; + if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0) + goto done; + goto fail; } ok: retval = 0; @@ -270,6 +277,9 @@ validate_leafref(cxobj *xt, if (xvec) free(xvec); return retval; + fail: + retval = 0; + goto done; } /*! Validate xml node of type identityref, ensure value is a defined identity @@ -285,14 +295,18 @@ validate_leafref(cxobj *xt, * @param[in] xt XML leaf node of type identityref * @param[in] ys Yang spec of leaf * @param[in] ytype Yang type field of type identityref + * @param[out] cbret Error buffer + * @retval 1 Validation OK + * @retval 0 Validation failed + * @retval -1 Error * @see ys_populate_identity where the derived types are set * @see RFC7950 Sec 9.10.2: - */ static int validate_identityref(cxobj *xt, yang_stmt *ys, - yang_stmt *ytype) + yang_stmt *ytype, + cbuf *cbret) { int retval = -1; char *node; @@ -305,37 +319,46 @@ validate_identityref(cxobj *xt, * Always add default prefix because derived identifiers are stored with * prefixes in the base identifiers derived-list. */ + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } if ((node = xml_body(xt)) == NULL) return 0; if (strchr(node, ':') == NULL){ prefix = yang_find_myprefix(ys); - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } cprintf(cb, "%s:%s", prefix, node); node = cbuf_get(cb); } /* This is the type's base reference */ if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){ - clicon_err(OE_DB, 0, "Identityref validation failed, no base"); - goto done; + if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0) + goto done; + goto fail; } /* This is the actual base identity */ if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){ - clicon_err(OE_DB, 0, "Identityref validation failed, no base identity"); - goto done; + if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0) + goto done; + goto fail; } /* Here check if node is in the derived node list of the base identity */ if (cvec_find(ybaseid->ys_cvec, node) == NULL){ - clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument); - goto done; + cbuf_reset(cb); + cprintf(cb, "Identityref validation failed, %s not derived from %s", + node, ybaseid->ys_argument); + if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0) + goto done; + goto fail; } - retval = 0; + retval = 1; done: if (cb) cbuf_free(cb); return retval; + fail: + retval = 0; + goto done; } /*! Validate an RPC node @@ -377,24 +400,33 @@ validate_identityref(cxobj *xt, * the same order as they are defined within the "output" statement. */ int -xml_yang_validate_rpc(cxobj *xrpc) +xml_yang_validate_rpc(cxobj *xrpc, + cbuf *cbret) { int retval = -1; yang_stmt *yn=NULL; /* rpc name */ cxobj *xn; /* rpc name */ - yang_stmt *yi=NULL; /* input name */ - cxobj *xi; /* input name */ + int ret; - assert(strcmp(xml_name(xrpc), "rpc")==0); + if (strcmp(xml_name(xrpc), "rpc")){ + clicon_err(OE_XML, EINVAL, "Expected RPC"); + goto done; + } xn = NULL; + /* xn is name of rpc, ie */ while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) { if ((yn = xml_spec(xn)) == NULL) goto fail; - xi = NULL; - while ((xi = xml_child_each(xn, xi, CX_ELMNT)) != NULL) { - if ((yi = xml_spec(xi)) == NULL) - goto fail; - } + if ((ret = xml_yang_validate_all(xn, cbret)) < 0) + goto fail; + if (ret == 0) + goto fail; + if ((ret = xml_yang_validate_add(xn, cbret)) < 0) + goto fail; + if (ret == 0) + goto fail; + if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0) + goto done; } // ok: /* pass validation */ retval = 1; @@ -408,14 +440,24 @@ xml_yang_validate_rpc(cxobj *xrpc) /*! Validate a single XML node with yang specification for added entry * 1. Check if mandatory leafs present as subs. * 2. Check leaf values, eg int ranges and string regexps. - * @param[in] xt XML node to be validated - * @retval 0 Valid OK - * @retval -1 Validation failed + * @param[in] xt XML node to be validated + * @param[out] cbret Error buffer + * @retval 1 Validation OK + * @retval 0 Validation failed + * @retval -1 Error + * @code + * cxobj *x; + * cbuf *cbret = cbuf_new(); + * if ((ret = xml_yang_validate_add(x, cbret)) < 0) + * err; + * if (ret == 0) + * fail; + * @endcode * @see xml_yang_validate_all */ int xml_yang_validate_add(cxobj *xt, - void *arg) + cbuf *cbret) { int retval = -1; cg_var *cv = NULL; @@ -424,11 +466,14 @@ xml_yang_validate_add(cxobj *xt, int i; yang_stmt *ys; char *body; + int ret; + cxobj *x; /* if not given by argument (overide) use default link and !Node has a config sub-statement and it is false */ if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){ switch (ys->ys_keyword){ + case Y_RPC: case Y_INPUT: case Y_LIST: /* fall thru */ @@ -440,9 +485,9 @@ xml_yang_validate_add(cxobj *xt, if (yang_config(yc)==0) continue; if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){ - clicon_err(OE_CFG, 0,"Missing mandatory variable: %s", - yc->ys_argument); - goto done; + if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0) + goto done; + goto fail; } } break; @@ -463,12 +508,11 @@ xml_yang_validate_add(cxobj *xt, goto done; } if ((ys_cv_validate(cv, ys, &reason)) != 1){ - clicon_err(OE_DB, 0, - "validation of %s failed %s", - ys->ys_argument, reason?reason:""); + if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0) + goto done; if (reason) free(reason); - goto done; + goto fail; } } break; @@ -476,28 +520,43 @@ xml_yang_validate_add(cxobj *xt, break; } } - retval = 0; + x = NULL; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { + if ((ret = xml_yang_validate_add(x, cbret)) < 0) + goto done; + if (ret == 0) + goto fail; + } + retval = 1; done: if (cv) cv_free(cv); return retval; + fail: + retval = 0; + goto done; } /*! Validate a single XML node with yang specification for all (not only added) entries * 1. Check leafrefs. Eg you delete a leaf and a leafref references it. * @param[in] xt XML node to be validated - * @param[in] arg Not used - * @retval -1 Validation failed - * @retval 0 Validation OK + * @param[out] cbret Error buffer + * @retval 1 Validation OK + * @retval 0 Validation failed + * @retval -1 Error * @see xml_yang_validate_add * @code - * if (xml_apply(x, CX_ELMNT, (xml_applyfn_t*)xml_yang_validate_all, 0) < 0) + * cxobj *x; + * cbuf *cbret = cbuf_new(); + * if ((ret = xml_yang_validate_all(x, cbret)) < 0) * err; + * if (ret == 0) + * fail; * @endcode */ int xml_yang_validate_all(cxobj *xt, - void *arg) + cbuf *cbret) { int retval = -1; yang_stmt *ys; /* yang node */ @@ -505,13 +564,24 @@ xml_yang_validate_all(cxobj *xt, yang_stmt *ye; /* yang must error-message */ char *xpath; int nr; - + int ret; + cxobj *x; + /* if not given by argument (overide) use default link and !Node has a config sub-statement and it is false */ - if ((ys = xml_spec(xt)) != NULL && - yang_config(ys) != 0){ + ys=xml_spec(xt); + if (ys==NULL){ + if (netconf_unknown_element(cbret, "application", xml_name(xt), NULL) < 0) + goto done; + goto fail; + } + if (ys != NULL && yang_config(ys) != 0){ /* Node-specific validation */ switch (ys->ys_keyword){ + case Y_ANYXML: + case Y_ANYDATA: + goto ok; + break; case Y_LEAF: /* fall thru */ case Y_LEAF_LIST: @@ -520,11 +590,11 @@ xml_yang_validate_all(cxobj *xt, */ if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){ if (strcmp(yc->ys_argument, "leafref") == 0){ - if (validate_leafref(xt, yc) < 0) + if (validate_leafref(xt, yc, cbret) < 0) goto done; } else if (strcmp(yc->ys_argument, "identityref") == 0){ - if (validate_identityref(xt, ys, yc) < 0) + if (validate_identityref(xt, ys, yc, cbret) < 0) goto done; } } @@ -568,11 +638,11 @@ xml_yang_validate_all(cxobj *xt, if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0) goto done; if (!nr){ - if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL) - clicon_err(OE_DB, 0, "%s", ye->ys_argument); - else - clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); - goto done; + ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL); + if (netconf_operation_failed(cbret, "application", + ye?ye->ys_argument:"must xpath validation failed") < 0) + goto done; + goto fail; } } /* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ @@ -581,14 +651,42 @@ xml_yang_validate_all(cxobj *xt, if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0) goto done; if (!nr){ - clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); - goto done; + if (netconf_operation_failed(cbret, "application", + "when xpath validation failed") < 0) + goto done; + goto fail; } } } - retval = 0; + x = NULL; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { + if ((ret = xml_yang_validate_all(x, cbret)) < 0) + goto done; + if (ret == 0) + goto fail; + } + ok: + retval = 1; done: return retval; + fail: + retval = 0; + goto done; +} + +int +xml_yang_validate_all_top(cxobj *xt, + cbuf *cbret) +{ + int ret; + cxobj *x; + + x = NULL; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { + if ((ret = xml_yang_validate_all(x, cbret)) < 1) + return ret; + } + return 1; } /*! Translate a single xml node to a cligen variable vector. Note not recursive @@ -1310,6 +1408,11 @@ xml_tree_prune_flagged(cxobj *xt, /*! Add default values (if not set) * @param[in] xt XML tree with some node marked + * @param[in] arg Ignored + * Typically called in a recursive apply function: + * @code + * xml_apply(xt, CX_ELMNT, xml_default, NULL); + * @endcode */ int xml_default(cxobj *xt, @@ -1328,7 +1431,8 @@ xml_default(cxobj *xt, goto done; } /* Check leaf defaults */ - if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){ + if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST || + ys->ys_keyword == Y_INPUT){ for (i=0; iys_len; i++){ y = ys->ys_stmt[i]; if (y->ys_keyword != Y_LEAF) @@ -1493,9 +1597,9 @@ xml_spec_populate_rpc(clicon_handle h, yang_stmt *y=NULL; /* yang node */ yang_stmt *ymod=NULL; /* yang module */ yang_stmt *yi = NULL; /* input */ - yang_stmt *ya = NULL; /* arg */ + // yang_stmt *ya = NULL; /* arg */ cxobj *x; - cxobj *xi; + // cxobj *xi; int i; if ((strcmp(xml_name(xrpc), "rpc"))!=0){ @@ -1521,11 +1625,18 @@ xml_spec_populate_rpc(clicon_handle h, if (y){ xml_spec_set(x, y); if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){ + /* kludge rpc -> input */ + xml_spec_set(x, yi); +#if 1 + if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0) + goto done; +#else xi = NULL; while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) { if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL) xml_spec_set(xi, ya); - } + } +#endif } } } diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index 7072ed018..7600d249d 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -172,7 +172,7 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya, goto done; if ((x = xml_new(name, xp, y)) == NULL) goto done; - if (xml_namespace_set(x, prefix) < 0) + if (xml_prefix_set(x, prefix) < 0) goto done; ya->ya_xelement = x; retval = 0; @@ -223,9 +223,9 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, xml_name(x), name); goto done; } - if (xml_namespace(x)!=NULL){ + if (xml_prefix(x)!=NULL){ clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s", - xml_namespace(x), xml_name(x), name); + xml_prefix(x), xml_name(x), name); goto done; } /* Strip pretty-print. Ad-hoc algorithm @@ -263,16 +263,16 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya, if (strcmp(xml_name(x), name)){ clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", - xml_namespace(x), + xml_prefix(x), xml_name(x), namespace, name); goto done; } - if (xml_namespace(x)==NULL || - strcmp(xml_namespace(x), namespace)){ + if (xml_prefix(x)==NULL || + strcmp(xml_prefix(x), namespace)){ clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", - xml_namespace(x), + xml_prefix(x), xml_name(x), namespace, name); @@ -324,7 +324,7 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya, if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); - if (prefix && xml_namespace_set(xa, prefix) < 0) + if (prefix && xml_prefix_set(xa, prefix) < 0) goto done; if (xml_value_set(xa, attval) < 0) goto done; diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 7e68d2b83..befb5bcb8 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -118,6 +118,9 @@ xml_child_spec(char *name, } else y = NULL; + /* kludge rpc -> input */ + if (y && y->ys_keyword == Y_RPC && yang_find((yang_node*)y, Y_INPUT, NULL)) + y = yang_find((yang_node*)y, Y_INPUT, NULL); *yresult = y; retval = 0; done: diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 8b9af6d05..68c9ce9fe 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -862,7 +862,7 @@ ys_module_by_xml(yang_spec *ysp, if (ymodp) *ymodp = NULL; - prefix = xml_namespace(xt); + prefix = xml_prefix(xt); if (prefix){ /* Get namespace for prefix */ if (xml2ns(xt, prefix, &namespace) < 0) diff --git a/test/test_cli.sh b/test/test_cli.sh index 3bf4af77b..aa1379d95 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -13,8 +13,10 @@ APPNAME=example . ./lib.sh cfg=$dir/conf_yang.xml +# Use yang in example + cat < $cfg - + $cfg /usr/local/share/$APPNAME/yang /usr/local/share/clixon @@ -115,7 +117,7 @@ expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" 0 "^$" new "cli rpc" expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 "ipv4" "2.3.4.5" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_feature.sh b/test/test_feature.sh index 4bbb334ca..dfb991727 100755 --- a/test/test_feature.sh +++ b/test/test_feature.sh @@ -96,8 +96,7 @@ new "netconf validate enabled feature" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf disabled feature" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^operation-failedapplicationerrorValidation failed]]>]]>$' -#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^operation-failedprotocolerrorXML node config/A has no corresponding yang specification (Invalid XML or wrong Yang spec?' +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^applicationoperation-failederrorValidation failed]]>]]>$' # This test has been broken up into all different modules instead of one large # reply since the modules change so often @@ -162,7 +161,7 @@ if [ -z "$match" ]; then err "$expect" "$ret" fi -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_identity.sh b/test/test_identity.sh index 1acbc7645..14d6b9963 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -162,7 +162,7 @@ new "Set crypto to foo:bar" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo:bar]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationoperation-failederrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$" new "cli set crypto to mc:aes" expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto mc:aes" 0 "^$" @@ -182,7 +182,7 @@ expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto des:des3" 0 "^$" new "cli validate" expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 72faaa4eb..6e66c7f35 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -105,8 +105,8 @@ expecteof "$clixon_netconf -qf $cfg" 0 'eth3
10.0.4.6
]]>]]>" "^]]>]]>$" -new "leafref validate XXX shouldnt really be operation-failed, more work in validate code" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failed" +new "leafref validate" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" '^applicationbad-elementeth3errorLeafref validation failed: No such leaf]]>]]>$' new "leafref discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" @@ -126,7 +126,7 @@ new "leafref delete leaf" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0]]>]]>" "^" new "leafref validate (should fail)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failed" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" '^applicationbad-elementeth0errorLeafref validation failed: No such leaf]]>]]>$' new "leafref discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" @@ -143,7 +143,7 @@ expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" 0 "^$" new "cli sender template" expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" 0 "^$" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_list.sh b/test/test_list.sh index 7e42a23bd..37fb8c6c0 100755 --- a/test/test_list.sh +++ b/test/test_list.sh @@ -125,7 +125,7 @@ new "minmax: validate should fail" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" fi # NYI -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_nacm.sh b/test/test_nacm.sh index c22159c23..afd1c29d0 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -141,10 +141,10 @@ new "commit it" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new2 "auth get (no user: access denied)" -expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (wrong passwd: access denied)" -expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (access)" expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} @@ -164,21 +164,21 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "admin edit nacm" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" new2 "limited edit nacm" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new2 "guest edit nacm" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index 1db3949f5..2701b2dab 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -170,10 +170,10 @@ new "Set x to 0" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 0}' http://localhost/restconf/data/x)" "" new2 "auth get (no user: access denied)" -expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (wrong passwd: access denied)" -expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' +expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (access)" expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} @@ -188,16 +188,16 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "admin edit nacm" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" new2 "limited edit nacm" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new2 "guest edit nacm" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "cli show conf as admin" expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" @@ -220,7 +220,7 @@ expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index 2ab70b4b6..aebaa8433 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 -D 1 -- -a" -s /bin/sh www-data & +sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data & sleep $RCWAIT @@ -166,20 +166,20 @@ expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x" # Rule 1: deny-kill-session new "deny-kill-session: limited fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "44]]>]]>" "^access-deniedprotocolerroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new "deny-kill-session: guest fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "44]]>]]>" "^access-deniedprotocolerroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new "deny-kill-session: admin ok (netconf)" expecteof "$clixon_netconf -qf $cfg -y $fyang -U andy" 0 "44]]>]]>" "^]]>]]>$" # Rule 2: deny-delete-config new "deny-delete-config: limited fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "]]>]]>" "^access-deniedprotocolerroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new2 "deny-delete-config: guest fail (restconf)" -expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' # In restconf delete-config is translated to edit-config which is permitted new "deny-delete-config: limited fail (restconf) ok" @@ -207,12 +207,12 @@ new "permit-edit-config: limited ok restconf" expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '' new2 "permit-edit-config: guest fail restconf" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_netconf.sh b/test/test_netconf.sh index a0eae7d80..7b060875b 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -209,7 +209,7 @@ new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf edit state operation should fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42]]>]]>" "^protocolinvalid-value" new "netconf get state operation" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^42]]>]]>$" @@ -254,7 +254,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" new "netconf client-side rpc" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "example]]>]]>" "^ok]]>]]>$" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_order.sh b/test/test_order.sh index a06a036b8..cb9eb7801 100755 --- a/test/test_order.sh +++ b/test/test_order.sh @@ -139,7 +139,7 @@ new "get each ordered-by user leaf-list" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^bbar]]>]]>$" new "delete candidate" -expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 'none]]>]]>' "^]]>]]>$" # LEAF_LISTS @@ -172,7 +172,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^cbarbfooafie]]>]]>$" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_perf.sh b/test/test_perf.sh index d03fcbdee..66c7a5b1e 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -173,7 +173,7 @@ expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "< new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index f98b87e42..03d20bd93 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -52,14 +52,6 @@ module example{ } rpc empty { } - rpc input { - input { - } - } - rpc output { - output { - } - } rpc client-rpc { description "Example local client-side rpc"; input { @@ -104,12 +96,15 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo su -c "$clixon_restconf -f $cfg -y $fyang -D 1" -s /bin/sh www-data & +sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data & sleep $RCWAIT new "restconf tests" +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 root discovery. RFC 8040 3.1 (xml+xrd)" expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" " @@ -125,12 +120,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r ' new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)" -expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:input": null,"example:output": null,"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null}} +expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null,"example:empty": null}} ' new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) -expect='' +expect='' match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" @@ -161,6 +156,9 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK" new "restconf empty rpc" expecteq "$(curl -s -X POST -d {\"input\":null} http://localhost/restconf/operations/example:empty)" "" +new2 "restconf empty rpc with extra args (should fail)" +expecteq "$(curl -s -X POST -d {\"input\":{\"extra\":null}} http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}} ' + new2 "restconf get empty config + state json" expecteq "$(curl -sSG http://localhost/restconf/data/state)" '{"state": {"op": "42"}} ' @@ -170,7 +168,7 @@ expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"state": ' new2 "restconf get empty config + state json with wrong module name" -expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "No yang node found: badmodule:state"}}}} ' +expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No yang node found: badmodule:state"}}} ' new "restconf get empty config + state xml" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state) @@ -215,10 +213,10 @@ new "restconf Add subtree to datastore using POST" expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK' new "restconf Re-add subtree which should give error" -expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' +expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' # XXX Cant get this to work -#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' +#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}' new "restconf Check interfaces eth/0/0 added" expectfn "curl -s -G http://localhost/restconf/data" 0 '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]},"state": {"op": "42"}} @@ -244,13 +242,13 @@ expecteq "$(curl -s -G http://localhost/restconf/data/state)" '{"state": {"op": ' new2 "restconf Re-post eth/0/0 which should generate error" -expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "Add leaf description using POST" expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" new "Add nothing using POST" -expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:' +expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": " on line 1: syntax error at or before:' new2 "restconf Check description added" expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}} @@ -263,7 +261,7 @@ new "Check deleted eth/0/0" expectfn 'curl -s -G http://localhost/restconf/data' 0 $state new2 "restconf Re-Delete eth/0/0 using none should generate error" -expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-missing","error-type": "application","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} ' +expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}} ' new "restconf Add subtree eth/0/0 using PUT" expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" "" @@ -273,37 +271,38 @@ expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces ' new2 "restconf rpc using POST json" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}} +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4","destination-address":{"address-family":"ipv6"}}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"},"source-protocol": "static"}}} ' # Cant get this to work due to quoting #new2 "restconf rpc using POST wrong JSON" -#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}} ' +#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}} ' -new2 "restconf rpc using POST json w/o mandatory element" -expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' -new2 "restconf rpc non-existing rpc w/o namespace" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}} ' +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"}}} ' new2 "restconf rpc non-existing rpc" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","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": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang node not found"}}} ' new2 "restconf rpc missing name" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","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"}}} ' new2 "restconf rpc missing input" -expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}} ' +expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "routing-instance-name"},"error-severity": "error","error-message": "Mandatory variable"}}} ' new "restconf rpc using POST xml" ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route) -expect="ipv42.3.4.5" +expect="ipv42.3.4.5static" match=`echo $ret | grep -EZo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new2 "restconf rpc using wrong prefix" -expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang module not found"}}}} ' +expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}} ' new "restconf local client rpc using POST xml" ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc) @@ -321,7 +320,7 @@ fi new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index dbddee093..ee8200822 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -84,16 +84,16 @@ new "restconf GET if-type" expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" 0 '{"type": "regular"}' new "restconf POST interface without mandatory type" -expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' 0 '"error-message": "Missing mandatory variable: type"' +expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "type"},"error-severity": "error","error-message": "Mandatory variable"}}} ' new "restconf POST interface" expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 "" new2 "restconf POST again" -expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/cont1)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/cont1)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new2 "restconf POST from top" -expecteq "$(curl -s -X POST -d '{"cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' +expecteq "$(curl -s -X POST -d '{"cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}} ' new "restconf DELETE" expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 "" @@ -138,12 +138,12 @@ new "restconf PUT add interface" expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 "" new "restconf PUT change key error" -expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "api-path keys do not match data keys"}}}}' +expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_rpc.sh b/test/test_rpc.sh new file mode 100755 index 000000000..b1b042518 --- /dev/null +++ b/test/test_rpc.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# RPC tests +# Validate parameters in restconf and netconf, check namespaces, etc +# See rfc8040 3.6 +APPNAME=example + +# include err() and new() functions and creates $dir +. ./lib.sh +cfg=$dir/conf.xml + +# Use yang in example +cat < $cfg + + $cfg + /usr/local/share/$APPNAME/yang + /usr/local/share/clixon + $APPNAME + /usr/local/lib/$APPNAME/backend + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + false + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + +EOF + +new "test params: -f $cfg" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + sudo $clixon_backend -s init -f $cfg -D $DBG + if [ $? -ne 0 ]; then + err + fi +fi + +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 & + +sleep $RCWAIT + +new "rpc tests" + +# 1.First some positive tests vary media types +# +new2 "restconf example rpc json/json default - no headers" +expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}} + ' + +new2 "restconf example rpc json/json change y default" +expecteq "$(curl -s -X POST -d '{"input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "99"}} + ' + +new2 "restconf example rpc json/json" +# XXX example:input example:output +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}} + ' + +new2 "restconf example rpc xml/json" +expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '0' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}} + ' + +new2 "restconf example rpc json/xml" +# 042 + ' + +new2 "restconf example rpc xml/xml" +# 0' http://localhost/restconf/operations/example:example)" '042 + ' + +new "netconf example rpc" +expecteof "$clixon_netconf -qf $cfg" 0 '0]]>]]>' '^042]]>]]>$' + +# 2. Then error cases +# +new2 "restconf omit mandatory" +expecteq "$(curl -s -X POST -d '{"input":null}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}} ' + +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"}}} ' + +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$' + +# edit-config? + +new "Kill restconf daemon" +sudo pkill -u www-data -f "/www-data/clixon_restconf" + +if [ $BE -eq 0 ]; then + exit # BE +fi + +new "Kill backend" +# Check if premature kill +pid=`pgrep -u root -f clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -z -f $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir diff --git a/test/test_stream.sh b/test/test_stream.sh index 910a29c8b..19ad9802c 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -150,10 +150,10 @@ new "netconf EXAMPLE subscription with filter classifier" expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE]]>]]>" '^]]>]]>20' $NCWAIT new "netconf NONEXIST subscription" -expectwait "$clixon_netconf -qf $cfg -y $fyang" 'NONEXIST]]>]]>' '^invalid-valueapplicationerrorNo such stream]]>]]>$' $NCWAIT +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]]>]]>' '^bad-elementapplicationstartTimeerrorExpected timestamp]]>]]>$' 0 +expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLEkallekaka]]>]]>' '^applicationbad-elementstartTimeerrorExpected timestamp]]>]]>$' 0 #new "netconf EXAMPLE subscription with replay" #NOW=$(date +"%Y-%m-%dT%H:%M:%S") @@ -176,7 +176,7 @@ sleep 2 # Restconf stream subscription RFC8040 Sec 6.3 # Start Subscription w error new "restconf monitor event nonexist stream" -expectwait 'curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" http://localhost/streams/NOTEXIST' 0 'invalid-valueapplicationerrorNo such stream' 2 +expectwait 'curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" http://localhost/streams/NOTEXIST' 0 'applicationinvalid-valueerrorNo such stream' 2 # 2a) start subscription 8s - expect 1-2 notifications new "2a) start subscriptions 8s - expect 1-2 notifications" @@ -278,7 +278,7 @@ sleep 5 new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_type.sh b/test/test_type.sh index 4ff09bbc4..1261d2ba2 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -217,7 +217,7 @@ new "netconf set transitive string error" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "9xx]]>]]>" "^]]>]]>" new "netconf validate should fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorvalidation of talle failed regexp match fail" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" 'applicationbad-elementtalleerrorregexp match fail: "9xx" does not match \[a-z\]\[0-9\]\*]]>]]>$' new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -244,7 +244,7 @@ new "netconf set transitive union error int" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "55]]>]]>" "^]]>]]>" new "netconf validate should fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorvalidation of ulle failed" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationbad-elementulleerror'55' does not match enumeration]]>]]>$" new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -303,7 +303,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^directr2staticr1]]>]]>$" new "when: validate fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorxpath static-routes validation failed]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationoperation-failederrorwhen xpath validation failed]]>]]>$" new "when: discard-changes" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -133,15 +133,15 @@ new "must: add atm interface" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "atm32]]>]]>" "^]]>]]>$" new "must: atm validate fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn ATM MTU must be 64 .. 17966]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationoperation-failederrorAn ATM MTU must be 64 .. 17966]]>]]>$" new "must: add eth interface" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet989]]>]]>" "^]]>]]>$" new "must: eth validate fail" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^operation-failedapplicationerrorAn Ethernet MTU must be 1500]]>]]>" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^applicationoperation-failederrorAn Ethernet MTU must be 1500]]>]]>" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_yang.sh b/test/test_yang.sh index 9a3f7cfe4..1d953dc55 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -212,7 +212,7 @@ expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 "" new "cli show leaf-list" expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "foo" new "netconf set state data (not allowed)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42]]>]]>" "^invalid-value" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42]]>]]>" "^protocolinvalid-value" new "netconf set presence and not present" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -233,7 +233,7 @@ new "netconf validate anyxml" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" new "netconf delete candidate" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'none]]>]]>' "^]]>]]>$" # Check 3-keys new "netconf add one 3-key entry" @@ -262,7 +262,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'none]]>]]>' "^]]>]]>$" new "netconf commit empty candidate" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" @@ -279,7 +279,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_yang_load.sh b/test/test_yang_load.sh index 9bcae5fac..90fc10e42 100755 --- a/test/test_yang_load.sh +++ b/test/test_yang_load.sh @@ -88,12 +88,12 @@ new "1. Set newex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set oldex should fail (since oldex is in old revision and only the new is loaded)" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedapplicationerrorValidation failed]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederrorValidation failed]]>]]>$' new "Set other should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedapplicationerrorValidation failed]]>]]>$' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederrorValidation failed]]>]]>$' -if [ $BE -ne 0 ]; then +if [ $BE -eq 0 ]; then exit # BE fi @@ -140,10 +140,10 @@ new "Set oldex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set newex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederrorValidation failed]]>]]>$' new "Set other should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederrorValidation failed]]>]]>$' new "Kill backend" # Check if premature kill @@ -184,10 +184,10 @@ new "Set newex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set oldex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Kill backend" # Check if premature kill @@ -229,10 +229,10 @@ new "Set oldex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set newex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Kill backend" # Check if premature kill @@ -273,7 +273,7 @@ new "Set newex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set oldex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' @@ -318,7 +318,7 @@ new "Set oldex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set newex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' @@ -365,7 +365,7 @@ new "Set oldex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set newex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' @@ -411,10 +411,10 @@ new "Set oldex" expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^]]>]]>$' new "Set newex should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Set other should fail" -expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^operation-failedprotocolerrorXML' +expecteof "$clixon_netconf -qf $cfg" 0 'str]]>]]>' '^applicationoperation-failederror' new "Kill backend" # Check if premature kill diff --git a/test/test_yang_namespace.sh b/test/test_yang_namespace.sh index 215e91f31..19447f803 100755 --- a/test/test_yang_namespace.sh +++ b/test/test_yang_namespace.sh @@ -122,7 +122,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "