* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.
* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily. * Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK. * This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath` * Added new xml functions for specific types: `xml_child_nr_notype`, `xml_child_nr_notype`, `xml_child_i_type`, `xml_find_type`.
This commit is contained in:
parent
861300d6c0
commit
0baebc93fd
71 changed files with 2679 additions and 1573 deletions
|
|
@ -292,7 +292,7 @@ client_get_streams(clicon_handle h,
|
|||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 OK
|
||||
* @retval 1 Statedata callback failed
|
||||
* @retval 1 Statedata callback failed (clicon_err called)
|
||||
*/
|
||||
static int
|
||||
client_statedata(clicon_handle h,
|
||||
|
|
@ -309,15 +309,15 @@ client_statedata(clicon_handle h,
|
|||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
|
||||
(retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
|
||||
(retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
|
||||
(retval = yang_modules_state_get(h, yspec, xret)) != 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
|
||||
if ((retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040"))
|
||||
if ((retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895"))
|
||||
if ((retval = yang_modules_state_get(h, yspec, xret)) != 0)
|
||||
goto done;
|
||||
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
|
||||
goto done;
|
||||
/* Code complex to filter out anything that is outside of xpath */
|
||||
|
|
@ -339,7 +339,7 @@ client_statedata(clicon_handle h,
|
|||
/* reset flag */
|
||||
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
retval = 0; /* OK */
|
||||
done:
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
|
|
@ -363,7 +363,6 @@ from_client_get(clicon_handle h,
|
|||
char *xpath = "/";
|
||||
cxobj *xret = NULL;
|
||||
int ret;
|
||||
cbuf *cbx = NULL; /* Assist cbuf */
|
||||
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
||||
|
|
@ -379,33 +378,24 @@ from_client_get(clicon_handle h,
|
|||
clicon_err_reset();
|
||||
if ((ret = client_statedata(h, xpath, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* OK */
|
||||
cprintf(cbret, "<rpc-reply>");
|
||||
if (xret==NULL)
|
||||
cprintf(cbret, "<data/>");
|
||||
else{
|
||||
if (xml_name_set(xret, "data") < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbret, "</rpc-reply>");
|
||||
}
|
||||
else { /* 1 Error from callback */
|
||||
if ((cbx = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
if (ret == 1){ /* Error from callback (error in xret) */
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
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));
|
||||
goto ok;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply>"); /* OK */
|
||||
if (xret==NULL)
|
||||
cprintf(cbret, "<data/>");
|
||||
else{
|
||||
if (xml_name_set(xret, "data") < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbret, "</rpc-reply>");
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
|
|
@ -433,6 +423,7 @@ from_client_edit_config(clicon_handle h,
|
|||
int non_config = 0;
|
||||
yang_spec *yspec;
|
||||
cbuf *cbx = NULL; /* Assist cbuf */
|
||||
int ret;
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||
|
|
@ -491,24 +482,27 @@ from_client_edit_config(clicon_handle h,
|
|||
/* Cant do this earlier since we dont have a yang spec to
|
||||
* the upper part of the tree, until we get the "config" tree.
|
||||
*/
|
||||
if (xml_child_sort && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
if (clicon_xml_sort(h) && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
if (xmldb_put(h, target, operation, xc, cbret) < 0){
|
||||
if ((ret = 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;
|
||||
}
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
}
|
||||
assert(cbuf_len(cbret) == 0);
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
ok:
|
||||
if (!cbuf_len(cbret))
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
retval = 0;
|
||||
done:
|
||||
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
|
||||
|
|
|
|||
|
|
@ -165,12 +165,24 @@ validate_common(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
/* 2. Parse xml trees */
|
||||
/* 2. Parse xml trees
|
||||
* This is the state we are going from */
|
||||
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
|
||||
goto done;
|
||||
/* This is the state we are going to */
|
||||
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
|
||||
goto done;
|
||||
|
||||
/* Validate the target state. It is not completely clear this should be done
|
||||
* here. It is being made in generic_validate below.
|
||||
* But xml_diff requires some basic validation, at least check that yang-specs
|
||||
* have been assigned
|
||||
*/
|
||||
if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
||||
/* 3. Compute differences */
|
||||
if (xml_diff(yspec,
|
||||
td->td_src,
|
||||
|
|
@ -240,7 +252,7 @@ validate_common(clicon_handle h,
|
|||
* do something more drastic?
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] candidate A candidate database, not necessarily "candidate"
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval -1 Error - or validation failed
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
* @note Need to differentiate between error and validation fail
|
||||
|
|
@ -259,7 +271,9 @@ candidate_commit(clicon_handle h,
|
|||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Common steps (with validate). Note this is only call that uses 3-values */
|
||||
/* Common steps (with validate). Load candidate and running and compute diffs
|
||||
* Note this is only call that uses 3-values
|
||||
*/
|
||||
if ((ret = validate_common(h, candidate, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -270,12 +284,14 @@ candidate_commit(clicon_handle h,
|
|||
goto done;
|
||||
|
||||
/* 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, NULL) < 0)
|
||||
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
|
||||
if ((ret = xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
/* 8. Success: Copy candidate to running
|
||||
*/
|
||||
|
||||
if (xmldb_copy(h, candidate, "running") < 0)
|
||||
goto done;
|
||||
|
||||
|
|
@ -423,9 +439,11 @@ from_client_validate(clicon_handle h,
|
|||
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, NULL) < 0)
|
||||
goto done;
|
||||
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
|
||||
if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target, cbret)) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -176,22 +176,24 @@ db_reset(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Merge db1 into db2 without commit
|
||||
* @retval -1 Error
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
*/
|
||||
static int
|
||||
db_merge(clicon_handle h,
|
||||
const char *db1,
|
||||
const char *db2)
|
||||
const char *db2,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xt = NULL;
|
||||
|
||||
int retval = -1;
|
||||
cxobj *xt = NULL;
|
||||
|
||||
/* 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, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, cbret);
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
|
|
@ -288,18 +290,22 @@ nacm_load_external(clicon_handle h)
|
|||
}
|
||||
|
||||
/*! Merge xml in filename into database
|
||||
* @retval -1 Error
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
*/
|
||||
static int
|
||||
load_extraxml(clicon_handle h,
|
||||
char *filename,
|
||||
const char *db)
|
||||
const char *db,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xt = NULL;
|
||||
int fd = -1;
|
||||
|
||||
|
||||
if (filename == NULL)
|
||||
return 0;
|
||||
return 1;
|
||||
if ((fd = open(filename, O_RDONLY)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "open(%s)", filename);
|
||||
goto done;
|
||||
|
|
@ -310,9 +316,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, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
retval = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret);
|
||||
done:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
|
@ -385,9 +389,13 @@ static int
|
|||
startup_mode_running(clicon_handle h,
|
||||
char *extraxml_file)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cbret = NULL;
|
||||
int retval = -1;
|
||||
cbuf *cbret = NULL;
|
||||
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Stash original running to candidate for later commit */
|
||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||
goto done;
|
||||
|
|
@ -400,41 +408,46 @@ startup_mode_running(clicon_handle h,
|
|||
/* Application may define extra xml in its reset function*/
|
||||
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||
goto done;
|
||||
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||
_CLICON_XML_NS_STRICT = 0;
|
||||
/* Get application extra xml from file */
|
||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
||||
goto done;
|
||||
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
|
||||
goto fail;
|
||||
/* Clear running db */
|
||||
if (db_reset(h, "running") < 0)
|
||||
goto done;
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Commit original running. Assume -1 is validate fail */
|
||||
if (candidate_commit(h, "candidate", cbret) < 1){
|
||||
/* (1) We cannot differentiate between fatal errors and validation
|
||||
* failures
|
||||
* (2) If fatal error, we should exit
|
||||
* (3) If validation fails we cannot continue. How could we?
|
||||
* (4) Need to restore the running db since we destroyed it above
|
||||
*/
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting: %s.",
|
||||
__FUNCTION__, cbuf_get(cbret));
|
||||
/* Reinstate original */
|
||||
if (xmldb_copy(h, "candidate", "running") < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
if (candidate_commit(h, "candidate", cbret) < 1)
|
||||
goto fail;
|
||||
/* Merge user reset state and extra xml file (no commit) */
|
||||
if (db_merge(h, "tmp", "running") < 0)
|
||||
goto done;
|
||||
if (db_merge(h, "tmp", "running", cbret) < 1)
|
||||
goto fail;
|
||||
retval = 0;
|
||||
done:
|
||||
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xmldb_delete(h, "tmp") < 0)
|
||||
goto done;
|
||||
return retval;
|
||||
fail:
|
||||
/* (1) We cannot differentiate between fatal errors and validation
|
||||
* failures
|
||||
* (2) If fatal error, we should exit
|
||||
* (3) If validation fails we cannot continue. How could we?
|
||||
* (4) Need to restore the running db since we destroyed it above
|
||||
*/
|
||||
if (strlen(cbuf_get(cbret)))
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
|
||||
__FUNCTION__, cbuf_get(cbret));
|
||||
else
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
|
||||
__FUNCTION__, clicon_err_reason);
|
||||
/* Reinstate original */
|
||||
if (xmldb_copy(h, "candidate", "running") < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Clixon startup startup mode: Commit startup configuration into running state
|
||||
|
|
@ -460,11 +473,16 @@ startup -------------------------+--|
|
|||
*/
|
||||
static int
|
||||
startup_mode_startup(clicon_handle h,
|
||||
char *extraxml_file)
|
||||
char *extraxml_file)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cbret = NULL;
|
||||
|
||||
/* Create return buffer for netconf xml errors */
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Stash original running to backup */
|
||||
if (xmldb_copy(h, "running", "backup") < 0)
|
||||
goto done;
|
||||
|
|
@ -481,34 +499,25 @@ startup_mode_startup(clicon_handle h,
|
|||
/* Application may define extra xml in its reset function*/
|
||||
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||
goto done;
|
||||
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||
_CLICON_XML_NS_STRICT = 0;
|
||||
/* Get application extra xml from file */
|
||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
||||
goto done;
|
||||
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
|
||||
goto fail;
|
||||
/* Clear running db */
|
||||
if (db_reset(h, "running") < 0)
|
||||
goto done;
|
||||
/* Create return buffer (not used) */
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Commit startup */
|
||||
if (candidate_commit(h, "startup", cbret) < 1){ /* diff */
|
||||
/* We cannot differentiate between fatal errors and validation
|
||||
* failures
|
||||
* In both cases we copy back the original running and quit
|
||||
*/
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
|
||||
__FUNCTION__, cbuf_get(cbret));
|
||||
if (xmldb_copy(h, "backup", "running") < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
if (candidate_commit(h, "startup", cbret) < 1) /* diff */
|
||||
goto fail;
|
||||
/* Merge user reset state and extra xml file (no commit) */
|
||||
if (db_merge(h, "tmp", "running") < 0)
|
||||
goto done;
|
||||
if (db_merge(h, "tmp", "running", cbret) < 1)
|
||||
goto fail;
|
||||
retval = 0;
|
||||
done:
|
||||
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xmldb_delete(h, "backup") < 0)
|
||||
|
|
@ -516,6 +525,20 @@ startup_mode_startup(clicon_handle h,
|
|||
if (xmldb_delete(h, "tmp") < 0)
|
||||
goto done;
|
||||
return retval;
|
||||
fail:
|
||||
/* We cannot differentiate between fatal errors and validation
|
||||
* failures
|
||||
* In both cases we copy back the original running and quit
|
||||
*/
|
||||
if (strlen(cbuf_get(cbret)))
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
|
||||
__FUNCTION__, cbuf_get(cbret));
|
||||
else
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
|
||||
__FUNCTION__, clicon_err_reason);
|
||||
if (xmldb_copy(h, "backup", "running") < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -540,7 +563,6 @@ main(int argc,
|
|||
int sockfamily;
|
||||
char *xmldb_plugin;
|
||||
int xml_cache;
|
||||
int xml_pretty;
|
||||
char *xml_format;
|
||||
char *nacm_mode;
|
||||
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
|
||||
|
|
@ -808,9 +830,10 @@ main(int argc,
|
|||
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
|
||||
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
|
||||
goto done;
|
||||
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 (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
|
||||
goto done;
|
||||
if (xmldb_setopt(h, "sort", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XML_SORT")) < 0)
|
||||
goto done;
|
||||
/* Startup mode needs to be defined, */
|
||||
startup_mode = clicon_startup_mode(h);
|
||||
if (startup_mode == -1){
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ clixon_plugin_reset(clicon_handle h,
|
|||
* @param[in,out] xtop State XML tree is merged with existing tree.
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
* @retval 1 Statedata callback failed
|
||||
* @retval 1 Statedata callback failed (xret set with netconf-error)
|
||||
* @note xtop can be replaced
|
||||
*/
|
||||
int
|
||||
|
|
|
|||
|
|
@ -236,8 +236,8 @@ cli_dbxml(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
|
|
@ -293,7 +293,7 @@ cli_set(clicon_handle h,
|
|||
cvec *cvv,
|
||||
cvec *argv)
|
||||
{
|
||||
int retval = 1;
|
||||
int retval = -1;
|
||||
|
||||
if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0)
|
||||
goto done;
|
||||
|
|
@ -501,53 +501,54 @@ cli_start_shell(clicon_handle h,
|
|||
cvec *vars,
|
||||
cvec *argv)
|
||||
{
|
||||
char *cmd;
|
||||
char *cmd;
|
||||
struct passwd *pw;
|
||||
int retval;
|
||||
char bcmd[128];
|
||||
cg_var *cv1 = cvec_i(vars, 1);
|
||||
int retval = -1;
|
||||
char bcmd[128];
|
||||
cg_var *cv1 = cvec_i(vars, 1);
|
||||
|
||||
cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL);
|
||||
|
||||
if ((pw = getpwuid(getuid())) == NULL){
|
||||
fprintf(stderr, "%s: getpwuid: %s\n",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
if (chdir(pw->pw_dir) < 0){
|
||||
fprintf(stderr, "%s: chdir(%s): %s\n",
|
||||
__FUNCTION__, pw->pw_dir, strerror(errno));
|
||||
endpwent();
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
endpwent();
|
||||
cli_signal_flush(h);
|
||||
cli_signal_unblock(h);
|
||||
if (cmd){
|
||||
snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd);
|
||||
if ((retval = system(bcmd)) < 0){
|
||||
if (system(bcmd) < 0){
|
||||
cli_signal_block(h);
|
||||
fprintf(stderr, "%s: system(bash -c): %s\n",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
if ((retval = system("bash -l")) < 0){
|
||||
if (system("bash -l") < 0){
|
||||
cli_signal_block(h);
|
||||
fprintf(stderr, "%s: system(bash): %s\n",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
cli_signal_block(h);
|
||||
#if 0 /* Allow errcodes from bash */
|
||||
if (retval != 0){
|
||||
fprintf(stderr, "%s: system(%s) code=%d\n", __FUNCTION__, cmd, retval);
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic quit callback
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ expand_dbvar(void *h,
|
|||
/* This is primarily to get "y",
|
||||
* xpath2xml would have worked!!
|
||||
*/
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
|
||||
goto done;
|
||||
if (y==NULL)
|
||||
goto ok;
|
||||
|
|
@ -443,6 +443,7 @@ cli_show_config(clicon_handle h,
|
|||
cxobj *xc;
|
||||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
yang_spec *yspec;
|
||||
|
||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
||||
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<attr>]", cvec_len(argv));
|
||||
|
|
@ -496,6 +497,13 @@ cli_show_config(clicon_handle h,
|
|||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
/* Some formats (eg cli) require yang */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
/* Print configuration according to format */
|
||||
switch (format){
|
||||
case FORMAT_XML:
|
||||
|
|
@ -516,7 +524,7 @@ cli_show_config(clicon_handle h,
|
|||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
goto done;
|
||||
xc = NULL; /* Dont print xt itself */
|
||||
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
|
||||
xml2cli(stdout, xc, NULL, gt); /* cli syntax */
|
||||
break;
|
||||
case FORMAT_NETCONF:
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ process_incoming_packet(clicon_handle h,
|
|||
free(str0);
|
||||
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
|
||||
isrpc++;
|
||||
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
|
|
|
|||
|
|
@ -905,13 +905,9 @@ netconf_application_rpc(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
|
||||
if ((yrpc==NULL) && _CLICON_XML_NS_ITERATE){
|
||||
int i;
|
||||
for (i=0; i<yspec->yp_len; i++){
|
||||
ymod = yspec->yp_stmt[i];
|
||||
if ((yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn))) != NULL)
|
||||
break;
|
||||
}
|
||||
if ((yrpc==NULL) && !_CLICON_XML_NS_STRICT){
|
||||
if (xml_yang_find_non_strict(xn, yspec, &yrpc) < 0) /* Y_RPC */
|
||||
goto done;
|
||||
}
|
||||
/* Check if found */
|
||||
if (yrpc != NULL){
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Clixon Restconf
|
||||
|
||||
* [Installation](#Installation)
|
||||
* [Streams](Streams)
|
||||
* [Nchan Streams](Nchan)
|
||||
* [Debugging](Debugging)
|
||||
* [Installation](#installation)
|
||||
* [Streams](#streams)
|
||||
* [Nchan Streams](#nchan-streams)
|
||||
* [Debugging](#debugging)
|
||||
|
||||
### 1. Installation
|
||||
## 1. Installation
|
||||
|
||||
The examples are based on Nginx. Other reverse proxies should work but are not verified.
|
||||
|
||||
|
|
@ -44,39 +44,39 @@ sudo systemctl start start.service
|
|||
|
||||
Start clixon restconf daemon
|
||||
```
|
||||
olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
|
||||
> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
|
||||
```
|
||||
|
||||
Make restconf calls with curl
|
||||
```
|
||||
olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces
|
||||
> curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces
|
||||
[
|
||||
{
|
||||
"interfaces": {
|
||||
"ietf-interfaces:interfaces": {
|
||||
"interface":[
|
||||
{
|
||||
"name": "eth0",
|
||||
"type": "eth",
|
||||
"enabled": "true",
|
||||
"name": "eth9",
|
||||
"type": "eth",
|
||||
"enabled": "true"
|
||||
"type": "ex:eth",
|
||||
"enabled": true,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
|
||||
[
|
||||
{
|
||||
"type": "eth"
|
||||
}
|
||||
]
|
||||
|
||||
curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
|
||||
```
|
||||
Get the type of a specific interface:
|
||||
```
|
||||
> curl -G http://127.0.0.1/restconf/data/interfaces/interface=eth9/type
|
||||
{
|
||||
"ietf-interfaces:type": "eth"
|
||||
}
|
||||
```
|
||||
Example of writing a new interfaces specification:
|
||||
```
|
||||
curl -sX PUT http://localhost/restconf/data -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth1","type":"ex:eth","enabled":true}}}'
|
||||
```
|
||||
|
||||
### 2. Streams
|
||||
## 2. Streams
|
||||
|
||||
Clixon have two experimental restconf event stream implementations following
|
||||
RFC8040 Section 6 using SSE. One native and one using Nginx
|
||||
|
|
@ -112,7 +112,7 @@ Add the following to extend the nginx configuration file with the following stat
|
|||
|
||||
AN example of a stream access is as follows:
|
||||
```
|
||||
vandal> curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
|
||||
> curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
|
||||
data: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2018-11-04T14:47:11.373124</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>
|
||||
|
||||
data: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2018-11-04T14:47:16.375265</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>
|
||||
|
|
@ -125,7 +125,7 @@ You can also specify start and stop time. Start-time enables replay of existing
|
|||
|
||||
See (stream tests)[../test/test_streams.sh] for more examples.
|
||||
|
||||
### 3. Nchan
|
||||
## 3. Nchan
|
||||
|
||||
As an alternative streams implementation, Nginx/Nchan can be used.
|
||||
Nginx uses pub/sub channels and can be configured in a variety of
|
||||
|
|
@ -180,7 +180,7 @@ curl -H "Accept: text/event-stream" -H "Last-Event-ID: 1539961709:0" -s -X GET h
|
|||
|
||||
See (https://nchan.io/#eventsource) on more info on how to access an SSE sub endpoint.
|
||||
|
||||
### 4. Debugging
|
||||
## 4. Debugging
|
||||
|
||||
Start the restconf fastcgi program with debug flag:
|
||||
```
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -249,7 +249,7 @@ restconf_stream(clicon_handle h,
|
|||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "<rpc><create-subscription><stream>%s</stream>", name);
|
||||
cprintf(cb, "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>%s</stream>", name);
|
||||
/* Print all fields */
|
||||
for (i=0; i<cvec_len(qvec); i++){
|
||||
cv = cvec_i(qvec, i);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue