* 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)
This commit is contained in:
Olof hagsand 2018-12-21 01:33:41 +01:00
parent 03e618b1e5
commit f872c7e295
45 changed files with 807 additions and 405 deletions

View file

@ -6,7 +6,6 @@
* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized) * [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
### Major New features ### Major New features
* NACM extension (RFC8341) * NACM extension (RFC8341)
* NACM module support (RFC8341 A1+A2) * NACM module support (RFC8341 A1+A2)
* Recovery user "_nacm_recovery" added. * Recovery user "_nacm_recovery" added.
@ -20,6 +19,8 @@
* Support of submodule, include and belongs-to. * Support of submodule, include and belongs-to.
* Openconfig yang specs parsed: https://github.com/openconfig/public * Openconfig yang specs parsed: https://github.com/openconfig/public
* Improved "unknown" handling * Improved "unknown" handling
* More precise Yang validation and better error messages
* For Example, adding bad-, missing-, or unknown-element error messages, etc instead of operation-failed
* Yang load file configure options changed * Yang load file configure options changed
* `CLICON_YANG_DIR` is changed from a single directory to a path of directories * `CLICON_YANG_DIR` is changed from a single directory to a path of directories
* Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list * Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
@ -39,6 +40,8 @@
* See https://github.com/clicon/clixon/issues/49 * See https://github.com/clicon/clixon/issues/49
### API changes on existing features (you may need to change your code) ### 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. * Yang parser is stricter (see above) which may break parsing of existing yang specs.
* XML namespace handling is corrected (see above) * XML namespace handling is corrected (see above)
* For backward compatibility set config option CLICON_XML_NS_ITERATE * 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 * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
### Minor changes ### Minor changes
* Added example_rpc RPC to example backend
* Renamed xml_namespace[_set]() to xml_prefix[_set]()
* Changed all make tags --> make TAGS * Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3) * Keyvalue datastore removed (it has been disabled since 3.3.3)
* Removed return value ymodp from yang parse functions (eg yang_parse()). * Removed return value ymodp from yang parse functions (eg yang_parse()).
@ -60,7 +65,9 @@
* <!DOCTYPE (ie DTD) is not supported. * <!DOCTYPE (ie DTD) is not supported.
### Corrected Bugs ### Corrected Bugs
* Some restconf error messages contained "rpc-reply" or "rpc-error" which have now been removed.
* getopt return value changed from char to int (https://github.com/clicon/clixon/issues/58) * getopt return value changed from char to int (https://github.com/clicon/clixon/issues/58)
* Netconf/Restconf RPC extra input arguments are ignored (https://github.com/clicon/clixon/issues/47)
### Known issues ### Known issues
* debug rpc added in example application (should be in clixon-config). * debug rpc added in example application (should be in clixon-config).

View file

@ -125,8 +125,9 @@ However, the following YANG syntax modules are not implemented:
- belongs-to - belongs-to
Restrictions on Yang types are as follows: Restrictions on Yang types are as follows:
- The range statement does not support multiple values (RFC7895 sec 9.2.4) - The range statement does not support multiple values (RFC7950 9.2.4)
- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60) - Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60)
- default values on leaf-lists (RFC7950 7.7.2)
Netconf Netconf
======= =======

View file

