* Proper RFC 6241 Netconf error handling

* New functions added in clixon_netconf_lib.[ch]
  * Datastore code modified for RFC 6241
  * Remaining: validate, generic restconf and netconf code
This commit is contained in:
Olof hagsand 2018-03-18 18:06:02 +00:00
parent 52e510cfdf
commit efa72e9e6f
26 changed files with 1196 additions and 475 deletions

View file

@ -5,8 +5,14 @@
### Major changes:
* Restconf error handling for get, put and post. Several cornercases remain. Available both as xml and json (set accept header), pretty-printed and not (set clixon config option).
* Proper RFC 6241 Netconf error handling
* New functions added in clixon_netconf_lib.[ch]
* Datastore code modified for RFC 6241
* Remaining: validate, generic restconf and netconf code
### Minor changes:
* The key-value datastore is no longer supported. Use the default text datastore.
* Add username to rpc calls to prepare for authorization for backend:
clicon_rpc_config_get(h, db, xpath, xt) --> clicon_rpc_config_get(h, db, xpath, username, xt)
clicon_rpc_get(h, xpath, xt) --> clicon_rpc_get(h, xpath, username, xt)

View file

@ -180,7 +180,7 @@ backend_client_rm(clicon_handle h,
return backend_client_delete(h, ce); /* actually purge it */
}
/*! FInd target/source in netconf request. Assume sanity made so not finding is error */
/*! Find target/source in netconf request. Assume sanity- not finding is error */
static char*
netconf_db_find(cxobj *xn,
char *name)
@ -214,31 +214,28 @@ from_client_get_config(clicon_handle h,
cxobj *xfilter;
char *selector = "/";
cxobj *xret = NULL;
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "source")) == NULL){
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "No such database: %s", db);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((selector = xml_find_value(xfilter, "select"))==NULL)
selector="/";
if (xmldb_get(h, db, selector, 1, &xret) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply>");
@ -254,6 +251,8 @@ from_client_get_config(clicon_handle h,
ok:
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
if (xret)
xml_free(xret);
return retval;
@ -276,18 +275,15 @@ from_client_get(clicon_handle h,
char *selector = "/";
cxobj *xret = NULL;
int ret;
cbuf *cbx = NULL; /* Assist cbuf */
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((selector = xml_find_value(xfilter, "select"))==NULL)
selector="/";
/* Get config */
if (xmldb_get(h, "running", selector, 0, &xret) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
/* Get state data from plugins as defined by plugin_statedata(), if any */
@ -308,23 +304,25 @@ from_client_get(clicon_handle h,
cprintf(cbret, "</rpc-reply>");
}
else { /* 1 Error from callback */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Internal error:%s</error-message>"
"</rpc-error></rpc-reply>", clicon_err_reason);
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "Internal error:%s", clicon_err_reason);
if (netconf_operation_failed(cbret, "rpc", cbuf_get(cbx))< 0)
goto done;
clicon_log(LOG_NOTICE, "%s Error in backend_statedata_call:%s", __FUNCTION__, xml_name(xe));
}
ok:
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
if (xret)
xml_free(xret);
return retval;
}
/*! Internal message: edit-config
*
* @param[in] h Clicon handle
@ -340,14 +338,13 @@ from_client_edit_config(clicon_handle h,
{
int retval = -1;
char *target;
cbuf *cb = NULL;
cxobj *xret = NULL;
cxobj *xc;
cxobj *x;
enum operation_type operation = OP_MERGE;
int piddb;
int non_config = 0;
yang_spec *yspec;
cbuf *cbx = NULL; /* Assist cbuf */
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -357,50 +354,44 @@ from_client_edit_config(clicon_handle h,
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
cprintf(cbx, "No such database: %s", target);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Operation failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
goto done;
goto ok;
}
if ((x = xpath_first(xn, "default-operation")) != NULL){
if (xml_operation(xml_body(x), &operation) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
if (netconf_invalid_value(cbret, "protocol", "Wrong operation")< 0)
goto done;
goto ok;
}
}
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)
goto done;
goto ok;
}
else{
if (xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0)
goto done;
if (non_config){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>state data not allowed</error-message>"
"</rpc-error></rpc-reply>");
if (netconf_invalid_value(cbret, "protocol", "State data not allowed")< 0)
goto done;
goto ok;
}
/* Cant do this earlier since we dont have a yang spec to
@ -408,35 +399,23 @@ from_client_edit_config(clicon_handle h,
*/
if (xml_child_sort && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
if (xmldb_put(h, target, operation, xc) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>", clicon_err_reason);
if (xmldb_put(h, target, operation, xc, cbret) < 0){
clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
}
else{
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>config</bad-element></error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
if (!cbuf_len(cbret))
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
retval = 0;
done:
if (xret)
xml_free(xret);
if (cb)
cbuf_free(cb);
if (cbx)
cbuf_free(cbx);
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
return retval;
}
} /* from_client_edit_config */
/*! Internal message: Lock database
*
@ -454,26 +433,23 @@ from_client_lock(clicon_handle h,
int retval = -1;
char *db;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
goto done;
goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
cprintf(cbx, "No such database: %s", db);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
/*
* A lock MUST not be granted if either of the following conditions is true:
* 1) A lock is already held by any NETCONF session or another entity.
@ -482,23 +458,21 @@ from_client_lock(clicon_handle h,
*/
piddb = xmldb_islocked(h, db);
if (piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Lock failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
goto done;
goto ok;
}
else{
xmldb_lock(h, db, pid);
if (xmldb_lock(h, db, pid) < 0)
goto done;
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
}
ok:
retval = 0;
// done:
done:
if (cbx)
cbuf_free(cbx);
return retval;
}
@ -518,23 +492,21 @@ from_client_unlock(clicon_handle h,
int retval = -1;
char *db;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
goto done;
goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
cprintf(cbx, "No such database: %s", db);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
piddb = xmldb_islocked(h, db);
@ -546,14 +518,9 @@ from_client_unlock(clicon_handle h,
* session that obtained the lock
*/
if (piddb==0 || piddb != pid){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Unlock failed, lock is already held</error-message>"
"<error-info><session-id>pid=%d piddb=%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
pid, piddb);
cprintf(cbx, "<session-id>pid=%d piddb=%d</session-id>", pid, piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Unlock failed, lock is already held") < 0)
goto done;
goto ok;
}
else{
@ -588,12 +555,8 @@ from_client_kill_session(clicon_handle h,
if ((x = xml_find(xe, "session-id")) == NULL ||
(str = xml_find_value(x, "body")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>session-id</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>session-id</bad-element>", NULL) < 0)
goto done;
goto ok;
}
pid = atoi(str);
@ -618,18 +581,14 @@ from_client_kill_session(clicon_handle h,
xmldb_unlock(h, db);
}
else{ /* failed to kill client */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Faile to kill session</error-message>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", "Failed to kill session")< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
done:
return retval;
}
@ -651,70 +610,53 @@ from_client_copy_config(clicon_handle h,
char *target;
int retval = -1;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
if ((source = netconf_db_find(xe, "source")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>source</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0)
goto done;
goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xmldb_validate_db(source) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", source);
cprintf(cbx, "No such database: %s", source);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
if ((target = netconf_db_find(xe, "target")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
goto done;
goto ok;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
cprintf(cbx, "No such database: %s", target);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Operation failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Copy failed, lock is already held") < 0)
goto done;
goto ok;
}
if (xmldb_copy(h, source, target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
done:
if (cbx)
cbuf_free(cbx);
return retval;
}
@ -735,64 +677,48 @@ from_client_delete_config(clicon_handle h,
int retval = -1;
char *target;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
if ((target = netconf_db_find(xe, "target")) == NULL||
strcmp(target, "running")==0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
goto done;
goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
cprintf(cbx, "No such database: %s", target);
if (netconf_invalid_value(cbret, "protocol", cbuf_get(cbx))< 0)
goto done;
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Operation failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
goto done;
goto ok;
}
if (xmldb_delete(h, target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info>Internal error</error-info>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>", clicon_err_reason);
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
if (xmldb_create(h, target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info>Internal error</error-info>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>", clicon_err_reason);
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
done:
if (cbx)
cbuf_free(cbx);
return retval;
}
@ -829,13 +755,8 @@ from_client_create_subscription(clicon_handle h,
if ((ftype = xml_find_value(x, "type")) != NULL){
/* Only accept xpath as filter type */
if (strcmp(ftype, "xpath") != 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-message>only xpath filter type supported</error-message>"
"<error-info>type</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", "Only xpath filter type supported")< 0)
goto done;
goto ok;
}
}
@ -866,12 +787,8 @@ from_client_debug(clicon_handle h,
char *valstr;
if ((valstr = xml_find_body(xe, "level")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>level</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "application", "<bad-element>level</bad-element>", NULL) < 0)
goto done;
goto ok;
}
level = atoi(valstr);
@ -882,7 +799,7 @@ from_client_debug(clicon_handle h,
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
//done:
done:
return retval;
}
@ -917,23 +834,13 @@ from_client_msg(clicon_handle h,
goto done;
}
if (clicon_msg_decode(msg, &xt) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>rpc expected</error-message>"
"<error-info>Not recognized</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_malformed_message(cbret, "Not recognized, rpc expected")< 0)
goto done;
goto reply;
}
if ((x = xpath_first(xt, "/rpc")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>rpc expected</error-message>"
"<error-info>Not recognized</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_malformed_message(cbret, "Not recognized, rpc expected")< 0)
goto done;
goto reply;
}
xe = NULL;
@ -977,12 +884,8 @@ from_client_msg(clicon_handle h,
}
else if (strcmp(name, "validate") == 0){
if ((db = netconf_db_find(xe, "source")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>source</bad-element></error-info>"
"</rpc-error></rpc-reply>");
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0)
goto done;
goto reply;
}
if (from_client_validate(h, db, cbret) < 0)
@ -1007,34 +910,22 @@ from_client_msg(clicon_handle h,
else{
clicon_err_reset();
if ((ret = backend_rpc_cb_call(h, xe, ce, cbret)) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Internal error:%s</error-message>"
"</rpc-error></rpc-reply>", clicon_err_reason);
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
clicon_log(LOG_NOTICE, "%s Error in backend_rpc_call:%s", __FUNCTION__, xml_name(xe));
goto reply; /* Dont quit here on user callbacks */
}
if (ret == 0) /* not handled by callback */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"<error-info>Not recognized</error-info>"
"</rpc-error></rpc-reply>",
name);
if (ret == 0){ /* not handled by callback */
if (netconf_operation_failed(cbret, "application", "Callback not recognized")< 0)
goto done;
goto reply;
}
}
}
reply:
if (cbuf_len(cbret) == 0)
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Internal error %s</error-message>"
"</rpc-error></rpc-reply>",clicon_err_reason);
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
clicon_debug(1, "%s cbret:%s", __FUNCTION__, cbuf_get(cbret));
if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
switch (errno){

View file

@ -241,7 +241,7 @@ candidate_commit(clicon_handle h,
/* Optionally write (potentially modified) tree back to candidate */
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
if (xmldb_put(h, candidate, OP_REPLACE, td->td_target) < 0)
if (xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL) < 0)
goto done;
/* 8. Success: Copy candidate to running
*/
@ -282,35 +282,32 @@ from_client_commit(clicon_handle h,
{
int retval = -1;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
/* Check if target locked by other client */
piddb = xmldb_islocked(h, "running");
if (piddb && mypid != piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Operation failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
goto done;
goto ok;
}
if (candidate_commit(h, "candidate") < 0){ /* Assume validation fail, nofatal */
clicon_debug(1, "Commit candidate failed");
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
if (netconf_invalid_value(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
done:
if (cbx)
cbuf_free(cbx);
return retval; /* may be zero if we ignoring errors from commit */
} /* from_client_commit */
@ -328,33 +325,31 @@ from_client_discard_changes(clicon_handle h,
{
int retval = -1;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
/* Check if target locked by other client */
piddb = xmldb_islocked(h, "candidate");
if (piddb && mypid != piddb){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Operation failed, lock is already held</error-message>"
"<error-info><session-id>%d</session-id></error-info>"
"</rpc-error></rpc-reply>",
piddb);
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbx, "<session-id>%d</session-id>", piddb);
if (netconf_lock_denied(cbret, cbuf_get(cbx), "Operation failed, lock is already held") < 0)
goto done;
goto ok;
}
if (xmldb_copy(h, "running", "candidate") < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
done:
if (cbx)
cbuf_free(cbx);
return retval; /* may be zero if we ignoring errors from commit */
}
@ -374,11 +369,8 @@ from_client_validate(clicon_handle h,
transaction_data_t *td = NULL;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
if (netconf_invalid_value(cbret, "protocol", "No such database")< 0)
goto done;
goto ok;
}
clicon_debug(1, "Validate %s", db);
@ -390,18 +382,13 @@ from_client_validate(clicon_handle h,
if (validate_common(h, db, td) < 0){
clicon_debug(1, "Validate %s failed", db);
/* XXX: candidate_validate should have proper error handling */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
goto ok;
}
/* Optionally write (potentially modified) tree back to candidate */
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
if (xmldb_put(h, "candidate", OP_REPLACE, td->td_target) < 0)
if (xmldb_put(h, "candidate", OP_REPLACE, td->td_target, NULL) < 0)
goto done;
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:

View file

@ -174,8 +174,8 @@ db_merge(clicon_handle h,
/* Get data as xml from db1 */
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
goto done;
/* Merge xml into db2. WIthout commit */
if (xmldb_put(h, (char*)db2, OP_MERGE, xt) < 0)
/* Merge xml into db2. Without commit */
if (xmldb_put(h, (char*)db2, OP_MERGE, xt, NULL) < 0)
goto done;
retval = 0;
done:
@ -283,7 +283,7 @@ load_extraxml(clicon_handle h,
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0)
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
goto done;
retval = 0;
done:
@ -703,13 +703,12 @@ main(int argc, char **argv)
if ((xml_pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) >= 0)
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)xml_pretty) < 0)
goto done;
/* If startup mode is not defined, eg via OPTION or -s, assume old method */
/* Startup mode needs to be defined, */
startup_mode = clicon_startup_mode(h);
if (startup_mode == -1){
clicon_log(LOG_ERR, "Startup mode undefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend.\n");
goto done;
}
else {
/* Init running db if it is not there
*/
if (xmldb_exists(h, "running") != 1)
@ -736,10 +735,9 @@ main(int argc, char **argv)
/* Initiate the shared candidate. */
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
/* Call plugin_start with user -- options */
/* Call backend plugin_start with user -- options */
if (plugin_start_useroptions(h, argv0, argc, argv) <0)
goto done;
}
if (once)
goto done;

View file

@ -100,13 +100,9 @@ process_incoming_packet(clicon_handle h,
/* Parse incoming XML message */
if (xml_parse_string(str, NULL, &xreq) < 0){
if ((cbret = cbuf_new()) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>internal error</error-message>"
"</rpc-error></rpc-reply>");
netconf_output(1, cb, "rpc-error");
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
}
else
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);

View file

@ -42,7 +42,6 @@
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <fcgi_stdio.h>
#include <signal.h>
#include <dlfcn.h>
#include <sys/param.h>
@ -55,6 +54,8 @@
/* clicon */
#include <clixon/clixon.h>
#include <fcgi_stdio.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h"
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code

View file

@ -54,7 +54,7 @@
#include <syslog.h>
#include <fcntl.h>
#include <time.h>
#include <fcgi_stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
@ -66,6 +66,8 @@
/* clicon */
#include <clixon/clixon.h>
#include <fcgi_stdio.h> /* Need to be after clixon_xml-h due to attribute format */
/* restconf */
#include "restconf_lib.h"
#include "restconf_methods.h"
@ -208,9 +210,9 @@ api_well_known(clicon_handle h,
FCGX_FPrintF(r->out, "Content-Type: application/xrd+xml\r\n");
FCGX_FPrintF(r->out, "\r\n");
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\r\n");
FCGX_FPrintF(r->out, " <Link rel='restconf' href='/restconf'/>\r\n");
FCGX_FPrintF(r->out, "</XRD>\r\n");
FCGX_FPrintF(r->out, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\n");
FCGX_FPrintF(r->out, " <Link rel='restconf' href='/restconf'/>\n");
FCGX_FPrintF(r->out, "</XRD>\n");
return 0;
}
@ -258,7 +260,7 @@ api_root(clicon_handle h,
if (xml2json_cbuf(cb, xt, pretty) < 0)
goto done;
FCGX_FPrintF(r->out, "%s", cb?cbuf_get(cb):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
FCGX_FPrintF(r->out, "\n\n");
retval = 0;
done:
if (cb)
@ -307,8 +309,8 @@ api_yang_library_version(clicon_handle h,
goto done;
}
clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb));
FCGX_FPrintF(r->out, "%s\r\n", cb?cbuf_get(cb):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):"");
FCGX_FPrintF(r->out, "\n\n");
retval = 0;
done:
if (cb)

View file

@ -104,7 +104,6 @@ Mapping netconf error-tag -> status code
#include <fcntl.h>
#include <assert.h>
#include <time.h>
#include <fcgi_stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
@ -115,6 +114,8 @@ Mapping netconf error-tag -> status code
/* clicon */
#include <clixon/clixon.h>
#include <fcgi_stdio.h> /* Need to be after clixon_xml-h due to attribute format */
#include "restconf_lib.h"
#include "restconf_methods.h"
@ -157,6 +158,7 @@ api_return_err(clicon_handle h,
int retval = -1;
cbuf *cb = NULL;
cxobj *xtag;
char *tagstr;
int code;
const char *reason_phrase;
@ -167,7 +169,8 @@ api_return_err(clicon_handle h,
notfound(r); /* bad reply? */
goto ok;
}
code = restconf_err2code(xml_body(xtag));
tagstr = xml_body(xtag);
code = restconf_err2code(tagstr);
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
if (xml_name_set(xerr, "error") < 0)
@ -183,22 +186,29 @@ api_return_err(clicon_handle h,
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
use_xml?"xml":"json");
if (use_xml){
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">%s", cbuf_get(cb), pretty?"\r\n":"");
if (pretty){
FCGX_FPrintF(r->out, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, " </errors>\r\n");
FCGX_FPrintF(r->out, " </errors>\n");
}
else {
FCGX_FPrintF(r->out, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">", cbuf_get(cb));
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "</errors>\n");
}
}
else{
if (pretty){
FCGX_FPrintF(r->out, "{\r\n");
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : %s\r\n",
FCGX_FPrintF(r->out, "{\n");
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : %s\n",
cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
FCGX_FPrintF(r->out, "}\n");
}
else{
FCGX_FPrintF(r->out, "{");
FCGX_FPrintF(r->out, "\"ietf-restconf:errors\" : ");
FCGX_FPrintF(r->out, "%s", cbuf_get(cb));
FCGX_FPrintF(r->out, "}\r\n");
FCGX_FPrintF(r->out, "}\n");
}
}
ok:
@ -330,7 +340,7 @@ api_data_get2(clicon_handle h,
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
FCGX_FPrintF(r->out, "\n\n");
ok:
retval = 0;
done:
@ -1010,9 +1020,9 @@ api_operations_get(clicon_handle h,
clicon_debug(1, "%s ret:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
FCGX_FPrintF(r->out, "\r\n");
FCGX_FPrintF(r->out, "\n");
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
FCGX_FPrintF(r->out, "\n\n");
// ok:
retval = 0;
done:
@ -1183,7 +1193,7 @@ api_operations_post(clicon_handle h,
goto done;
clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
FCGX_FPrintF(r->out, "\n\n");
}
ok:
retval = 0;

View file

@ -124,6 +124,7 @@ main(int argc, char **argv)
cxobj *xt = NULL;
int i;
char *xpath;
cbuf *cbret = NULL;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
@ -261,7 +262,9 @@ main(int argc, char **argv)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
if (xmldb_put(h, db, op, xt) < 0)
if ((cbret = cbuf_new()) == NULL)
goto done;
if (xmldb_put(h, db, op, xt, cbret) < 0)
goto done;
}
else if (strcmp(cmd, "copy")==0){
@ -325,6 +328,8 @@ main(int argc, char **argv)
if (xmldb_plugin_unload(h) < 0)
goto done;
done:
if (cbret)
cbuf_free(cbret);
if (xt)
xml_free(xt);
if (h)

View file

@ -560,6 +560,7 @@ text_get(xmldb_handle xh,
* @param[in] x0p Parent of x0
* @param[in] x1 xml tree which modifies base
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[out] cbret Initialized cligen buffer. Contains return XML or "".
* Assume x0 and x1 are same on entry and that y is the spec
* @see put in clixon_keyvalue.c
*/
@ -568,7 +569,8 @@ text_modify(cxobj *x0,
yang_node *y0,
cxobj *x0p,
cxobj *x1,
enum operation_type op)
enum operation_type op,
cbuf *cbret)
{
int retval = -1;
char *opstr;
@ -594,8 +596,9 @@ text_modify(cxobj *x0,
switch(op){
case OP_CREATE:
if (x0){
clicon_err(OE_XML, 0, "Object to create already exists");
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto ok;
}
case OP_NONE: /* fall thru */
case OP_MERGE:
@ -631,8 +634,9 @@ text_modify(cxobj *x0,
break;
case OP_DELETE:
if (x0==NULL){
clicon_err(OE_XML, 0, "Object to delete does not exist");
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
}
case OP_REMOVE: /* fall thru */
if (x0){
@ -647,8 +651,9 @@ text_modify(cxobj *x0,
switch(op){
case OP_CREATE:
if (x0){
clicon_err(OE_XML, 0, "Object to create already exists");
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto ok;
}
case OP_REPLACE: /* fall thru */
if (x0){
@ -704,14 +709,18 @@ text_modify(cxobj *x0,
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
yc = yang_find_datanode(y0, x1cname);
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op) < 0)
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
}
break;
case OP_DELETE:
if (x0==NULL){
clicon_err(OE_XML, 0, "Object to delete does not exist");
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
}
case OP_REMOVE: /* fall thru */
if (x0)
@ -721,27 +730,29 @@ text_modify(cxobj *x0,
break;
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
// ok:
xml_sort(x0p, NULL);
ok:
retval = 0;
done:
if (x0vec)
free(x0vec);
return retval;
}
} /* text_modify */
/*! Modify a top-level base tree x0 with modification tree x1
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] x1 xml tree which modifies base
* @param[in] yspec Top-level yang spec (if y is NULL)
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[out] cbret Initialized cligen buffer. Contains return XML or "".
* @see text_modify
*/
static int
text_modify_top(cxobj *x0,
cxobj *x1,
yang_spec *yspec,
enum operation_type op)
enum operation_type op,
cbuf *cbret)
{
int retval = -1;
char *x1cname; /* child name */
@ -775,7 +786,9 @@ text_modify_top(cxobj *x0,
else /* base tree empty */
switch(op){
case OP_DELETE:
clicon_err(OE_XML, 0, "Object to delete does not exist");
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
break;
default:
break;
@ -799,13 +812,17 @@ text_modify_top(cxobj *x0,
/* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0)
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op, cbret) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
}
ok:
retval = 0;
done:
return retval;
}
} /* text_modify_top */
/*! For containers without presence and no children, remove
* @param[in] x XML tree node
@ -852,7 +869,8 @@ int
text_put(xmldb_handle xh,
const char *db,
enum operation_type op,
cxobj *x1)
cxobj *x1,
cbuf *cbret)
{
int retval = -1;
struct text_handle *th = handle(xh);
@ -863,7 +881,15 @@ text_put(xmldb_handle xh,
yang_spec *yspec;
cxobj *x0 = NULL;
struct db_element *de = NULL;
int cbretlocal = 0; /* Set if cbret is NULL on entry */
if (cbret == NULL){
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cbretlocal++;
}
if ((yspec = th->th_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
@ -928,8 +954,11 @@ text_put(xmldb_handle xh,
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
if (text_modify_top(x0, x1, yspec, op) < 0)
if (text_modify_top(x0, x1, yspec, op, cbret) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
@ -979,8 +1008,11 @@ text_put(xmldb_handle xh,
}
else if (clicon_xml2file(f, x0, 0, th->th_pretty) < 0)
goto done;
ok:
retval = 0;
done:
if (cbretlocal && cbret)
cbuf_free(cbret);
if (f != NULL)
fclose(f);
if (dbfile)
@ -1387,7 +1419,7 @@ main(int argc,
op = OP_REMOVE;
else
usage(argv[0]);
if (xmldb_put(h, db, op, NULL, xn) < 0)
if (xmldb_put(h, db, op, NULL, xn, NULL) < 0)
goto done;
}
else

View file

@ -40,7 +40,7 @@
* Prototypes
*/
int text_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt);
int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(xmldb_handle h, const char *from, const char *to);
int text_lock(xmldb_handle h, const char *db, int pid);

View file

@ -83,6 +83,7 @@ transaction_commit(clicon_handle h,
if (debug)
for (i=0; i<len; i++) /* Loop over added i/fs */
xml_print(stdout, vec[i]); /* Print the added interface */
// done:
if (vec)
free(vec);
return 0;
@ -250,7 +251,7 @@ plugin_reset(clicon_handle h,
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0)
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
goto done;
retval = 0;
done:

View file

@ -107,7 +107,7 @@ fib_route_rpc(clicon_handle h,
/* User supplied variable in CLI command */
instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
/* Create XML for fib-route netconf RPC */
if (xml_parse_va(&xtop, NULL, "<rpc><fib-route><routing-instance-name>%s</routing-instance-name></fib-route></rpc>", instance) < 0)
if (xml_parse_va(&xtop, NULL, "<rpc><fib-route><routing-instance-name>%s</routing-instance-name></fib-route></rpc>", cv_string_get(instance)) < 0)
goto done;
/* Skip top-level */
xrpc = xml_child_i(xtop, 0);

View file

@ -0,0 +1,63 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Netconf library functions. See RFC6241
*
*/
#ifndef _CLIXON_NETCONF_LIB_H
#define _CLIXON_NETCONF_LIB_H
/*
* Prototypes
*/
int netconf_in_use(cbuf *cb, char *type, char *message);
int netconf_invalid_value(cbuf *cb, char *type, char *message);
int netconf_too_big(cbuf *cb, char *type, char *message);
int netconf_missing_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_missing_element(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_element(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
int netconf_access_denied(cbuf *cb, char *type, char *message);
int netconf_lock_denied(cbuf *cb, char *info, char *message);
int netconf_resource_denied(cbuf *cb, char *type, char *message);
int netconf_rollback_failed(cbuf *cb, char *type, char *message);
int netconf_data_exists(cbuf *cb, char *message);
int netconf_data_missing(cbuf *cb, char *message);
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
int netconf_operation_failed(cbuf *cb, char *type, char *message);
int netconf_malformed_message(cbuf *cb, char *message);
#endif /* _CLIXON_NETCONF_LIB_H */

View file

@ -142,8 +142,11 @@ int clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint);
int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint);
int xml_parse_file(int fd, char *endtag, yang_spec *yspec, cxobj **xt);
int xml_parse_string(const char *str, yang_spec *yspec, cxobj **xml_top);
#if defined(__GNUC__) && __GNUC__ >= 3
int xml_parse_va(cxobj **xt, yang_spec *yspec, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
#else
int xml_parse_va(cxobj **xt, yang_spec *yspec, const char *format, ...);
#endif
int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
int xml_copy_one(cxobj *xn0, cxobj *xn1);
int xml_copy(cxobj *x0, cxobj *x1);

View file

@ -78,7 +78,7 @@ typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop);
/* Type of xmldb put function */
typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt);
typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
/* Type of xmldb copy function */
typedef int (xmldb_copy_t)(xmldb_handle xh, const char *from, const char *to);
@ -139,7 +139,7 @@ int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt);
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, const char *db, int pid);
int xmldb_unlock(clicon_handle h, const char *db);

View file

@ -68,7 +68,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_json.c clixon_yang.c clixon_yang_type.c \
clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_netconf_lib.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \

View file

@ -0,0 +1,698 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Netconf library functions. See RFC6241
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_err.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
*
* The request requires a resource that already is in use.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_in_use(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>in-use</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf invalid-value error XML tree according to RFC 6241 Appendix A
*
* The request specifies an unacceptable value for one or more parameters.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_invalid_value(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf too-big error XML tree according to RFC 6241 Appendix A
*
* The request or response (that would be generated) is
* too large for the implementation to handle.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "transport", "rpc", "application", "protocol"
* @param[in] message Error message
*/
int
netconf_too_big(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>too-big</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf missing-attribute error XML tree according to RFC 6241 App A
*
* An expected attribute is missing.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] info bad-attribute or bad-element xml
* @param[in] message Error message
*/
int
netconf_missing_attribute(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf bad-attribute error XML tree according to RFC 6241 App A
*
* An attribute value is not correct; e.g., wrong type,
* out of range, pattern mismatch.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] info bad-attribute or bad-element xml
* @param[in] message Error message
*/
int
netconf_bad_attribute(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>bad-attribute</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf unknwon-attribute error XML tree according to RFC 6241 App A
*
* An unexpected attribute is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] info bad-attribute or bad-element xml
* @param[in] message Error message
*/
int
netconf_unknown_attribute(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-attribute</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
*
* An expected element is missing.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element xml
* @param[in] message Error message
*/
int
netconf_missing_element(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
*
* An element value is not correct; e.g., wrong type, out of range,
* pattern mismatch.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element xml
* @param[in] message Error message
*/
int
netconf_bad_element(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>bad-element</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
*
* An unexpected element is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element xml
* @param[in] message Error message
*/
int
netconf_unknown_element(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-element</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
*
* An unexpected namespace is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] info bad-element or bad-namespace xml
* @param[in] message Error message
*/
int
netconf_unknown_namespace(cbuf *cb,
char *type,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>unknown-namespace</error-tag>"
"<error-type>%s</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
type, info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
*
* An expected element is missing.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_access_denied(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>access-denied</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf lock-denied error XML tree according to RFC 6241 App A
*
* Access to the requested lock is denied because the lock is currently held
* by another entity.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] info session-id xml
* @param[in] message Error message
*/
int
netconf_lock_denied(cbuf *cb,
char *info,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>lock-denied</error-tag>"
"<error-type>protocol</error-type>"
"<error-info>%s</error-info>"
"<error-severity>error</error-severity>",
info) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf resource-denied error XML tree according to RFC 6241 App A
*
* An expected element is missing.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "transport, "rpc", "application", "protocol"
* @param[in] message Error message
*/
int
netconf_resource_denied(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>resource-denied</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf rollback-failed error XML tree according to RFC 6241 App A
*
* Request to roll back some configuration change (via rollback-on-error or
* <discard-changes> operations) was not completed for some reason.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_rollback_failed(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>rollback-failed</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf data-exists error XML tree according to RFC 6241 Appendix A
*
* Request could not be completed because the relevant
* data model content already exists. For example,
* a "create" operation was attempted on data that already exists.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] message Error message
*/
int
netconf_data_exists(cbuf *cb,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>data-exists</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>") <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A
*
* Request could not be completed because the relevant data model content
* does not exist. For example, a "delete" operation was attempted on
* data that does not exist.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] message Error message
*/
int
netconf_data_missing(cbuf *cb,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>data-missing</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>") <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
*
* Request could not be completed because the requested operation is not
* supported by this implementation.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_operation_not_supported(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>operation-not-supported</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
*
* Request could not be completed because the requested operation failed for
* some reason not covered by any other error condition.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] message Error message
*/
int
netconf_operation_failed(cbuf *cb,
char *type,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>%s</error-type>"
"<error-severity>error</error-severity>",
type) <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
*
* A message could not be handled because it failed to be parsed correctly.
* For example, the message is not well-formed XML or it uses an
* invalid character set.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] message Error message
* @note New in :base:1.1
*/
int
netconf_malformed_message(cbuf *cb,
char *message)
{
int retval = -1;
if (cprintf(cb, "<rpc-reply><rpc-error>"
"<error-tag>malformed-message</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>") <0)
goto err;
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
goto err;
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
goto err;
retval = 0;
done:
return retval;
err:
clicon_err(OE_XML, errno, "cprintf");
goto done;
}

View file

@ -57,7 +57,6 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
@ -1424,10 +1423,7 @@ xml_parse_va(cxobj **xtop,
va_start(args, format);
len = vsnprintf(str, len, format, args) + 1;
va_end(args);
if (*xtop == NULL)
if ((*xtop = xml_new(XML_TOP_SYMBOL, NULL, NULL)) == NULL)
goto done;
if (_xml_parse(str, yspec, *xtop) < 0)
if (xml_parse_string(str, yspec, xtop) < 0)
goto done;
retval = 0;
done:

View file

@ -322,7 +322,7 @@ xmldb_setopt(clicon_handle h,
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[out] xtop Single XML tree. Free with xml_free()
* @param[out] xret Single return XML tree. Free with xml_free()
* @retval 0 OK
* @retval -1 Error
* @code
@ -339,7 +339,7 @@ xmldb_get(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xtop)
cxobj **xret)
{
int retval = -1;
xmldb_handle xh;
@ -357,11 +357,11 @@ xmldb_get(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_get_fn(xh, db, xpath, config, xtop);
retval = xa->xa_get_fn(xh, db, xpath, config, xret);
#if DEBUG
if (retval == 0) {
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, *xtop, 0, 0);
clicon_xml2cbuf(cb, *xret, 0, 0);
clicon_log(LOG_WARNING, "%s: db:%s xpath:%s xml:%s",
__FUNCTION__, db, xpath, cbuf_get(cb));
cbuf_free(cb);
@ -377,25 +377,29 @@ xmldb_get(clicon_handle h,
* @param[in] db running or candidate
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[out] cbret Initialized cligen buffer or NULL. On exit contains XML or "".
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* cxobj *xret = NULL;
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
* err;
* if (xmldb_put(xh, "running", OP_MERGE, xt) < 0)
* if (xmldb_put(xh, "running", OP_MERGE, xt, cbret) < 0)
* err;
* @endcode
* @note that you can add both config data and state data. In comparison,
* xmldb_get has a parameter to get config data only.
* @note if xret is non-null, it may contain error message
*
*/
int
xmldb_put(clicon_handle h,
const char *db,
enum operation_type op,
cxobj *xt)
cxobj *xt,
cbuf *cbret)
{
int retval = -1;
xmldb_handle xh;
@ -425,7 +429,7 @@ xmldb_put(clicon_handle h,
cbuf_free(cb);
}
#endif
retval = xa->xa_put_fn(xh, db, op, xt);
retval = xa->xa_put_fn(xh, db, op, xt, cbret);
done:
return retval;
}

View file

@ -56,9 +56,9 @@ expectfn(){
expect2=
fi
ret=`$cmd`
if [ $? -ne 0 ]; then
err "wrong args"
fi
# if [ $? -ne 0 ]; then
# err "wrong args"
# fi
# Match if both are empty string
if [ -z "$ret" -a -z "$expect" ]; then
return
@ -68,9 +68,7 @@ expectfn(){
fi
# grep extended grep
match=`echo $ret | grep -EZo "$expect"`
# echo "ret:\"$ret\""
# echo "expect:\"$expect\""
# echo "match:\"$match\""
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
@ -82,6 +80,37 @@ expectfn(){
fi
}
# Similar to expectfn, but checks for equality and not only match
expecteq2(){
ret=$1
expect=$2
# Match if both are empty string
if [ -z "$ret" -a -z "$expect" ]; then
return
fi
if [ "$ret" != "$expect" ]; then
err "$expect" "$ret"
fi
}
# Similar to expectfn, but checks for equality and not only match
expecteq(){
cmd=$1
expect=$2
ret=$($cmd)
if [ $? -ne 0 ]; then
err "wrong args"
fi
# Match if both are empty string
if [ -z "$ret" -a -z "$expect" ]; then
return
fi
if [ "$ret" != "$expect" ]; then
err "$expect" "$ret"
fi
}
# clixon tester. First arg is command second is stdin and
# third is expected outcome
expecteof(){

View file

@ -55,7 +55,7 @@ run(){
rm -rf $mydir/*
conf="-d candidate -b $mydir -p ../datastore/$name/$name.so -y $dir -m ietf-ip"
echo "conf:$conf"
new "datastore $name init"
expectfn "$datastore $conf init" ""

View file

@ -88,8 +88,8 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<r
new "leafref add wrong ref"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<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"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>"
new "leafref validate XXX shouldnt really be operation-failed, more work in validate code"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag>"
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -109,7 +109,7 @@ new "leafref delete leaf"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<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)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>missing-attribute</error-tag>"
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag>"
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"

View file

@ -34,7 +34,7 @@ if [ $? -ne 0 ]; then
fi
new "start backend -s init -f $cfg"
# start new backend
sudo clixon_backend -s init -f $cfg
sudo clixon_backend -s init -f $cfg # -D 1
if [ $? -ne 0 ]; then
err
fi

View file

@ -63,7 +63,7 @@ if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg -y $fyang"
sudo clixon_backend -s init -f $cfg -y $fyang
sudo clixon_backend -s init -f $cfg -y $fyang # -D 1
if [ $? -ne 0 ]; then
err
fi
@ -72,28 +72,26 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
new "start restconf daemon"
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -D
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg # -D
sleep 1
new "restconf tests"
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expectfn "curl -s -X GET http://localhost/.well-known/host-meta" "<Link rel='restconf' href='/restconf'/>"
expecteq2 "$(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'/>
</XRD>"
new "restconf get restconf resource. RFC 8040 3.3 (json)"
expectfn "curl -sG http://localhost/restconf" '{"data": null,"operations": null,"yang-library-version": "2016-06-21"}}'
expecteq2 "$(curl -sG http://localhost/restconf)" '{"restconf": {"data": null,"operations": null,"yang-library-version": "2016-06-21"}}'
new "restconf get restconf resource. RFC 8040 3.3 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf)
expect="<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>"
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
# Get XML instead of JSON?
expecteq2 $(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf) '<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>'
new "restconf get restconf/operations. RFC8040 3.3.2"
expectfn "curl -sG http://localhost/restconf/operations" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"rt:fib-route": null,"rt:route-count": null}}'
expecteq2 "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"rt:fib-route": null,"rt:route-count": null}}'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
@ -104,7 +102,7 @@ if [ -z "$match" ]; then
fi
new "restconf get restconf/yang-library-version. RFC8040 3.3.3"
expectfn "curl -sG http://localhost/restconf/yang-library-version" '{"yang-library-version": "2016-06-21"}'
expecteq2 "$(curl -sG http://localhost/restconf/yang-library-version)" '{"yang-library-version": "2016-06-21"}'
new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/yang-library-version)
@ -122,10 +120,10 @@ expectfn "curl -s -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json"
new "restconf empty rpc"
expectfn 'curl -s -X POST -d {"input":{"name":""}} http://localhost/restconf/operations/ex:empty' '{"output": null}'
expecteq2 "$(curl -s -X POST -d {\"input\":{\"name\":\"\"}} http://localhost/restconf/operations/ex:empty)" '{"output": null}'
new "restconf get empty config + state json"
expectfn "curl -sSG http://localhost/restconf/data" "{\"data\": $state}"
expecteq2 "$(curl -sSG http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}'
new "restconf get empty config + state xml"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data)
@ -136,7 +134,7 @@ if [ -z "$match" ]; then
fi
new "restconf get data/interfaces-state/interface=eth0 json"
expectfn "curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0" '{"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}'
expecteq2 "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0)" '{"interface": [{"name": "eth0","type": "eth","if-index": 42}]}'
new "restconf get state operation eth0 xml"
# Cant get shell macros to work, inline matching from lib.sh
@ -148,8 +146,7 @@ if [ -z "$match" ]; then
fi
new "restconf get state operation eth0 type json"
expectfn "curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0/type" '{"type": "eth"}
$'
expecteq2 "$(curl -s -G http://localhost/restconf/data/interfaces-state/interface=eth0/type)" '{"type": "eth"}'
new "restconf get state operation eth0 type xml"
# Cant get shell macros to work, inline matching from lib.sh
@ -161,76 +158,78 @@ if [ -z "$match" ]; then
fi
new "restconf GET datastore"
expectfn "curl -s -X GET http://localhost/restconf/data" "data"
expecteq2 "$(curl -s -X GET http://localhost/restconf/data)" '{"data": {"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}'
# Exact match
new "restconf Add subtree to datastore using POST"
ret=$(curl -s -i -X POST -H "Accept: application/yang-data+json" -d '{"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}}' http://localhost/restconf/data)
expect="HTTP/1.1 200 OK"
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' 'HTTP/1.1 200 OK'
new "restconf Re-add subtree which should give error"
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":true}}} http://localhost/restconf/data' '{"ietf-restconf:errors" : {"error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Object to create already exists"}}}'
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"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"}}}'
# XXX Cant get this to work
#expecteq2 "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"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"}}}'
new "restconf Check interfaces eth/0/0 added"
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}
$'
new "restconf delete interfaces"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/interfaces' ""
expecteq2 $(curl -s -X DELETE http://localhost/restconf/data/interfaces) ""
new "restconf Check empty config"
expectfn "curl -sG http://localhost/restconf/data" $state
expectfn "curl -sG http://localhost/restconf/data" "$state"
new "restconf Add interfaces subtree eth/0/0 using POST"
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces' ""
# XXX cant get this to work
#expecteq2 "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"eth","enabled":true}}' http://localhost/restconf/data/interfaces)" ""
new "restconf Check eth/0/0 added"
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}
$'
expecteq 'curl -s -G http://localhost/restconf/data' '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}'
new "restconf Re-post eth/0/0 which should generate error"
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces' 'Object to create already exists'
expecteq 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"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"}}}'
new "Add leaf description using POST"
expectfn '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"
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "data is in some way badly formed"
new "restconf Check description added"
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": true}\]}
$'
expecteq "curl -s -G http://localhost/restconf/data" '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}'
new "restconf delete eth/0/0"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
expecteq 'curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
new "Check deleted eth/0/0"
expectfn 'curl -s -G http://localhost/restconf/data' $state
new "restconf Re-Delete eth/0/0 using none should generate error"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' '"error-message": "Object to delete does not exist"'
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"}}}'
new "restconf Add subtree eth/0/0 using PUT"
expectfn 'curl -s -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
expecteq 'curl -s -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":true}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
new "restconf get subtree"
expectfn "curl -s -G http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "eth","enabled": true}\]},"interfaces-state": {"interface": \[{"name": "eth0","type": "eth","if-index": 42}\]}}
$'
expecteq 'curl -s -G http://localhost/restconf/data' '{"data": {"interfaces": {"interface": [{"name": "eth/0/0","type": "eth","enabled": true}]},"interfaces-state": {"interface": [{"name": "eth0","type": "eth","if-index": 42}]}}}'
new "restconf rpc using POST json"
expectfn 'curl -s -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt: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"}} http://localhost/restconf/operations/rt:fib-route' '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}}'
new "restconf rpc using POST xml"
# Cant get shell macros to work, inline matching from lib.sh
ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt: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>"
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
# XXX cant get -H to work
#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>'
# Cant get shell macros to work, inline matching from lib.sh
new "Kill restconf daemon"
sudo pkill -u www-data clixon_restconf

View file

@ -84,10 +84,10 @@ new "restconf POST interface"
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' ""
new "restconf POST again"
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' "Object to create already exists"
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"}}}'
new "restconf POST from top"
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"TEST","type":"eth0"}}} http://localhost/restconf/data' "Object to create already exists"
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"}}}'
new "restconf DELETE"
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' ""