@ -1,36 +1,34 @@
# Clixon roadmap # Clixon roadmap
In prio order ## High prio
High prio
- NACM (RFC 8341) - NACM (RFC 8341)
- Module rules - Module rules (done)
- Data node rules (read/create/delete/update/execute) - Data node rules (read/create/delete/update/execute)
- Special handling of the initial startup transaction to avoid exit at startup - Special handling of the initial startup transaction to avoid exit at startup
- Possibly - draft-wu-netconf-restconf-factory-restore-03 - Possibly - draft-wu-netconf-restconf-factory-restore-03
- Handle revisions to data model. - Handle revisions to data model.
- Possibly draft-wang-netmod-module-revision-management-01 - Possibly draft-wang-netmod-module-revision-management-01
- XML [Namespace handling](https://github.com/clicon/clixon/issues/49)
Medium prio: ## Medium prio:
- Input validation on custom RPCs/ - Input validation on custom RPCs/ (done)
- [Sanity checks](https://github.com/clicon/clixon/issues/47) - [Sanity checks](https://github.com/clicon/clixon/issues/47)
- Support for XML regex's. - Support for XML regex's.
- Currently Posix extended regular expressions - Currently Posix extended regular expressions
- Support a plugin callback that is invoked when copy-config is called. - Support a plugin callback that is invoked when copy-config is called.
- Preserve CLI command history across sessions. The up/down arrows - Preserve CLI command history across sessions. The up/down arrows
Low prio: ## Low prio:
- Provide a client library to access netconf APIs provided by system services. - Provide a client library to access netconf APIs provided by system services.
- Netconf backend (Clixon acts as netconf controller)
- Support for restconf call-home (RFC 8071) - Support for restconf call-home (RFC 8071)
- Support for restconf PATCH method
Not prioritized: Not prioritized:
- XML [Namespace handling](https://github.com/clicon/clixon/issues/49) - Support for restconf PATCH method
- NETCONF - NETCONF
- Support for additional Netconf [edit-config modes](https://github.com/clicon/clixon/issues/53) - Support for additional Netconf [edit-config modes](https://github.com/clicon/clixon/issues/53)
- Netconf [framing](https://github.com/clicon/clixon/issues/50) - Netconf [framing](https://github.com/clicon/clixon/issues/50)
- [Child ordering](https://github.com/clicon/clixon/issues/22) - [Child ordering](https://github.com/clicon/clixon/issues/22)
- Netconf backend (Clixon acts as netconf controller)
- Restconf - Restconf
- Query parameters - Query parameters
- Streams (netconf and restconf) - Streams (netconf and restconf)
@ -38,7 +36,7 @@ Not prioritized:
- Jenkins CI/CD and webhooks - Jenkins CI/CD and webhooks
- YANG - YANG
- RFC 6022 [NETCONF monitoring](https://github.com/clicon/clixon/issues/39) - RFC 6022 [NETCONF monitoring](https://github.com/clicon/clixon/issues/39)
- Deviation, belongs-to, min/max-elements, action, unique - Deviation, min/max-elements, action, unique
- Containers - Containers
- [Docker improvements](https://github.com/clicon/clixon/issues/44) - [Docker improvements](https://github.com/clicon/clixon/issues/44)
- Kubernetes Helm chart definition - Kubernetes Helm chart definition

View file

@ -468,12 +468,12 @@ from_client_edit_config(clicon_handle h,
} }
} }
if ((xc = xpath_first(xn, "config")) == NULL){ if ((xc = xpath_first(xn, "config")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>config</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
else{ else{
/* <config> yang spec may be set to anyxml by ingress yang check,...*/ /* <config> yang spec may be set to anyxmly by ingress yang check,...*/
if (xml_spec(xc) != NULL) if (xml_spec(xc) != NULL)
xml_spec_set(xc, NULL); xml_spec_set(xc, NULL);
/* Populate XML with Yang spec (why not do this in parser?) /* 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 */ cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){ if ((db = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -589,7 +589,7 @@ from_client_unlock(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */ cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){ if ((db = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -651,7 +651,7 @@ from_client_kill_session(clicon_handle h,
if ((x = xml_find(xe, "session-id")) == NULL || if ((x = xml_find(xe, "session-id")) == NULL ||
(str = xml_find_value(x, "body")) == NULL){ (str = xml_find_value(x, "body")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>session-id</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -709,7 +709,7 @@ from_client_copy_config(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */ cbuf *cbx = NULL; /* Assist cbuf */
if ((source = netconf_db_find(xe, "source")) == NULL){ if ((source = netconf_db_find(xe, "source")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -724,7 +724,7 @@ from_client_copy_config(clicon_handle h,
goto ok; goto ok;
} }
if ((target = netconf_db_find(xe, "target")) == NULL){ if ((target = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -777,7 +777,7 @@ from_client_delete_config(clicon_handle h,
if ((target = netconf_db_find(xe, "target")) == NULL|| if ((target = netconf_db_find(xe, "target")) == NULL||
strcmp(target, "running")==0){ strcmp(target, "running")==0){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -856,7 +856,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//stopTime")) != NULL){ if ((x = xpath_first(xe, "//stopTime")) != NULL){
if ((stoptime = xml_find_value(x, "body")) != NULL && if ((stoptime = xml_find_value(x, "body")) != NULL &&
str2time(stoptime, &stop) < 0){ str2time(stoptime, &stop) < 0){
if (netconf_bad_element(cbret, "application", "<bad-element>stopTime</bad-element>", "Expected timestamp") < 0) if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -864,7 +864,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//startTime")) != NULL){ if ((x = xpath_first(xe, "//startTime")) != NULL){
if ((starttime = xml_find_value(x, "body")) != NULL && if ((starttime = xml_find_value(x, "body")) != NULL &&
str2time(starttime, &start) < 0){ str2time(starttime, &start) < 0){
if (netconf_bad_element(cbret, "application", "<bad-element>startTime</bad-element>", "Expected timestamp") < 0) if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -925,7 +925,7 @@ from_client_debug(clicon_handle h,
char *valstr; char *valstr;
if ((valstr = xml_find_body(xe, "level")) == NULL){ if ((valstr = xml_find_body(xe, "level")) == NULL){
if (netconf_missing_element(cbret, "application", "<bad-element>level</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "application", "level", NULL) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -993,13 +993,10 @@ from_client_msg(clicon_handle h,
/* Populate incoming XML tree with yang */ /* Populate incoming XML tree with yang */
if (xml_spec_populate_rpc(h, x, yspec) < 0) if (xml_spec_populate_rpc(h, x, yspec) < 0)
goto done; 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; goto done;
if (ret == 0)
goto reply; goto reply;
}
xe = NULL; xe = NULL;
username = xml_find_value(x, "username"); username = xml_find_value(x, "username");
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) { 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){ else if (strcmp(rpc, "validate") == 0){
if ((db = netconf_db_find(xe, "source")) == NULL){ if ((db = netconf_db_find(xe, "source")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0) if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done; goto done;
goto reply; goto reply;
} }

View file

@ -81,67 +81,85 @@
* are if code comes via XML/NETCONF. * are if code comes via XML/NETCONF.
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] td Transaction data * @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 static int
generic_validate(yang_spec *yspec, generic_validate(yang_spec *yspec,
transaction_data_t *td) transaction_data_t *td,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
cxobj *x1; cxobj *x1;
cxobj *x2; cxobj *x2;
yang_stmt *ys; yang_stmt *ys;
int i; int i;
int ret;
/* All entries */ /* All entries */
if (xml_apply(td->td_target, CX_ELMNT, if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done; goto done;
if (ret == 0)
goto fail;
/* changed entries */ /* changed entries */
for (i=0; i<td->td_clen; i++){ for (i=0; i<td->td_clen; i++){
x1 = td->td_scvec[i]; /* source changed */ x1 = td->td_scvec[i]; /* source changed */
x2 = td->td_tcvec[i]; /* target 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; goto done;
if (ret == 0)
goto fail;
} }
/* deleted entries */ /* deleted entries */
for (i=0; i<td->td_dlen; i++){ for (i=0; i<td->td_dlen; i++){
x1 = td->td_dvec[i]; x1 = td->td_dvec[i];
ys = xml_spec(x1); ys = xml_spec(x1);
if (ys && yang_mandatory(ys)){ if (ys && yang_mandatory(ys)){
clicon_err(OE_CFG, 0,"Removed mandatory variable: %s", if (netconf_missing_element(cbret, "protocol", xml_name(x1), "Removed mandatory variable") < 0)
xml_name(x1));
goto done; goto done;
goto fail;
} }
} }
/* added entries */ /* added entries */
for (i=0; i<td->td_alen; i++){ for (i=0; i<td->td_alen; i++){
x2 = td->td_avec[i]; x2 = td->td_avec[i];
if (xml_apply0(x2, CX_ELMNT, if ((ret = xml_yang_validate_add(x2, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_add, NULL) < 0)
goto done; goto done;
if (ret == 0)
goto fail;
} }
retval = 0; // ok:
retval = 1;
done: done:
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Common code of candidate_validate and candidate_commit /*! Common code of candidate_validate and candidate_commit
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state * @param[in] candidate The candidate database. The wanted backend state
* @retval 0 OK * @retval -1 Error - or validation failed (but cbret not set)
* @retval -1 Fatal error or validation fail * @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
* @note Need to differentiate between error and validation fail * @note Need to differentiate between error and validation fail
* (only done for generic_validate)
*/ */
static int static int
validate_common(clicon_handle h, validate_common(clicon_handle h,
char *candidate, char *candidate,
transaction_data_t *td) transaction_data_t *td,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_spec *yspec; yang_spec *yspec;
int i; int i;
cxobj *xn; cxobj *xn;
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
@ -193,9 +211,12 @@ validate_common(clicon_handle h,
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin(h, td) < 0)
goto done; goto done;
/* 5. Make generic validation on all new or changed data. */ /* 5. Make generic validation on all new or changed data.
if (generic_validate(yspec, td) < 0) Note this is only call that uses 3-values */
if ((ret = generic_validate(yspec, td, cbret)) < 0)
goto done; goto done;
if (ret == 0)
goto fail;
/* 6. Call plugin transaction validate callbacks */ /* 6. Call plugin transaction validate callbacks */
if (plugin_transaction_validate(h, td) < 0) if (plugin_transaction_validate(h, td) < 0)
@ -204,9 +225,12 @@ validate_common(clicon_handle h,
/* 7. Call plugin transaction complete callbacks */ /* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0) if (plugin_transaction_complete(h, td) < 0)
goto done; goto done;
retval = 0; retval = 1;
done: done:
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Do a diff between candidate and running, then start a commit transaction /*! 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? * do something more drastic?
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] candidate A candidate database, not necessarily "candidate" * @param[in] candidate A candidate database, not necessarily "candidate"
* @retval 0 OK * @retval -1 Error - or validation failed (but cbret not set)
* @retval -1 Fatal error or validation fail * @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
* @note Need to differentiate between error and validation fail * @note Need to differentiate between error and validation fail
* (only done for validate_common)
*/ */
int int
candidate_commit(clicon_handle h, candidate_commit(clicon_handle h,
char *candidate) char *candidate,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
transaction_data_t *td = NULL; transaction_data_t *td = NULL;
int ret;
/* 1. Start transaction */ /* 1. Start transaction */
if ((td = transaction_new()) == NULL) if ((td = transaction_new()) == NULL)
goto done; goto done;
/* Common steps (with validate) */ /* Common steps (with validate). Note this is only call that uses 3-values */
if (validate_common(h, candidate, td) < 0) if ((ret = validate_common(h, candidate, td, cbret)) < 0)
goto done; goto done;
if (ret == 0)
goto fail;
/* 7. Call plugin transaction commit callbacks */ /* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit(h, td) < 0)
@ -252,21 +282,23 @@ candidate_commit(clicon_handle h,
/* 9. Call plugin transaction end callbacks */ /* 9. Call plugin transaction end callbacks */
plugin_transaction_end(h, td); plugin_transaction_end(h, td);
/* 8. Copy running back to candidate in case end functions updated running */ /* 8. Copy running back to candidate in case end functions updated running */
if (xmldb_copy(h, "running", candidate) < 0){ if (xmldb_copy(h, "running", candidate) < 0){
/* ignore errors or signal major setback ? */ /* ignore errors or signal major setback ? */
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue"); clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
goto done; goto done;
} }
retval = 0; retval = 1;
done: done:
/* In case of failure, call plugin transaction termination callbacks */ /* In case of failure (or error), call plugin transaction termination callbacks */
if (retval < 0 && td) if (retval < 1 && td)
plugin_transaction_abort(h, td); plugin_transaction_abort(h, td);
if (td) if (td)
transaction_free(td); transaction_free(td);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Commit changes from candidate to running /*! Commit changes from candidate to running
@ -283,6 +315,7 @@ from_client_commit(clicon_handle h,
int retval = -1; int retval = -1;
int piddb; int piddb;
cbuf *cbx = NULL; /* Assist cbuf */ cbuf *cbx = NULL; /* Assist cbuf */
int ret;
/* Check if target locked by other client */ /* Check if target locked by other client */
piddb = xmldb_islocked(h, "running"); piddb = xmldb_islocked(h, "running");
@ -296,9 +329,10 @@ from_client_commit(clicon_handle h,
goto done; goto done;
goto ok; 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"); 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 done;
goto ok; goto ok;
} }
@ -367,6 +401,7 @@ from_client_validate(clicon_handle h,
{ {
int retval = -1; int retval = -1;
transaction_data_t *td = NULL; transaction_data_t *td = NULL;
int ret;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){ if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
if (netconf_invalid_value(cbret, "protocol", "No such database")< 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) if ((td = transaction_new()) == NULL)
goto done; goto done;
/* Common steps (with commit) */ /* 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); clicon_debug(1, "Validate %s failed", db);
/* XXX: candidate_validate should have proper error handling */ if (ret < 0){
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done; goto done;
}
goto ok; goto ok;
} }
/* Optionally write (potentially modified) tree back to candidate */ /* Optionally write (potentially modified) tree back to candidate */

View file

@ -43,6 +43,6 @@
int from_client_validate(clicon_handle h, char *db, cbuf *cbret); 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_commit(clicon_handle h, int pid, cbuf *cbret);
int from_client_discard_changes(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_ */ #endif /* _BACKEND_COMMIT_H_ */

View file

@ -386,6 +386,7 @@ startup_mode_running(clicon_handle h,
char *extraxml_file) char *extraxml_file)
{ {
int retval = -1; int retval = -1;
cbuf *cbret = NULL;
/* Stash original running to candidate for later commit */ /* Stash original running to candidate for later commit */
if (xmldb_copy(h, "running", "candidate") < 0) if (xmldb_copy(h, "running", "candidate") < 0)
@ -405,15 +406,20 @@ startup_mode_running(clicon_handle h,
/* Clear running db */ /* Clear running db */
if (db_reset(h, "running") < 0) if (db_reset(h, "running") < 0)
goto done; goto done;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Commit original running. Assume -1 is validate fail */ /* 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 /* (1) We cannot differentiate between fatal errors and validation
* failures * failures
* (2) If fatal error, we should exit * (2) If fatal error, we should exit
* (3) If validation fails we cannot continue. How could we? * (3) If validation fails we cannot continue. How could we?
* (4) Need to restore the running db since we destroyed it above * (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 */ /* Reinstate original */
if (xmldb_copy(h, "candidate", "running") < 0) if (xmldb_copy(h, "candidate", "running") < 0)
goto done; goto done;
@ -424,6 +430,8 @@ startup_mode_running(clicon_handle h,
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (cbret)
cbuf_free(cbret);
if (xmldb_delete(h, "tmp") < 0) if (xmldb_delete(h, "tmp") < 0)
goto done; goto done;
return retval; return retval;
@ -455,6 +463,7 @@ startup_mode_startup(clicon_handle h,
char *extraxml_file) char *extraxml_file)
{ {
int retval = -1; int retval = -1;
cbuf *cbret = NULL;
/* Stash original running to backup */ /* Stash original running to backup */
if (xmldb_copy(h, "running", "backup") < 0) if (xmldb_copy(h, "running", "backup") < 0)
@ -478,13 +487,19 @@ startup_mode_startup(clicon_handle h,
/* Clear running db */ /* Clear running db */
if (db_reset(h, "running") < 0) if (db_reset(h, "running") < 0)
goto done; goto done;
/* Create return buffer (not used) */
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Commit startup */ /* 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 /* We cannot differentiate between fatal errors and validation
* failures * failures
* In both cases we copy back the original running and quit * 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) if (xmldb_copy(h, "backup", "running") < 0)
goto done; goto done;
goto done; goto done;
@ -494,6 +509,8 @@ startup_mode_startup(clicon_handle h,
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (cbret)
cbuf_free(cbret);
if (xmldb_delete(h, "backup") < 0) if (xmldb_delete(h, "backup") < 0)
goto done; goto done;
if (xmldb_delete(h, "tmp") < 0) if (xmldb_delete(h, "tmp") < 0)

View file

@ -83,6 +83,7 @@ static int
process_incoming_packet(clicon_handle h, process_incoming_packet(clicon_handle h,
cbuf *cb) cbuf *cb)
{ {
int retval = -1;
char *str; char *str;
char *str0; char *str0;
cxobj *xreq = NULL; /* Request (in) */ cxobj *xreq = NULL; /* Request (in) */
@ -92,6 +93,7 @@ process_incoming_packet(clicon_handle h,
cxobj *xrpc; cxobj *xrpc;
cxobj *xc; cxobj *xc;
yang_spec *yspec; yang_spec *yspec;
int ret;
clicon_debug(1, "RECV"); clicon_debug(1, "RECV");
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb)); clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
@ -115,15 +117,12 @@ process_incoming_packet(clicon_handle h,
} }
free(str0); free(str0);
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){ if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
int ret;
isrpc++; isrpc++;
if ((ret = xml_yang_validate_rpc(xrpc)) < 0) if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
goto done; goto done;
if (ret == 0){ if (ret == 0){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
netconf_output(1, cbret, "rpc-error"); netconf_output(1, cbret, "rpc-error");
goto done; goto ok;
} }
} }
else else
@ -168,6 +167,8 @@ process_incoming_packet(clicon_handle h,
} }
} }
} }
ok:
retval = 0;
done: done:
if (xreq) if (xreq)
xml_free(xreq); xml_free(xreq);
@ -175,7 +176,7 @@ process_incoming_packet(clicon_handle h,
xml_free(xret); xml_free(xret);
if (cbret) if (cbret)
cbuf_free(cbret); cbuf_free(cbret);
return 0; return retval;
} }
/*! Get netconf message: detect end-of-msg /*! Get netconf message: detect end-of-msg
@ -229,7 +230,7 @@ netconf_input_cb(int s,
/* Remove trailer */ /* Remove trailer */
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0'; *(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
if (process_incoming_packet(h, cb) < 0) if (process_incoming_packet(h, cb) < 0)
goto done; ; //goto done; // ignore errors
if (cc_closed) if (cc_closed)
break; break;
cbuf_reset(cb); cbuf_reset(cb);

View file

@ -56,6 +56,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <syslog.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/param.h> #include <sys/param.h>
@ -882,6 +883,10 @@ netconf_application_rpc(clicon_handle h,
clicon_err(OE_UNIX, 0, "cbuf_new"); clicon_err(OE_UNIX, 0, "cbuf_new");
goto done; 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 /* Find yang rpc statement, return yang rpc statement if found
Check application RPC */ Check application RPC */
if ((yspec = clicon_dbspec_yang(h)) == NULL){ 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 */ xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
if (xml_apply(xn, CX_ELMNT, if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done;
if (xml_yang_validate_add(xn, NULL) < 0)
goto done; goto done;
if (ret == 0){
netconf_output(1, cbret, "rpc-error");
goto ok;
} }
if ((cbret = cbuf_new()) == NULL){ if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done; goto done;
if (ret == 0){
netconf_output(1, cbret, "rpc-error");
goto ok;
}
} }
/* Look for local (client-side) netconf plugins. */ /* Look for local (client-side) netconf plugins. */
if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0) 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 */ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
if (xml_apply(xoutput, CX_ELMNT, if ((ret = xml_yang_validate_all_top(xoutput, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done; 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; goto done;
if (ret == 0){
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
goto ok;
}
} }
retval = 1; /* handled by callback */ retval = 1; /* handled by callback */
goto done; goto done;

View file

@ -71,10 +71,11 @@ static const map_str2int netconf_restconf_map[] = {
{"missing-attribute", 400}, {"missing-attribute", 400},
{"bad-attribute", 400}, {"bad-attribute", 400},
{"unknown-attribute", 400}, {"unknown-attribute", 400},
{"missing-element", 400},
{"bad-element", 400}, {"bad-element", 400},
{"unknown-element", 400}, {"unknown-element", 400},
{"unknown-namespace", 400}, {"unknown-namespace", 400},
{"access-denied", 401}, {"access-denied", 401}, /* or 403 */
{"access-denied", 403}, {"access-denied", 403},
{"lock-denied", 409}, {"lock-denied", 409},
{"resource-denied", 409}, {"resource-denied", 409},
@ -436,7 +437,8 @@ api_return_err(clicon_handle h,
goto ok; goto ok;
} }
tagstr = xml_body(xtag); 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) if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase=""; reason_phrase="";
if (xml_name_set(xerr, "error") < 0) if (xml_name_set(xerr, "error") < 0)
@ -448,6 +450,7 @@ api_return_err(clicon_handle h,
else else
if (xml2json_cbuf(cb, xerr, pretty) < 0) if (xml2json_cbuf(cb, xerr, pretty) < 0)
goto done; goto done;
FCGX_SetExitStatus(code, r->out); /* Created */
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase); FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n", FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
use_xml?"xml":"json"); use_xml?"xml":"json");

View file

@ -190,7 +190,7 @@ api_data_get2(clicon_handle h,
yang_spec *yspec; yang_spec *yspec;
cxobj *xret = NULL; cxobj *xret = NULL;
cxobj *xerr = NULL; /* malloced */ cxobj *xerr = NULL; /* malloced */
cxobj *xe; cxobj *xe = NULL;
cxobj **xvec = NULL; cxobj **xvec = NULL;
size_t xlen; size_t xlen;
int i; int i;
@ -206,7 +206,8 @@ api_data_get2(clicon_handle h,
if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){ if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -215,7 +216,8 @@ api_data_get2(clicon_handle h,
if (clicon_rpc_get(h, path, &xret) < 0){ if (clicon_rpc_get(h, path, &xret) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -434,7 +436,8 @@ api_data_post(clicon_handle h,
if (xml_parse_string(data, NULL, &xdata) < 0){ if (xml_parse_string(data, NULL, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -442,7 +445,8 @@ api_data_post(clicon_handle h,
else if (json_parse_str(data, &xdata) < 0){ else if (json_parse_str(data, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -452,7 +456,8 @@ api_data_post(clicon_handle h,
if (xml_child_nr(xdata) != 1){ if (xml_child_nr(xdata) != 1){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -662,7 +667,8 @@ api_data_put(clicon_handle h,
if (xml_parse_string(data, NULL, &xdata) < 0){ if (xml_parse_string(data, NULL, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -670,7 +676,8 @@ api_data_put(clicon_handle h,
else if (json_parse_str(data, &xdata) < 0){ else if (json_parse_str(data, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -680,7 +687,8 @@ api_data_put(clicon_handle h,
if (xml_child_nr(xdata) != 1){ if (xml_child_nr(xdata) != 1){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -705,7 +713,8 @@ api_data_put(clicon_handle h,
if (strcmp(xml_name(x), xml_name(xbot))){ if (strcmp(xml_name(x), xml_name(xbot))){
if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0) if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -714,7 +723,8 @@ api_data_put(clicon_handle h,
if (match_list_keys((yang_stmt*)y, x, xbot) < 0){ 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) if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1054,7 +1064,6 @@ api_operations_post(clicon_handle h,
cxobj *xdata = NULL; cxobj *xdata = NULL;
cxobj *xret = NULL; cxobj *xret = NULL;
cxobj *xerr = NULL; /* malloced must be freed */ cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xer; /* non-malloced error */
cbuf *cbx = NULL; cbuf *cbx = NULL;
cxobj *xtop = NULL; /* xpath root */ cxobj *xtop = NULL; /* xpath root */
cxobj *xbot = NULL; cxobj *xbot = NULL;
@ -1076,12 +1085,17 @@ api_operations_post(clicon_handle h,
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done; goto done;
} }
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
for (i=0; i<pi; i++) for (i=0; i<pi; i++)
oppath = index(oppath+1, '/'); oppath = index(oppath+1, '/');
if (oppath == NULL || strcmp(oppath,"/")==0){ if (oppath == NULL || strcmp(oppath,"/")==0){
if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0) if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1098,21 +1112,24 @@ api_operations_post(clicon_handle h,
if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){ if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){ if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (yrpc == NULL){ if (yrpc == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0) if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1142,7 +1159,8 @@ api_operations_post(clicon_handle h,
if (xml_parse_string(data, NULL, &xdata) < 0){ if (xml_parse_string(data, NULL, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1150,7 +1168,8 @@ api_operations_post(clicon_handle h,
else if (json_parse_str(data, &xdata) < 0){ else if (json_parse_str(data, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if ((xe = xpath_first(xerr, "rpc-error")) != NULL)
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1160,10 +1179,13 @@ api_operations_post(clicon_handle h,
xml_name_set(xdata, "input"); xml_name_set(xdata, "input");
xml_spec_set(xdata, yinput); /* needed for xml_spec_populate */ xml_spec_set(xdata, yinput); /* needed for xml_spec_populate */
if (yinput){ if (yinput){
if (xml_yang_validate_add(xdata, NULL) < 0){ if ((ret = xml_yang_validate_add(xdata, cbret)) < 0)
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done; goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0) if (ret == 0){ /* validation failed */
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 done;
goto ok; goto ok;
} }
@ -1182,23 +1204,31 @@ api_operations_post(clicon_handle h,
/* XXX yinput <-> h ?*/ /* XXX yinput <-> h ?*/
if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
if (xml_apply(xbot, CX_ELMNT, if ((ret = xml_yang_validate_all(xbot, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done; goto done;
if (xml_yang_validate_add(xbot, NULL) < 0){ if (ret == 0){ /* validation failed */
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) clicon_debug(1, "%s err: %s", __FUNCTION__, cbuf_get(cbret));
if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0)
goto done; 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 done;
goto ok; 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)
}
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done; goto done;
} }
}
}
ret = 0;
xe = NULL; xe = NULL;
while ((xe = xml_child_each(xtop, xe, CX_ELMNT)) != NULL) { while ((xe = xml_child_each(xtop, xe, CX_ELMNT)) != NULL) {
/* Look for local (client-side) restconf plugins. */ /* 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) if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
goto done; goto done;
/* Local error: return it and quit */ /* Local error: return it and quit */
if ((xer = xpath_first(xret, "//rpc-error")) != NULL){ if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
if (api_return_err(h, r, xer, pretty, use_xml) < 0) if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1219,8 +1249,8 @@ api_operations_post(clicon_handle h,
if (ret == 0){ /* Send to backend */ if (ret == 0){ /* Send to backend */
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
goto done; goto done;
if ((xer = xpath_first(xret, "//rpc-error")) != NULL){ if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
if (api_return_err(h, r, xer, pretty, use_xml) < 0) if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -1240,18 +1270,31 @@ api_operations_post(clicon_handle h,
goto done; goto done;
if ((xoutput=xpath_first(xret, "/")) != NULL){ if ((xoutput=xpath_first(xret, "/")) != NULL){
xml_name_set(xoutput, "output"); xml_name_set(xoutput, "output");
#if 0
clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
#endif
cbuf_reset(cbx); cbuf_reset(cbx);
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
if (xml_apply(xoutput, CX_ELMNT, if ((ret = xml_yang_validate_all(xoutput, cbret)) < 0)
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done; 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; 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 */ /* Sanity check of outgoing XML */
FCGX_SetExitStatus(200, r->out); /* OK */ FCGX_SetExitStatus(200, r->out); /* OK */

View file

@ -132,6 +132,7 @@ fib_route(clicon_handle h, /* Clicon handle */
cprintf(cbret, "<rpc-reply><route>" cprintf(cbret, "<rpc-reply><route>"
"<address-family>ipv4</address-family>" "<address-family>ipv4</address-family>"
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>" "<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
"<source-protocol>static</source-protocol>"
"</route></rpc-reply>"); "</route></rpc-reply>");
return 0; return 0;
} }
@ -157,7 +158,7 @@ route_count(clicon_handle h,
* in [RFC6241]. * in [RFC6241].
*/ */
static int static int
empty(clicon_handle h, /* Clicon handle */ empty_rpc(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */ cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */ cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* client_entry */ void *arg, /* client_entry */
@ -167,6 +168,31 @@ empty(clicon_handle h, /* Clicon handle */
return 0; 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: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* client_entry */
void *regarg) /* Argument given at register */
{
int retval = -1;
cxobj *x = NULL;
cprintf(cbret, "<rpc-reply>");
while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
retval = 0;
done:
return retval;
}
/*! Called to get state data from plugin /*! Called to get state data from plugin
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all * @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) if (example_stream_timer_setup(h) < 0)
goto done; goto done;
/* Register callback for routing rpc calls */ /* Register callback for routing rpc calls
*/
if (rpc_callback_register(h, fib_route, if (rpc_callback_register(h, fib_route,
NULL, NULL,
"fib-route"/* Xml tag when callback is made */ "fib-route"/* Xml tag when callback is made */
) < 0) ) < 0)
goto done; goto done;
/* From ietf-routing.yang */
if (rpc_callback_register(h, route_count, if (rpc_callback_register(h, route_count,
NULL, NULL,
"route-count"/* Xml tag when callback is made */ "route-count"/* Xml tag when callback is made */
) < 0) ) < 0)
goto done; goto done;
if (rpc_callback_register(h, empty, /* From example.yang (clicon) */
if (rpc_callback_register(h, empty_rpc,
NULL, NULL,
"empty"/* Xml tag when callback is made */ "empty"/* Xml tag when callback is made */
) < 0) ) < 0)
goto done; goto done;
if (rpc_callback_register(h, example_rpc,
NULL,
"example"/* Xml tag when callback is made */
) < 0)
goto done;
/* Return plugin API */ /* Return plugin API */
return &api; return &api;

View file

@ -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_missing_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message); int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message); int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_missing_element(cbuf *cb, char *type, char *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 *message); int netconf_bad_element(cbuf *cb, char *type, char *info, char *element);
int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message); 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_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
int netconf_access_denied(cbuf *cb, char *type, char *message); int netconf_access_denied(cbuf *cb, char *type, char *message);
int netconf_access_denied_xml(cxobj **xret, char *type, char *message); int netconf_access_denied_xml(cxobj **xret, char *type, char *message);

View file

@ -99,8 +99,8 @@ extern int _CLICON_XML_NS_ITERATE;
char *xml_type2str(enum cxobj_type type); char *xml_type2str(enum cxobj_type type);
char *xml_name(cxobj *xn); char *xml_name(cxobj *xn);
int xml_name_set(cxobj *xn, char *name); int xml_name_set(cxobj *xn, char *name);
char *xml_namespace(cxobj *xn); char *xml_prefix(cxobj *xn);
int xml_namespace_set(cxobj *xn, char *name); int xml_prefix_set(cxobj *xn, char *name);
int xml2ns(cxobj *x, char *localname, char **namespace); int xml2ns(cxobj *x, char *localname, char **namespace);
cxobj *xml_parent(cxobj *xn); cxobj *xml_parent(cxobj *xn);
int xml_parent_set(cxobj *xn, cxobj *parent); int xml_parent_set(cxobj *xn, cxobj *parent);

View file

@ -43,9 +43,10 @@
*/ */
int xml2txt(FILE *f, cxobj *x, int level); int xml2txt(FILE *f, cxobj *x, int level);
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt); int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
int xml_yang_validate_rpc(cxobj *xrpc); int xml_yang_validate_rpc(cxobj *xrpc, cbuf *cbret);
int xml_yang_validate_add(cxobj *xt, void *arg); int xml_yang_validate_add(cxobj *xt, cbuf *cbret);
int xml_yang_validate_all(cxobj *xt, void *arg); 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 xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0); int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2, int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,

View file

@ -315,7 +315,7 @@ xml2json1_cbuf(cbuf *cb,
* Harder if x has a prefix, then that should also be translated to associated * Harder if x has a prefix, then that should also be translated to associated
* module name * module name
*/ */
prefix = xml_namespace(x); prefix = xml_prefix(x);
if (xml2ns(x, prefix, &namespace) < 0) if (xml2ns(x, prefix, &namespace) < 0)
goto done; goto done;
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */ if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */

View file

@ -80,8 +80,8 @@ netconf_in_use(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>in-use</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>in-use</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -119,8 +119,8 @@ netconf_invalid_value(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>invalid-value</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -159,8 +159,8 @@ netconf_too_big(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>too-big</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>too-big</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -200,8 +200,8 @@ netconf_missing_attribute(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>missing-attribute</error-tag>"
"<error-info>%s</error-info>" "<error-info>%s</error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, info) <0)
@ -241,8 +241,8 @@ netconf_bad_attribute(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>bad-attribute</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>bad-attribute</error-tag>"
"<error-info>%s</error-info>" "<error-info>%s</error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, info) <0)
@ -283,8 +283,8 @@ netconf_unknown_attribute(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-attribute</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>unknown-attribute</error-tag>"
"<error-info>%s</error-info>" "<error-info>%s</error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, info) <0)
@ -318,18 +318,18 @@ netconf_unknown_attribute(cbuf *cb,
int int
netconf_missing_element(cbuf *cb, netconf_missing_element(cbuf *cb,
char *type, char *type,
char *info, char *element,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-info>%s</error-info>" "<error-tag>missing-element</error-tag>"
"<error-info><bad-element>%s</bad-element></error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, element) <0)
goto err; goto err;
if (message){ if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0) if (xml_chardata_encode(&encstr, "%s", message) < 0)
@ -355,24 +355,24 @@ netconf_missing_element(cbuf *cb,
* pattern mismatch. * pattern mismatch.
* @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol" * @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element xml * @param[in] elemnt Bad element name
* @param[in] message Error message * @param[in] message Error message
*/ */
int int
netconf_bad_element(cbuf *cb, netconf_bad_element(cbuf *cb,
char *type, char *type,
char *info, char *element,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>bad-element</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-info>%s</error-info>" "<error-tag>bad-element</error-tag>"
"<error-info><bad-element>%s</bad-element></error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, element) <0)
goto err; goto err;
if (message){ if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0) if (xml_chardata_encode(&encstr, "%s", message) < 0)
@ -397,24 +397,24 @@ netconf_bad_element(cbuf *cb,
* An unexpected element is present. * An unexpected element is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer * @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol" * @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element xml * @param[in] element Bad element name
* @param[in] message Error message * @param[in] message Error message
*/ */
int int
netconf_unknown_element(cbuf *cb, netconf_unknown_element(cbuf *cb,
char *type, char *type,
char *info, char *element,
char *message) char *message)
{ {
int retval = -1; int retval = -1;
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-element</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-info>%s</error-info>" "<error-tag>unknown-element</error-tag>"
"<error-info><bad-element>%s</bad-element></error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) type, element) <0)
goto err; goto err;
if (message){ if (message){
if (xml_chardata_encode(&encstr, "%s", message) < 0) if (xml_chardata_encode(&encstr, "%s", message) < 0)
@ -452,8 +452,8 @@ netconf_unknown_namespace(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-namespace</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>unknown-namespace</error-tag>"
"<error-info>%s</error-info>" "<error-info>%s</error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type, info) <0) 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 /*! 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[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol" * @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message * @param[in] message Error message
@ -492,8 +493,8 @@ netconf_access_denied(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>access-denied</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>access-denied</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -517,7 +518,8 @@ netconf_access_denied(cbuf *cb,
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A /*! Create Netconf access-denied error XML tree according to RFC 6241 App A
* *
* 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[out] xret Error XML tree
* @param[in] type Error type: "application" or "protocol" * @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message * @param[in] message Error message
@ -538,8 +540,8 @@ netconf_access_denied_xml(cxobj **xret,
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done; goto done;
if (xml_parse_va(&xerr, NULL, "<error-tag>access-denied</error-tag>" if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
"<error-type>%s</error-type>" "<error-tag>access-denied</error-tag>"
"<error-severity>error</error-severity>", type) < 0) "<error-severity>error</error-severity>", type) < 0)
goto done; goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>", if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
@ -567,8 +569,8 @@ netconf_lock_denied(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>" "<error-type>protocol</error-type>"
"<error-tag>lock-denied</error-tag>"
"<error-info>%s</error-info>" "<error-info>%s</error-info>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
info) <0) info) <0)
@ -593,7 +595,7 @@ netconf_lock_denied(cbuf *cb,
/*! Create Netconf resource-denied error XML tree according to RFC 6241 App A /*! 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[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "transport, "rpc", "application", "protocol" * @param[in] type Error type: "transport, "rpc", "application", "protocol"
* @param[in] message Error message * @param[in] message Error message
@ -607,8 +609,8 @@ netconf_resource_denied(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>resource-denied</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>resource-denied</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -647,8 +649,8 @@ netconf_rollback_failed(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>rollback-failed</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>rollback-failed</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -686,8 +688,8 @@ netconf_data_exists(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>data-exists</error-tag>"
"<error-type>application</error-type>" "<error-type>application</error-type>"
"<error-tag>data-exists</error-tag>"
"<error-severity>error</error-severity>") <0) "<error-severity>error</error-severity>") <0)
goto err; goto err;
if (message){ if (message){
@ -724,8 +726,8 @@ netconf_data_missing(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>data-missing</error-tag>"
"<error-type>application</error-type>" "<error-type>application</error-type>"
"<error-tag>data-missing</error-tag>"
"<error-severity>error</error-severity>") <0) "<error-severity>error</error-severity>") <0)
goto err; goto err;
if (message){ if (message){
@ -763,8 +765,8 @@ netconf_operation_not_supported(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>operation-not-supported</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>operation-not-supported</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -803,8 +805,8 @@ netconf_operation_failed(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>%s</error-type>" "<error-type>%s</error-type>"
"<error-tag>operation-failed</error-tag>"
"<error-severity>error</error-severity>", "<error-severity>error</error-severity>",
type) <0) type) <0)
goto err; goto err;
@ -850,8 +852,8 @@ netconf_operation_failed_xml(cxobj **xret,
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done; goto done;
if (xml_parse_va(&xerr, NULL, "<error-tag>operation-failed</error-tag>" if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
"<error-type>%s</error-type>" "<error-tag>operation-failed</error-tag>"
"<error-severity>error</error-severity>", type) < 0) "<error-severity>error</error-severity>", type) < 0)
goto done; goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>", if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
@ -879,8 +881,8 @@ netconf_malformed_message(cbuf *cb,
char *encstr = NULL; char *encstr = NULL;
if (cprintf(cb, "<rpc-reply><rpc-error>" if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>malformed-message</error-tag>"
"<error-type>rpc</error-type>" "<error-type>rpc</error-type>"
"<error-tag>malformed-message</error-tag>"
"<error-severity>error</error-severity>") <0) "<error-severity>error</error-severity>") <0)
goto err; goto err;
if (message){ if (message){
@ -925,8 +927,8 @@ netconf_malformed_message_xml(cxobj **xret,
goto done; goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL) if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done; goto done;
if (xml_parse_va(&xerr, NULL, "<error-tag>malformed-message</error-tag>" if (xml_parse_va(&xerr, NULL, "<error-type>rpc</error-type>"
"<error-type>rpc</error-type>" "<error-tag>malformed-message</error-tag>"
"<error-severity>error</error-severity>") < 0) "<error-severity>error</error-severity>") < 0)
goto done; goto done;
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>", if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
@ -990,7 +992,6 @@ netconf_module_load(clicon_handle h)
clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded"); clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
goto done; goto done;
} }
/* Enable features (hardcoded here) */ /* Enable features (hardcoded here) */
if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:candidate</CLICON_FEATURE>", yspec, &xc) < 0) if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:candidate</CLICON_FEATURE>", yspec, &xc) < 0)
goto done; goto done;

View file

@ -136,6 +136,8 @@ parse_configfile(clicon_handle h,
char *name; char *name;
char *body; char *body;
clicon_hash_t *copt = clicon_options(h); clicon_hash_t *copt = clicon_options(h);
cbuf *cbret = NULL;
int ret;
if (filename == NULL || !strlen(filename)){ if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified"); 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) if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0)
goto done; 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; 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) { while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) {
name = xml_name(x); name = xml_name(x);
body = xml_body(x); body = xml_body(x);

View file

@ -444,7 +444,7 @@ clicon_rpc_delete_config(clicon_handle h,
char *username; char *username;
username = clicon_username_get(h); username = clicon_username_get(h);
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><delete-config><target><%s/></target></delete-config></rpc>", if ((msg = clicon_msg_encode("<rpc username=\"%s\"><edit-config><target><%s/></target><default-operation>none</default-operation><config operation=\"delete\"/></edit-config></rpc>",
username?username:"", db)) == NULL) username?username:"", db)) == NULL)
goto done; goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)

View file

@ -196,10 +196,9 @@ xml_name_set(cxobj *xn,
/*! Get namespace of xnode /*! Get namespace of xnode
* @param[in] xn xml node * @param[in] xn xml node
* @retval namespace of xml node * @retval namespace of xml node
* XXX change to xml_localname
*/ */
char* char*
xml_namespace(cxobj *xn) xml_prefix(cxobj *xn)
{ {
return xn->x_prefix; return xn->x_prefix;
} }
@ -209,10 +208,9 @@ xml_namespace(cxobj *xn)
* @param[in] localname new namespace, null-terminated string, copied by function * @param[in] localname new namespace, null-terminated string, copied by function
* @retval -1 on error with clicon-err set * @retval -1 on error with clicon-err set
* @retval 0 OK * @retval 0 OK
* XXX change to xml_localname_set
*/ */
int int
xml_namespace_set(cxobj *xn, xml_prefix_set(cxobj *xn,
char *localname) char *localname)
{ {
if (xn->x_prefix){ if (xn->x_prefix){
@ -288,7 +286,7 @@ xmlns_check(cxobj *xn,
char *xns; char *xns;
while ((x = xml_child_each(xn, x, CX_ATTR)) != NULL) 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) strcmp(xml_name(x), nsn) == 0)
return xml_value(x); return xml_value(x);
return NULL; return NULL;
@ -311,7 +309,7 @@ xml_localname_check(cxobj *xn,
yang_stmt *ys = xml_spec(xn); yang_stmt *ys = xml_spec(xn);
/* No namespace name - comply */ /* No namespace name - comply */
if ((nsn = xml_namespace(xn)) == NULL) if ((nsn = xml_prefix(xn)) == NULL)
return 0; return 0;
/* Check if NSN defined in same node */ /* Check if NSN defined in same node */
if (xmlns_check(xn, nsn) != NULL) if (xmlns_check(xn, nsn) != NULL)
@ -965,7 +963,7 @@ xml_find_type_value(cxobj *xt,
char *xprefix; /* xprefix */ char *xprefix; /* xprefix */
while ((x = xml_child_each(xt, x, type)) != NULL) { while ((x = xml_child_each(xt, x, type)) != NULL) {
xprefix = xml_namespace(x); xprefix = xml_prefix(x);
if (prefix) if (prefix)
pmatch = xprefix?strcmp(prefix,xprefix)==0:0; pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
else else
@ -1121,7 +1119,7 @@ clicon_xml2file(FILE *f,
if (x == NULL) if (x == NULL)
goto ok; goto ok;
name = xml_name(x); name = xml_name(x);
namespace = xml_namespace(x); namespace = xml_prefix(x);
switch(xml_type(x)){ switch(xml_type(x)){
case CX_BODY: case CX_BODY:
if ((val = xml_value(x)) == NULL) /* incomplete tree */ if ((val = xml_value(x)) == NULL) /* incomplete tree */
@ -1246,7 +1244,7 @@ clicon_xml2cbuf(cbuf *cb,
char *val; char *val;
name = xml_name(x); name = xml_name(x);
namespace = xml_namespace(x); namespace = xml_prefix(x);
switch(xml_type(x)){ switch(xml_type(x)){
case CX_BODY: case CX_BODY:
if ((val = xml_value(x)) == NULL) /* incomplete tree */ if ((val = xml_value(x)) == NULL) /* incomplete tree */
@ -1333,10 +1331,10 @@ xmltree2cbuf(cbuf *cb,
cprintf(cb, " "); cprintf(cb, " ");
if (xml_type(x) != CX_BODY) if (xml_type(x) != CX_BODY)
cprintf(cb, "%s", xml_type2str(xml_type(x))); cprintf(cb, "%s", xml_type2str(xml_type(x)));
if (xml_namespace(x)==NULL) if (xml_prefix(x)==NULL)
cprintf(cb, " %s", xml_name(x)); cprintf(cb, " %s", xml_name(x));
else 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)) if (xml_value(x))
cprintf(cb, " value:\"%s\"", xml_value(x)); cprintf(cb, " value:\"%s\"", xml_value(x));
if (x->x_flags) if (x->x_flags)
@ -1612,8 +1610,8 @@ xml_copy_one(cxobj *x0,
if ((s = xml_name(x0))) /* malloced string */ if ((s = xml_name(x0))) /* malloced string */
if ((xml_name_set(x1, s)) < 0) if ((xml_name_set(x1, s)) < 0)
return -1; return -1;
if ((s = xml_namespace(x0))) /* malloced string */ if ((s = xml_prefix(x0))) /* malloced string */
if ((xml_namespace_set(x1, s)) < 0) if ((xml_prefix_set(x1, s)) < 0)
return -1; return -1;
return 0; return 0;
} }

View file

@ -87,6 +87,7 @@
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h" #include "clixon_xml_sort.h"
#include "clixon_xml_map.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 /*! 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] xt XML leaf node of type leafref
* @param[in] ytype Yang type statement belonging to the XML node * @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 static int
validate_leafref(cxobj *xt, validate_leafref(cxobj *xt,
yang_stmt *ytype) yang_stmt *ytype,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_stmt *ypath; yang_stmt *ypath;
@ -247,8 +253,9 @@ validate_leafref(cxobj *xt,
if ((leafrefbody = xml_body(xt)) == NULL) if ((leafrefbody = xml_body(xt)) == NULL)
goto ok; goto ok;
if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){ if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){
clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument); if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0)
goto done; goto done;
goto fail;
} }
if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0) if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0)
goto done; goto done;
@ -260,9 +267,9 @@ validate_leafref(cxobj *xt,
break; break;
} }
if (i==xlen){ if (i==xlen){
clicon_err(OE_DB, 0, "Leafref validation failed, no such leaf: %s", if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
leafrefbody);
goto done; goto done;
goto fail;
} }
ok: ok:
retval = 0; retval = 0;
@ -270,6 +277,9 @@ validate_leafref(cxobj *xt,
if (xvec) if (xvec)
free(xvec); free(xvec);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Validate xml node of type identityref, ensure value is a defined identity /*! 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] xt XML leaf node of type identityref
* @param[in] ys Yang spec of leaf * @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type field of type identityref * @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 ys_populate_identity where the derived types are set
* @see RFC7950 Sec 9.10.2: * @see RFC7950 Sec 9.10.2:
*/ */
static int static int
validate_identityref(cxobj *xt, validate_identityref(cxobj *xt,
yang_stmt *ys, yang_stmt *ys,
yang_stmt *ytype) yang_stmt *ytype,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
char *node; char *node;
@ -305,37 +319,46 @@ validate_identityref(cxobj *xt,
* Always add default prefix because derived identifiers are stored with * Always add default prefix because derived identifiers are stored with
* prefixes in the base identifiers derived-list. * prefixes in the base identifiers derived-list.
*/ */
if ((node = xml_body(xt)) == NULL)
return 0;
if (strchr(node, ':') == NULL){
prefix = yang_find_myprefix(ys);
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} }
if ((node = xml_body(xt)) == NULL)
return 0;
if (strchr(node, ':') == NULL){
prefix = yang_find_myprefix(ys);
cprintf(cb, "%s:%s", prefix, node); cprintf(cb, "%s:%s", prefix, node);
node = cbuf_get(cb); node = cbuf_get(cb);
} }
/* This is the type's base reference */ /* This is the type's base reference */
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){ if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base"); if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0)
goto done; goto done;
goto fail;
} }
/* This is the actual base identity */ /* This is the actual base identity */
if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){ if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base identity"); if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0)
goto done; goto done;
goto fail;
} }
/* Here check if node is in the derived node list of the base identity */ /* Here check if node is in the derived node list of the base identity */
if (cvec_find(ybaseid->ys_cvec, node) == NULL){ 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); 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 done;
goto fail;
} }
retval = 0; retval = 1;
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Validate an RPC node /*! Validate an RPC node
@ -377,24 +400,33 @@ validate_identityref(cxobj *xt,
* the same order as they are defined within the "output" statement. * the same order as they are defined within the "output" statement.
*/ */
int int
xml_yang_validate_rpc(cxobj *xrpc) xml_yang_validate_rpc(cxobj *xrpc,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_stmt *yn=NULL; /* rpc name */ yang_stmt *yn=NULL; /* rpc name */
cxobj *xn; /* rpc name */ cxobj *xn; /* rpc name */
yang_stmt *yi=NULL; /* input name */ int ret;
cxobj *xi; /* input name */
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 = NULL;
/* xn is name of rpc, ie <rcp><xn/></rpc> */
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) { while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
if ((yn = xml_spec(xn)) == NULL) if ((yn = xml_spec(xn)) == NULL)
goto fail; goto fail;
xi = NULL; if ((ret = xml_yang_validate_all(xn, cbret)) < 0)
while ((xi = xml_child_each(xn, xi, CX_ELMNT)) != NULL) {
if ((yi = xml_spec(xi)) == NULL)
goto fail; 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 */ // ok: /* pass validation */
retval = 1; retval = 1;
@ -409,13 +441,23 @@ xml_yang_validate_rpc(cxobj *xrpc)
* 1. Check if mandatory leafs present as subs. * 1. Check if mandatory leafs present as subs.
* 2. Check leaf values, eg int ranges and string regexps. * 2. Check leaf values, eg int ranges and string regexps.
* @param[in] xt XML node to be validated * @param[in] xt XML node to be validated
* @retval 0 Valid OK * @param[out] cbret Error buffer
* @retval -1 Validation failed * @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 * @see xml_yang_validate_all
*/ */
int int
xml_yang_validate_add(cxobj *xt, xml_yang_validate_add(cxobj *xt,
void *arg) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
cg_var *cv = NULL; cg_var *cv = NULL;
@ -424,11 +466,14 @@ xml_yang_validate_add(cxobj *xt,
int i; int i;
yang_stmt *ys; yang_stmt *ys;
char *body; char *body;
int ret;
cxobj *x;
/* if not given by argument (overide) use default link /* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */ and !Node has a config sub-statement and it is false */
if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){ if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){
switch (ys->ys_keyword){ switch (ys->ys_keyword){
case Y_RPC:
case Y_INPUT: case Y_INPUT:
case Y_LIST: case Y_LIST:
/* fall thru */ /* fall thru */
@ -440,9 +485,9 @@ xml_yang_validate_add(cxobj *xt,
if (yang_config(yc)==0) if (yang_config(yc)==0)
continue; continue;
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){ if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
clicon_err(OE_CFG, 0,"Missing mandatory variable: %s", if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
yc->ys_argument);
goto done; goto done;
goto fail;
} }
} }
break; break;
@ -463,12 +508,11 @@ xml_yang_validate_add(cxobj *xt,
goto done; goto done;
} }
if ((ys_cv_validate(cv, ys, &reason)) != 1){ if ((ys_cv_validate(cv, ys, &reason)) != 1){
clicon_err(OE_DB, 0, if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
"validation of %s failed %s", goto done;
ys->ys_argument, reason?reason:"");
if (reason) if (reason)
free(reason); free(reason);
goto done; goto fail;
} }
} }
break; break;
@ -476,28 +520,43 @@ xml_yang_validate_add(cxobj *xt,
break; 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: done:
if (cv) if (cv)
cv_free(cv); cv_free(cv);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Validate a single XML node with yang specification for all (not only added) entries /*! 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. * 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
* @param[in] xt XML node to be validated * @param[in] xt XML node to be validated
* @param[in] arg Not used * @param[out] cbret Error buffer
* @retval -1 Validation failed * @retval 1 Validation OK
* @retval 0 Validation OK * @retval 0 Validation failed
* @retval -1 Error
* @see xml_yang_validate_add * @see xml_yang_validate_add
* @code * @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; * err;
* if (ret == 0)
* fail;
* @endcode * @endcode
*/ */
int int
xml_yang_validate_all(cxobj *xt, xml_yang_validate_all(cxobj *xt,
void *arg) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_stmt *ys; /* yang node */ yang_stmt *ys; /* yang node */
@ -505,13 +564,24 @@ xml_yang_validate_all(cxobj *xt,
yang_stmt *ye; /* yang must error-message */ yang_stmt *ye; /* yang must error-message */
char *xpath; char *xpath;
int nr; int nr;
int ret;
cxobj *x;
/* if not given by argument (overide) use default link /* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */ and !Node has a config sub-statement and it is false */
if ((ys = xml_spec(xt)) != NULL && ys=xml_spec(xt);
yang_config(ys) != 0){ 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 */ /* Node-specific validation */
switch (ys->ys_keyword){ switch (ys->ys_keyword){
case Y_ANYXML:
case Y_ANYDATA:
goto ok;
break;
case Y_LEAF: case Y_LEAF:
/* fall thru */ /* fall thru */
case Y_LEAF_LIST: 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 ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){ if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, yc) < 0) if (validate_leafref(xt, yc, cbret) < 0)
goto done; goto done;
} }
else if (strcmp(yc->ys_argument, "identityref") == 0){ 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; goto done;
} }
} }
@ -568,11 +638,11 @@ xml_yang_validate_all(cxobj *xt,
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0) if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done; goto done;
if (!nr){ if (!nr){
if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL) ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL);
clicon_err(OE_DB, 0, "%s", ye->ys_argument); if (netconf_operation_failed(cbret, "application",
else ye?ye->ys_argument:"must xpath validation failed") < 0)
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
goto done; goto done;
goto fail;
} }
} }
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */ /* "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) if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done; goto done;
if (!nr){ if (!nr){
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt)); if (netconf_operation_failed(cbret, "application",
"when xpath validation failed") < 0)
goto done; 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: done:
return retval; 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 /*! 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) /*! Add default values (if not set)
* @param[in] xt XML tree with some node marked * @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 int
xml_default(cxobj *xt, xml_default(cxobj *xt,
@ -1328,7 +1431,8 @@ xml_default(cxobj *xt,
goto done; goto done;
} }
/* Check leaf defaults */ /* 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; i<ys->ys_len; i++){ for (i=0; i<ys->ys_len; i++){
y = ys->ys_stmt[i]; y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF) 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 *y=NULL; /* yang node */
yang_stmt *ymod=NULL; /* yang module */ yang_stmt *ymod=NULL; /* yang module */
yang_stmt *yi = NULL; /* input */ yang_stmt *yi = NULL; /* input */
yang_stmt *ya = NULL; /* arg */ // yang_stmt *ya = NULL; /* arg */
cxobj *x; cxobj *x;
cxobj *xi; // cxobj *xi;
int i; int i;
if ((strcmp(xml_name(xrpc), "rpc"))!=0){ if ((strcmp(xml_name(xrpc), "rpc"))!=0){
@ -1521,11 +1625,18 @@ xml_spec_populate_rpc(clicon_handle h,
if (y){ if (y){
xml_spec_set(x, y); xml_spec_set(x, y);
if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){ 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; xi = NULL;
while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) { while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) {
if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL) if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL)
xml_spec_set(xi, ya); xml_spec_set(xi, ya);
} }
#endif
} }
} }
} }

View file

@ -172,7 +172,7 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
goto done; goto done;
if ((x = xml_new(name, xp, y)) == NULL) if ((x = xml_new(name, xp, y)) == NULL)
goto done; goto done;
if (xml_namespace_set(x, prefix) < 0) if (xml_prefix_set(x, prefix) < 0)
goto done; goto done;
ya->ya_xelement = x; ya->ya_xelement = x;
retval = 0; retval = 0;
@ -223,9 +223,9 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
xml_name(x), name); xml_name(x), name);
goto done; 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", 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; goto done;
} }
/* Strip pretty-print. Ad-hoc algorithm /* 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)){ if (strcmp(xml_name(x), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
xml_namespace(x), xml_prefix(x),
xml_name(x), xml_name(x),
namespace, namespace,
name); name);
goto done; goto done;
} }
if (xml_namespace(x)==NULL || if (xml_prefix(x)==NULL ||
strcmp(xml_namespace(x), namespace)){ strcmp(xml_prefix(x), namespace)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s", clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
xml_namespace(x), xml_prefix(x),
xml_name(x), xml_name(x),
namespace, namespace,
name); name);
@ -324,7 +324,7 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL) if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
goto done; goto done;
xml_type_set(xa, CX_ATTR); xml_type_set(xa, CX_ATTR);
if (prefix && xml_namespace_set(xa, prefix) < 0) if (prefix && xml_prefix_set(xa, prefix) < 0)
goto done; goto done;
if (xml_value_set(xa, attval) < 0) if (xml_value_set(xa, attval) < 0)
goto done; goto done;

View file

@ -118,6 +118,9 @@ xml_child_spec(char *name,
} }
else else
y = NULL; 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; *yresult = y;
retval = 0; retval = 0;
done: done:

View file

@ -862,7 +862,7 @@ ys_module_by_xml(yang_spec *ysp,
if (ymodp) if (ymodp)
*ymodp = NULL; *ymodp = NULL;
prefix = xml_namespace(xt); prefix = xml_prefix(xt);
if (prefix){ if (prefix){
/* Get namespace for prefix */ /* Get namespace for prefix */
if (xml2ns(xt, prefix, &namespace) < 0) if (xml2ns(xt, prefix, &namespace) < 0)

View file

@ -13,8 +13,10 @@ APPNAME=example
. ./lib.sh . ./lib.sh
cfg=$dir/conf_yang.xml cfg=$dir/conf_yang.xml
# Use yang in example
cat <<EOF > $cfg cat <<EOF > $cfg
<config xmlns="http://clicon.org"> <config xmlns="urn:example:clixon">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR> <CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR> <CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
@ -115,7 +117,7 @@ expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" 0 "^$"
new "cli rpc" new "cli rpc"
expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 "<address-family>ipv4</address-family>" "<next-hop-list>2.3.4.5</next-hop-list>" expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 "<address-family>ipv4</address-family>" "<next-hop-list>2.3.4.5</next-hop-list>"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -96,8 +96,7 @@ new "netconf validate enabled feature"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf disabled feature" new "netconf disabled feature"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><A>foo</A></config></edit-config></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><A>foo</A></config></edit-config></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><A>foo</A></config></edit-config></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML node config/A has no corresponding yang specification (Invalid XML or wrong Yang spec?'
# This test has been broken up into all different modules instead of one large # This test has been broken up into all different modules instead of one large
# reply since the modules change so often # reply since the modules change so often
@ -162,7 +161,7 @@ if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi fi
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -162,7 +162,7 @@ new "Set crypto to foo:bar"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>foo:bar</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>foo:bar</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf validate" new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
new "cli set crypto to mc:aes" new "cli set crypto to mc:aes"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto mc:aes" 0 "^$" 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" new "cli validate"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$" expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -105,8 +105,8 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></s
new "leafref add wrong ref" new "leafref add wrong ref"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref validate XXX shouldnt really be operation-failed, more work in validate code" new "leafref validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
new "leafref discard-changes" new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -126,7 +126,7 @@ new "leafref delete leaf"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
new "leafref validate (should fail)" new "leafref validate (should fail)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
new "leafref discard-changes" new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -143,7 +143,7 @@ expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" 0 "^$"
new "cli sender template" new "cli sender template"
expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" 0 "^$" 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 exit # BE
fi fi

View file

@ -125,7 +125,7 @@ new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
fi # NYI fi # NYI
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -141,10 +141,10 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new2 "auth get (no user: access denied)" 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)" 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)" new2 "auth get (access)"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} 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" 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" new "admin edit nacm"
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
new2 "limited edit nacm" 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" 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" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -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)" "" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 0}' http://localhost/restconf/data/x)" ""
new2 "auth get (no user: access denied)" 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)" 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)" new2 "auth get (access)"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0} 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" 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" new "admin edit nacm"
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" "" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
new2 "limited edit nacm" 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" 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" new "cli show conf as admin"
expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" 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" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -147,7 +147,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
sleep 1 sleep 1
new "start restconf daemon (-a is enable basic authentication)" new "start restconf daemon (-a is enable basic authentication)"
sudo su -c "$clixon_restconf -f $cfg -y $fyang -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 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 # Rule 1: deny-kill-session
new "deny-kill-session: limited fail (netconf)" new "deny-kill-session: limited fail (netconf)"
expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>access-denied</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$"
new "deny-kill-session: guest fail (netconf)" new "deny-kill-session: guest fail (netconf)"
expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>access-denied</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$"
new "deny-kill-session: admin ok (netconf)" new "deny-kill-session: admin ok (netconf)"
expecteof "$clixon_netconf -qf $cfg -y $fyang -U andy" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang -U andy" 0 "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Rule 2: deny-delete-config # Rule 2: deny-delete-config
new "deny-delete-config: limited fail (netconf)" new "deny-delete-config: limited fail (netconf)"
expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>access-denied</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$"
new2 "deny-delete-config: guest fail (restconf)" 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 # In restconf delete-config is translated to edit-config which is permitted
new "deny-delete-config: limited fail (restconf) ok" 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)" '' expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" ''
new2 "permit-edit-config: guest fail restconf" 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" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -209,7 +209,7 @@ new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit state operation should fail" new "netconf edit state operation should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag>"
new "netconf get state operation" new "netconf get state operation"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><state><op>42</op></state></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><state><op>42</op></state></data></rpc-reply>]]>]]>$"
@ -254,7 +254,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><ex:empty/></rpc>]]>]]>"
new "netconf client-side rpc" new "netconf client-side rpc"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><ex:client-rpc><request>example</request></ex:client-rpc></rpc>]]>]]>" "^<rpc-reply><result>ok</result></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><ex:client-rpc><request>example</request></ex:client-rpc></rpc>]]>]]>" "^<rpc-reply><result>ok</result></rpc-reply>]]>]]>$"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -139,7 +139,7 @@ new "get each ordered-by user leaf-list"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='b']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='b']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
new "delete candidate" new "delete candidate"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><delete-config><target><candidate/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# LEAF_LISTS # LEAF_LISTS
@ -172,7 +172,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><can
new "verify list user order (as entered)" new "verify list user order (as entered)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/y2\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2><y2><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/y2\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2><y2><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -173,7 +173,7 @@ expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><
new "Kill restconf daemon" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -52,14 +52,6 @@ module example{
} }
rpc empty { rpc empty {
} }
rpc input {
input {
}
}
rpc output {
output {
}
}
rpc client-rpc { rpc client-rpc {
description "Example local client-side rpc"; description "Example local client-side rpc";
input { input {
@ -104,12 +96,15 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf sudo pkill -u www-data clixon_restconf
new "start restconf daemon" new "start restconf daemon"
sudo su -c "$clixon_restconf -f $cfg -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 sleep $RCWAIT
new "restconf tests" 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)" new2 "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'> expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
<Link rel='restconf' href='/restconf'/> <Link rel='restconf' href='/restconf'/>
@ -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)" 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)" new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
expect='<operations><empty xmlns="urn:example:clixon"/><input xmlns="urn:example:clixon"/><output xmlns="urn:example:clixon"/><client-rpc xmlns="urn:example:clixon"/><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><route-count xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/></operations>' expect='<operations><client-rpc xmlns="urn:example:clixon"/><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><route-count xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><empty xmlns="urn:example:clixon"/></operations>'
match=`echo $ret | grep -EZo "$expect"` match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" 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" new "restconf empty rpc"
expecteq "$(curl -s -X POST -d {\"input\":null} http://localhost/restconf/operations/example:empty)" "" 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" new2 "restconf get empty config + state json"
expecteq "$(curl -sSG http://localhost/restconf/data/state)" '{"state": {"op": "42"}} 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" 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" new "restconf get empty config + state xml"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state) 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' 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" 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 # 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" 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"}} 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" 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" 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)" "" expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
new "Add nothing using POST" 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" 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}]}} 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 expectfn 'curl -s -G http://localhost/restconf/data' 0 $state
new2 "restconf Re-Delete eth/0/0 using none should generate error" 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" 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)" "" 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" 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 # Cant get this to work due to quoting
#new2 "restconf rpc using POST wrong JSON" #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" 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": {"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 '{"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 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 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" 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" 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" 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" 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) 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="<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>" expect="<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop><source-protocol>static</source-protocol></route></output>"
match=`echo $ret | grep -EZo "$expect"` match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi fi
new2 "restconf rpc using wrong prefix" 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" 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) 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" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -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"}' expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" 0 '{"type": "regular"}'
new "restconf POST interface without mandatory type" 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" new "restconf POST interface"
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 "" expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 ""
new2 "restconf POST again" 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" 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" new "restconf DELETE"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 "" 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 "" 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" 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" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

125
test/test_rpc.sh Executable file
View file

@ -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 <<EOF > $cfg
<config xmlns="urn:example:clixon">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>$APPNAME</CLICON_YANG_MODULE_MAIN>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
</config>
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 '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}}
'
new2 "restconf example rpc json/xml"
# <output xmlns="rn:example:clixon"
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '<output><x>0</x><y>42</y></output>
'
new2 "restconf example rpc xml/xml"
# <output xmlns="rn:example:clixon"
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '<output><x>0</x><y>42</y></output>
'
new "netconf example rpc"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example><x>0</x></example></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x>0</x><y>42</y></rpc-reply>]]>]]>$'
# 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 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>$'
# edit-config?
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

View file

@ -150,10 +150,10 @@ new "netconf EXAMPLE subscription with filter classifier"
expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
new "netconf NONEXIST subscription" new "netconf NONEXIST subscription"
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
new "netconf EXAMPLE subscription with wrong date" new "netconf EXAMPLE subscription with wrong date"
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>bad-element</error-tag><error-type>application</error-type><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Expected timestamp</error-message></rpc-error></rpc-reply>]]>]]>$' 0 expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Expected timestamp</error-message></rpc-error></rpc-reply>]]>]]>$' 0
#new "netconf EXAMPLE subscription with replay" #new "netconf EXAMPLE subscription with replay"
#NOW=$(date +"%Y-%m-%dT%H:%M:%S") #NOW=$(date +"%Y-%m-%dT%H:%M:%S")
@ -176,7 +176,7 @@ sleep 2
# Restconf stream subscription RFC8040 Sec 6.3 # Restconf stream subscription RFC8040 Sec 6.3
# Start Subscription w error # Start Subscription w error
new "restconf monitor event nonexist stream" 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 '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-tag>invalid-value</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>No such stream</error-message></error></errors>' 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 '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></error></errors>' 2
# 2a) start subscription 8s - expect 1-2 notifications # 2a) start subscription 8s - expect 1-2 notifications
new "2a) start subscriptions 8s - expect 1-2 notifications" new "2a) start subscriptions 8s - expect 1-2 notifications"
@ -278,7 +278,7 @@ sleep 5
new "Kill restconf daemon" new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf" sudo pkill -u www-data -f "/www-data/clixon_restconf"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -217,7 +217,7 @@ new "netconf set transitive string error"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><talle>9xx</talle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><talle>9xx</talle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>"
new "netconf validate should fail" new "netconf validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>validation of talle failed regexp match fail" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>talle</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail: "9xx" does not match \[a-z\]\[0-9\]\*</error-message></rpc-error></rpc-reply>]]>]]>$'
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -244,7 +244,7 @@ new "netconf set transitive union error int"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><ulle>55</ulle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><ulle>55</ulle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>"
new "netconf validate should fail" new "netconf validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>validation of ulle failed" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>ulle</bad-element></error-info><error-severity>error</error-severity><error-message>'55' does not match enumeration</error-message></rpc-error></rpc-reply>]]>]]>$"
new "netconf discard-changes" new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -303,7 +303,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><can
new "cli bits validate" new "cli bits validate"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$" expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -100,7 +100,7 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 33" 0 "^$"
new "cli set transitive union error" new "cli set transitive union error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle kalle" 255 "" expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle kalle" 255 ""
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -118,7 +118,7 @@ new "when get config"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><whenex><type>direct</type><name>r2</name><static-routes/></whenex><whenex><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><whenex><type>direct</type><name>r2</name><static-routes/></whenex><whenex><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$"
new "when: validate fail" new "when: validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>xpath static-routes validation failed</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
new "when: discard-changes" new "when: discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -133,15 +133,15 @@ new "must: add atm interface"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "must: atm validate fail" new "must: atm validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$"
new "must: add eth interface" new "must: add eth interface"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "must: eth validate fail" new "must: eth validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -212,7 +212,7 @@ expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
new "cli show leaf-list" new "cli show leaf-list"
expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "<e>foo</e>" expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "<e>foo</e>"
new "netconf set state data (not allowed)" new "netconf set state data (not allowed)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value"
new "netconf set presence and not present" new "netconf set presence and not present"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -233,7 +233,7 @@ new "netconf validate anyxml"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf delete candidate" new "netconf delete candidate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><delete-config><target><candidate/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Check 3-keys # Check 3-keys
new "netconf add one 3-key entry" new "netconf add one 3-key entry"
@ -262,7 +262,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><cand
# clear db for next test # clear db for next test
new "netconf delete candidate" new "netconf delete candidate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><delete-config><target><candidate/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit empty candidate" new "netconf commit empty candidate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -279,7 +279,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candid
new "netconf submodule discard-changes" new "netconf submodule discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -88,12 +88,12 @@ new "1. Set newex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set oldex should fail (since oldex is in old revision and only the new is loaded)" new "Set oldex should fail (since oldex is in old revision and only the new is loaded)"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
new "Set other should fail" new "Set other should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>application</error-type><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi
@ -140,10 +140,10 @@ new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set newex should fail" new "Set newex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
new "Set other should fail" new "Set other should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
new "Kill backend" new "Kill backend"
# Check if premature kill # Check if premature kill
@ -184,10 +184,10 @@ new "Set newex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set oldex should fail" new "Set oldex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other should fail" new "Set other should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Kill backend" new "Kill backend"
# Check if premature kill # Check if premature kill
@ -229,10 +229,10 @@ new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set newex should fail" new "Set newex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other should fail" new "Set other should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Kill backend" new "Kill backend"
# Check if premature kill # Check if premature kill
@ -273,7 +273,7 @@ new "Set newex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set oldex should fail" new "Set oldex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other" new "Set other"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
@ -318,7 +318,7 @@ new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set newex should fail" new "Set newex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other" new "Set other"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
@ -365,7 +365,7 @@ new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set newex should fail" new "Set newex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other" new "Set other"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
@ -411,10 +411,10 @@ new "Set oldex"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "Set newex should fail" new "Set newex should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Set other should fail" new "Set other should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-message>XML' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
new "Kill backend" new "Kill backend"
# Check if premature kill # Check if premature kill

View file

@ -122,7 +122,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><cand
# rpc # rpc
if [ $BE -ne 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi
new "Kill backend" new "Kill backend"