Merge branch 'develop'
This commit is contained in:
commit
e4efb6ded0
55 changed files with 2841 additions and 685 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -1,10 +1,27 @@
|
||||||
# Clixon CHANGELOG
|
# Clixon CHANGELOG
|
||||||
|
|
||||||
## 3.3.1
|
- Added new backend plugin callback: "plugin_statedata()" for retreiving state data
|
||||||
|
|
||||||
|
- Added yang dir with ietf-netconf and clixon-config yang specs for internal usage.
|
||||||
|
|
||||||
|
- Added state data: Netconf <get> operation introduced; Error when
|
||||||
|
adding state data in <edit-config>.
|
||||||
|
|
||||||
|
- Fixed bug where cli set of leaf-list were doubled, eg cli set foo -> foofoo
|
||||||
|
|
||||||
|
- Restricted yang (sub)module file match to match RFC6020 exactly
|
||||||
|
|
||||||
|
- Generalized yang type resolution to all included (sub)modules not just the topmost
|
||||||
|
|
||||||
|
- Generic map_str2int generic mapping tables
|
||||||
|
|
||||||
|
- Removed vector return values from xmldb_get()
|
||||||
|
|
||||||
|
## 3.3.1 June 7 2017
|
||||||
|
|
||||||
- Fixed yang leafref cli completion.
|
- Fixed yang leafref cli completion.
|
||||||
|
|
||||||
- Removed non-standard api_path extension from internal netconf so that the internal com.
|
- Removed non-standard api_path extension from the internal netconf protocol so that the internal netcinf is now fully standard.
|
||||||
|
|
||||||
- Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
|
- Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,10 +219,20 @@ from_client_get_config(clicon_handle h,
|
||||||
clicon_err(OE_XML, 0, "db not found");
|
clicon_err(OE_XML, 0, "db not found");
|
||||||
goto done;
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||||
if ((selector = xml_find_value(xfilter, "select"))==NULL)
|
if ((selector = xml_find_value(xfilter, "select"))==NULL)
|
||||||
selector="/";
|
selector="/";
|
||||||
if (xmldb_get(h, db, selector, &xret, NULL, NULL) < 0){
|
if (xmldb_get(h, db, selector, 1, &xret) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
"<error-type>application</error-type>"
|
"<error-type>application</error-type>"
|
||||||
|
|
@ -232,7 +242,6 @@ from_client_get_config(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
cprintf(cbret, "<rpc-reply><data>");
|
cprintf(cbret, "<rpc-reply><data>");
|
||||||
/* if empty only <data/>, if any data then <data><config>..</config></data> */
|
|
||||||
if (xret!=NULL){
|
if (xret!=NULL){
|
||||||
if (xml_child_nr(xret)){
|
if (xml_child_nr(xret)){
|
||||||
if (xml_name_set(xret, "config") < 0)
|
if (xml_name_set(xret, "config") < 0)
|
||||||
|
|
@ -250,6 +259,60 @@ from_client_get_config(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Internal message: get
|
||||||
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xe Netconf request xml tree
|
||||||
|
* @param[out] cbret Return xml value cligen buffer
|
||||||
|
* @see from_client_get_config
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
from_client_get(clicon_handle h,
|
||||||
|
cxobj *xe,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xfilter;
|
||||||
|
char *selector = "/";
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
|
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>");
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
/* Get state data from plugins as defined by plugin_statedata(), if any */
|
||||||
|
assert(xret);
|
||||||
|
if (backend_statedata_call(h, selector, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
cprintf(cbret, "<rpc-reply><data>");
|
||||||
|
/* if empty only <config/> */
|
||||||
|
if (xret!=NULL){
|
||||||
|
if (xml_child_nr(xret)){
|
||||||
|
if (xml_name_set(xret, "config") < 0)
|
||||||
|
goto done;
|
||||||
|
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cprintf(cbret, "</data></rpc-reply>");
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Internal message: edit-config
|
/*! Internal message: edit-config
|
||||||
*
|
*
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
|
@ -271,11 +334,26 @@ from_client_edit_config(clicon_handle h,
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
enum operation_type operation = OP_MERGE;
|
enum operation_type operation = OP_MERGE;
|
||||||
int piddb;
|
int piddb;
|
||||||
|
int non_config = 0;
|
||||||
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((target = netconf_db_find(xn, "target")) == NULL){
|
if ((target = netconf_db_find(xn, "target")) == NULL){
|
||||||
clicon_err(OE_XML, 0, "db not found");
|
clicon_err(OE_XML, 0, "db not found");
|
||||||
goto done;
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
/* Check if target locked by other client */
|
/* Check if target locked by other client */
|
||||||
piddb = xmldb_islocked(h, target);
|
piddb = xmldb_islocked(h, target);
|
||||||
if (piddb && mypid != piddb){
|
if (piddb && mypid != piddb){
|
||||||
|
|
@ -300,6 +378,20 @@ from_client_edit_config(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((xc = xpath_first(xn, "config")) != NULL){
|
if ((xc = xpath_first(xn, "config")) != NULL){
|
||||||
|
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>");
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
if (xmldb_put(h, target, operation, xc) < 0){
|
if (xmldb_put(h, target, operation, xc) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
|
@ -356,6 +448,16 @@ from_client_lock(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A lock MUST not be granted if either of the following conditions is true:
|
* 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.
|
* 1) A lock is already held by any NETCONF session or another entity.
|
||||||
|
|
@ -410,6 +512,15 @@ from_client_unlock(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
piddb = xmldb_islocked(h, db);
|
piddb = xmldb_islocked(h, db);
|
||||||
/*
|
/*
|
||||||
* An unlock operation will not succeed if any of the following
|
* An unlock operation will not succeed if any of the following
|
||||||
|
|
@ -534,6 +645,16 @@ from_client_copy_config(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
if ((target = netconf_db_find(xe, "target")) == NULL){
|
if ((target = netconf_db_find(xe, "target")) == NULL){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>missing-element</error-tag>"
|
"<error-tag>missing-element</error-tag>"
|
||||||
|
|
@ -543,6 +664,15 @@ from_client_copy_config(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
/* Check if target locked by other client */
|
/* Check if target locked by other client */
|
||||||
piddb = xmldb_islocked(h, target);
|
piddb = xmldb_islocked(h, target);
|
||||||
if (piddb && mypid != piddb){
|
if (piddb && mypid != piddb){
|
||||||
|
|
@ -556,7 +686,6 @@ from_client_copy_config(clicon_handle h,
|
||||||
piddb);
|
piddb);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xmldb_copy(h, source, target) < 0){
|
if (xmldb_copy(h, source, target) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
|
@ -601,6 +730,16 @@ from_client_delete_config(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
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);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if target locked by other client */
|
/* Check if target locked by other client */
|
||||||
piddb = xmldb_islocked(h, target);
|
piddb = xmldb_islocked(h, target);
|
||||||
if (piddb && mypid != piddb){
|
if (piddb && mypid != piddb){
|
||||||
|
|
@ -808,6 +947,10 @@ from_client_msg(clicon_handle h,
|
||||||
if (from_client_unlock(h, xe, pid, cbret) < 0)
|
if (from_client_unlock(h, xe, pid, cbret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(name, "get") == 0){
|
||||||
|
if (from_client_get(h, xe, cbret) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
else if (strcmp(name, "close-session") == 0){
|
else if (strcmp(name, "close-session") == 0){
|
||||||
xmldb_unlock_all(h, pid);
|
xmldb_unlock_all(h, pid);
|
||||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
|
|
@ -846,7 +989,7 @@ from_client_msg(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if ((ret = backend_netconf_plugin_callbacks(h, xe, ce, cbret)) < 0)
|
if ((ret = backend_rpc_cb_call(h, xe, ce, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0) /* not handled by callback */
|
if (ret == 0) /* not handled by callback */
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
|
|
|
||||||
|
|
@ -146,9 +146,9 @@ validate_common(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* 2. Parse xml trees */
|
/* 2. Parse xml trees */
|
||||||
if (xmldb_get(h, "running", "/", &td->td_src, NULL, NULL) < 0)
|
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_get(h, candidate, "/", &td->td_target, NULL, NULL) < 0)
|
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* 3. Compute differences */
|
/* 3. Compute differences */
|
||||||
|
|
@ -213,6 +213,7 @@ validate_common(clicon_handle h,
|
||||||
* fails, we just ignore the errors and proceed. Maybe we should
|
* fails, we just ignore the errors and proceed. Maybe we should
|
||||||
* do something more drastic?
|
* do something more drastic?
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] candidate A candidate database, not necessarily "candidate"
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
candidate_commit(clicon_handle h,
|
candidate_commit(clicon_handle h,
|
||||||
|
|
@ -283,18 +284,18 @@ from_client_commit(clicon_handle h,
|
||||||
piddb);
|
piddb);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidate_commit(h, "candidate") < 0){
|
if (candidate_commit(h, "candidate") < 0){
|
||||||
clicon_debug(1, "Commit candidate failed");
|
clicon_debug(1, "Commit candidate failed");
|
||||||
/* XXX: candidate_validate should have proper error handling */
|
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>missing-attribute</error-tag>"
|
"<error-tag>invalid-value</error-tag>"
|
||||||
"<error-type>protocol</error-type>"
|
"<error-type>protocol</error-type>"
|
||||||
"<error-severity>error</error-severity>"
|
"<error-severity>error</error-severity>"
|
||||||
"<error-message>%s</error-message>"
|
"<error-message>%s</error-message>"
|
||||||
"</rpc-error></rpc-reply>",
|
"</rpc-error></rpc-reply>",
|
||||||
clicon_err_reason);
|
clicon_err_reason);
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
ok:
|
ok:
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ backend_terminate(clicon_handle h)
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
plugin_finish(h);
|
plugin_finish(h);
|
||||||
|
/* Delete all backend plugin RPC callbacks */
|
||||||
|
backend_rpc_cb_delete_all();
|
||||||
if (pidfile)
|
if (pidfile)
|
||||||
unlink(pidfile);
|
unlink(pidfile);
|
||||||
if (sockpath)
|
if (sockpath)
|
||||||
|
|
@ -590,7 +592,7 @@ main(int argc, char **argv)
|
||||||
*(argv-1) = tmp;
|
*(argv-1) = tmp;
|
||||||
|
|
||||||
if (reload_running){
|
if (reload_running){
|
||||||
/* This could be afailed validation, and we should not fail for that */
|
/* This could be a failed validation, and we should not fail for that */
|
||||||
(void)candidate_commit(h, "candidate");
|
(void)candidate_commit(h, "candidate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,22 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
/* Following are specific to backend. For common see clicon_plugin.h
|
/* Following are specific to backend. For common see clicon_plugin.h
|
||||||
* @note the following should match the prototypes in clicon_backend.h
|
* @note the following should match the prototypes in clixon_backend.h
|
||||||
*/
|
*/
|
||||||
#define PLUGIN_RESET "plugin_reset"
|
#define PLUGIN_RESET "plugin_reset"
|
||||||
typedef int (plgreset_t)(clicon_handle h, char *dbname); /* Reset system status */
|
typedef int (plgreset_t)(clicon_handle h, char *dbname); /* Reset system status */
|
||||||
|
|
||||||
|
/*! Plugin callback, if defined called to get state data from plugin
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
* @param[in] xtop XML tree, <config/> on entry.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see xmldb_get
|
||||||
|
*/
|
||||||
|
#define PLUGIN_STATEDATA "plugin_statedata"
|
||||||
|
typedef int (plgstatedata_t)(clicon_handle h, char *xpath, cxobj *xtop);
|
||||||
|
|
||||||
#define PLUGIN_TRANS_BEGIN "transaction_begin"
|
#define PLUGIN_TRANS_BEGIN "transaction_begin"
|
||||||
#define PLUGIN_TRANS_VALIDATE "transaction_validate"
|
#define PLUGIN_TRANS_VALIDATE "transaction_validate"
|
||||||
#define PLUGIN_TRANS_COMPLETE "transaction_complete"
|
#define PLUGIN_TRANS_COMPLETE "transaction_complete"
|
||||||
|
|
@ -80,6 +91,7 @@ typedef int (plgreset_t)(clicon_handle h, char *dbname); /* Reset system status
|
||||||
#define PLUGIN_TRANS_END "transaction_end"
|
#define PLUGIN_TRANS_END "transaction_end"
|
||||||
#define PLUGIN_TRANS_ABORT "transaction_abort"
|
#define PLUGIN_TRANS_ABORT "transaction_abort"
|
||||||
|
|
||||||
|
|
||||||
typedef int (trans_cb_t)(clicon_handle h, transaction_data td); /* Transaction cbs */
|
typedef int (trans_cb_t)(clicon_handle h, transaction_data td); /* Transaction cbs */
|
||||||
|
|
||||||
/* Backend (config) plugins */
|
/* Backend (config) plugins */
|
||||||
|
|
@ -90,12 +102,14 @@ struct plugin {
|
||||||
plgstart_t *p_start; /* Start */
|
plgstart_t *p_start; /* Start */
|
||||||
plgexit_t *p_exit; /* Exit */
|
plgexit_t *p_exit; /* Exit */
|
||||||
plgreset_t *p_reset; /* Reset state */
|
plgreset_t *p_reset; /* Reset state */
|
||||||
|
plgstatedata_t *p_statedata; /* State-data callback */
|
||||||
trans_cb_t *p_trans_begin; /* Transaction start */
|
trans_cb_t *p_trans_begin; /* Transaction start */
|
||||||
trans_cb_t *p_trans_validate; /* Transaction validation */
|
trans_cb_t *p_trans_validate; /* Transaction validation */
|
||||||
trans_cb_t *p_trans_complete; /* Transaction validation complete */
|
trans_cb_t *p_trans_complete; /* Transaction validation complete */
|
||||||
trans_cb_t *p_trans_commit; /* Transaction commit */
|
trans_cb_t *p_trans_commit; /* Transaction commit */
|
||||||
trans_cb_t *p_trans_end; /* Transaction completed */
|
trans_cb_t *p_trans_end; /* Transaction completed */
|
||||||
trans_cb_t *p_trans_abort; /* Transaction aborted */
|
trans_cb_t *p_trans_abort; /* Transaction aborted */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -212,6 +226,8 @@ backend_plugin_load (clicon_handle h,
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_EXIT);
|
clicon_debug(2, "%s callback registered.", PLUGIN_EXIT);
|
||||||
if ((new->p_reset = dlsym(handle, PLUGIN_RESET)) != NULL)
|
if ((new->p_reset = dlsym(handle, PLUGIN_RESET)) != NULL)
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_RESET);
|
clicon_debug(2, "%s callback registered.", PLUGIN_RESET);
|
||||||
|
if ((new->p_statedata = dlsym(handle, PLUGIN_STATEDATA)) != NULL)
|
||||||
|
clicon_debug(2, "%s callback registered.", PLUGIN_STATEDATA);
|
||||||
if ((new->p_trans_begin = dlsym(handle, PLUGIN_TRANS_BEGIN)) != NULL)
|
if ((new->p_trans_begin = dlsym(handle, PLUGIN_TRANS_BEGIN)) != NULL)
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_BEGIN);
|
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_BEGIN);
|
||||||
if ((new->p_trans_validate = dlsym(handle, PLUGIN_TRANS_VALIDATE)) != NULL)
|
if ((new->p_trans_validate = dlsym(handle, PLUGIN_TRANS_VALIDATE)) != NULL)
|
||||||
|
|
@ -700,3 +716,82 @@ plugin_transaction_abort(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------
|
||||||
|
* Backend state data callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! Go through all backend statedata callbacks and collect state data
|
||||||
|
* This is internal system call, plugin is invoked (does not call) this function
|
||||||
|
* Backend plugins can register
|
||||||
|
* @param[in] h clicon handle
|
||||||
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
* @param[in,out] xml XML tree.
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 OK
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
backend_statedata_call(clicon_handle h,
|
||||||
|
char *xpath,
|
||||||
|
cxobj *xtop)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct plugin *p;
|
||||||
|
int i;
|
||||||
|
cxobj *x = NULL;
|
||||||
|
yang_spec *yspec;
|
||||||
|
cxobj **xvec = NULL;
|
||||||
|
size_t xlen;
|
||||||
|
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xtop==NULL){
|
||||||
|
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i = 0; i < nplugins; i++) {
|
||||||
|
p = &plugins[i];
|
||||||
|
if (p->p_statedata) {
|
||||||
|
if ((x = xml_new("config", NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((p->p_statedata)(h, xpath, x) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_merge(xtop, x, yspec) < 0)
|
||||||
|
goto done;
|
||||||
|
if (x){
|
||||||
|
xml_free(x);
|
||||||
|
x = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* Code complex to filter out anything that is outside of xpath */
|
||||||
|
if (xpath_vec(xtop, xpath?xpath:"/", &xvec, &xlen) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* If vectors are specified then mark the nodes found and
|
||||||
|
* then filter out everything else,
|
||||||
|
* otherwise return complete tree.
|
||||||
|
*/
|
||||||
|
if (xvec != NULL){
|
||||||
|
for (i=0; i<xlen; i++)
|
||||||
|
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
||||||
|
}
|
||||||
|
/* Remove everything that is not marked */
|
||||||
|
if (!xml_flag(xtop, XML_FLAG_MARK))
|
||||||
|
if (xml_tree_prune_flagged_sub(xtop, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
/* reset flag */
|
||||||
|
if (xml_apply(xtop, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (x)
|
||||||
|
xml_free(x);
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ int plugin_finish(clicon_handle h);
|
||||||
int plugin_reset_state(clicon_handle h, char *dbname);
|
int plugin_reset_state(clicon_handle h, char *dbname);
|
||||||
int plugin_start_hooks(clicon_handle h, int argc, char **argv);
|
int plugin_start_hooks(clicon_handle h, int argc, char **argv);
|
||||||
|
|
||||||
|
int backend_statedata_call(clicon_handle h, char *xpath, cxobj *xml);
|
||||||
|
|
||||||
transaction_data_t * transaction_new(void);
|
transaction_data_t * transaction_new(void);
|
||||||
int transaction_free(transaction_data_t *);
|
int transaction_free(transaction_data_t *);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,11 @@ int plugin_exit(clicon_handle h);
|
||||||
*/
|
*/
|
||||||
int plugin_reset(clicon_handle h, char *dbname);
|
int plugin_reset(clicon_handle h, char *dbname);
|
||||||
|
|
||||||
|
/*! Retreive statedata, add statedata to XML tree
|
||||||
|
* @see plgstatedata_ t
|
||||||
|
*/
|
||||||
|
int plugin_statedata(clicon_handle h, char *xpath, cxobj *xtop);
|
||||||
|
|
||||||
/*! Called before a commit/validate sequence begins. Eg setup state before commit
|
/*! Called before a commit/validate sequence begins. Eg setup state before commit
|
||||||
* @see trans_cb_t
|
* @see trans_cb_t
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -430,48 +430,57 @@ subscription_each(clicon_handle h,
|
||||||
return hs;
|
return hs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Database dependency description */
|
/*--------------------------------------------------------------------
|
||||||
struct backend_netconf_reg {
|
* Backend netconf rpc callbacks
|
||||||
qelem_t nr_qelem; /* List header */
|
*/
|
||||||
backend_netconf_cb_t nr_callback; /* Validation/Commit Callback */
|
typedef struct {
|
||||||
void *nr_arg; /* Application specific argument to cb */
|
qelem_t rc_qelem; /* List header */
|
||||||
char *nr_tag; /* Xml tag when matched, callback called */
|
backend_rpc_cb rc_callback; /* RPC Callback */
|
||||||
};
|
void *rc_arg; /* Application specific argument to cb */
|
||||||
typedef struct backend_netconf_reg backend_netconf_reg_t;
|
char *rc_tag; /* Xml tag when matched, callback called */
|
||||||
|
} backend_rpc_cb_entry;
|
||||||
|
|
||||||
static backend_netconf_reg_t *deps = NULL;
|
/* List of backend rpc callback entries */
|
||||||
/*! Register netconf callback
|
static backend_rpc_cb_entry *rpc_cb_list = NULL;
|
||||||
|
|
||||||
|
/*! Register netconf backend rpc callback
|
||||||
* Called from plugin to register a callback for a specific netconf XML tag.
|
* Called from plugin to register a callback for a specific netconf XML tag.
|
||||||
|
*
|
||||||
|
* @param[in] h clicon handle
|
||||||
|
* @param[in] cb, Callback called
|
||||||
|
* @param[in] arg, Arg to send to callback
|
||||||
|
* @param[in] tag Xml tag when callback is made
|
||||||
|
* @see backend_rpc_cb_call
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
backend_netconf_register_callback(clicon_handle h,
|
backend_rpc_cb_register(clicon_handle h,
|
||||||
backend_netconf_cb_t cb, /* Callback called */
|
backend_rpc_cb cb,
|
||||||
void *arg, /* Arg to send to callback */
|
void *arg,
|
||||||
char *tag) /* Xml tag when callback is made */
|
char *tag)
|
||||||
{
|
{
|
||||||
backend_netconf_reg_t *nr;
|
backend_rpc_cb_entry *rc;
|
||||||
|
|
||||||
if ((nr = malloc(sizeof(backend_netconf_reg_t))) == NULL) {
|
if ((rc = malloc(sizeof(backend_rpc_cb_entry))) == NULL) {
|
||||||
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||||
goto catch;
|
goto catch;
|
||||||
}
|
}
|
||||||
memset (nr, 0, sizeof (*nr));
|
memset (rc, 0, sizeof (*rc));
|
||||||
nr->nr_callback = cb;
|
rc->rc_callback = cb;
|
||||||
nr->nr_arg = arg;
|
rc->rc_arg = arg;
|
||||||
nr->nr_tag = strdup(tag); /* XXX strdup memleak */
|
rc->rc_tag = strdup(tag); /* XXX strdup memleak */
|
||||||
INSQ(nr, deps);
|
INSQ(rc, rpc_cb_list);
|
||||||
return 0;
|
return 0;
|
||||||
catch:
|
catch:
|
||||||
if (nr){
|
if (rc){
|
||||||
if (nr->nr_tag)
|
if (rc->rc_tag)
|
||||||
free(nr->nr_tag);
|
free(rc->rc_tag);
|
||||||
free(nr);
|
free(rc);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! See if there is any callback registered for this tag
|
/*! Search netconf backend callbacks and invoke if match
|
||||||
*
|
* This is internal system call, plugin is invoked (does not call) this functino
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] xe Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
* @param[in] xe Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||||
* @param[in] ce Client (session) entry
|
* @param[in] ce Client (session) entry
|
||||||
|
|
@ -480,28 +489,49 @@ catch:
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK, not found handler.
|
* @retval 0 OK, not found handler.
|
||||||
* @retval 1 OK, handler called
|
* @retval 1 OK, handler called
|
||||||
|
* @see backend_rpc_cb_register
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
backend_netconf_plugin_callbacks(clicon_handle h,
|
backend_rpc_cb_call(clicon_handle h,
|
||||||
cxobj *xe,
|
cxobj *xe,
|
||||||
struct client_entry *ce,
|
struct client_entry *ce,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
backend_netconf_reg_t *nreg;
|
backend_rpc_cb_entry *rc;
|
||||||
int retval;
|
int retval = -1;
|
||||||
|
|
||||||
if (deps == NULL)
|
if (rpc_cb_list == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
nreg = deps;
|
rc = rpc_cb_list;
|
||||||
do {
|
do {
|
||||||
if (strcmp(nreg->nr_tag, xml_name(xe)) == 0){
|
if (strcmp(rc->rc_tag, xml_name(xe)) == 0){
|
||||||
if ((retval = nreg->nr_callback(h, xe, ce, cbret, nreg->nr_arg)) < 0)
|
if ((retval = rc->rc_callback(h, xe, ce, cbret, rc->rc_arg)) < 0)
|
||||||
return -1;
|
goto done;
|
||||||
else
|
else{
|
||||||
return 1; /* handled */
|
retval = 1; /* handled */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = NEXTQ(backend_rpc_cb_entry *, rc);
|
||||||
|
} while (rc != rpc_cb_list);
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Delete all state data callbacks.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
backend_rpc_cb_delete_all(void)
|
||||||
|
{
|
||||||
|
backend_rpc_cb_entry *rc;
|
||||||
|
|
||||||
|
while((rc = rpc_cb_list) != NULL) {
|
||||||
|
DELQ(rc, rpc_cb_list, backend_rpc_cb_entry *);
|
||||||
|
if (rc->rc_tag)
|
||||||
|
free(rc->rc_tag);
|
||||||
|
free(rc);
|
||||||
}
|
}
|
||||||
nreg = NEXTQ(backend_netconf_reg_t *, nreg);
|
|
||||||
} while (nreg != deps);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,15 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
struct client_entry;
|
struct client_entry;
|
||||||
typedef int (*backend_netconf_cb_t)(
|
typedef int (*backend_rpc_cb)(
|
||||||
clicon_handle h,
|
clicon_handle h,
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
struct client_entry *ce, /* Client session */
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
cbuf *cbret,/* Reply eg <rpc-reply>... */
|
||||||
void *arg /* Argument given at register */
|
void *arg /* Argument given at register */
|
||||||
);
|
);
|
||||||
|
typedef backend_rpc_cb backend_netconf_cb_t; /* XXX backward compat */
|
||||||
|
|
||||||
|
|
||||||
/*! Generic downcall registration.
|
/*! Generic downcall registration.
|
||||||
* Enables any function to be called from (cli) frontend
|
* Enables any function to be called from (cli) frontend
|
||||||
|
|
@ -90,12 +92,14 @@ int subscription_delete(clicon_handle h, char *stream,
|
||||||
struct handle_subscription *subscription_each(clicon_handle h,
|
struct handle_subscription *subscription_each(clicon_handle h,
|
||||||
struct handle_subscription *hprev);
|
struct handle_subscription *hprev);
|
||||||
|
|
||||||
int backend_netconf_register_callback(clicon_handle h,
|
/* XXX backward compat */
|
||||||
backend_netconf_cb_t cb, /* Callback called */
|
#define backend_netconf_register_callback(a,b,c,d) backend_rpc_cb_register(a,b,c,d)
|
||||||
void *arg, /* Arg to send to callback */
|
int backend_rpc_cb_register(clicon_handle h, backend_rpc_cb cb, void *arg,
|
||||||
char *tag); /* Xml tag when callback is made */
|
char *tag);
|
||||||
|
|
||||||
int backend_netconf_plugin_callbacks(clicon_handle h, cxobj *xe,
|
int backend_rpc_cb_call(clicon_handle h, cxobj *xe, struct client_entry *ce,
|
||||||
struct client_entry *ce, cbuf *cbret);
|
cbuf *cbret);
|
||||||
|
|
||||||
|
int backend_rpc_cb_delete_all(void);
|
||||||
|
|
||||||
#endif /* _CLIXON_BACKEND_HANDLE_H_ */
|
#endif /* _CLIXON_BACKEND_HANDLE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ cli_dbxml(clicon_handle h,
|
||||||
xml_type_set(xa, CX_ATTR);
|
xml_type_set(xa, CX_ATTR);
|
||||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (y->yn_keyword != Y_LIST){
|
if (y->yn_keyword != Y_LIST && y->yn_keyword != Y_LEAF_LIST){
|
||||||
len = cvec_len(cvv);
|
len = cvec_len(cvv);
|
||||||
if (len > 1){
|
if (len > 1){
|
||||||
cval = cvec_i(cvv, len-1);
|
cval = cvec_i(cvv, len-1);
|
||||||
|
|
@ -644,6 +644,7 @@ compare_dbs(clicon_handle h,
|
||||||
{
|
{
|
||||||
cxobj *xc1 = NULL; /* running xml */
|
cxobj *xc1 = NULL; /* running xml */
|
||||||
cxobj *xc2 = NULL; /* candidate xml */
|
cxobj *xc2 = NULL; /* candidate xml */
|
||||||
|
cxobj *xerr;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int astext;
|
int astext;
|
||||||
|
|
||||||
|
|
@ -657,8 +658,16 @@ compare_dbs(clicon_handle h,
|
||||||
astext = 0;
|
astext = 0;
|
||||||
if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
|
if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
|
if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */
|
if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -800,6 +809,7 @@ save_config_file(clicon_handle h,
|
||||||
char *dbstr;
|
char *dbstr;
|
||||||
char *varstr;
|
char *varstr;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
|
|
||||||
if (cvec_len(argv) != 2){
|
if (cvec_len(argv) != 2){
|
||||||
|
|
@ -825,6 +835,10 @@ save_config_file(clicon_handle h,
|
||||||
filename = cv_string_get(cv);
|
filename = cv_string_get(cv);
|
||||||
if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
|
if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((f = fopen(filename, "wb")) == NULL){
|
if ((f = fopen(filename, "wb")) == NULL){
|
||||||
clicon_err(OE_CFG, errno, "Creating file %s", filename);
|
clicon_err(OE_CFG, errno, "Creating file %s", filename);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1122,6 +1136,7 @@ cli_copy_config(clicon_handle h,
|
||||||
char *tovar;
|
char *tovar;
|
||||||
cg_var *tocv;
|
cg_var *tocv;
|
||||||
char *toname;
|
char *toname;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
if (cvec_len(argv) != 5){
|
if (cvec_len(argv) != 5){
|
||||||
clicon_err(OE_PLUGIN, 0, "%s: Requires four elements: <db> <xpath> <keyname> <from> <to>", __FUNCTION__);
|
clicon_err(OE_PLUGIN, 0, "%s: Requires four elements: <db> <xpath> <keyname> <from> <to>", __FUNCTION__);
|
||||||
|
|
@ -1164,6 +1179,10 @@ cli_copy_config(clicon_handle h,
|
||||||
/* Get from object configuration and store in x1 */
|
/* Get from object configuration and store in x1 */
|
||||||
if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0)
|
if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get to variable -> cv -> to name */
|
/* Get to variable -> cv -> to name */
|
||||||
if ((tocv = cvec_find_var(cvv, tovar)) == NULL){
|
if ((tocv = cvec_find_var(cvv, tovar)) == NULL){
|
||||||
|
|
|
||||||
|
|
@ -657,7 +657,7 @@ clicon_parse(clicon_handle h,
|
||||||
}
|
}
|
||||||
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
|
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
|
||||||
if (res != CG_MATCH)
|
if (res != CG_MATCH)
|
||||||
pt_expand_cleanup_1(pt);
|
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
|
||||||
if (msav){
|
if (msav){
|
||||||
cli_tree_active_set(h, msav);
|
cli_tree_active_set(h, msav);
|
||||||
free(msav);
|
free(msav);
|
||||||
|
|
@ -689,7 +689,7 @@ clicon_parse(clicon_handle h,
|
||||||
}
|
}
|
||||||
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
|
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
|
||||||
cli_handler_err(stdout);
|
cli_handler_err(stdout);
|
||||||
pt_expand_cleanup_1(pt);
|
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
|
||||||
if (result)
|
if (result)
|
||||||
*result = r;
|
*result = r;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ expand_dbvar(void *h,
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
size_t xlen = 0;
|
size_t xlen = 0;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
char *bodystr;
|
char *bodystr;
|
||||||
|
|
@ -142,6 +143,10 @@ expand_dbvar(void *h,
|
||||||
/* XXX read whole configuration, why not send xpath? */
|
/* XXX read whole configuration, why not send xpath? */
|
||||||
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
|
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
/* One round to detect duplicates
|
/* One round to detect duplicates
|
||||||
* XXX The code below would benefit from some cleanup
|
* XXX The code below would benefit from some cleanup
|
||||||
*/
|
*/
|
||||||
|
|
@ -379,6 +384,7 @@ cli_show_config(clicon_handle h,
|
||||||
char *val = NULL;
|
char *val = NULL;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
cxobj *xerr;
|
||||||
enum genmodel_type gt;
|
enum genmodel_type gt;
|
||||||
|
|
||||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
||||||
|
|
@ -428,6 +434,10 @@ cli_show_config(clicon_handle h,
|
||||||
/* Get configuration from database */
|
/* Get configuration from database */
|
||||||
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), &xt) < 0)
|
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
/* Print configuration according to format */
|
/* Print configuration according to format */
|
||||||
switch (format){
|
switch (format){
|
||||||
case FORMAT_XML:
|
case FORMAT_XML:
|
||||||
|
|
@ -487,6 +497,7 @@ show_conf_xpath(clicon_handle h,
|
||||||
char *xpath;
|
char *xpath;
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
cxobj **xv = NULL;
|
cxobj **xv = NULL;
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -507,6 +518,10 @@ show_conf_xpath(clicon_handle h,
|
||||||
xpath = cv_string_get(cv);
|
xpath = cv_string_get(cv);
|
||||||
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
|
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if (xpath_vec(xt, xpath, &xv, &xlen) < 0)
|
if (xpath_vec(xt, xpath, &xv, &xlen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
for (i=0; i<xlen; i++)
|
for (i=0; i<xlen; i++)
|
||||||
|
|
|
||||||
|
|
@ -530,6 +530,76 @@ netconf_unlock(clicon_handle h,
|
||||||
return netconf_lock(h, xn, xret);
|
return netconf_lock(h, xn, xret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get running configuration and device state information
|
||||||
|
*
|
||||||
|
*
|
||||||
|
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
|
||||||
|
* @param[out] xret Return XML, error or OK
|
||||||
|
* @note filter type subtree and xpath is supported, but xpath is preferred, and
|
||||||
|
* better performance and tested. Please use xpath.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <rpc><get><filter type="xpath" select="//SenderTwampIpv4"/>
|
||||||
|
* </get></rpc>]]>]]>
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
netconf_get(clicon_handle h,
|
||||||
|
cxobj *xn,
|
||||||
|
cxobj **xret)
|
||||||
|
{
|
||||||
|
cxobj *xfilter; /* filter */
|
||||||
|
int retval = -1;
|
||||||
|
char *ftype = NULL;
|
||||||
|
cxobj *xfilterconf;
|
||||||
|
cxobj *xconf;
|
||||||
|
|
||||||
|
/* ie <filter>...</filter> */
|
||||||
|
if ((xfilter = xpath_first(xn, "filter")) != NULL)
|
||||||
|
ftype = xml_find_value(xfilter, "type");
|
||||||
|
if (ftype == NULL || strcmp(ftype, "xpath")==0){
|
||||||
|
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else if (strcmp(ftype, "subtree")==0){
|
||||||
|
/* Default rfc filter is subtree. I prefer xpath and use it internally.
|
||||||
|
Get whole subtree and then filter aftwerwards. This is suboptimal.
|
||||||
|
Therefore please use xpath.
|
||||||
|
*/
|
||||||
|
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xfilter &&
|
||||||
|
(xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL &&
|
||||||
|
(xconf = xpath_first(*xret, "/rpc-reply/data/configuration")) != NULL){
|
||||||
|
/* xml_filter removes parts of xml tree not matching */
|
||||||
|
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
|
||||||
|
xml_filter(xfilterconf, xconf) < 0){
|
||||||
|
clicon_xml_parse(xret, "<rpc-reply><rpc-error>"
|
||||||
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
"<error-type>applicatio</error-type>"
|
||||||
|
"<error-severity>error</error-severity>"
|
||||||
|
"<error-info>filtering</error-info>"
|
||||||
|
"</rpc-error></rpc-reply>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
clicon_xml_parse(xret, "<rpc-reply><rpc-error>"
|
||||||
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
"<error-type>applicatio</error-type>"
|
||||||
|
"<error-severity>error</error-severity>"
|
||||||
|
"<error-message>filter type not supported</error-message>"
|
||||||
|
"<error-info>type</error-info>"
|
||||||
|
"</rpc-error></rpc-reply>");
|
||||||
|
}
|
||||||
|
// ok: /* netconf error is not fatal */
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Close a (user) session
|
/*! Close a (user) session
|
||||||
<close-session/>
|
<close-session/>
|
||||||
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
|
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
|
||||||
|
|
@ -818,6 +888,8 @@ netconf_rpc_dispatch(clicon_handle h,
|
||||||
return netconf_lock(h, xe, xret);
|
return netconf_lock(h, xe, xret);
|
||||||
else if (strcmp(xml_name(xe), "unlock") == 0)
|
else if (strcmp(xml_name(xe), "unlock") == 0)
|
||||||
return netconf_unlock(h, xe, xret);
|
return netconf_unlock(h, xe, xret);
|
||||||
|
else if (strcmp(xml_name(xe), "get") == 0)
|
||||||
|
return netconf_get(h, xe, xret);
|
||||||
else if (strcmp(xml_name(xe), "close-session") == 0)
|
else if (strcmp(xml_name(xe), "close-session") == 0)
|
||||||
return netconf_close_session(h, xe, xret);
|
return netconf_close_session(h, xe, xret);
|
||||||
else if (strcmp(xml_name(xe), "kill-session") == 0)
|
else if (strcmp(xml_name(xe), "kill-session") == 0)
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
|
curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,96 @@
|
||||||
|
|
||||||
#include "restconf_lib.h"
|
#include "restconf_lib.h"
|
||||||
|
|
||||||
|
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
|
||||||
|
* and RFC 6241 Appendix A. NETCONF Error list
|
||||||
|
*/
|
||||||
|
static const map_str2int netconf_restconf_map[] = {
|
||||||
|
{"in-use", 409},
|
||||||
|
{"invalid-value", 400},
|
||||||
|
{"invalid-value", 404},
|
||||||
|
{"invalid-value", 406},
|
||||||
|
{"too-big", 413}, /* request */
|
||||||
|
{"too-big", 400}, /* response */
|
||||||
|
{"missing-attribute", 400},
|
||||||
|
{"bad-attribute", 400},
|
||||||
|
{"unknown-attribute", 400},
|
||||||
|
{"bad-element", 400},
|
||||||
|
{"unknown-element", 400},
|
||||||
|
{"unknown-namespace", 400},
|
||||||
|
{"access-denied", 401},
|
||||||
|
{"access-denied", 403},
|
||||||
|
{"lock-denied", 409},
|
||||||
|
{"resource-denied", 409},
|
||||||
|
{"rollback-failed", 500},
|
||||||
|
{"data-exists", 409},
|
||||||
|
{"data-missing", 409},
|
||||||
|
{"operation-not-supported",405},
|
||||||
|
{"operation-not-supported",501},
|
||||||
|
{"operation-failed", 412},
|
||||||
|
{"operation-failed", 500},
|
||||||
|
{"partial-operation", 500},
|
||||||
|
{"malformed-message", 400},
|
||||||
|
{NULL, -1}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* See 7231 Section 6.1
|
||||||
|
*/
|
||||||
|
static const map_str2int http_reason_phrase_map[] = {
|
||||||
|
{"Continue", 100},
|
||||||
|
{"Switching Protocols", 101},
|
||||||
|
{"OK", 200},
|
||||||
|
{"Created", 201},
|
||||||
|
{"Accepted", 202},
|
||||||
|
{"Non-Authoritative Information", 203},
|
||||||
|
{"No Content", 204},
|
||||||
|
{"Reset Content", 205},
|
||||||
|
{"Partial Content", 206},
|
||||||
|
{"Multiple Choices", 300},
|
||||||
|
{"Moved Permanently", 301},
|
||||||
|
{"Found", 302},
|
||||||
|
{"See Other", 303},
|
||||||
|
{"Not Modified", 304},
|
||||||
|
{"Use Proxy", 305},
|
||||||
|
{"Temporary Redirect", 307},
|
||||||
|
{"Bad Request", 400},
|
||||||
|
{"Unauthorized", 401},
|
||||||
|
{"Payment Required", 402},
|
||||||
|
{"Forbidden", 403},
|
||||||
|
{"Not Found", 404},
|
||||||
|
{"Method Not Allowed", 405},
|
||||||
|
{"Not Acceptable", 406},
|
||||||
|
{"Proxy Authentication Required", 407},
|
||||||
|
{"Request Timeout", 408},
|
||||||
|
{"Conflict", 409},
|
||||||
|
{"Gone", 410},
|
||||||
|
{"Length Required", 411},
|
||||||
|
{"Precondition Failed", 412},
|
||||||
|
{"Payload Too Large", 413},
|
||||||
|
{"URI Too Long", 414},
|
||||||
|
{"Unsupported Media Type", 415},
|
||||||
|
{"Range Not Satisfiable", 416},
|
||||||
|
{"Expectation Failed", 417},
|
||||||
|
{"Upgrade Required", 426},
|
||||||
|
{"Internal Server Error", 500},
|
||||||
|
{"Not Implemented", 501},
|
||||||
|
{"Bad Gateway", 502},
|
||||||
|
{"Service Unavailable", 503},
|
||||||
|
{"Gateway Timeout", 504},
|
||||||
|
{"HTTP Version Not Supported", 505},
|
||||||
|
{NULL, -1}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
restconf_err2code(char *tag)
|
||||||
|
{
|
||||||
|
return clicon_str2int(netconf_restconf_map, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
restconf_code2reason(int code)
|
||||||
|
{
|
||||||
|
return clicon_int2str(http_reason_phrase_map, code);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
|
|
@ -90,6 +180,16 @@ badrequest(FCGX_Request *r)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
notimplemented(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 501\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<h1>Not Implemented/h1>\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
conflict(FCGX_Request *r)
|
conflict(FCGX_Request *r)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,11 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
int restconf_err2code(char *tag);
|
||||||
|
const char *restconf_code2reason(int code);
|
||||||
int notfound(FCGX_Request *r);
|
int notfound(FCGX_Request *r);
|
||||||
int badrequest(FCGX_Request *r);
|
int badrequest(FCGX_Request *r);
|
||||||
|
int notimplemented(FCGX_Request *r);
|
||||||
int conflict(FCGX_Request *r);
|
int conflict(FCGX_Request *r);
|
||||||
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
||||||
int test(FCGX_Request *r, int dbg);
|
int test(FCGX_Request *r, int dbg);
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,11 @@ api_data_get_gen(clicon_handle h,
|
||||||
cxobj **vec = NULL;
|
cxobj **vec = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
cxobj *xtag;
|
||||||
|
cbuf *cbj = NULL;;
|
||||||
|
int code;
|
||||||
|
const char *reason_phrase;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
|
|
@ -166,16 +171,45 @@ api_data_get_gen(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||||
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
if (clicon_rpc_get(h, cbuf_get(path), &xret) < 0){
|
||||||
notfound(r);
|
notfound(r);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
#if 0 /* DEBUG */
|
||||||
{
|
{
|
||||||
cbuf *cb = cbuf_new();
|
cbuf *cb = cbuf_new();
|
||||||
clicon_xml2cbuf(cb, xret, 0, 0);
|
xml2json_cbuf(cb, xret, 1);
|
||||||
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
|
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
if ((xerr = xpath_first(xret, "/rpc-error")) != NULL){
|
||||||
|
if ((cbj = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((xtag = xpath_first(xerr, "/error-tag")) == NULL){
|
||||||
|
notfound(r); /* bad reply? */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
code = restconf_err2code(xml_body(xtag));
|
||||||
|
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||||
|
reason_phrase="";
|
||||||
|
clicon_debug(1, "%s code:%d reason phrase:%s",
|
||||||
|
__FUNCTION__, code, reason_phrase);
|
||||||
|
|
||||||
|
if (xml_name_set(xerr, "error") < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml2json_cbuf(cbj, xerr, 1) < 0)
|
||||||
|
goto done;
|
||||||
|
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+json\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "{\r\n");
|
||||||
|
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : {\r\n");
|
||||||
|
FCGX_FPrintF(r->out, " %s", cbuf_get(cbj));
|
||||||
|
FCGX_FPrintF(r->out, " }\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "}\r\n");
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
|
|
@ -197,6 +231,8 @@ api_data_get_gen(clicon_handle h,
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
if (cbx)
|
if (cbx)
|
||||||
cbuf_free(cbx);
|
cbuf_free(cbx);
|
||||||
|
if (cbj)
|
||||||
|
cbuf_free(cbj);
|
||||||
if (path)
|
if (path)
|
||||||
cbuf_free(path);
|
cbuf_free(path);
|
||||||
if (xret)
|
if (xret)
|
||||||
|
|
@ -505,8 +541,7 @@ api_data_patch(clicon_handle h,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
char *data)
|
char *data)
|
||||||
{
|
{
|
||||||
badrequest(r);
|
notimplemented(r);
|
||||||
// return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,6 +590,7 @@ api_data_delete(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_edit_config(h, "candidate",
|
if (clicon_rpc_edit_config(h, "candidate",
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,6 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
|
||||||
# Set if all configuration changes are committed directly, commit command unnecessary
|
# Set if all configuration changes are committed directly, commit command unnecessary
|
||||||
# CLICON_AUTOCOMMIT 0
|
# CLICON_AUTOCOMMIT 0
|
||||||
|
|
||||||
# CLICON_COMMIT_ORDER 0
|
|
||||||
|
|
||||||
# Name of master plugin (both frontend and backend). Master plugin has special
|
# Name of master plugin (both frontend and backend). Master plugin has special
|
||||||
# callbacks for frontends. See clicon user manual for more info.
|
# callbacks for frontends. See clicon user manual for more info.
|
||||||
# CLICON_MASTER_PLUGIN master
|
# CLICON_MASTER_PLUGIN master
|
||||||
|
|
|
||||||
6
configure
vendored
6
configure
vendored
|
|
@ -2136,7 +2136,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||||
|
|
||||||
CLIXON_VERSION_MAJOR="3"
|
CLIXON_VERSION_MAJOR="3"
|
||||||
CLIXON_VERSION_MINOR="3"
|
CLIXON_VERSION_MINOR="3"
|
||||||
CLIXON_VERSION_PATCH="1"
|
CLIXON_VERSION_PATCH="2"
|
||||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||||
# Fix to specific version (eg 3.5) or head (3)
|
# Fix to specific version (eg 3.5) or head (3)
|
||||||
CLIGEN_VERSION="3"
|
CLIGEN_VERSION="3"
|
||||||
|
|
@ -2172,8 +2172,8 @@ _ACEOF
|
||||||
|
|
||||||
# Bind to specific CLIgen version
|
# Bind to specific CLIgen version
|
||||||
|
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CLIXON version is ${CLIXON_VERSION}" >&5
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CLIXON version is ${CLIXON_VERSION}_PRE" >&5
|
||||||
$as_echo "CLIXON version is ${CLIXON_VERSION}" >&6; }
|
$as_echo "CLIXON version is ${CLIXON_VERSION}_PRE" >&6; }
|
||||||
|
|
||||||
ac_aux_dir=
|
ac_aux_dir=
|
||||||
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
|
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ AC_INIT(lib/clixon/clixon.h.in)
|
||||||
|
|
||||||
CLIXON_VERSION_MAJOR="3"
|
CLIXON_VERSION_MAJOR="3"
|
||||||
CLIXON_VERSION_MINOR="3"
|
CLIXON_VERSION_MINOR="3"
|
||||||
CLIXON_VERSION_PATCH="1"
|
CLIXON_VERSION_PATCH="2"
|
||||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||||
# Fix to specific version (eg 3.5) or head (3)
|
# Fix to specific version (eg 3.5) or head (3)
|
||||||
CLIGEN_VERSION="3"
|
CLIGEN_VERSION="3"
|
||||||
|
|
@ -62,7 +62,7 @@ AC_SUBST(CLIXON_VERSION_MAJOR)
|
||||||
AC_SUBST(CLIXON_VERSION_MINOR)
|
AC_SUBST(CLIXON_VERSION_MINOR)
|
||||||
AC_SUBST(CLIGEN_VERSION) # Bind to specific CLIgen version
|
AC_SUBST(CLIGEN_VERSION) # Bind to specific CLIgen version
|
||||||
|
|
||||||
AC_MSG_RESULT(CLIXON version is ${CLIXON_VERSION})
|
AC_MSG_RESULT(CLIXON version is ${CLIXON_VERSION}_PRE)
|
||||||
|
|
||||||
AC_CANONICAL_TARGET
|
AC_CANONICAL_TARGET
|
||||||
AC_SUBST(CC)
|
AC_SUBST(CC)
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ main(int argc, char **argv)
|
||||||
if (strcmp(cmd, "get")==0){
|
if (strcmp(cmd, "get")==0){
|
||||||
if (argc != 1 && argc != 2)
|
if (argc != 1 && argc != 2)
|
||||||
usage(argv0);
|
usage(argv0);
|
||||||
if (xmldb_get(h, db, argc==2?argv[1]:"/", &xt, NULL, 0) < 0)
|
if (xmldb_get(h, db, argc==2?argv[1]:"/", 0, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_xml2file(stdout, xt, 0, 0);
|
clicon_xml2file(stdout, xt, 0, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,17 +71,6 @@
|
||||||
* cli_expand_var_generate | yang2api_path_fmt |
|
* cli_expand_var_generate | yang2api_path_fmt |
|
||||||
* yang -------------> | |
|
* yang -------------> | |
|
||||||
* +----------------+
|
* +----------------+
|
||||||
* xmldb_get_tree
|
|
||||||
* - compare_dbs
|
|
||||||
* - netconf
|
|
||||||
* - validate
|
|
||||||
* - from_client_save
|
|
||||||
*
|
|
||||||
* xmldb_get_vec
|
|
||||||
* - restconf
|
|
||||||
* - expand_dbvar
|
|
||||||
* - show_conf_xpath
|
|
||||||
*
|
|
||||||
* dependency on clixon handle:
|
* dependency on clixon handle:
|
||||||
* clixon_xmldb_dir()
|
* clixon_xmldb_dir()
|
||||||
* clicon_dbspec_yang(h)
|
* clicon_dbspec_yang(h)
|
||||||
|
|
@ -571,38 +560,15 @@ kv_setopt(xmldb_handle xh,
|
||||||
/*! Get content of database using xpath. return a set of matching sub-trees
|
/*! Get content of database using xpath. return a set of matching sub-trees
|
||||||
* The function returns a minimal tree that includes all sub-trees that match
|
* The function returns a minimal tree that includes all sub-trees that match
|
||||||
* xpath.
|
* xpath.
|
||||||
* @param[in] dbname Name of database to search in (filename including dir path
|
* This is a clixon datastore plugin of the the xmldb api
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
|
||||||
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
|
|
||||||
* @param[out] xvec Vector of xml trees. Free after use.
|
|
||||||
* @param[out] xlen Length of vector.
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* @code
|
|
||||||
* cxobj *xt;
|
|
||||||
* cxobj **xvec;
|
|
||||||
* size_t xlen;
|
|
||||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
|
|
||||||
* &xt, &xvec, &xlen) < 0)
|
|
||||||
* err;
|
|
||||||
* for (i=0; i<xlen; i++){
|
|
||||||
* xn = xv[i];
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* xml_free(xt);
|
|
||||||
* free(xvec);
|
|
||||||
* @endcode
|
|
||||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
|
||||||
* @see xpath_vec
|
|
||||||
* @see xmldb_get
|
* @see xmldb_get
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
kv_get(xmldb_handle xh,
|
kv_get(xmldb_handle xh,
|
||||||
char *db,
|
char *db,
|
||||||
char *xpath,
|
char *xpath,
|
||||||
cxobj **xtop,
|
int config,
|
||||||
cxobj ***xvec0,
|
cxobj **xtop)
|
||||||
size_t *xlen0)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct kv_handle *kh = handle(xh);
|
struct kv_handle *kh = handle(xh);
|
||||||
|
|
@ -651,19 +617,14 @@ kv_get(xmldb_handle xh,
|
||||||
}
|
}
|
||||||
/* Top is special case */
|
/* Top is special case */
|
||||||
if (!xml_flag(xt, XML_FLAG_MARK))
|
if (!xml_flag(xt, XML_FLAG_MARK))
|
||||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xvec0 && xlen0){
|
/* Add default values (if not set) */
|
||||||
*xvec0 = xvec;
|
|
||||||
xvec = NULL;
|
|
||||||
*xlen0 = xlen;
|
|
||||||
xlen = 0;
|
|
||||||
}
|
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* XXX does not work for top-level */
|
/* Order XML children according to YANG */
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
|
||||||
|
|
@ -816,27 +777,9 @@ put(char *dbfile,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Modify database provided an xml tree and an operation
|
/*! Modify database provided an xml tree and an operation
|
||||||
*
|
* This is a clixon datastore plugin of the the xmldb api
|
||||||
* @param[in] xh XMLDB handle
|
* @see xmldb_put
|
||||||
* @param[in] db running or candidate
|
|
||||||
* @param[in] xt xml-tree. Top-level symbol is dummy
|
|
||||||
* @param[in] op OP_MERGE: just add it.
|
|
||||||
* OP_REPLACE: first delete whole database
|
|
||||||
* OP_NONE: operation attribute in xml determines operation
|
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* The xml may contain the "operation" attribute which defines the operation.
|
|
||||||
* @code
|
|
||||||
* cxobj *xt;
|
|
||||||
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
|
|
||||||
* err;
|
|
||||||
* if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0)
|
|
||||||
* err;
|
|
||||||
* @endcode
|
|
||||||
* @see xmldb_put_xkey for single key
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
kv_put(xmldb_handle xh,
|
kv_put(xmldb_handle xh,
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,7 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int kv_get(xmldb_handle h, char *db, char *xpath,
|
int kv_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
|
||||||
int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||||
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
|
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||||
int kv_copy(xmldb_handle h, char *from, char *to);
|
int kv_copy(xmldb_handle h, char *from, char *to);
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ install: $(PLUGIN)
|
||||||
install-include:
|
install-include:
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf $(DESTDIR)$(clixon_LIBDIR)/xmldb/$(PLUGIN)
|
rm -rf $(DESTDIR)$(libdir)/xmldb/$(PLUGIN)
|
||||||
|
|
||||||
TAGS:
|
TAGS:
|
||||||
find . -name '*.[chyl]' -print | etags -
|
find . -name '*.[chyl]' -print | etags -
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ struct text_handle {
|
||||||
int th_magic; /* magic */
|
int th_magic; /* magic */
|
||||||
char *th_dbdir; /* Directory of database files */
|
char *th_dbdir; /* Directory of database files */
|
||||||
yang_spec *th_yangspec; /* Yang spec if this datastore */
|
yang_spec *th_yangspec; /* Yang spec if this datastore */
|
||||||
|
clicon_hash_t *th_dbs; /* Hash of databases */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Check struct magic number for sanity checks
|
/*! Check struct magic number for sanity checks
|
||||||
|
|
@ -85,15 +86,6 @@ text_handle_check(xmldb_handle xh)
|
||||||
return th->th_magic == TEXT_HANDLE_MAGIC ? 0 : -1;
|
return th->th_magic == TEXT_HANDLE_MAGIC ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Database locking for candidate and running non-persistent
|
|
||||||
* Store an integer for running and candidate containing
|
|
||||||
* the session-id of the client holding the lock.
|
|
||||||
* @note This should probably be on file-system
|
|
||||||
*/
|
|
||||||
static int _running_locked = 0;
|
|
||||||
static int _candidate_locked = 0;
|
|
||||||
static int _startup_locked = 0;
|
|
||||||
|
|
||||||
/*! Translate from symbolic database name to actual filename in file-system
|
/*! Translate from symbolic database name to actual filename in file-system
|
||||||
* @param[in] th text handle handle
|
* @param[in] th text handle handle
|
||||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||||
|
|
@ -122,13 +114,6 @@ text_db2file(struct text_handle *th,
|
||||||
clicon_err(OE_XML, errno, "dbdir not set");
|
clicon_err(OE_XML, errno, "dbdir not set");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (strcmp(db, "running") != 0 &&
|
|
||||||
strcmp(db, "candidate") != 0 &&
|
|
||||||
strcmp(db, "startup") != 0 &&
|
|
||||||
strcmp(db, "tmp") != 0){
|
|
||||||
clicon_err(OE_XML, 0, "No such database: %s", db);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
cprintf(cb, "%s/%s_db", dir, db);
|
cprintf(cb, "%s/%s_db", dir, db);
|
||||||
if ((*filename = strdup4(cbuf_get(cb))) == NULL){
|
if ((*filename = strdup4(cbuf_get(cb))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
|
@ -159,6 +144,8 @@ text_connect(void)
|
||||||
}
|
}
|
||||||
memset(th, 0, size);
|
memset(th, 0, size);
|
||||||
th->th_magic = TEXT_HANDLE_MAGIC;
|
th->th_magic = TEXT_HANDLE_MAGIC;
|
||||||
|
if ((th->th_dbs = hash_init()) == NULL)
|
||||||
|
goto done;
|
||||||
xh = (xmldb_handle)th;
|
xh = (xmldb_handle)th;
|
||||||
done:
|
done:
|
||||||
return xh;
|
return xh;
|
||||||
|
|
@ -178,6 +165,8 @@ text_disconnect(xmldb_handle xh)
|
||||||
if (th){
|
if (th){
|
||||||
if (th->th_dbdir)
|
if (th->th_dbdir)
|
||||||
free(th->th_dbdir);
|
free(th->th_dbdir);
|
||||||
|
if (th->th_dbs)
|
||||||
|
hash_free(th->th_dbs);
|
||||||
free(th);
|
free(th);
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -245,37 +234,6 @@ text_setopt(xmldb_handle xh,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Populate with spec
|
|
||||||
* @param[in] xt XML tree with some node marked
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_spec_populate(cxobj *x,
|
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_spec *yspec = (yang_spec*)arg;
|
|
||||||
char *name;
|
|
||||||
yang_stmt *y; /* yang node */
|
|
||||||
cxobj *xp; /* xml parent */
|
|
||||||
yang_stmt *yp; /* parent yang */
|
|
||||||
|
|
||||||
name = xml_name(x);
|
|
||||||
if ((xp = xml_parent(x)) != NULL &&
|
|
||||||
(yp = xml_spec(xp)) != NULL)
|
|
||||||
y = yang_find_syntax((yang_node*)yp, xml_name(x));
|
|
||||||
else
|
|
||||||
y = yang_find_topnode(yspec, name); /* still NULL for config */
|
|
||||||
if (y==NULL){
|
|
||||||
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
|
|
||||||
name,
|
|
||||||
xp?xml_name(xp):"", yp?yp->ys_argument:"");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
xml_spec_set(x, y);
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Ensure that xt only has a single sub-element and that is "config"
|
/*! Ensure that xt only has a single sub-element and that is "config"
|
||||||
*/
|
*/
|
||||||
|
|
@ -318,39 +276,15 @@ singleconfigroot(cxobj *xt,
|
||||||
/*! Get content of database using xpath. return a set of matching sub-trees
|
/*! Get content of database using xpath. return a set of matching sub-trees
|
||||||
* The function returns a minimal tree that includes all sub-trees that match
|
* The function returns a minimal tree that includes all sub-trees that match
|
||||||
* xpath.
|
* xpath.
|
||||||
* @param[in] xh XMLDB handle
|
* This is a clixon datastore plugin of the the xmldb api
|
||||||
* @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[out] xtop Single XML tree which xvec points to. Free with xml_free()
|
|
||||||
* @param[out] xvec Vector of xml trees. Free after use.
|
|
||||||
* @param[out] xlen Length of vector.
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* @code
|
|
||||||
* cxobj *xt;
|
|
||||||
* cxobj **xvec;
|
|
||||||
* size_t xlen;
|
|
||||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
|
|
||||||
* &xt, &xvec, &xlen) < 0)
|
|
||||||
* err;
|
|
||||||
* for (i=0; i<xlen; i++){
|
|
||||||
* xn = xv[i];
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* xml_free(xt);
|
|
||||||
* free(xvec);
|
|
||||||
* @endcode
|
|
||||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
|
||||||
* @see xpath_vec
|
|
||||||
* @see xmldb_get
|
* @see xmldb_get
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
text_get(xmldb_handle xh,
|
text_get(xmldb_handle xh,
|
||||||
char *db,
|
char *db,
|
||||||
char *xpath,
|
char *xpath,
|
||||||
cxobj **xtop,
|
int config,
|
||||||
cxobj ***xvec0,
|
cxobj **xtop)
|
||||||
size_t *xlen0)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *dbfile = NULL;
|
char *dbfile = NULL;
|
||||||
|
|
@ -393,45 +327,44 @@ text_get(xmldb_handle xh,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Here xt looks like: <config>...</config> */
|
/* Here xt looks like: <config>...</config> */
|
||||||
/* Validate existing config tree */
|
/* Add yang specification backpointer to all XML nodes */
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* XXX Maybe the below is general function and should be moved to xmldb? */
|
|
||||||
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
|
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* If vectors are specified then filter out everything else,
|
/* If vectors are specified then mark the nodes found and
|
||||||
|
* then filter out everything else,
|
||||||
* otherwise return complete tree.
|
* otherwise return complete tree.
|
||||||
*/
|
*/
|
||||||
if (xvec != NULL){
|
if (xvec != NULL){
|
||||||
for (i=0; i<xlen; i++)
|
for (i=0; i<xlen; i++)
|
||||||
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
||||||
}
|
}
|
||||||
/* Top is special case */
|
/* Remove everything that is not marked */
|
||||||
if (!xml_flag(xt, XML_FLAG_MARK))
|
if (!xml_flag(xt, XML_FLAG_MARK))
|
||||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* reset flag */
|
||||||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
/* filter out state (operations) data if config not set. Mark all nodes
|
||||||
|
that are not config data */
|
||||||
|
if (config && xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Remove (prune) nodes that are marked (that does not pass test) */
|
||||||
|
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Add default values (if not set) */
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* XXX does not work for top-level */
|
/* Order XML children according to YANG */
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (debug>1)
|
if (debug>1)
|
||||||
clicon_xml2file(stderr, xt, 0, 1);
|
clicon_xml2file(stderr, xt, 0, 1);
|
||||||
if (xvec0 && xlen0){
|
|
||||||
*xvec0 = xvec;
|
|
||||||
xvec = NULL;
|
|
||||||
*xlen0 = xlen;
|
|
||||||
xlen = 0;
|
|
||||||
}
|
|
||||||
*xtop = xt;
|
*xtop = xt;
|
||||||
xt = NULL;
|
xt = NULL;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -526,11 +459,10 @@ match_base_child(cxobj *x0,
|
||||||
|
|
||||||
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
||||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||||
|
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||||
* @param[in] x0p Parent of x0
|
* @param[in] x0p Parent of x0
|
||||||
* @param[in] x1 xml tree which modifies base
|
* @param[in] x1 xml tree which modifies base
|
||||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
||||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
|
||||||
* @param[in] yspec Top-level yang spec (if y is NULL)
|
|
||||||
* Assume x0 and x1 are same on entry and that y is the spec
|
* Assume x0 and x1 are same on entry and that y is the spec
|
||||||
* @see put in clixon_keyvalue.c
|
* @see put in clixon_keyvalue.c
|
||||||
*/
|
*/
|
||||||
|
|
@ -733,27 +665,10 @@ text_modify_top(cxobj *x0,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Modify database provided an xml tree and an operation
|
/*! Modify database provided an xml tree and an operation
|
||||||
*
|
* This is a clixon datastore plugin of the the xmldb api
|
||||||
* @param[in] xh XMLDB handle
|
* @see xmldb_put
|
||||||
* @param[in] db running or candidate
|
*/
|
||||||
* @param[in] op OP_MERGE: just add it.
|
|
||||||
* OP_REPLACE: first delete whole database
|
|
||||||
* OP_NONE: operation attribute in xml determines operation
|
|
||||||
* @param[in] x1 xml-tree to merge/replace. Top-level symbol is 'config'.
|
|
||||||
* Should be empty or '<config/>' if delete?
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* The xml may contain the "operation" attribute which defines the operation.
|
|
||||||
* @code
|
|
||||||
* cxobj *xt;
|
|
||||||
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
|
|
||||||
* err;
|
|
||||||
* if (xmldb_put(h, "running", OP_MERGE, "/", xt) < 0)
|
|
||||||
* err;
|
|
||||||
* @endcode
|
|
||||||
y */
|
|
||||||
int
|
int
|
||||||
text_put(xmldb_handle xh,
|
text_put(xmldb_handle xh,
|
||||||
char *db,
|
char *db,
|
||||||
|
|
@ -799,7 +714,6 @@ text_put(xmldb_handle xh,
|
||||||
}
|
}
|
||||||
/* 2. File is not empty <top><config>...</config></top> -> replace root */
|
/* 2. File is not empty <top><config>...</config></top> -> replace root */
|
||||||
else{
|
else{
|
||||||
|
|
||||||
/* There should only be one element and called config */
|
/* There should only be one element and called config */
|
||||||
if (singleconfigroot(x0, &x0) < 0)
|
if (singleconfigroot(x0, &x0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -811,11 +725,11 @@ text_put(xmldb_handle xh,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate existing config tree */
|
/* Add yang specification backpointer to all XML nodes */
|
||||||
if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Validate modification tree */
|
/* Add yang specification backpointer to all XML nodes */
|
||||||
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
@ -826,7 +740,7 @@ text_put(xmldb_handle xh,
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Remove NONE nodes if all subs recursively are also NONE */
|
/* Remove NONE nodes if all subs recursively are also NONE */
|
||||||
if (xml_tree_prune_flagged(x0, XML_FLAG_NONE, 0, NULL) <0)
|
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||||
(void*)XML_FLAG_NONE) < 0)
|
(void*)XML_FLAG_NONE) < 0)
|
||||||
|
|
@ -907,14 +821,9 @@ text_lock(xmldb_handle xh,
|
||||||
char *db,
|
char *db,
|
||||||
int pid)
|
int pid)
|
||||||
{
|
{
|
||||||
// struct text_handle *th = handle(xh);
|
struct text_handle *th = handle(xh);
|
||||||
|
|
||||||
if (strcmp("running", db) == 0)
|
hash_add(th->th_dbs, db, &pid, sizeof(pid));
|
||||||
_running_locked = pid;
|
|
||||||
else if (strcmp("candidate", db) == 0)
|
|
||||||
_candidate_locked = pid;
|
|
||||||
else if (strcmp("startup", db) == 0)
|
|
||||||
_startup_locked = pid;
|
|
||||||
clicon_debug(1, "%s: locked by %u", db, pid);
|
clicon_debug(1, "%s: locked by %u", db, pid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -931,14 +840,11 @@ int
|
||||||
text_unlock(xmldb_handle xh,
|
text_unlock(xmldb_handle xh,
|
||||||
char *db)
|
char *db)
|
||||||
{
|
{
|
||||||
// struct text_handle *th = handle(xh);
|
struct text_handle *th = handle(xh);
|
||||||
|
int zero = 0;
|
||||||
|
|
||||||
if (strcmp("running", db) == 0)
|
hash_add(th->th_dbs, db, &zero, sizeof(zero));
|
||||||
_running_locked = 0;
|
// hash_del(th->th_dbs, db);
|
||||||
else if (strcmp("candidate", db) == 0)
|
|
||||||
_candidate_locked = 0;
|
|
||||||
else if (strcmp("startup", db) == 0)
|
|
||||||
_startup_locked = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -952,14 +858,19 @@ int
|
||||||
text_unlock_all(xmldb_handle xh,
|
text_unlock_all(xmldb_handle xh,
|
||||||
int pid)
|
int pid)
|
||||||
{
|
{
|
||||||
// struct text_handle *th = handle(xh);
|
struct text_handle *th = handle(xh);
|
||||||
|
char **keys;
|
||||||
|
size_t klen;
|
||||||
|
int i;
|
||||||
|
int *val;
|
||||||
|
size_t vlen;
|
||||||
|
|
||||||
if (_running_locked == pid)
|
if ((keys = hash_keys(th->th_dbs, &klen)) == NULL)
|
||||||
_running_locked = 0;
|
return 0;
|
||||||
if (_candidate_locked == pid)
|
for(i = 0; i < klen; i++)
|
||||||
_candidate_locked = 0;
|
if ((val = hash_value(th->th_dbs, keys[i], &vlen)) != NULL &&
|
||||||
if (_startup_locked == pid)
|
*val == pid)
|
||||||
_startup_locked = 0;
|
hash_del(th->th_dbs, keys[i]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -974,14 +885,13 @@ int
|
||||||
text_islocked(xmldb_handle xh,
|
text_islocked(xmldb_handle xh,
|
||||||
char *db)
|
char *db)
|
||||||
{
|
{
|
||||||
// struct text_handle *th = handle(xh);
|
struct text_handle *th = handle(xh);
|
||||||
|
size_t vlen;
|
||||||
|
int *val;
|
||||||
|
|
||||||
if (strcmp("running", db) == 0)
|
if ((val = hash_value(th->th_dbs, db, &vlen)) == NULL)
|
||||||
return (_running_locked);
|
return 0;
|
||||||
else if (strcmp("candidate", db) == 0)
|
return *val;
|
||||||
return(_candidate_locked);
|
|
||||||
else if (strcmp("startup", db) == 0)
|
|
||||||
return(_startup_locked);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1078,6 +988,7 @@ text_plugin_exit(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct xmldb_api api;
|
||||||
static const struct xmldb_api api;
|
static const struct xmldb_api api;
|
||||||
|
|
||||||
/*! plugin init function */
|
/*! plugin init function */
|
||||||
|
|
@ -1167,7 +1078,7 @@ main(int argc, char **argv)
|
||||||
if (argc < 5)
|
if (argc < 5)
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
xpath = argc>5?argv[5]:NULL;
|
xpath = argc>5?argv[5]:NULL;
|
||||||
if (xmldb_get(h, db, xpath, &xt, NULL, NULL) < 0)
|
if (xmldb_get(h, db, xpath, &xt, NULL, 1, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_xml2file(stdout, xt, 0, 1);
|
clicon_xml2file(stdout, xt, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,7 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int text_get(xmldb_handle h, char *db, char *xpath,
|
int text_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
|
||||||
int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||||
int text_dump(FILE *f, char *dbfilename, char *rxkey);
|
int text_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||||
int text_copy(xmldb_handle h, char *from, char *to);
|
int text_copy(xmldb_handle h, char *from, char *to);
|
||||||
|
|
|
||||||
39
doc/FAQ.md
39
doc/FAQ.md
|
|
@ -56,7 +56,7 @@ use the web resource: http://clicon.org/ref/index.html
|
||||||
|
|
||||||
## How is configuration data stored?
|
## How is configuration data stored?
|
||||||
Configuration data is stored in an XML datastore. The default is a
|
Configuration data is stored in an XML datastore. The default is a
|
||||||
text-based addatastore, but there also exists a key-value datastore
|
text-based datastore, but there also exists a key-value datastore
|
||||||
using qdbm. In the example the datastore are regular files found in
|
using qdbm. In the example the datastore are regular files found in
|
||||||
/usr/local/var/routing/.
|
/usr/local/var/routing/.
|
||||||
|
|
||||||
|
|
@ -110,13 +110,33 @@ and then invoke it from a client using
|
||||||
ssh -s netconf <host>
|
ssh -s netconf <host>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## How do I use restconf?
|
||||||
|
|
||||||
|
You can access clixon via REST API using restconf, such as using
|
||||||
|
curl. GET, PUT, POST are supported.
|
||||||
|
|
||||||
|
You need a web-server, such as nginx, and start a restconf fcgi
|
||||||
|
daemon, clixon_restconf. Read more in the restconf docs.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "eth"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## How do I use notifications?
|
## How do I use notifications?
|
||||||
|
|
||||||
The example has a prebuilt notification stream called "ROUTING" that triggers every 10s.
|
The example has a prebuilt notification stream called "ROUTING" that triggers every 10s.
|
||||||
You enable the notification either via the cli or via netconf:
|
You enable the notification either via the cli:
|
||||||
|
```
|
||||||
cli> notify
|
cli> notify
|
||||||
cli> Routing notification
|
cli>
|
||||||
Routing notification
|
```
|
||||||
|
or via netconf:
|
||||||
```
|
```
|
||||||
clixon_netconf -qf /usr/local/etc/routing.conf
|
clixon_netconf -qf /usr/local/etc/routing.conf
|
||||||
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
||||||
|
|
@ -141,7 +161,7 @@ backend. It has a 'transaction_data td' argument which is used to fetch
|
||||||
information on added, deleted and changed entries. You access this
|
information on added, deleted and changed entries. You access this
|
||||||
information using access functions as defined in clixon_backend_transaction.h
|
information using access functions as defined in clixon_backend_transaction.h
|
||||||
|
|
||||||
## How do i check what has changed on commit?
|
## How do I check what has changed on commit?
|
||||||
You use XPATHs on the XML trees in the transaction commit callback.
|
You use XPATHs on the XML trees in the transaction commit callback.
|
||||||
Suppose you want to print all added interfaces:
|
Suppose you want to print all added interfaces:
|
||||||
```
|
```
|
||||||
|
|
@ -181,3 +201,12 @@ Check for inconsistencies in the XML trees and if they fail, make an clicon_err(
|
||||||
return -1;
|
return -1;
|
||||||
The validation or commit will then be aborted.
|
The validation or commit will then be aborted.
|
||||||
|
|
||||||
|
## How do I write a state data callback function?
|
||||||
|
|
||||||
|
Netconf <get> and restconf GET also returns state data, in contrast to
|
||||||
|
config data. In YANG state data is specified with "config false;".
|
||||||
|
|
||||||
|
To return state data, you need to write a backend state data callback
|
||||||
|
with the name "plugin_statedata()" where you return an XML tree.
|
||||||
|
|
||||||
|
Please look at the example for an example on how to write a state data callback.
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,28 @@ cli> downcall "This is a string"
|
||||||
This is a string
|
This is a string
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## State data
|
||||||
|
|
||||||
|
Netconf <get> and restconf GET also returns state data, in contrast to
|
||||||
|
config data.
|
||||||
|
|
||||||
|
In YANG state data is specified with "config false;". In the example, interface-state is state data.
|
||||||
|
|
||||||
|
To return state data, you need to write a backend state data callback
|
||||||
|
with the name "plugin_statedata" where you return an XML tree with
|
||||||
|
state. This is then merged with config data by the system.
|
||||||
|
|
||||||
|
pA static example of returning state data is in the example. Note that
|
||||||
|
a real example would poll or get the interface counters via a system
|
||||||
|
call, as well as use the "xpath" argument to identify the requested
|
||||||
|
state data.
|
||||||
|
|
||||||
|
|
||||||
## Run as docker container
|
## Run as docker container
|
||||||
```
|
```
|
||||||
cd docker
|
cd docker
|
||||||
# look in README
|
# look in README
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
module ietf-ip {
|
module ietf-ip {
|
||||||
|
|
||||||
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
|
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
|
||||||
prefix ip;
|
prefix ip;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,40 @@ routing_downcall(clicon_handle h,
|
||||||
cprintf(cbret, "<rpc-reply><ok>%s</ok></rpc-reply>", xml_body(xe));
|
cprintf(cbret, "<rpc-reply><ok>%s</ok></rpc-reply>", xml_body(xe));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Called to get state data from plugin
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
* @param[in] xtop XML tree, <config/> on entry.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see xmldb_get
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
plugin_statedata(clicon_handle h,
|
||||||
|
char *xpath,
|
||||||
|
cxobj *xstate)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj **xvec = NULL;
|
||||||
|
|
||||||
|
/* Example of (static) statedata, real code would poll state */
|
||||||
|
if (0 && (xml_parse("<interfaces-state><interface>"
|
||||||
|
"<name>eth0</name>"
|
||||||
|
"<type>eth</type>"
|
||||||
|
"<admin-status>up</admin-status>"
|
||||||
|
"<oper-status>up</oper-status>"
|
||||||
|
"<if-index>42</if-index>"
|
||||||
|
"<speed>1000000000</speed>"
|
||||||
|
"</interface></interfaces-state>", xstate)) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Plugin initialization
|
* Plugin initialization
|
||||||
*/
|
*/
|
||||||
|
|
@ -139,7 +173,8 @@ plugin_init(clicon_handle h)
|
||||||
|
|
||||||
if (notification_timer_setup(h) < 0)
|
if (notification_timer_setup(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (backend_netconf_register_callback(h, routing_downcall,
|
/* Register callback for netconf application-specific rpc call */
|
||||||
|
if (backend_rpc_cb_register(h, routing_downcall,
|
||||||
NULL,
|
NULL,
|
||||||
"myrouting"/* Xml tag when callback is made */
|
"myrouting"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
|
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
|
||||||
&xret) < 0)
|
&xret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
xml_print(stdout, xret);
|
xml_print(stdout, xret);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2);
|
||||||
int clicon_rpc_delete_config(clicon_handle h, char *db);
|
int clicon_rpc_delete_config(clicon_handle h, char *db);
|
||||||
int clicon_rpc_lock(clicon_handle h, char *db);
|
int clicon_rpc_lock(clicon_handle h, char *db);
|
||||||
int clicon_rpc_unlock(clicon_handle h, char *db);
|
int clicon_rpc_unlock(clicon_handle h, char *db);
|
||||||
|
int clicon_rpc_get(clicon_handle h, char *xpath, cxobj **xret);
|
||||||
int clicon_rpc_close_session(clicon_handle h);
|
int clicon_rpc_close_session(clicon_handle h);
|
||||||
int clicon_rpc_kill_session(clicon_handle h, int session_id);
|
int clicon_rpc_kill_session(clicon_handle h, int session_id);
|
||||||
int clicon_rpc_validate(clicon_handle h, char *db);
|
int clicon_rpc_validate(clicon_handle h, char *db);
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,23 @@
|
||||||
#ifndef _CLIXON_STRING_H_
|
#ifndef _CLIXON_STRING_H_
|
||||||
#define _CLIXON_STRING_H_
|
#define _CLIXON_STRING_H_
|
||||||
|
|
||||||
|
/* Struct used to map between int and strings. Typically used to map between
|
||||||
|
* values and their names. Note NULL terminated
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
static const map_str2int atmap[] = {
|
||||||
|
{"One", 1},
|
||||||
|
{"Two", 2},
|
||||||
|
{NULL, -1}
|
||||||
|
};
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
struct map_str2int{
|
||||||
|
char *ms_str;
|
||||||
|
int ms_int;
|
||||||
|
};
|
||||||
|
typedef struct map_str2int map_str2int;
|
||||||
|
|
||||||
/*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
|
/*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
|
||||||
#define align4(s) (((s)/4)*4 + 4)
|
#define align4(s) (((s)/4)*4 + 4)
|
||||||
|
|
||||||
|
|
@ -59,6 +76,9 @@ char *clicon_strjoin (int argc, char **argv, char *delim);
|
||||||
int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
|
int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
|
||||||
int percent_encode(char *str, char **escp);
|
int percent_encode(char *str, char **escp);
|
||||||
int percent_decode(char *esc, char **str);
|
int percent_decode(char *esc, char **str);
|
||||||
|
const char *clicon_int2str(const map_str2int *mstab, int i);
|
||||||
|
int clicon_str2int(const map_str2int *mstab, char *str);
|
||||||
|
|
||||||
#ifndef HAVE_STRNDUP
|
#ifndef HAVE_STRNDUP
|
||||||
char *clicon_strndup (const char *, size_t);
|
char *clicon_strndup (const char *, size_t);
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#endif /* ! HAVE_STRNDUP */
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag);
|
||||||
#define clicon_xml_parse_string(str, x) clicon_xml_parse_str((*str), x)
|
#define clicon_xml_parse_string(str, x) clicon_xml_parse_str((*str), x)
|
||||||
int clicon_xml_parse_str(char *str, cxobj **xml_top);
|
int clicon_xml_parse_str(char *str, cxobj **xml_top);
|
||||||
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
||||||
|
int xml_parse(char *str, cxobj *x_up);
|
||||||
|
|
||||||
int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
|
int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
|
||||||
int xml_copy(cxobj *x0, cxobj *x1);
|
int xml_copy(cxobj *x0, cxobj *x1);
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,10 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
|
||||||
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
|
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
|
||||||
|
|
||||||
/* Type of xmldb get function */
|
/* Type of xmldb get function */
|
||||||
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath,
|
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, int config, cxobj **xtop);
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
|
||||||
|
|
||||||
/* Type of xmldb put function */
|
/* Type of xmldb put function */
|
||||||
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op,
|
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op, cxobj *xt);
|
||||||
cxobj *xt);
|
|
||||||
|
|
||||||
/* Type of xmldb copy function */
|
/* Type of xmldb copy function */
|
||||||
typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to);
|
typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to);
|
||||||
|
|
@ -135,12 +133,12 @@ struct xmldb_api{
|
||||||
int xmldb_plugin_load(clicon_handle h, char *filename);
|
int xmldb_plugin_load(clicon_handle h, char *filename);
|
||||||
int xmldb_plugin_unload(clicon_handle h);
|
int xmldb_plugin_unload(clicon_handle h);
|
||||||
|
|
||||||
|
int xmldb_validate_db(char *db);
|
||||||
int xmldb_connect(clicon_handle h);
|
int xmldb_connect(clicon_handle h);
|
||||||
int xmldb_disconnect(clicon_handle h);
|
int xmldb_disconnect(clicon_handle h);
|
||||||
int xmldb_getopt(clicon_handle h, char *optname, void **value);
|
int xmldb_getopt(clicon_handle h, char *optname, void **value);
|
||||||
int xmldb_setopt(clicon_handle h, char *optname, void *value);
|
int xmldb_setopt(clicon_handle h, char *optname, void *value);
|
||||||
int xmldb_get(clicon_handle h, char *db, char *xpath,
|
int xmldb_get(clicon_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
|
||||||
int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt);
|
int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||||
int xmldb_copy(clicon_handle h, char *from, char *to);
|
int xmldb_copy(clicon_handle h, char *from, char *to);
|
||||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ enum {
|
||||||
LVXML_VECVAL2, /* key: a.b.0{x=1} -> <a><x>1</x></a> och */
|
LVXML_VECVAL2, /* key: a.b.0{x=1} -> <a><x>1</x></a> och */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
|
@ -64,12 +63,16 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
|
||||||
int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt);
|
int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt);
|
||||||
int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path);
|
int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path);
|
||||||
int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath);
|
int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath);
|
||||||
int xml_tree_prune_flagged(cxobj *xt, int flag, int test, int *upmark);
|
int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark);
|
||||||
|
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
|
||||||
int xml_default(cxobj *x, void *arg);
|
int xml_default(cxobj *x, void *arg);
|
||||||
int xml_order(cxobj *x, void *arg);
|
int xml_order(cxobj *x, void *arg);
|
||||||
int xml_sanity(cxobj *x, void *arg);
|
int xml_sanity(cxobj *x, void *arg);
|
||||||
|
int xml_non_config_data(cxobj *xt, void *arg);
|
||||||
|
int xml_spec_populate(cxobj *x, void *arg);
|
||||||
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||||
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
||||||
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, cxobj **xpathp, yang_node **ypathp);
|
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, cxobj **xpathp, yang_node **ypathp);
|
||||||
|
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_MAP_H_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ char *ytype_prefix(yang_stmt *ys);
|
||||||
char *ytype_id(yang_stmt *ys);
|
char *ytype_id(yang_stmt *ys);
|
||||||
yang_stmt *ys_module(yang_stmt *ys);
|
yang_stmt *ys_module(yang_stmt *ys);
|
||||||
yang_spec *ys_spec(yang_stmt *ys);
|
yang_spec *ys_spec(yang_stmt *ys);
|
||||||
yang_stmt *ys_module_import(yang_stmt *ymod, char *prefix);
|
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
||||||
yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
|
yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
|
||||||
yang_stmt *yang_find_syntax(yang_node *yn, char *argument);
|
yang_stmt *yang_find_syntax(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name);
|
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name);
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ hash_del(clicon_hash_t *hash,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Return vector of keys in has table
|
/*! Return vector of keys in hash table
|
||||||
*
|
*
|
||||||
* @param[in] hash Hash table
|
* @param[in] hash Hash table
|
||||||
* @param[out] nkeys Size of key vector
|
* @param[out] nkeys Size of key vector
|
||||||
|
|
|
||||||
|
|
@ -230,16 +230,22 @@ clicon_rpc_generate_error(cxobj *xerr)
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @param[in] xpath XPath (or "")
|
* @param[in] xpath XPath (or "")
|
||||||
* @param[out] xt XML tree. must be freed by caller with xml_free
|
* @param[out] xt XML tree. Free with xml_free.
|
||||||
|
* Either <config> or <rpc-error>.
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error, fatal or xml
|
* @retval -1 Error, fatal or xml
|
||||||
* @code
|
* @code
|
||||||
* cxobj *xt = NULL;
|
* cxobj *xt = NULL;
|
||||||
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
|
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
|
||||||
* err;
|
* err;
|
||||||
|
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
* clicon_rpc_generate_error(xerr);
|
||||||
|
* err;
|
||||||
|
* }
|
||||||
* if (xt)
|
* if (xt)
|
||||||
* xml_free(xt);
|
* xml_free(xt);
|
||||||
* @endcode
|
* @endcode
|
||||||
|
* @see clicon_rpc_generate_error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_get_config(clicon_handle h,
|
clicon_rpc_get_config(clicon_handle h,
|
||||||
|
|
@ -251,7 +257,6 @@ clicon_rpc_get_config(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
|
||||||
cxobj *xd;
|
cxobj *xd;
|
||||||
|
|
||||||
if ((cb = cbuf_new()) == NULL)
|
if ((cb = cbuf_new()) == NULL)
|
||||||
|
|
@ -264,11 +269,10 @@ clicon_rpc_get_config(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
/* Send xml error back: first check error, then ok */
|
||||||
clicon_rpc_generate_error(xerr);
|
if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL)
|
||||||
goto done;
|
xd = xml_parent(xd); /* point to rpc-reply */
|
||||||
}
|
else if ((xd = xpath_first(xret, "/rpc-reply/data/config")) == NULL)
|
||||||
if ((xd = xpath_first(xret, "//data/config")) == NULL)
|
|
||||||
if ((xd = xml_new("config", NULL)) == NULL)
|
if ((xd = xml_new("config", NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (xt){
|
if (xt){
|
||||||
|
|
@ -472,6 +476,70 @@ clicon_rpc_unlock(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get database configuration and state data
|
||||||
|
* Same as clicon_proto_change just with a cvec instead of lvec
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @param[in] xpath XPath (or "")
|
||||||
|
* @param[out] xt XML tree. Free with xml_free.
|
||||||
|
* Either <config> or <rpc-error>.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error, fatal or xml
|
||||||
|
* @code
|
||||||
|
* cxobj *xt = NULL;
|
||||||
|
* if (clicon_rpc_get(h, "/", &xt) < 0)
|
||||||
|
* err;
|
||||||
|
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||||
|
* clicon_rpc_generate_error(xerr);
|
||||||
|
* err;
|
||||||
|
* }
|
||||||
|
* if (xt)
|
||||||
|
* xml_free(xt);
|
||||||
|
* @endcode
|
||||||
|
* @see clicon_rpc_generate_error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_get(clicon_handle h,
|
||||||
|
char *xpath,
|
||||||
|
cxobj **xt)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xd;
|
||||||
|
|
||||||
|
if ((cb = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
cprintf(cb, "<rpc><get>");
|
||||||
|
if (xpath && strlen(xpath))
|
||||||
|
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
|
||||||
|
cprintf(cb, "</get></rpc>");
|
||||||
|
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Send xml error back: first check error, then ok */
|
||||||
|
if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL)
|
||||||
|
xd = xml_parent(xd); /* point to rpc-reply */
|
||||||
|
else if ((xd = xpath_first(xret, "/rpc-reply/data/config")) == NULL)
|
||||||
|
if ((xd = xml_new("config", NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xt){
|
||||||
|
if (xml_rm(xd) < 0)
|
||||||
|
goto done;
|
||||||
|
*xt = xd;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Close a (user) session
|
/*! Close a (user) session
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,43 @@ str2cvec(char *string,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Map from int to string using str2int map
|
||||||
|
* @param[in] ms String, integer map
|
||||||
|
* @param[in] i Input integer
|
||||||
|
* @retval str String value
|
||||||
|
* @retval NULL Error, not found
|
||||||
|
* @note linear search
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
clicon_int2str(const map_str2int *mstab,
|
||||||
|
int i)
|
||||||
|
{
|
||||||
|
const struct map_str2int *ms;
|
||||||
|
|
||||||
|
for (ms = &mstab[0]; ms->ms_str; ms++)
|
||||||
|
if (ms->ms_int == i)
|
||||||
|
return ms->ms_str;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Map from string to int using str2int map
|
||||||
|
* @param[in] ms String, integer map
|
||||||
|
* @param[in] str Input string
|
||||||
|
* @retval int Value
|
||||||
|
* @retval -1 Error, not found
|
||||||
|
* @note linear search
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_str2int(const map_str2int *mstab,
|
||||||
|
char *str)
|
||||||
|
{
|
||||||
|
const struct map_str2int *ms;
|
||||||
|
|
||||||
|
for (ms = &mstab[0]; ms->ms_str; ms++)
|
||||||
|
if (strcmp(ms->ms_str, str) == 0)
|
||||||
|
return ms->ms_int;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*! strndup() for systems without it, such as xBSD
|
/*! strndup() for systems without it, such as xBSD
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
/* clixon */
|
/* clixon */
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xml_parse.h"
|
#include "clixon_xml_parse.h"
|
||||||
|
|
@ -82,14 +83,8 @@ struct xml{
|
||||||
cg_var *x_cv; /* If body this contains the typed value */
|
cg_var *x_cv; /* If body this contains the typed value */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Type to string conversion */
|
|
||||||
struct map_str2int{
|
|
||||||
char *ms_str;
|
|
||||||
enum cxobj_type ms_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Mapping between xml type <--> string */
|
/* Mapping between xml type <--> string */
|
||||||
static const struct map_str2int xsmap[] = {
|
static const map_str2int xsmap[] = {
|
||||||
{"error", CX_ERROR},
|
{"error", CX_ERROR},
|
||||||
{"element", CX_ELMNT},
|
{"element", CX_ELMNT},
|
||||||
{"attr", CX_ATTR},
|
{"attr", CX_ATTR},
|
||||||
|
|
@ -104,12 +99,7 @@ static const struct map_str2int xsmap[] = {
|
||||||
char *
|
char *
|
||||||
xml_type2str(enum cxobj_type type)
|
xml_type2str(enum cxobj_type type)
|
||||||
{
|
{
|
||||||
const struct map_str2int *xs;
|
return (char*)clicon_int2str(xsmap, type);
|
||||||
|
|
||||||
for (xs = &xsmap[0]; xs->ms_str; xs++)
|
|
||||||
if (xs->ms_type == type)
|
|
||||||
return xs->ms_str;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -476,9 +466,14 @@ xml_childvec_get(cxobj *x)
|
||||||
*
|
*
|
||||||
* @param[in] name Name of new
|
* @param[in] name Name of new
|
||||||
* @param[in] xp The parent where the new xml node should be inserted
|
* @param[in] xp The parent where the new xml node should be inserted
|
||||||
*
|
* @retval xml Created xml object if successful
|
||||||
* @retval created xml object if successful
|
* @retval NULL Error and clicon_err() called
|
||||||
* @retval NULL if error and clicon_err() called
|
* @code
|
||||||
|
* cxobj *x;
|
||||||
|
* if ((x = xml_new(name, xparent)) == NULL)
|
||||||
|
* err;
|
||||||
|
* @endcode
|
||||||
|
* @see xml_new_spec Also sets yang spec.
|
||||||
*/
|
*/
|
||||||
cxobj *
|
cxobj *
|
||||||
xml_new(char *name,
|
xml_new(char *name,
|
||||||
|
|
@ -521,7 +516,6 @@ xml_new_spec(char *name,
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void *
|
void *
|
||||||
xml_spec(cxobj *x)
|
xml_spec(cxobj *x)
|
||||||
{
|
{
|
||||||
|
|
@ -971,10 +965,12 @@ clicon_xml2cbuf(cbuf *cb,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Internal xml parsing function.
|
/*! Basic xml parsing function.
|
||||||
|
* @param[in] str Pointer to string containing XML definition.
|
||||||
|
* @param[out] xtop Top of XML parse tree. Assume created.
|
||||||
* @see clicon_xml_parse_file clicon_xml_parse_string
|
* @see clicon_xml_parse_file clicon_xml_parse_string
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
xml_parse(char *str,
|
xml_parse(char *str,
|
||||||
cxobj *x_up)
|
cxobj *x_up)
|
||||||
{
|
{
|
||||||
|
|
@ -1355,6 +1351,10 @@ cxvec_append(cxobj *x,
|
||||||
* @param[in] type matching type or -1 for any
|
* @param[in] type matching type or -1 for any
|
||||||
* @param[in] fn Callback
|
* @param[in] fn Callback
|
||||||
* @param[in] arg Argument
|
* @param[in] arg Argument
|
||||||
|
* @retval -1 Error, aborted at first error encounter
|
||||||
|
* @retval 0 OK, all nodes traversed
|
||||||
|
* @retval n OK, aborted at first encounter of first match
|
||||||
|
*
|
||||||
* @code
|
* @code
|
||||||
* int x_fn(cxobj *x, void *arg)
|
* int x_fn(cxobj *x, void *arg)
|
||||||
* {
|
* {
|
||||||
|
|
@ -1363,7 +1363,7 @@ cxvec_append(cxobj *x,
|
||||||
* xml_apply(xn, CX_ELMNT, x_fn, NULL);
|
* xml_apply(xn, CX_ELMNT, x_fn, NULL);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note do not delete or move around any children during this function
|
* @note do not delete or move around any children during this function
|
||||||
* @note It does not apply fn to the root node,..
|
* @note return value > 0 aborts the traversal
|
||||||
* @see xml_apply0 including top object
|
* @see xml_apply0 including top object
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -1373,13 +1373,19 @@ xml_apply(cxobj *xn,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x = NULL;
|
cxobj *x;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
x = NULL;
|
||||||
while ((x = xml_child_each(xn, x, type)) != NULL) {
|
while ((x = xml_child_each(xn, x, type)) != NULL) {
|
||||||
if (fn(x, arg) < 0)
|
if (fn(x, arg) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply(x, type, fn, arg) < 0)
|
if ((ret = xml_apply(x, type, fn, arg)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret > 0){
|
||||||
|
retval = ret;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -1387,7 +1393,11 @@ xml_apply(cxobj *xn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Apply a function call on top object and all xml node children recursively
|
/*! Apply a function call on top object and all xml node children recursively
|
||||||
|
* @retval -1 Error, aborted at first error encounter
|
||||||
|
* @retval 0 OK, all nodes traversed
|
||||||
|
* @retval n OK, aborted at first encounter of first match
|
||||||
* @see xml_apply not including top object
|
* @see xml_apply not including top object
|
||||||
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_apply0(cxobj *xn,
|
xml_apply0(cxobj *xn,
|
||||||
|
|
@ -1396,9 +1406,13 @@ xml_apply0(cxobj *xn,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (fn(xn, arg) < 0)
|
if ((ret = fn(xn, arg)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret > 0)
|
||||||
|
retval = ret;
|
||||||
|
else
|
||||||
retval = xml_apply(xn, type, fn, arg);
|
retval = xml_apply(xn, type, fn, arg);
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -1412,6 +1426,9 @@ xml_apply0(cxobj *xn,
|
||||||
* @param[in] xn XML node
|
* @param[in] xn XML node
|
||||||
* @param[in] fn Callback
|
* @param[in] fn Callback
|
||||||
* @param[in] arg Argument
|
* @param[in] arg Argument
|
||||||
|
* @retval -1 Error, aborted at first error encounter
|
||||||
|
* @retval 0 OK, all nodes traversed
|
||||||
|
* @retval n OK, aborted at first encounter of first match
|
||||||
* @code
|
* @code
|
||||||
* int x_fn(cxobj *x, void *arg)
|
* int x_fn(cxobj *x, void *arg)
|
||||||
* {
|
* {
|
||||||
|
|
@ -1430,12 +1447,17 @@ xml_apply_ancestor(cxobj *xn,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xp = NULL;
|
cxobj *xp = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
while ((xp = xml_parent(xn)) != NULL) {
|
while ((xp = xml_parent(xn)) != NULL) {
|
||||||
if (fn(xp, arg) < 0)
|
if (fn(xp, arg) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply_ancestor(xp, fn, arg) < 0)
|
if ((ret = xml_apply_ancestor(xp, fn, arg)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret > 0){
|
||||||
|
retval = ret;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
xn = xp;
|
xn = xp;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,23 @@ xmldb_plugin_unload(clicon_handle h)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Validate database name
|
||||||
|
* @param[in] db Name of database
|
||||||
|
* @param[out] xret Return value as cligen buffer containing xml netconf return
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Failed validate, xret set to error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xmldb_validate_db(char *db)
|
||||||
|
{
|
||||||
|
if (strcmp(db, "running") != 0 &&
|
||||||
|
strcmp(db, "candidate") != 0 &&
|
||||||
|
strcmp(db, "startup") != 0 &&
|
||||||
|
strcmp(db, "tmp") != 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Connect to a datastore plugin, allocate handle to be used in API calls
|
/*! Connect to a datastore plugin, allocate handle to be used in API calls
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -305,36 +322,25 @@ xmldb_setopt(clicon_handle h,
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] dbname Name of database to search in (filename including dir path
|
* @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] xpath String with XPATH syntax. or NULL for all
|
||||||
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
|
* @param[in] config If set only configuration data, else also state
|
||||||
* @param[out] xvec Vector of xml trees. Free after use.
|
* @param[out] xtop Single XML tree. Free with xml_free()
|
||||||
* @param[out] xlen Length of vector.
|
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* cxobj *xt;
|
* cxobj *xt;
|
||||||
* cxobj **xvec;
|
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt) < 0)
|
||||||
* size_t xlen;
|
|
||||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
|
|
||||||
* &xt, &xvec, &xlen) < 0)
|
|
||||||
* err;
|
* err;
|
||||||
* for (i=0; i<xlen; i++){
|
|
||||||
* xn = xv[i];
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* xml_free(xt);
|
* xml_free(xt);
|
||||||
* free(xvec);
|
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
* @note if xvec is given, then purge tree, if not return whole tree.
|
||||||
* @see xpath_vec
|
* @see xpath_vec
|
||||||
* @see xmldb_get
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xmldb_get(clicon_handle h,
|
xmldb_get(clicon_handle h,
|
||||||
char *db,
|
char *db,
|
||||||
char *xpath,
|
char *xpath,
|
||||||
cxobj **xtop,
|
int config,
|
||||||
cxobj ***xvec,
|
cxobj **xtop)
|
||||||
size_t *xlen)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
xmldb_handle xh;
|
xmldb_handle xh;
|
||||||
|
|
@ -352,7 +358,7 @@ xmldb_get(clicon_handle h,
|
||||||
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = xa->xa_get_fn(xh, db, xpath, xtop, xvec, xlen);
|
retval = xa->xa_get_fn(xh, db, xpath, config, xtop);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
cbuf *cb = cbuf_new();
|
cbuf *cb = cbuf_new();
|
||||||
|
|
@ -366,14 +372,12 @@ xmldb_get(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Modify database provided an xml tree and an operation
|
/*! Modify database given an xml tree and an operation
|
||||||
*
|
*
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db running or candidate
|
* @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[in] xt xml-tree. Top-level symbol is dummy
|
||||||
* @param[in] op OP_MERGE: just add it.
|
|
||||||
* OP_REPLACE: first delete whole database
|
|
||||||
* OP_NONE: operation attribute in xml determines operation
|
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* The xml may contain the "operation" attribute which defines the operation.
|
* The xml may contain the "operation" attribute which defines the operation.
|
||||||
|
|
@ -384,6 +388,9 @@ xmldb_get(clicon_handle h,
|
||||||
* if (xmldb_put(xh, "running", OP_MERGE, xt) < 0)
|
* if (xmldb_put(xh, "running", OP_MERGE, xt) < 0)
|
||||||
* err;
|
* err;
|
||||||
* @endcode
|
* @endcode
|
||||||
|
* @note that you can add both config data and state data. In comparison,
|
||||||
|
* xmldb_get has a parameter to get config data only.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xmldb_put(clicon_handle h,
|
xmldb_put(clicon_handle h,
|
||||||
|
|
@ -581,7 +588,7 @@ xmldb_islocked(clicon_handle h,
|
||||||
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval =xa->xa_islocked_fn(xh, db);
|
retval = xa->xa_islocked_fn(xh, db);
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1084,20 +1084,22 @@ api_path_fmt2xpath(char *api_path_fmt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Prune everything that does not pass test
|
/*! Prune everything that does not pass test or have at least a child* does not
|
||||||
* @param[in] xt XML tree with some node marked
|
* @param[in] xt XML tree with some node marked
|
||||||
* @param[in] flag Which flag to test for
|
* @param[in] flag Which flag to test for
|
||||||
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||||
* @param[out] upmark Set if a child (recursively) has marked set.
|
* @param[out] upmark Set if a child (recursively) has marked set.
|
||||||
* The function removes all branches that does not a child that pass the test
|
* The function removes all branches that does not pass the test
|
||||||
* Purge all nodes that dont have MARK flag set recursively.
|
* Purge all nodes that dont have MARK flag set recursively.
|
||||||
* Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
|
* Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
|
||||||
* @code
|
* @code
|
||||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
* xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
|
||||||
* @endcode
|
* @endcode
|
||||||
|
* @note This function seems a little too complex semantics
|
||||||
|
* @see xml_tree_prune_flagged for a simpler variant
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_tree_prune_flagged(cxobj *xt,
|
xml_tree_prune_flagged_sub(cxobj *xt,
|
||||||
int flag,
|
int flag,
|
||||||
int test,
|
int test,
|
||||||
int *upmark)
|
int *upmark)
|
||||||
|
|
@ -1117,6 +1119,7 @@ xml_tree_prune_flagged(cxobj *xt,
|
||||||
xprev = x = NULL;
|
xprev = x = NULL;
|
||||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||||
if (xml_flag(x, flag) == test?flag:0){
|
if (xml_flag(x, flag) == test?flag:0){
|
||||||
|
/* Pass test */
|
||||||
mark++;
|
mark++;
|
||||||
xprev = x;
|
xprev = x;
|
||||||
continue; /* mark and stop here */
|
continue; /* mark and stop here */
|
||||||
|
|
@ -1131,7 +1134,7 @@ xml_tree_prune_flagged(cxobj *xt,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xml_tree_prune_flagged(x, flag, test, &submark) < 0)
|
if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* if xt is list and submark anywhere, then key subs are also marked
|
/* if xt is list and submark anywhere, then key subs are also marked
|
||||||
*/
|
*/
|
||||||
|
|
@ -1167,6 +1170,43 @@ xml_tree_prune_flagged(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Prune everything that passes test
|
||||||
|
* @param[in] xt XML tree with some node marked
|
||||||
|
* @param[in] flag Which flag to test for
|
||||||
|
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||||
|
* The function removes all branches that does not pass test
|
||||||
|
* @code
|
||||||
|
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_tree_prune_flagged(cxobj *xt,
|
||||||
|
int flag,
|
||||||
|
int test)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *x;
|
||||||
|
cxobj *xprev;
|
||||||
|
|
||||||
|
x = NULL;
|
||||||
|
xprev = x = NULL;
|
||||||
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||||
|
if (xml_flag(x, flag) == test?flag:0){ /* Pass test means purge */
|
||||||
|
if (xml_purge(x) < 0)
|
||||||
|
goto done;
|
||||||
|
x = xprev;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (xml_tree_prune_flagged(x, flag, test) < 0)
|
||||||
|
goto done;
|
||||||
|
xprev = x;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Add default values (if not set)
|
/*! Add default values (if not set)
|
||||||
* @param[in] xt XML tree with some node marked
|
* @param[in] xt XML tree with some node marked
|
||||||
*/
|
*/
|
||||||
|
|
@ -1293,10 +1333,6 @@ xml_sanity(cxobj *xt,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
name = xml_name(xt);
|
name = xml_name(xt);
|
||||||
if (ys==NULL){
|
|
||||||
clicon_err(OE_XML, 0, "No spec for xml node %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (strstr(ys->ys_argument, name)==NULL){
|
if (strstr(ys->ys_argument, name)==NULL){
|
||||||
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
|
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
|
||||||
name, ys->ys_argument);
|
name, ys->ys_argument);
|
||||||
|
|
@ -1307,6 +1343,69 @@ xml_sanity(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Mark all nodes that are not configure data and set return
|
||||||
|
* @param[in] xt XML tree
|
||||||
|
* @param[out] arg If set, set to 1 as int* if not config data
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_non_config_data(cxobj *xt,
|
||||||
|
void *arg) /* Set to 1 if state node */
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *ys;
|
||||||
|
|
||||||
|
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||||
|
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, xml_name(xt));
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (!yang_config(ys)){ /* config == false means state data: mark for remove */
|
||||||
|
xml_flag_set(xt, XML_FLAG_MARK);
|
||||||
|
if (arg)
|
||||||
|
(*(int*)arg) = 1;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Add yang specification backpoint to XML node
|
||||||
|
* @param[in] xt XML tree node
|
||||||
|
* @note This should really be unnecessary since yspec should be set on creation
|
||||||
|
* @code
|
||||||
|
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_spec_populate(cxobj *x,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_spec *yspec = (yang_spec*)arg;
|
||||||
|
char *name;
|
||||||
|
yang_stmt *y; /* yang node */
|
||||||
|
cxobj *xp; /* xml parent */
|
||||||
|
yang_stmt *yp; /* parent yang */
|
||||||
|
|
||||||
|
name = xml_name(x);
|
||||||
|
if ((xp = xml_parent(x)) != NULL &&
|
||||||
|
(yp = xml_spec(xp)) != NULL)
|
||||||
|
y = yang_find_syntax((yang_node*)yp, xml_name(x));
|
||||||
|
else
|
||||||
|
y = yang_find_topnode(yspec, name); /* still NULL for config */
|
||||||
|
if (y==NULL){
|
||||||
|
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
|
||||||
|
name,
|
||||||
|
xp?xml_name(xp):"", yp?yp->ys_argument:"");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
xml_spec_set(x, y);
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Translate from restconf api-path in cvv form to xml xpath
|
/*! Translate from restconf api-path in cvv form to xml xpath
|
||||||
* eg a/b=c -> a/[b=c]
|
* eg a/b=c -> a/[b=c]
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
|
|
@ -1617,3 +1716,195 @@ api_path2xml(char *api_path,
|
||||||
free(vec);
|
free(vec);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Given a modification tree, check existing matching child in the base tree
|
||||||
|
* param[in] x0 Base tree node
|
||||||
|
* param[in] x1c Modification tree child
|
||||||
|
* param[in] yc Yang spec of tree child
|
||||||
|
* param[out] x0cp Matching base tree child (if any)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
match_base_child(cxobj *x0,
|
||||||
|
cxobj *x1c,
|
||||||
|
yang_stmt *yc,
|
||||||
|
cxobj **x0cp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *x0c = NULL;
|
||||||
|
char *keyname;
|
||||||
|
cvec *cvk = NULL;
|
||||||
|
cg_var *cvi;
|
||||||
|
char *b0;
|
||||||
|
char *b1;
|
||||||
|
yang_stmt *ykey;
|
||||||
|
char *cname;
|
||||||
|
int ok;
|
||||||
|
char *x1bstr; /* body string */
|
||||||
|
|
||||||
|
cname = xml_name(x1c);
|
||||||
|
switch (yc->ys_keyword){
|
||||||
|
case Y_LEAF_LIST: /* Match with name and value */
|
||||||
|
x1bstr = xml_body(x1c);
|
||||||
|
x0c = NULL;
|
||||||
|
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
|
||||||
|
if (strcmp(cname, xml_name(x0c)) == 0 &&
|
||||||
|
strcmp(xml_body(x0c), x1bstr)==0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Y_LIST: /* Match with key values */
|
||||||
|
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||||
|
__FUNCTION__, yc->ys_argument);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* The value is a list of keys: <key>[ <key>]* */
|
||||||
|
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||||
|
goto done;
|
||||||
|
x0c = NULL;
|
||||||
|
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
|
||||||
|
if (strcmp(xml_name(x0c), cname))
|
||||||
|
continue;
|
||||||
|
cvi = NULL;
|
||||||
|
ok = 0;
|
||||||
|
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||||
|
keyname = cv_string_get(cvi);
|
||||||
|
ok = 1; /* if we come here */
|
||||||
|
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
|
||||||
|
break; /* error case */
|
||||||
|
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
|
||||||
|
break; /* error case */
|
||||||
|
if (strcmp(b0, b1))
|
||||||
|
break;
|
||||||
|
ok = 2; /* and reaches here for all keynames, x0c is found. */
|
||||||
|
}
|
||||||
|
if (ok == 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: /* Just match with name */
|
||||||
|
x0c = xml_find(x0, cname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*x0cp = x0c;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cvk)
|
||||||
|
cvec_free(cvk);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Merge a base tree x0 with x1 with yang spec y
|
||||||
|
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||||
|
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||||
|
* @param[in] x0p Parent of x0
|
||||||
|
* @param[in] x1 xml tree which modifies base
|
||||||
|
* Assume x0 and x1 are same on entry and that y is the spec
|
||||||
|
* @see put in clixon_keyvalue.c
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xml_merge1(cxobj *x0,
|
||||||
|
yang_node *y0,
|
||||||
|
cxobj *x0p,
|
||||||
|
cxobj *x1)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *x1name;
|
||||||
|
char *x1cname; /* child name */
|
||||||
|
cxobj *x0c; /* base child */
|
||||||
|
cxobj *x0b; /* base body */
|
||||||
|
cxobj *x1c; /* mod child */
|
||||||
|
char *x1bstr; /* mod body string */
|
||||||
|
yang_stmt *yc; /* yang child */
|
||||||
|
|
||||||
|
assert(x1 && xml_type(x1) == CX_ELMNT);
|
||||||
|
assert(y0);
|
||||||
|
|
||||||
|
x1name = xml_name(x1);
|
||||||
|
if (y0->yn_keyword == Y_LEAF_LIST || y0->yn_keyword == Y_LEAF){
|
||||||
|
x1bstr = xml_body(x1);
|
||||||
|
if (x0==NULL){
|
||||||
|
if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (x1bstr){ /* empty type does not have body */
|
||||||
|
if ((x0b = xml_new("body", x0)) == NULL)
|
||||||
|
goto done;
|
||||||
|
xml_type_set(x0b, CX_BODY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x1bstr){
|
||||||
|
if ((x0b = xml_body_get(x0)) == NULL){
|
||||||
|
if ((x0b = xml_new("body", x0)) == NULL)
|
||||||
|
goto done;
|
||||||
|
xml_type_set(x0b, CX_BODY);
|
||||||
|
}
|
||||||
|
if (xml_value_set(x0b, x1bstr) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* if LEAF|LEAF_LIST */
|
||||||
|
else { /* eg Y_CONTAINER, Y_LIST */
|
||||||
|
if (x0==NULL){
|
||||||
|
if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* Loop through children of the modification tree */
|
||||||
|
x1c = NULL;
|
||||||
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
|
x1cname = xml_name(x1c);
|
||||||
|
/* Get yang spec of the child */
|
||||||
|
if ((yc = yang_find_syntax(y0, x1cname)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "No yang node found: %s", x1cname);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* See if there is a corresponding node in the base tree */
|
||||||
|
x0c = NULL;
|
||||||
|
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} /* else Y_CONTAINER */
|
||||||
|
// ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Merge XML trees x1 into x0 according to yang spec yspec
|
||||||
|
* @note both x0 and x1 need to be top-level trees
|
||||||
|
* @see text_modify_top as more generic variant (in datastore text)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_merge(cxobj *x0,
|
||||||
|
cxobj *x1,
|
||||||
|
yang_spec *yspec)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *x1cname; /* child name */
|
||||||
|
cxobj *x0c; /* base child */
|
||||||
|
cxobj *x1c; /* mod child */
|
||||||
|
yang_stmt *yc;
|
||||||
|
|
||||||
|
/* Assure top-levels are 'config' */
|
||||||
|
assert(x0 && strcmp(xml_name(x0),"config")==0);
|
||||||
|
assert(x1 && strcmp(xml_name(x1),"config")==0);
|
||||||
|
/* Loop through children of the modification tree */
|
||||||
|
x1c = NULL;
|
||||||
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
|
x1cname = xml_name(x1c);
|
||||||
|
/* Get yang spec of the child */
|
||||||
|
if ((yc = yang_find_topnode(yspec, x1cname)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* See if there is a corresponding node in the base tree */
|
||||||
|
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ in
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xsl.h"
|
#include "clixon_xsl.h"
|
||||||
|
|
||||||
|
|
@ -130,13 +131,8 @@ enum axis_type{
|
||||||
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
|
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct map_str2int{
|
|
||||||
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
|
|
||||||
int ms_int;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Mapping between axis type string <--> int */
|
/* Mapping between axis type string <--> int */
|
||||||
static const struct map_str2int atmap[] = {
|
static const map_str2int axismap[] = {
|
||||||
{"self", A_SELF},
|
{"self", A_SELF},
|
||||||
{"child", A_CHILD},
|
{"child", A_CHILD},
|
||||||
{"parent", A_PARENT},
|
{"parent", A_PARENT},
|
||||||
|
|
@ -160,19 +156,6 @@ struct xpath_element{
|
||||||
|
|
||||||
static int xpath_split(char *xpathstr, char **pathexpr);
|
static int xpath_split(char *xpathstr, char **pathexpr);
|
||||||
|
|
||||||
static char *axis_type2str(enum axis_type type) __attribute__ ((unused));
|
|
||||||
|
|
||||||
static char *
|
|
||||||
axis_type2str(enum axis_type type)
|
|
||||||
{
|
|
||||||
const struct map_str2int *at;
|
|
||||||
|
|
||||||
for (at = &atmap[0]; at->ms_str; at++)
|
|
||||||
if (at->ms_int == type)
|
|
||||||
return at->ms_str;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xpath_print(FILE *f, struct xpath_element *xplist)
|
xpath_print(FILE *f, struct xpath_element *xplist)
|
||||||
{
|
{
|
||||||
|
|
@ -180,7 +163,7 @@ xpath_print(FILE *f, struct xpath_element *xplist)
|
||||||
struct xpath_predicate *xp;
|
struct xpath_predicate *xp;
|
||||||
|
|
||||||
for (xe=xplist; xe; xe=xe->xe_next){
|
for (xe=xplist; xe; xe=xe->xe_next){
|
||||||
fprintf(f, "\t:%s %s ", axis_type2str(xe->xe_type),
|
fprintf(f, "\t:%s %s ", clicon_int2str(axismap, xe->xe_type),
|
||||||
xe->xe_str?xe->xe_str:"");
|
xe->xe_str?xe->xe_str:"");
|
||||||
for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
|
for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
|
||||||
fprintf(f, "[%s]", xp->xp_expr);
|
fprintf(f, "[%s]", xp->xp_expr);
|
||||||
|
|
@ -598,7 +581,7 @@ xpath_find(struct xpath_element *xe,
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
fprintf(stderr, "%s: %s: \"%s\"\n", __FUNCTION__,
|
fprintf(stderr, "%s: %s: \"%s\"\n", __FUNCTION__,
|
||||||
axis_type2str(xe->xe_type), xe->xe_str?xe->xe_str:"");
|
clicon_int2str(axismap, xe->xe_type), xe->xe_str?xe->xe_str:"");
|
||||||
#endif
|
#endif
|
||||||
switch (xe->xe_type){
|
switch (xe->xe_type){
|
||||||
case A_SELF:
|
case A_SELF:
|
||||||
|
|
@ -942,12 +925,12 @@ xpath_each(cxobj *cxtop,
|
||||||
* @retval -1 error.
|
* @retval -1 error.
|
||||||
*
|
*
|
||||||
* @code
|
* @code
|
||||||
* cxobj **vec;
|
* cxobj **xvec;
|
||||||
* size_t veclen;
|
* size_t xlen;
|
||||||
* if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0)
|
* if (xpath_vec(cxtop, "//symbol/foo", &xvec, &xlen) < 0)
|
||||||
* goto err;
|
* goto err;
|
||||||
* for (i=0; i<veclen; i++){
|
* for (i=0; i<xlen; i++){
|
||||||
* xn = vec[i];
|
* xn = xvec[i];
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* free(vec);
|
* free(vec);
|
||||||
|
|
|
||||||
|
|
@ -71,25 +71,9 @@
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
#include "clixon_yang_parse.h"
|
#include "clixon_yang_parse.h"
|
||||||
|
|
||||||
/* Instead of using dynamic type lookup, use a cache that is evaluated early
|
|
||||||
for static scope type binding */
|
|
||||||
#define YANG_TYPE_CACHE 1
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Private data types
|
|
||||||
*/
|
|
||||||
/* Struct used to map between int and strings. Used for:
|
|
||||||
* - mapping yang types/typedefs (strings) and cligen types (ints).
|
|
||||||
* - mapping yang keywords (strings) and enum (clicon)
|
|
||||||
*/
|
|
||||||
struct map_str2int{
|
|
||||||
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
|
|
||||||
int ms_int;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Mapping between yang keyword string <--> clicon constants */
|
/* Mapping between yang keyword string <--> clicon constants */
|
||||||
static const struct map_str2int ykmap[] = {
|
static const map_str2int ykmap[] = {
|
||||||
{"anyxml", Y_ANYXML},
|
{"anyxml", Y_ANYXML},
|
||||||
{"argument", Y_ARGUMENT},
|
{"argument", Y_ARGUMENT},
|
||||||
{"augment", Y_AUGMENT},
|
{"augment", Y_AUGMENT},
|
||||||
|
|
@ -545,18 +529,10 @@ ys_flag_reset(yang_stmt *ys,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate from RFC 6020 keywords to printable string.
|
|
||||||
linear search,...
|
|
||||||
*/
|
|
||||||
char *
|
char *
|
||||||
yang_key2str(int keyword)
|
yang_key2str(int keyword)
|
||||||
{
|
{
|
||||||
const struct map_str2int *yk;
|
return (char*)clicon_int2str(ykmap, keyword);
|
||||||
|
|
||||||
for (yk = &ykmap[0]; yk->ms_str; yk++)
|
|
||||||
if (yk->ms_int == keyword)
|
|
||||||
return yk->ms_str;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Find top module or sub-module. Note that ultimate top is yang spec
|
/*! Find top module or sub-module. Note that ultimate top is yang spec
|
||||||
|
|
@ -635,29 +611,63 @@ ytype_prefix(yang_stmt *ys)
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given a module and a prefix, find the import statement fo that prefix
|
|
||||||
|
/*! Given a yang statement and a prefix, return yang module to that prefix
|
||||||
* Note, not the other module but the proxy import statement only
|
* Note, not the other module but the proxy import statement only
|
||||||
* @param[in] ytop yang module
|
* @param[in] ys A yang statement
|
||||||
|
* @param[in] prefix prefix
|
||||||
|
* @retval ymod Yang module statement if found
|
||||||
|
* @retval NULL not found
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
ys_module_import(yang_stmt *ymod,
|
yang_find_module_by_prefix(yang_stmt *ys,
|
||||||
char *prefix)
|
char *prefix)
|
||||||
{
|
{
|
||||||
yang_stmt *yimport = NULL;
|
yang_stmt *yimport;
|
||||||
yang_stmt *yprefix;
|
yang_stmt *yprefix;
|
||||||
|
yang_stmt *my_ymod;
|
||||||
|
yang_stmt *ymod = NULL;
|
||||||
|
yang_spec *yspec;
|
||||||
|
|
||||||
assert(ymod->ys_keyword == Y_MODULE || ymod->ys_keyword == Y_SUBMODULE);
|
if ((my_ymod = ys_module(ys)) == NULL){
|
||||||
while ((yimport = yn_each((yang_node*)ymod, yimport)) != NULL) {
|
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((yspec = ys_spec(my_ymod)) == NULL){
|
||||||
|
clicon_err(OE_YANG, 0, "My yang spec not found");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (my_ymod->ys_keyword != Y_MODULE &&
|
||||||
|
my_ymod->ys_keyword != Y_SUBMODULE){
|
||||||
|
clicon_err(OE_YANG, 0, "%s not module or sub-module", my_ymod->ys_argument);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((yprefix = yang_find((yang_node*)my_ymod, Y_PREFIX, NULL)) != NULL &&
|
||||||
|
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||||
|
ymod = my_ymod;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
yimport = NULL;
|
||||||
|
while ((yimport = yn_each((yang_node*)my_ymod, yimport)) != NULL) {
|
||||||
if (yimport->ys_keyword != Y_IMPORT)
|
if (yimport->ys_keyword != Y_IMPORT)
|
||||||
continue;
|
continue;
|
||||||
if ((yprefix = yang_find((yang_node*)yimport, Y_PREFIX, NULL)) != NULL &&
|
if ((yprefix = yang_find((yang_node*)yimport, Y_PREFIX, NULL)) != NULL &&
|
||||||
strcmp(yprefix->ys_argument, prefix) == 0)
|
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||||
return yimport;
|
break;
|
||||||
}
|
}
|
||||||
return NULL;
|
}
|
||||||
|
if (yimport){
|
||||||
|
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL){
|
||||||
|
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
|
||||||
|
yimport->ys_argument);
|
||||||
|
yimport = NULL;
|
||||||
|
goto done; /* unresolved */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
return ymod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! string is quoted if it contains space or tab, needs double '' */
|
/*! string is quoted if it contains space or tab, needs double '' */
|
||||||
static int inline
|
static int inline
|
||||||
quotedstring(char *s)
|
quotedstring(char *s)
|
||||||
|
|
@ -1056,22 +1066,13 @@ ys_grouping_resolve(yang_stmt *ys,
|
||||||
yang_stmt **ygrouping0)
|
yang_stmt **ygrouping0)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *yimport;
|
|
||||||
yang_spec *yspec;
|
|
||||||
yang_stmt *ymodule;
|
yang_stmt *ymodule;
|
||||||
yang_stmt *ygrouping = NULL;
|
yang_stmt *ygrouping = NULL;
|
||||||
yang_node *yn;
|
yang_node *yn;
|
||||||
yang_stmt *ymod;
|
|
||||||
|
|
||||||
/* find the grouping associated with argument and expand(?) */
|
/* find the grouping associated with argument and expand(?) */
|
||||||
if (prefix){ /* Go to top and find import that matches */
|
if (prefix){ /* Go to top and find import that matches */
|
||||||
ymod = ys_module(ys);
|
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) != NULL)
|
||||||
if ((yimport = ys_module_import(ymod, prefix)) == NULL){
|
|
||||||
clicon_err(OE_DB, 0, "Prefix %s not defined not found", prefix);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
yspec = ys_spec(ys);
|
|
||||||
if ((ymodule = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) != NULL)
|
|
||||||
ygrouping = yang_find((yang_node*)ymodule, Y_GROUPING, name);
|
ygrouping = yang_find((yang_node*)ymodule, Y_GROUPING, name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -1087,7 +1088,7 @@ ys_grouping_resolve(yang_stmt *ys,
|
||||||
}
|
}
|
||||||
*ygrouping0 = ygrouping;
|
*ygrouping0 = ygrouping;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
// done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1266,8 +1267,8 @@ yang_expand(yang_node *yn)
|
||||||
* @param str String of yang statements
|
* @param str String of yang statements
|
||||||
* @param name Log string, typically filename
|
* @param name Log string, typically filename
|
||||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
||||||
* @retval 0 Everything OK
|
* @retval ymod Top-level yang (sub)module
|
||||||
* @retval -1 Error encountered
|
* @retval NULL Error encountered
|
||||||
* Calling order:
|
* Calling order:
|
||||||
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
||||||
* yang_parse1 # Parse one yang module, go through its (sub)modules and parse them
|
* yang_parse1 # Parse one yang module, go through its (sub)modules and parse them
|
||||||
|
|
@ -1283,7 +1284,7 @@ yang_parse_str(clicon_handle h,
|
||||||
yang_spec *yspec)
|
yang_spec *yspec)
|
||||||
{
|
{
|
||||||
struct clicon_yang_yacc_arg yy = {0,};
|
struct clicon_yang_yacc_arg yy = {0,};
|
||||||
yang_stmt *ym = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
|
|
||||||
yy.yy_handle = h;
|
yy.yy_handle = h;
|
||||||
yy.yy_name = (char*)name;
|
yy.yy_name = (char*)name;
|
||||||
|
|
@ -1312,10 +1313,10 @@ yang_parse_str(clicon_handle h,
|
||||||
if (yang_scan_exit(&yy) < 0)
|
if (yang_scan_exit(&yy) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
ym = yy.yy_module;
|
ymod = yy.yy_module;
|
||||||
done:
|
done:
|
||||||
ystack_pop(&yy);
|
ystack_pop(&yy);
|
||||||
return ym;
|
return ymod; /* top-level (sub)module */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Read an opened file into a string and call yang string parsing
|
/*! Read an opened file into a string and call yang string parsing
|
||||||
|
|
@ -1326,8 +1327,8 @@ yang_parse_str(clicon_handle h,
|
||||||
* @param f Open file handle
|
* @param f Open file handle
|
||||||
* @param name Log string, typically filename
|
* @param name Log string, typically filename
|
||||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
||||||
* @retval 0 Everything OK
|
* @retval ymod Top-level yang (sub)module
|
||||||
* @retval -1 Error encountered
|
* @retval NULL Error encountered
|
||||||
|
|
||||||
* The database symbols are inserted in alphabetical order.
|
* The database symbols are inserted in alphabetical order.
|
||||||
* Calling order:
|
* Calling order:
|
||||||
|
|
@ -1349,7 +1350,7 @@ yang_parse_file(clicon_handle h,
|
||||||
int i;
|
int i;
|
||||||
int c;
|
int c;
|
||||||
int len;
|
int len;
|
||||||
yang_stmt *ymodule = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "Yang parse file: %s", name);
|
clicon_debug(1, "Yang parse file: %s", name);
|
||||||
len = 1024; /* any number is fine */
|
len = 1024; /* any number is fine */
|
||||||
|
|
@ -1373,12 +1374,12 @@ yang_parse_file(clicon_handle h,
|
||||||
}
|
}
|
||||||
buf[i++] = (char)(c&0xff);
|
buf[i++] = (char)(c&0xff);
|
||||||
} /* read a line */
|
} /* read a line */
|
||||||
if ((ymodule = yang_parse_str(h, buf, name, ysp)) < 0)
|
if ((ymod = yang_parse_str(h, buf, name, ysp)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
if (buf)
|
if (buf)
|
||||||
free(buf);
|
free(buf);
|
||||||
return ymodule;
|
return ymod; /* top-level (sub)module */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! No specific revision give. Match a yang file given dir and module
|
/*! No specific revision give. Match a yang file given dir and module
|
||||||
|
|
@ -1387,7 +1388,7 @@ yang_parse_file(clicon_handle h,
|
||||||
* @param[in] module Name of main YANG module.
|
* @param[in] module Name of main YANG module.
|
||||||
* @param[out] fbuf Buffer containing filename
|
* @param[out] fbuf Buffer containing filename
|
||||||
*
|
*
|
||||||
* @retval 1 Match founbd, Most recent entry returned in fbuf
|
* @retval 1 Match found, Most recent entry returned in fbuf
|
||||||
* @retval 0 No matching entry found
|
* @retval 0 No matching entry found
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
|
|
@ -1401,17 +1402,20 @@ yang_parse_find_match(clicon_handle h,
|
||||||
struct dirent *dp = NULL;
|
struct dirent *dp = NULL;
|
||||||
int ndp;
|
int ndp;
|
||||||
cbuf *regex = NULL;
|
cbuf *regex = NULL;
|
||||||
char *regexstr;
|
|
||||||
|
|
||||||
if ((regex = cbuf_new()) == NULL){
|
if ((regex = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "cbuf_new");
|
clicon_err(OE_YANG, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(regex, "^%s.*(.yang)$", module);
|
/* RFC 6020: The name of the file SHOULD be of the form:
|
||||||
regexstr = cbuf_get(regex);
|
module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
|
||||||
|
revision-date ::= 4DIGIT "-" 2DIGIT "-" 2DIGIT
|
||||||
|
*/
|
||||||
|
cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$",
|
||||||
|
module);
|
||||||
if ((ndp = clicon_file_dirent(yang_dir,
|
if ((ndp = clicon_file_dirent(yang_dir,
|
||||||
&dp,
|
&dp,
|
||||||
regexstr,
|
cbuf_get(regex),
|
||||||
S_IFREG)) < 0)
|
S_IFREG)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Entries are sorted, last entry should be most recent date */
|
/* Entries are sorted, last entry should be most recent date */
|
||||||
|
|
@ -1436,8 +1440,8 @@ yang_parse_find_match(clicon_handle h,
|
||||||
* @param module Name of main YANG module. More modules may be parsed if imported
|
* @param module Name of main YANG module. More modules may be parsed if imported
|
||||||
* @param revision Optional module revision date
|
* @param revision Optional module revision date
|
||||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
||||||
* @retval 0 Everything OK
|
* @retval ymod Top-level yang (sub)module
|
||||||
* @retval -1 Error encountered
|
* @retval NULL Error encountered
|
||||||
* module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
|
* module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
|
||||||
* Calling order:
|
* Calling order:
|
||||||
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
||||||
|
|
@ -1457,7 +1461,7 @@ yang_parse2(clicon_handle h,
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
cbuf *fbuf = NULL;
|
cbuf *fbuf = NULL;
|
||||||
char *filename;
|
char *filename;
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int nr;
|
int nr;
|
||||||
|
|
||||||
|
|
@ -1485,14 +1489,14 @@ yang_parse2(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "fopen(%s)", filename);
|
clicon_err(OE_UNIX, errno, "fopen(%s)", filename);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((ys = yang_parse_file(h, f, filename, ysp)) == NULL)
|
if ((ymod = yang_parse_file(h, f, filename, ysp)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
if (fbuf)
|
if (fbuf)
|
||||||
cbuf_free(fbuf);
|
cbuf_free(fbuf);
|
||||||
if (f)
|
if (f)
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return ys;
|
return ymod; /* top-level (sub)module */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Parse one yang module then go through (sub)modules and parse them recursively
|
/*! Parse one yang module then go through (sub)modules and parse them recursively
|
||||||
|
|
@ -1501,9 +1505,9 @@ yang_parse2(clicon_handle h,
|
||||||
* @param yang_dir Directory where all YANG module files reside
|
* @param yang_dir Directory where all YANG module files reside
|
||||||
* @param module Name of main YANG module. More modules may be parsed if imported
|
* @param module Name of main YANG module. More modules may be parsed if imported
|
||||||
* @param revision Optional module revision date
|
* @param revision Optional module revision date
|
||||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
* @param ysp Yang specification. Should have been created by caller using yspec_new
|
||||||
* @retval 0 Everything OK
|
* @retval ymod Top-level yang (sub)module
|
||||||
* @retval -1 Error encountered
|
* @retval NULL Error encountered
|
||||||
* Find a yang module file, and then recursively parse all its imported modules.
|
* Find a yang module file, and then recursively parse all its imported modules.
|
||||||
* Calling order:
|
* Calling order:
|
||||||
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
* yang_parse # Parse top-level yang module. Expand and populate yang tree
|
||||||
|
|
@ -1521,15 +1525,15 @@ yang_parse1(clicon_handle h,
|
||||||
yang_spec *ysp)
|
yang_spec *ysp)
|
||||||
{
|
{
|
||||||
yang_stmt *yi = NULL; /* import */
|
yang_stmt *yi = NULL; /* import */
|
||||||
yang_stmt *ys;
|
yang_stmt *ymod;
|
||||||
yang_stmt *yrev;
|
yang_stmt *yrev;
|
||||||
char *modname;
|
char *modname;
|
||||||
char *subrevision;
|
char *subrevision;
|
||||||
|
|
||||||
if ((ys = yang_parse2(h, yang_dir, module, revision, ysp)) == NULL)
|
if ((ymod = yang_parse2(h, yang_dir, module, revision, ysp)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* go through all import statements of ysp (or its module) */
|
/* go through all import statements of ysp (or its module) */
|
||||||
while ((yi = yn_each((yang_node*)ys, yi)) != NULL){
|
while ((yi = yn_each((yang_node*)ymod, yi)) != NULL){
|
||||||
if (yi->ys_keyword != Y_IMPORT)
|
if (yi->ys_keyword != Y_IMPORT)
|
||||||
continue;
|
continue;
|
||||||
modname = yi->ys_argument;
|
modname = yi->ys_argument;
|
||||||
|
|
@ -1539,12 +1543,12 @@ yang_parse1(clicon_handle h,
|
||||||
subrevision = NULL;
|
subrevision = NULL;
|
||||||
if (yang_find((yang_node*)ysp, Y_MODULE, modname) == NULL)
|
if (yang_find((yang_node*)ysp, Y_MODULE, modname) == NULL)
|
||||||
if (yang_parse1(h, yang_dir, modname, subrevision, ysp) == NULL){
|
if (yang_parse1(h, yang_dir, modname, subrevision, ysp) == NULL){
|
||||||
ys = NULL;
|
ymod = NULL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
return ys;
|
return ymod; /* top-level (sub)module */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||||
|
|
@ -1574,22 +1578,21 @@ yang_parse(clicon_handle h,
|
||||||
yang_spec *ysp)
|
yang_spec *ysp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *ys;
|
yang_stmt *ymod; /* Top-level yang (sub)module */
|
||||||
|
|
||||||
/* Step 1: parse from text to yang parse-tree. */
|
/* Step 1: parse from text to yang parse-tree. */
|
||||||
if ((ys = yang_parse1(h, yang_dir, module, revision, ysp)) == NULL)
|
if ((ymod = yang_parse1(h, yang_dir, module, revision, ysp)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* Add top module name as dbspec-name */
|
/* Add top module name as dbspec-name */
|
||||||
clicon_dbspec_name_set(h, ys->ys_argument);
|
clicon_dbspec_name_set(h, ymod->ys_argument);
|
||||||
|
|
||||||
#ifdef YANG_TYPE_CACHE
|
|
||||||
/* Resolve all types */
|
/* Resolve all types */
|
||||||
yang_apply((yang_node*)ys, ys_resolve_type, NULL);
|
yang_apply((yang_node*)ysp, ys_resolve_type, NULL);
|
||||||
#endif
|
|
||||||
/* Step 2: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
/* Step 2: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
||||||
if (yang_expand((yang_node*)ysp) < 0)
|
if (yang_expand((yang_node*)ysp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
yang_apply((yang_node*)ys, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
yang_apply((yang_node*)ymod, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
||||||
/* Step 4: Go through parse tree and populate it with cv types */
|
/* Step 4: Go through parse tree and populate it with cv types */
|
||||||
if (yang_apply((yang_node*)ysp, ys_populate, NULL) < 0)
|
if (yang_apply((yang_node*)ysp, ys_populate, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1698,8 +1701,6 @@ yang_xpath_abs(yang_node *yn,
|
||||||
int nvec;
|
int nvec;
|
||||||
yang_node *ys = NULL;
|
yang_node *ys = NULL;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
yang_spec *yspec;
|
|
||||||
yang_stmt *yimport;
|
|
||||||
char *id;
|
char *id;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
|
|
||||||
|
|
@ -1736,16 +1737,9 @@ yang_xpath_abs(yang_node *yn,
|
||||||
}
|
}
|
||||||
prefix[id-vec[1]] = '\0';
|
prefix[id-vec[1]] = '\0';
|
||||||
id++;
|
id++;
|
||||||
if ((yimport = ys_module_import(ymod, prefix)) == NULL){
|
if ((ymod = yang_find_module_by_prefix((yang_stmt*)yn, prefix)) == NULL)
|
||||||
clicon_err(OE_DB, 0, "Prefix %s not defined not found", prefix);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
yspec = ys_spec(ymod);
|
|
||||||
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL){
|
|
||||||
clicon_err(OE_DB, 0, "Module referred to with prefix %s not found", prefix);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ys = yang_xpath_vec((yang_node*)ymod, vec+1, nvec-1);
|
ys = yang_xpath_vec((yang_node*)ymod, vec+1, nvec-1);
|
||||||
done:
|
done:
|
||||||
if (vec)
|
if (vec)
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,11 @@ clixon_yang_parsewrap(void)
|
||||||
<KEYWORD>yang-version { BEGIN(ARGUMENT); return K_YANG_VERSION; }
|
<KEYWORD>yang-version { BEGIN(ARGUMENT); return K_YANG_VERSION; }
|
||||||
<KEYWORD>yin-element { BEGIN(ARGUMENT); return K_YIN_ELEMENT; }
|
<KEYWORD>yin-element { BEGIN(ARGUMENT); return K_YIN_ELEMENT; }
|
||||||
|
|
||||||
<KEYWORD>. { return K_UNKNOWN; }
|
<KEYWORD>: { return *yytext; }
|
||||||
|
<KEYWORD>; { return *yytext; }
|
||||||
|
<KEYWORD>. { clixon_yang_parselval.string = strdup(yytext);
|
||||||
|
return CHAR;}
|
||||||
|
|
||||||
|
|
||||||
<ARGUMENT>; { BEGIN(KEYWORD); return *yytext; }
|
<ARGUMENT>; { BEGIN(KEYWORD); return *yytext; }
|
||||||
<ARGUMENT>\{ { BEGIN(KEYWORD); return *yytext; }
|
<ARGUMENT>\{ { BEGIN(KEYWORD); return *yytext; }
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,9 @@ ystack_push(struct clicon_yang_yacc_arg *yy, yang_node *yn)
|
||||||
* Note: consumes 'argument' which assumes it is malloced and not freed by caller
|
* Note: consumes 'argument' which assumes it is malloced and not freed by caller
|
||||||
*/
|
*/
|
||||||
static yang_stmt *
|
static yang_stmt *
|
||||||
ysp_add(struct clicon_yang_yacc_arg *yy, enum rfc_6020 keyword, char *argument)
|
ysp_add(struct clicon_yang_yacc_arg *yy,
|
||||||
|
enum rfc_6020 keyword,
|
||||||
|
char *argument)
|
||||||
{
|
{
|
||||||
struct ys_stack *ystack = yy->yy_stack;
|
struct ys_stack *ystack = yy->yy_stack;
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
|
|
@ -453,7 +455,8 @@ body_stmts : body_stmts body_stmt { clicon_debug(2,"body-stmts -> body-stmts
|
||||||
| body_stmt { clicon_debug(2,"body-stmts -> body-stmt");}
|
| body_stmt { clicon_debug(2,"body-stmts -> body-stmt");}
|
||||||
;
|
;
|
||||||
|
|
||||||
body_stmt : feature_stmt { clicon_debug(2,"body-stmt -> feature-stmt");}
|
body_stmt : extension_stmt { clicon_debug(2,"body-stmt -> extension-stmt");}
|
||||||
|
| feature_stmt { clicon_debug(2,"body-stmt -> feature-stmt");}
|
||||||
| identity_stmt { clicon_debug(2,"body-stmt -> identity-stmt");}
|
| identity_stmt { clicon_debug(2,"body-stmt -> identity-stmt");}
|
||||||
| typedef_stmt { clicon_debug(2,"body-stmt -> typedef-stmt");}
|
| typedef_stmt { clicon_debug(2,"body-stmt -> typedef-stmt");}
|
||||||
| grouping_stmt { clicon_debug(2,"body-stmt -> grouping-stmt");}
|
| grouping_stmt { clicon_debug(2,"body-stmt -> grouping-stmt");}
|
||||||
|
|
@ -467,6 +470,7 @@ data_def_stmt : container_stmt { clicon_debug(2,"data-def-stmt -> containe
|
||||||
| leaf_list_stmt { clicon_debug(2,"data-def-stmt -> leaf-list-stmt");}
|
| leaf_list_stmt { clicon_debug(2,"data-def-stmt -> leaf-list-stmt");}
|
||||||
| list_stmt { clicon_debug(2,"data-def-stmt -> list-stmt");}
|
| list_stmt { clicon_debug(2,"data-def-stmt -> list-stmt");}
|
||||||
| choice_stmt { clicon_debug(2,"data-def-stmt -> choice-stmt");}
|
| choice_stmt { clicon_debug(2,"data-def-stmt -> choice-stmt");}
|
||||||
|
| anyxml_stmt { clicon_debug(2,"data-def-stmt -> anyxml-stmt");}
|
||||||
| uses_stmt { clicon_debug(2,"data-def-stmt -> uses-stmt");}
|
| uses_stmt { clicon_debug(2,"data-def-stmt -> uses-stmt");}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -633,16 +637,17 @@ short_case_stmt : container_stmt { clicon_debug(2,"short-case-substmt -> conta
|
||||||
| leaf_stmt { clicon_debug(2,"short-case-substmt -> leaf-stmt"); }
|
| leaf_stmt { clicon_debug(2,"short-case-substmt -> leaf-stmt"); }
|
||||||
| leaf_list_stmt { clicon_debug(2,"short-case-substmt -> leaf-list-stmt"); }
|
| leaf_list_stmt { clicon_debug(2,"short-case-substmt -> leaf-list-stmt"); }
|
||||||
| list_stmt { clicon_debug(2,"short-case-substmt -> list-stmt"); }
|
| list_stmt { clicon_debug(2,"short-case-substmt -> list-stmt"); }
|
||||||
|
| anyxml_stmt { clicon_debug(2,"short-case-substmt -> anyxml-stmt");}
|
||||||
;
|
;
|
||||||
|
|
||||||
/* case */
|
/* case */
|
||||||
case_stmt : K_CASE id_arg_str ';'
|
case_stmt : K_CASE id_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_CASE, $2) == NULL) _YYERROR("19");
|
{ if (ysp_add(_yy, Y_CASE, $2) == NULL) _YYERROR("22");
|
||||||
clicon_debug(2,"case-stmt -> CASE id-arg-str ;"); }
|
clicon_debug(2,"case-stmt -> CASE id-arg-str ;"); }
|
||||||
| K_CASE id_arg_str
|
| K_CASE id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_CASE, $2) == NULL) _YYERROR("20"); }
|
{ if (ysp_add_push(_yy, Y_CASE, $2) == NULL) _YYERROR("23"); }
|
||||||
'{' case_substmts '}'
|
'{' case_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("21");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("24");
|
||||||
clicon_debug(2,"case-stmt -> CASE id-arg-str { case-substmts }"); }
|
clicon_debug(2,"case-stmt -> CASE id-arg-str { case-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -663,15 +668,42 @@ case_substmt : when_stmt { clicon_debug(2,"case-substmt -> when-stmt
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/* anyxml */
|
||||||
|
anyxml_stmt : K_ANYXML id_arg_str ';'
|
||||||
|
{ if (ysp_add(_yy, Y_ANYXML, $2) == NULL) _YYERROR("25");
|
||||||
|
clicon_debug(2,"anyxml-stmt -> ANYXML id-arg-str ;"); }
|
||||||
|
| K_ANYXML id_arg_str
|
||||||
|
{ if (ysp_add_push(_yy, Y_ANYXML, $2) == NULL) _YYERROR("26"); }
|
||||||
|
'{' anyxml_substmts '}'
|
||||||
|
{ if (ystack_pop(_yy) < 0) _YYERROR("27");
|
||||||
|
clicon_debug(2,"anyxml-stmt -> ANYXML id-arg-str { anyxml-substmts }"); }
|
||||||
|
;
|
||||||
|
|
||||||
|
anyxml_substmts : anyxml_substmts anyxml_substmt
|
||||||
|
{ clicon_debug(2,"anyxml-substmts -> anyxml-substmts anyxml-substmt"); }
|
||||||
|
| anyxml_substmt
|
||||||
|
{ clicon_debug(2,"anyxml-substmts -> anyxml-substmt"); }
|
||||||
|
;
|
||||||
|
|
||||||
|
anyxml_substmt : when_stmt { clicon_debug(2,"anyxml-substmt -> when-stmt"); }
|
||||||
|
| if_feature_stmt { clicon_debug(2,"anyxml-substmt -> if-feature-stmt"); }
|
||||||
|
| must_stmt { clicon_debug(2,"anyxml-substmt -> must-stmt"); }
|
||||||
|
| config_stmt { clicon_debug(2,"anyxml-substmt -> config-stmt"); }
|
||||||
|
| mandatory_stmt { clicon_debug(2,"anyxml-substmt -> mandatory-stmt"); }
|
||||||
|
| status_stmt { clicon_debug(2,"anyxml-substmt -> status-stmt"); }
|
||||||
|
| description_stmt { clicon_debug(2,"anyxml-substmt -> description-stmt"); }
|
||||||
|
| reference_stmt { clicon_debug(2,"anyxml-substmt -> reference-stmt"); }
|
||||||
|
| ustring ':' ustring ';' { clicon_debug(2,"anyxml-substmt -> anyxml extension"); }
|
||||||
|
;
|
||||||
|
|
||||||
/* uses */
|
/* uses */
|
||||||
uses_stmt : K_USES identifier_ref_arg_str ';'
|
uses_stmt : K_USES identifier_ref_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_USES, $2) == NULL) _YYERROR("19");
|
{ if (ysp_add(_yy, Y_USES, $2) == NULL) _YYERROR("28");
|
||||||
clicon_debug(2,"uses-stmt -> USES id-arg-str ;"); }
|
clicon_debug(2,"uses-stmt -> USES id-arg-str ;"); }
|
||||||
| K_USES identifier_ref_arg_str
|
| K_USES identifier_ref_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_USES, $2) == NULL) _YYERROR("20"); }
|
{ if (ysp_add_push(_yy, Y_USES, $2) == NULL) _YYERROR("29"); }
|
||||||
'{' uses_substmts '}'
|
'{' uses_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("21");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("30");
|
||||||
clicon_debug(2,"uses-stmt -> USES id-arg-str { uses-substmts }"); }
|
clicon_debug(2,"uses-stmt -> USES id-arg-str { uses-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -694,12 +726,12 @@ uses_substmt : when_stmt { clicon_debug(2,"uses-substmt -> when-stmt
|
||||||
|
|
||||||
/* refine XXX need further refining */
|
/* refine XXX need further refining */
|
||||||
refine_stmt : K_REFINE id_arg_str ';'
|
refine_stmt : K_REFINE id_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_REFINE, $2) == NULL) _YYERROR("21");
|
{ if (ysp_add(_yy, Y_REFINE, $2) == NULL) _YYERROR("31");
|
||||||
clicon_debug(2,"refine-stmt -> REFINE id-arg-str ;"); }
|
clicon_debug(2,"refine-stmt -> REFINE id-arg-str ;"); }
|
||||||
| K_REFINE id_arg_str
|
| K_REFINE id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_REFINE, $2) == NULL) _YYERROR("22"); }
|
{ if (ysp_add_push(_yy, Y_REFINE, $2) == NULL) _YYERROR("32"); }
|
||||||
'{' refine_substmts '}'
|
'{' refine_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("23");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("33");
|
||||||
clicon_debug(2,"refine-stmt -> REFINE id-arg-str { refine-substmts }"); }
|
clicon_debug(2,"refine-stmt -> REFINE id-arg-str { refine-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -720,9 +752,9 @@ refine_substmt : must_stmt { clicon_debug(2,"refine-substmt -> must-stmt");
|
||||||
uses_augment_stmt : augment_stmt;
|
uses_augment_stmt : augment_stmt;
|
||||||
|
|
||||||
augment_stmt : K_AUGMENT string
|
augment_stmt : K_AUGMENT string
|
||||||
{ if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("22"); }
|
{ if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("34"); }
|
||||||
'{' augment_substmts '}'
|
'{' augment_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("23");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("35");
|
||||||
clicon_debug(2,"augment-stmt -> AUGMENT string { augment-substmts }"); }
|
clicon_debug(2,"augment-stmt -> AUGMENT string { augment-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -744,12 +776,12 @@ augment_substmt : when_stmt { clicon_debug(2,"augment-substmt -> when-s
|
||||||
|
|
||||||
/* when */
|
/* when */
|
||||||
when_stmt : K_WHEN string ';'
|
when_stmt : K_WHEN string ';'
|
||||||
{ if (ysp_add(_yy, Y_WHEN, $2) == NULL) _YYERROR("21");
|
{ if (ysp_add(_yy, Y_WHEN, $2) == NULL) _YYERROR("36");
|
||||||
clicon_debug(2,"when-stmt -> WHEN string ;"); }
|
clicon_debug(2,"when-stmt -> WHEN string ;"); }
|
||||||
| K_WHEN string
|
| K_WHEN string
|
||||||
{ if (ysp_add_push(_yy, Y_WHEN, $2) == NULL) _YYERROR("22"); }
|
{ if (ysp_add_push(_yy, Y_WHEN, $2) == NULL) _YYERROR("37"); }
|
||||||
'{' when_substmts '}'
|
'{' when_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("23");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("38");
|
||||||
clicon_debug(2,"when-stmt -> WHEN string { when-substmts }"); }
|
clicon_debug(2,"when-stmt -> WHEN string { when-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -766,12 +798,12 @@ when_substmt : description_stmt { clicon_debug(2,"when-substmt -> description-s
|
||||||
|
|
||||||
/* rpc */
|
/* rpc */
|
||||||
rpc_stmt : K_RPC id_arg_str ';'
|
rpc_stmt : K_RPC id_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_RPC, $2) == NULL) _YYERROR("21");
|
{ if (ysp_add(_yy, Y_RPC, $2) == NULL) _YYERROR("39");
|
||||||
clicon_debug(2,"rpc-stmt -> RPC id-arg-str ;"); }
|
clicon_debug(2,"rpc-stmt -> RPC id-arg-str ;"); }
|
||||||
| K_RPC id_arg_str
|
| K_RPC id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_RPC, $2) == NULL) _YYERROR("22"); }
|
{ if (ysp_add_push(_yy, Y_RPC, $2) == NULL) _YYERROR("40"); }
|
||||||
'{' rpc_substmts '}'
|
'{' rpc_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("23");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("41");
|
||||||
clicon_debug(2,"rpc-stmt -> RPC id-arg-str { rpc-substmts }"); }
|
clicon_debug(2,"rpc-stmt -> RPC id-arg-str { rpc-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -794,9 +826,9 @@ rpc_substmt : if_feature_stmt { clicon_debug(2,"rpc-substmt -> if-feature-stm
|
||||||
|
|
||||||
/* input */
|
/* input */
|
||||||
input_stmt : K_INPUT
|
input_stmt : K_INPUT
|
||||||
{ if (ysp_add_push(_yy, Y_INPUT, NULL) == NULL) _YYERROR("24"); }
|
{ if (ysp_add_push(_yy, Y_INPUT, NULL) == NULL) _YYERROR("42"); }
|
||||||
'{' input_substmts '}'
|
'{' input_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("25");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("43");
|
||||||
clicon_debug(2,"input-stmt -> INPUT { input-substmts }"); }
|
clicon_debug(2,"input-stmt -> INPUT { input-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -814,18 +846,18 @@ input_substmt : typedef_stmt { clicon_debug(2,"input-substmt -> typedef-
|
||||||
|
|
||||||
/* output */
|
/* output */
|
||||||
output_stmt : K_OUTPUT /* XXX reuse input-substatements since they are same */
|
output_stmt : K_OUTPUT /* XXX reuse input-substatements since they are same */
|
||||||
{ if (ysp_add_push(_yy, Y_OUTPUT, NULL) == NULL) _YYERROR("24"); }
|
{ if (ysp_add_push(_yy, Y_OUTPUT, NULL) == NULL) _YYERROR("44"); }
|
||||||
'{' input_substmts '}'
|
'{' input_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("25");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("45");
|
||||||
clicon_debug(2,"output-stmt -> OUTPUT { input-substmts }"); }
|
clicon_debug(2,"output-stmt -> OUTPUT { input-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
/* Typedef */
|
/* Typedef */
|
||||||
typedef_stmt : K_TYPEDEF id_arg_str
|
typedef_stmt : K_TYPEDEF id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("24"); }
|
{ if (ysp_add_push(_yy, Y_TYPEDEF, $2) == NULL) _YYERROR("46"); }
|
||||||
'{' typedef_substmts '}'
|
'{' typedef_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("25");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("47");
|
||||||
clicon_debug(2,"typedef-stmt -> TYPEDEF id-arg-str { typedef-substmts }"); }
|
clicon_debug(2,"typedef-stmt -> TYPEDEF id-arg-str { typedef-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -847,13 +879,13 @@ typedef_substmt : type_stmt { clicon_debug(2,"typedef-substmt -> type-s
|
||||||
|
|
||||||
/* Type */
|
/* Type */
|
||||||
type_stmt : K_TYPE identifier_ref_arg_str ';'
|
type_stmt : K_TYPE identifier_ref_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_TYPE, $2) == NULL) _YYERROR("26");
|
{ if (ysp_add(_yy, Y_TYPE, $2) == NULL) _YYERROR("48");
|
||||||
clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str ;");}
|
clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str ;");}
|
||||||
| K_TYPE identifier_ref_arg_str
|
| K_TYPE identifier_ref_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_TYPE, $2) == NULL) _YYERROR("27");
|
{ if (ysp_add_push(_yy, Y_TYPE, $2) == NULL) _YYERROR("49");
|
||||||
}
|
}
|
||||||
'{' type_body_stmts '}'
|
'{' type_body_stmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("28");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("50");
|
||||||
clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str { type-body-stmts }");}
|
clicon_debug(2,"type-stmt -> TYPE identifier-ref-arg-str { type-body-stmts }");}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -892,9 +924,9 @@ type_body_stmt/* numerical-restrictions */
|
||||||
|
|
||||||
/* Grouping */
|
/* Grouping */
|
||||||
grouping_stmt : K_GROUPING id_arg_str
|
grouping_stmt : K_GROUPING id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_GROUPING, $2) == NULL) _YYERROR("29"); }
|
{ if (ysp_add_push(_yy, Y_GROUPING, $2) == NULL) _YYERROR("51"); }
|
||||||
'{' grouping_substmts '}'
|
'{' grouping_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("30");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("52");
|
||||||
clicon_debug(2,"grouping-stmt -> GROUPING id-arg-str { grouping-substmts }"); }
|
clicon_debug(2,"grouping-stmt -> GROUPING id-arg-str { grouping-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -915,13 +947,13 @@ grouping_substmt : status_stmt { clicon_debug(2,"grouping-substmt -> st
|
||||||
|
|
||||||
/* length-stmt */
|
/* length-stmt */
|
||||||
length_stmt : K_LENGTH string ';' /* XXX length-arg-str */
|
length_stmt : K_LENGTH string ';' /* XXX length-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_LENGTH, $2) == NULL) _YYERROR("31");
|
{ if (ysp_add(_yy, Y_LENGTH, $2) == NULL) _YYERROR("53");
|
||||||
clicon_debug(2,"length-stmt -> LENGTH string ;"); }
|
clicon_debug(2,"length-stmt -> LENGTH string ;"); }
|
||||||
|
|
||||||
| K_LENGTH string
|
| K_LENGTH string
|
||||||
{ if (ysp_add_push(_yy, Y_LENGTH, $2) == NULL) _YYERROR("32"); }
|
{ if (ysp_add_push(_yy, Y_LENGTH, $2) == NULL) _YYERROR("54"); }
|
||||||
'{' length_substmts '}'
|
'{' length_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("33");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("55");
|
||||||
clicon_debug(2,"length-stmt -> LENGTH string { length-substmts }"); }
|
clicon_debug(2,"length-stmt -> LENGTH string { length-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -940,13 +972,13 @@ length_substmt : error_message_stmt { clicon_debug(2,"length-substmt -> error-m
|
||||||
|
|
||||||
/* Pattern */
|
/* Pattern */
|
||||||
pattern_stmt : K_PATTERN string ';'
|
pattern_stmt : K_PATTERN string ';'
|
||||||
{ if (ysp_add(_yy, Y_PATTERN, $2) == NULL) _YYERROR("34");
|
{ if (ysp_add(_yy, Y_PATTERN, $2) == NULL) _YYERROR("56");
|
||||||
clicon_debug(2,"pattern-stmt -> PATTERN string ;"); }
|
clicon_debug(2,"pattern-stmt -> PATTERN string ;"); }
|
||||||
|
|
||||||
| K_PATTERN string
|
| K_PATTERN string
|
||||||
{ if (ysp_add_push(_yy, Y_PATTERN, $2) == NULL) _YYERROR("35"); }
|
{ if (ysp_add_push(_yy, Y_PATTERN, $2) == NULL) _YYERROR("57"); }
|
||||||
'{' pattern_substmts '}'
|
'{' pattern_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("36");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("58");
|
||||||
clicon_debug(2,"pattern-stmt -> PATTERN string { pattern-substmts }"); }
|
clicon_debug(2,"pattern-stmt -> PATTERN string { pattern-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -962,18 +994,49 @@ pattern_substmt : reference_stmt { clicon_debug(2,"pattern-substmt -> refere
|
||||||
| { clicon_debug(2,"pattern-substmt -> "); }
|
| { clicon_debug(2,"pattern-substmt -> "); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* Extension */
|
||||||
|
extension_stmt: K_EXTENSION id_arg_str ';'
|
||||||
|
{ if (ysp_add(_yy, Y_EXTENSION, $2) == NULL) _YYERROR("59");
|
||||||
|
clicon_debug(2,"extenstion-stmt -> EXTENSION id-arg-str ;"); }
|
||||||
|
| K_EXTENSION id_arg_str
|
||||||
|
{ if (ysp_add_push(_yy, Y_EXTENSION, $2) == NULL) _YYERROR("60"); }
|
||||||
|
'{' extension_substmts '}'
|
||||||
|
{ if (ystack_pop(_yy) < 0) _YYERROR("61");
|
||||||
|
clicon_debug(2,"extension-stmt -> FEATURE id-arg-str { extension-substmts }"); }
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/* extension substmts */
|
||||||
|
extension_substmts : extension_substmts extension_substmt
|
||||||
|
{ clicon_debug(2,"extension-substmts -> extension-substmts extension-substmt"); }
|
||||||
|
| extension_substmt
|
||||||
|
{ clicon_debug(2,"extension-substmts -> extension-substmt"); }
|
||||||
|
;
|
||||||
|
|
||||||
|
extension_substmt : argument_stmt { clicon_debug(2,"extension-substmt -> argument-stmt"); }
|
||||||
|
| status_stmt { clicon_debug(2,"extension-substmt -> status-stmt"); }
|
||||||
|
| description_stmt { clicon_debug(2,"extension-substmt -> description-stmt"); }
|
||||||
|
| reference_stmt { clicon_debug(2,"extension-substmt -> reference-stmt"); }
|
||||||
|
| unknown_stmt { clicon_debug(2,"extension-substmt -> unknown-stmt");}
|
||||||
|
| { clicon_debug(2,"extension-substmt -> "); }
|
||||||
|
;
|
||||||
|
|
||||||
|
argument_stmt : K_ARGUMENT id_arg_str ';'
|
||||||
|
| K_ARGUMENT id_arg_str '{' '}'
|
||||||
|
;
|
||||||
|
|
||||||
/* Feature */
|
/* Feature */
|
||||||
feature_stmt : K_FEATURE id_arg_str ';'
|
feature_stmt : K_FEATURE id_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_FEATURE, $2) == NULL) _YYERROR("50");
|
{ if (ysp_add(_yy, Y_FEATURE, $2) == NULL) _YYERROR("62");
|
||||||
clicon_debug(2,"feature-stmt -> FEATURE id-arg-str ;"); }
|
clicon_debug(2,"feature-stmt -> FEATURE id-arg-str ;"); }
|
||||||
|
|
||||||
| K_FEATURE id_arg_str
|
| K_FEATURE id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_FEATURE, $2) == NULL) _YYERROR("51"); }
|
{ if (ysp_add_push(_yy, Y_FEATURE, $2) == NULL) _YYERROR("63"); }
|
||||||
'{' feature_substmts '}'
|
'{' feature_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("52");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("64");
|
||||||
clicon_debug(2,"feature-stmt -> FEATURE id-arg-str { feature-substmts }"); }
|
clicon_debug(2,"feature-stmt -> FEATURE id-arg-str { feature-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/* feature substmts */
|
||||||
feature_substmts : feature_substmts feature_substmt
|
feature_substmts : feature_substmts feature_substmt
|
||||||
{ clicon_debug(2,"feature-substmts -> feature-substmts feature-substmt"); }
|
{ clicon_debug(2,"feature-substmts -> feature-substmts feature-substmt"); }
|
||||||
| feature_substmt
|
| feature_substmt
|
||||||
|
|
@ -990,13 +1053,13 @@ feature_substmt : if_feature_stmt { clicon_debug(2,"feature-substmt -> if-fea
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
identity_stmt : K_IDENTITY string ';' /* XXX identifier-arg-str */
|
identity_stmt : K_IDENTITY string ';' /* XXX identifier-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("53");
|
{ if (ysp_add(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("65");
|
||||||
clicon_debug(2,"identity-stmt -> IDENTITY string ;"); }
|
clicon_debug(2,"identity-stmt -> IDENTITY string ;"); }
|
||||||
|
|
||||||
| K_IDENTITY string
|
| K_IDENTITY string
|
||||||
{ if (ysp_add_push(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("54"); }
|
{ if (ysp_add_push(_yy, Y_IDENTITY, $2) == NULL) _YYERROR("66"); }
|
||||||
'{' identity_substmts '}'
|
'{' identity_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("55");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("67");
|
||||||
clicon_debug(2,"identity-stmt -> IDENTITY string { identity-substmts }"); }
|
clicon_debug(2,"identity-stmt -> IDENTITY string { identity-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1016,13 +1079,13 @@ identity_substmt : base_stmt { clicon_debug(2,"identity-substmt -> base-
|
||||||
|
|
||||||
/* range-stmt */
|
/* range-stmt */
|
||||||
range_stmt : K_RANGE string ';' /* XXX range-arg-str */
|
range_stmt : K_RANGE string ';' /* XXX range-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_RANGE, $2) == NULL) _YYERROR("56");
|
{ if (ysp_add(_yy, Y_RANGE, $2) == NULL) _YYERROR("68");
|
||||||
clicon_debug(2,"range-stmt -> RANGE string ;"); }
|
clicon_debug(2,"range-stmt -> RANGE string ;"); }
|
||||||
|
|
||||||
| K_RANGE string
|
| K_RANGE string
|
||||||
{ if (ysp_add_push(_yy, Y_RANGE, $2) == NULL) _YYERROR("57"); }
|
{ if (ysp_add_push(_yy, Y_RANGE, $2) == NULL) _YYERROR("69"); }
|
||||||
'{' range_substmts '}'
|
'{' range_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("58");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("70");
|
||||||
clicon_debug(2,"range-stmt -> RANGE string { range-substmts }"); }
|
clicon_debug(2,"range-stmt -> RANGE string { range-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1041,13 +1104,12 @@ range_substmt : error_message_stmt { clicon_debug(2,"range-substmt -> error-me
|
||||||
|
|
||||||
/* enum-stmt */
|
/* enum-stmt */
|
||||||
enum_stmt : K_ENUM string ';'
|
enum_stmt : K_ENUM string ';'
|
||||||
{ if (ysp_add(_yy, Y_ENUM, $2) == NULL) _YYERROR("59");
|
{ if (ysp_add(_yy, Y_ENUM, $2) == NULL) _YYERROR("71");
|
||||||
clicon_debug(2,"enum-stmt -> ENUM string ;"); }
|
clicon_debug(2,"enum-stmt -> ENUM string ;"); }
|
||||||
|
|
||||||
| K_ENUM string
|
| K_ENUM string
|
||||||
{ if (ysp_add_push(_yy, Y_ENUM, $2) == NULL) _YYERROR("60"); }
|
{ if (ysp_add_push(_yy, Y_ENUM, $2) == NULL) _YYERROR("72"); }
|
||||||
'{' enum_substmts '}'
|
'{' enum_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("61");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("73");
|
||||||
clicon_debug(2,"enum-stmt -> ENUM string { enum-substmts }"); }
|
clicon_debug(2,"enum-stmt -> ENUM string { enum-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1067,13 +1129,12 @@ enum_substmt : value_stmt { clicon_debug(2,"enum-substmt -> value-stm
|
||||||
|
|
||||||
/* bit-stmt */
|
/* bit-stmt */
|
||||||
bit_stmt : K_BIT string ';'
|
bit_stmt : K_BIT string ';'
|
||||||
{ if (ysp_add(_yy, Y_BIT, $2) == NULL) _YYERROR("62");
|
{ if (ysp_add(_yy, Y_BIT, $2) == NULL) _YYERROR("74");
|
||||||
clicon_debug(2,"bit-stmt -> BIT string ;"); }
|
clicon_debug(2,"bit-stmt -> BIT string ;"); }
|
||||||
|
|
||||||
| K_BIT string
|
| K_BIT string
|
||||||
{ if (ysp_add_push(_yy, Y_BIT, $2) == NULL) _YYERROR("63"); }
|
{ if (ysp_add_push(_yy, Y_BIT, $2) == NULL) _YYERROR("75"); }
|
||||||
'{' bit_substmts '}'
|
'{' bit_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("64");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("76");
|
||||||
clicon_debug(2,"bit-stmt -> BIT string { bit-substmts }"); }
|
clicon_debug(2,"bit-stmt -> BIT string { bit-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1092,13 +1153,13 @@ bit_substmt : position_stmt { clicon_debug(2,"bit-substmt -> positition
|
||||||
|
|
||||||
/* mus-stmt */
|
/* mus-stmt */
|
||||||
must_stmt : K_MUST string ';'
|
must_stmt : K_MUST string ';'
|
||||||
{ if (ysp_add(_yy, Y_MUST, $2) == NULL) _YYERROR("65");
|
{ if (ysp_add(_yy, Y_MUST, $2) == NULL) _YYERROR("77");
|
||||||
clicon_debug(2,"must-stmt -> MUST string ;"); }
|
clicon_debug(2,"must-stmt -> MUST string ;"); }
|
||||||
|
|
||||||
| K_MUST string
|
| K_MUST string
|
||||||
{ if (ysp_add_push(_yy, Y_MUST, $2) == NULL) _YYERROR("66"); }
|
{ if (ysp_add_push(_yy, Y_MUST, $2) == NULL) _YYERROR("78"); }
|
||||||
'{' must_substmts '}'
|
'{' must_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("67");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("79");
|
||||||
clicon_debug(2,"must-stmt -> MUST string { must-substmts }"); }
|
clicon_debug(2,"must-stmt -> MUST string { must-substmts }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1116,13 +1177,13 @@ must_substmt : error_message_stmt { clicon_debug(2,"must-substmt -> error-mes
|
||||||
|
|
||||||
/* error-message-stmt */
|
/* error-message-stmt */
|
||||||
error_message_stmt : K_ERROR_MESSAGE string ';'
|
error_message_stmt : K_ERROR_MESSAGE string ';'
|
||||||
{ if (ysp_add(_yy, Y_ERROR_MESSAGE, $2) == NULL) _YYERROR("68"); }
|
{ if (ysp_add(_yy, Y_ERROR_MESSAGE, $2) == NULL) _YYERROR("80"); }
|
||||||
|
|
||||||
/* import */
|
/* import */
|
||||||
import_stmt : K_IMPORT id_arg_str
|
import_stmt : K_IMPORT id_arg_str
|
||||||
{ if (ysp_add_push(_yy, Y_IMPORT, $2) == NULL) _YYERROR("69"); }
|
{ if (ysp_add_push(_yy, Y_IMPORT, $2) == NULL) _YYERROR("81"); }
|
||||||
'{' import_substmts '}'
|
'{' import_substmts '}'
|
||||||
{ if (ystack_pop(_yy) < 0) _YYERROR("70");
|
{ if (ystack_pop(_yy) < 0) _YYERROR("82");
|
||||||
clicon_debug(2,"import-stmt -> IMPORT id-arg-str { import-substmts }");}
|
clicon_debug(2,"import-stmt -> IMPORT id-arg-str { import-substmts }");}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1139,144 +1200,144 @@ import_substmt : prefix_stmt { clicon_debug(2,"import-stmt -> prefix-stmt"); }
|
||||||
|
|
||||||
/* Simple statements */
|
/* Simple statements */
|
||||||
yang_version_stmt : K_YANG_VERSION string ';' /* XXX yang-version-arg-str */
|
yang_version_stmt : K_YANG_VERSION string ';' /* XXX yang-version-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_YANG_VERSION, $2) == NULL) _YYERROR("71");
|
{ if (ysp_add(_yy, Y_YANG_VERSION, $2) == NULL) _YYERROR("83");
|
||||||
clicon_debug(2,"yang-version-stmt -> YANG-VERSION string"); }
|
clicon_debug(2,"yang-version-stmt -> YANG-VERSION string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
fraction_digits_stmt : K_FRACTION_DIGITS string ';' /* XXX: fraction-digits-arg-str */
|
fraction_digits_stmt : K_FRACTION_DIGITS string ';' /* XXX: fraction-digits-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_FRACTION_DIGITS, $2) == NULL) _YYERROR("72");
|
{ if (ysp_add(_yy, Y_FRACTION_DIGITS, $2) == NULL) _YYERROR("84");
|
||||||
clicon_debug(2,"fraction-digits-stmt -> FRACTION-DIGITS string"); }
|
clicon_debug(2,"fraction-digits-stmt -> FRACTION-DIGITS string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
if_feature_stmt : K_IF_FEATURE identifier_ref_arg_str ';'
|
if_feature_stmt : K_IF_FEATURE identifier_ref_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_IF_FEATURE, $2) == NULL) _YYERROR("73");
|
{ if (ysp_add(_yy, Y_IF_FEATURE, $2) == NULL) _YYERROR("85");
|
||||||
clicon_debug(2,"if-feature-stmt -> IF-FEATURE identifier-ref-arg-str"); }
|
clicon_debug(2,"if-feature-stmt -> IF-FEATURE identifier-ref-arg-str"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
value_stmt : K_VALUE integer_value ';'
|
value_stmt : K_VALUE integer_value ';'
|
||||||
{ if (ysp_add(_yy, Y_VALUE, $2) == NULL) _YYERROR("74");
|
{ if (ysp_add(_yy, Y_VALUE, $2) == NULL) _YYERROR("86");
|
||||||
clicon_debug(2,"value-stmt -> VALUE integer-value"); }
|
clicon_debug(2,"value-stmt -> VALUE integer-value"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
position_stmt : K_POSITION integer_value ';'
|
position_stmt : K_POSITION integer_value ';'
|
||||||
{ if (ysp_add(_yy, Y_POSITION, $2) == NULL) _YYERROR("75");
|
{ if (ysp_add(_yy, Y_POSITION, $2) == NULL) _YYERROR("87");
|
||||||
clicon_debug(2,"position-stmt -> POSITION integer-value"); }
|
clicon_debug(2,"position-stmt -> POSITION integer-value"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
status_stmt : K_STATUS string ';' /* XXX: status-arg-str */
|
status_stmt : K_STATUS string ';' /* XXX: status-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_STATUS, $2) == NULL) _YYERROR("76");
|
{ if (ysp_add(_yy, Y_STATUS, $2) == NULL) _YYERROR("88");
|
||||||
clicon_debug(2,"status-stmt -> STATUS string"); }
|
clicon_debug(2,"status-stmt -> STATUS string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
config_stmt : K_CONFIG config_arg_str ';'
|
config_stmt : K_CONFIG config_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_CONFIG, $2) == NULL) _YYERROR("77");
|
{ if (ysp_add(_yy, Y_CONFIG, $2) == NULL) _YYERROR("89");
|
||||||
clicon_debug(2,"config-stmt -> CONFIG config-arg-str"); }
|
clicon_debug(2,"config-stmt -> CONFIG config-arg-str"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
base_stmt : K_BASE identifier_ref_arg_str ';'
|
base_stmt : K_BASE identifier_ref_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_BASE, $2)== NULL) _YYERROR("78");
|
{ if (ysp_add(_yy, Y_BASE, $2)== NULL) _YYERROR("90");
|
||||||
clicon_debug(2,"base-stmt -> BASE identifier-ref-arg-str"); }
|
clicon_debug(2,"base-stmt -> BASE identifier-ref-arg-str"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
path_stmt : K_PATH string ';' /* XXX: path-arg-str */
|
path_stmt : K_PATH string ';' /* XXX: path-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_PATH, $2)== NULL) _YYERROR("79");
|
{ if (ysp_add(_yy, Y_PATH, $2)== NULL) _YYERROR("91");
|
||||||
clicon_debug(2,"path-stmt -> PATH string"); }
|
clicon_debug(2,"path-stmt -> PATH string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
require_instance_stmt : K_REQUIRE_INSTANCE string ';' /* XXX: require-instance-arg-str */
|
require_instance_stmt : K_REQUIRE_INSTANCE string ';' /* XXX: require-instance-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_REQUIRE_INSTANCE, $2)== NULL) _YYERROR("90");
|
{ if (ysp_add(_yy, Y_REQUIRE_INSTANCE, $2)== NULL) _YYERROR("92");
|
||||||
clicon_debug(2,"require-instance-stmt -> REQUIRE-INSTANCE string"); }
|
clicon_debug(2,"require-instance-stmt -> REQUIRE-INSTANCE string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
units_stmt : K_UNITS string ';'
|
units_stmt : K_UNITS string ';'
|
||||||
{ if (ysp_add(_yy, Y_UNITS, $2)== NULL) _YYERROR("91");
|
{ if (ysp_add(_yy, Y_UNITS, $2)== NULL) _YYERROR("93");
|
||||||
clicon_debug(2,"units-stmt -> UNITS string"); }
|
clicon_debug(2,"units-stmt -> UNITS string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
default_stmt : K_DEFAULT string ';'
|
default_stmt : K_DEFAULT string ';'
|
||||||
{ if (ysp_add(_yy, Y_DEFAULT, $2)== NULL) _YYERROR("92");
|
{ if (ysp_add(_yy, Y_DEFAULT, $2)== NULL) _YYERROR("94");
|
||||||
clicon_debug(2,"default-stmt -> DEFAULT string"); }
|
clicon_debug(2,"default-stmt -> DEFAULT string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
contact_stmt : K_CONTACT string ';'
|
contact_stmt : K_CONTACT string ';'
|
||||||
{ if (ysp_add(_yy, Y_CONTACT, $2)== NULL) _YYERROR("93");
|
{ if (ysp_add(_yy, Y_CONTACT, $2)== NULL) _YYERROR("95");
|
||||||
clicon_debug(2,"contact-stmt -> CONTACT string"); }
|
clicon_debug(2,"contact-stmt -> CONTACT string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
revision_date_stmt : K_REVISION_DATE string ';' /* XXX date-arg-str */
|
revision_date_stmt : K_REVISION_DATE string ';' /* XXX date-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_REVISION_DATE, $2) == NULL) _YYERROR("94");
|
{ if (ysp_add(_yy, Y_REVISION_DATE, $2) == NULL) _YYERROR("96");
|
||||||
clicon_debug(2,"revision-date-stmt -> date;"); }
|
clicon_debug(2,"revision-date-stmt -> date;"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
include_stmt : K_INCLUDE id_arg_str ';'
|
include_stmt : K_INCLUDE id_arg_str ';'
|
||||||
{ if (ysp_add(_yy, Y_INCLUDE, $2)== NULL) _YYERROR("95");
|
{ if (ysp_add(_yy, Y_INCLUDE, $2)== NULL) _YYERROR("97");
|
||||||
clicon_debug(2,"include-stmt -> id-arg-str"); }
|
clicon_debug(2,"include-stmt -> id-arg-str"); }
|
||||||
| K_INCLUDE id_arg_str '{' revision_date_stmt '}'
|
| K_INCLUDE id_arg_str '{' revision_date_stmt '}'
|
||||||
{ if (ysp_add(_yy, Y_INCLUDE, $2)== NULL) _YYERROR("96");
|
{ if (ysp_add(_yy, Y_INCLUDE, $2)== NULL) _YYERROR("98");
|
||||||
clicon_debug(2,"include-stmt -> id-arg-str { revision-date-stmt }"); }
|
clicon_debug(2,"include-stmt -> id-arg-str { revision-date-stmt }"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
namespace_stmt : K_NAMESPACE string ';' /* XXX uri-str */
|
namespace_stmt : K_NAMESPACE string ';' /* XXX uri-str */
|
||||||
{ if (ysp_add(_yy, Y_NAMESPACE, $2)== NULL) _YYERROR("97");
|
{ if (ysp_add(_yy, Y_NAMESPACE, $2)== NULL) _YYERROR("99");
|
||||||
clicon_debug(2,"namespace-stmt -> NAMESPACE string"); }
|
clicon_debug(2,"namespace-stmt -> NAMESPACE string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
prefix_stmt : K_PREFIX string ';' /* XXX prefix-arg-str */
|
prefix_stmt : K_PREFIX string ';' /* XXX prefix-arg-str */
|
||||||
{ if (ysp_add(_yy, Y_PREFIX, $2)== NULL) _YYERROR("98");
|
{ if (ysp_add(_yy, Y_PREFIX, $2)== NULL) _YYERROR("100");
|
||||||
clicon_debug(2,"prefix-stmt -> PREFIX string ;");}
|
clicon_debug(2,"prefix-stmt -> PREFIX string ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
description_stmt: K_DESCRIPTION string ';'
|
description_stmt: K_DESCRIPTION string ';'
|
||||||
{ if (ysp_add(_yy, Y_DESCRIPTION, $2)== NULL) _YYERROR("99");
|
{ if (ysp_add(_yy, Y_DESCRIPTION, $2)== NULL) _YYERROR("101");
|
||||||
clicon_debug(2,"description-stmt -> DESCRIPTION string ;");}
|
clicon_debug(2,"description-stmt -> DESCRIPTION string ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
organization_stmt: K_ORGANIZATION string ';'
|
organization_stmt: K_ORGANIZATION string ';'
|
||||||
{ if (ysp_add(_yy, Y_ORGANIZATION, $2)== NULL) _YYERROR("100");
|
{ if (ysp_add(_yy, Y_ORGANIZATION, $2)== NULL) _YYERROR("102");
|
||||||
clicon_debug(2,"organization-stmt -> ORGANIZATION string ;");}
|
clicon_debug(2,"organization-stmt -> ORGANIZATION string ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
min_elements_stmt: K_MIN_ELEMENTS integer_value ';'
|
min_elements_stmt: K_MIN_ELEMENTS integer_value ';'
|
||||||
{ if (ysp_add(_yy, Y_MIN_ELEMENTS, $2)== NULL) _YYERROR("101");
|
{ if (ysp_add(_yy, Y_MIN_ELEMENTS, $2)== NULL) _YYERROR("103");
|
||||||
clicon_debug(2,"min-elements-stmt -> MIN-ELEMENTS integer ;");}
|
clicon_debug(2,"min-elements-stmt -> MIN-ELEMENTS integer ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
max_elements_stmt: K_MAX_ELEMENTS integer_value ';'
|
max_elements_stmt: K_MAX_ELEMENTS integer_value ';'
|
||||||
{ if (ysp_add(_yy, Y_MAX_ELEMENTS, $2)== NULL) _YYERROR("101");
|
{ if (ysp_add(_yy, Y_MAX_ELEMENTS, $2)== NULL) _YYERROR("104");
|
||||||
clicon_debug(2,"max-elements-stmt -> MIN-ELEMENTS integer ;");}
|
clicon_debug(2,"max-elements-stmt -> MIN-ELEMENTS integer ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
reference_stmt: K_REFERENCE string ';'
|
reference_stmt: K_REFERENCE string ';'
|
||||||
{ if (ysp_add(_yy, Y_REFERENCE, $2)== NULL) _YYERROR("101");
|
{ if (ysp_add(_yy, Y_REFERENCE, $2)== NULL) _YYERROR("105");
|
||||||
clicon_debug(2,"reference-stmt -> REFERENCE string ;");}
|
clicon_debug(2,"reference-stmt -> REFERENCE string ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
mandatory_stmt: K_MANDATORY string ';'
|
mandatory_stmt: K_MANDATORY string ';'
|
||||||
{ yang_stmt *ys;
|
{ yang_stmt *ys;
|
||||||
if ((ys = ysp_add(_yy, Y_MANDATORY, $2))== NULL) _YYERROR("102");
|
if ((ys = ysp_add(_yy, Y_MANDATORY, $2))== NULL) _YYERROR("106");
|
||||||
clicon_debug(2,"mandatory-stmt -> MANDATORY mandatory-arg-str ;");}
|
clicon_debug(2,"mandatory-stmt -> MANDATORY mandatory-arg-str ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
presence_stmt: K_PRESENCE string ';'
|
presence_stmt: K_PRESENCE string ';'
|
||||||
{ yang_stmt *ys;
|
{ yang_stmt *ys;
|
||||||
if ((ys = ysp_add(_yy, Y_PRESENCE, $2))== NULL) _YYERROR("102");
|
if ((ys = ysp_add(_yy, Y_PRESENCE, $2))== NULL) _YYERROR("107");
|
||||||
clicon_debug(2,"presence-stmt -> PRESENCE string ;");}
|
clicon_debug(2,"presence-stmt -> PRESENCE string ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
ordered_by_stmt: K_ORDERED_BY string ';'
|
ordered_by_stmt: K_ORDERED_BY string ';'
|
||||||
{ yang_stmt *ys;
|
{ yang_stmt *ys;
|
||||||
if ((ys = ysp_add(_yy, Y_ORDERED_BY, $2))== NULL) _YYERROR("102");
|
if ((ys = ysp_add(_yy, Y_ORDERED_BY, $2))== NULL) _YYERROR("108");
|
||||||
clicon_debug(2,"ordered-by-stmt -> ORDERED-BY ordered-by-arg ;");}
|
clicon_debug(2,"ordered-by-stmt -> ORDERED-BY ordered-by-arg ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
key_stmt : K_KEY id_arg_str ';' /* XXX key_arg_str */
|
key_stmt : K_KEY id_arg_str ';' /* XXX key_arg_str */
|
||||||
{ if (ysp_add(_yy, Y_KEY, $2)== NULL) _YYERROR("103");
|
{ if (ysp_add(_yy, Y_KEY, $2)== NULL) _YYERROR("109");
|
||||||
clicon_debug(2,"key-stmt -> KEY id-arg-str ;");}
|
clicon_debug(2,"key-stmt -> KEY id-arg-str ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
unique_stmt : K_UNIQUE id_arg_str ';' /* XXX key_arg_str */
|
unique_stmt : K_UNIQUE id_arg_str ';' /* XXX key_arg_str */
|
||||||
{ if (ysp_add(_yy, Y_UNIQUE, $2)== NULL) _YYERROR("104");
|
{ if (ysp_add(_yy, Y_UNIQUE, $2)== NULL) _YYERROR("110");
|
||||||
clicon_debug(2,"key-stmt -> KEY id-arg-str ;");}
|
clicon_debug(2,"key-stmt -> KEY id-arg-str ;");}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1287,10 +1348,10 @@ integer_value : string { $$=$1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
identifier_ref_arg_str : string
|
identifier_ref_arg_str : string
|
||||||
{ if (($$=prefix_id_join(NULL, $1)) == NULL) _YYERROR("105");
|
{ if (($$=prefix_id_join(NULL, $1)) == NULL) _YYERROR("111");
|
||||||
clicon_debug(2,"identifier-ref-arg-str -> string"); }
|
clicon_debug(2,"identifier-ref-arg-str -> string"); }
|
||||||
| string ':' string
|
| string ':' string
|
||||||
{ if (($$=prefix_id_join($1, $3)) == NULL) _YYERROR("106");
|
{ if (($$=prefix_id_join($1, $3)) == NULL) _YYERROR("112");
|
||||||
clicon_debug(2,"identifier-ref-arg-str -> prefix : string"); }
|
clicon_debug(2,"identifier-ref-arg-str -> prefix : string"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,19 +71,10 @@
|
||||||
/*
|
/*
|
||||||
* Local types and variables
|
* Local types and variables
|
||||||
*/
|
*/
|
||||||
/* Struct used to map between int and strings. Used for:
|
|
||||||
* - mapping yang types/typedefs (strings) and cligen types (ints).
|
|
||||||
* - mapping yang keywords (strings) and enum (clicon)
|
|
||||||
* (same struct in clicon_yang.c)
|
|
||||||
*/
|
|
||||||
struct map_str2int{
|
|
||||||
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
|
|
||||||
int ms_int;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Mapping between yang types <--> cligen types
|
/* Mapping between yang types <--> cligen types
|
||||||
Note, first match used wne translating from cv to yang --> order is significant */
|
Note, first match used wne translating from cv to yang --> order is significant */
|
||||||
static const struct map_str2int ytmap[] = {
|
static const map_str2int ytmap[] = {
|
||||||
{"int32", CGV_INT32}, /* NOTE, first match on right is significant, dont move */
|
{"int32", CGV_INT32}, /* NOTE, first match on right is significant, dont move */
|
||||||
{"string", CGV_STRING}, /* NOTE, first match on right is significant, dont move */
|
{"string", CGV_STRING}, /* NOTE, first match on right is significant, dont move */
|
||||||
{"string", CGV_REST}, /* For cv -> yang translation of rest */
|
{"string", CGV_REST}, /* For cv -> yang translation of rest */
|
||||||
|
|
@ -108,6 +99,21 @@ static const struct map_str2int ytmap[] = {
|
||||||
{NULL, -1}
|
{NULL, -1}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* return 1 if built-in, 0 if not */
|
||||||
|
static int
|
||||||
|
yang_builtin(char *type)
|
||||||
|
{
|
||||||
|
if (clicon_str2int(ytmap, type) != -1)
|
||||||
|
return 1;
|
||||||
|
#if 0
|
||||||
|
const struct map_str2int *yt;
|
||||||
|
for (yt = &ytmap[0]; yt->ms_str; yt++)
|
||||||
|
if (strcmp(yt->ms_str, type) == 0)
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
yang_type_cache_set(yang_type_cache **ycache0,
|
yang_type_cache_set(yang_type_cache **ycache0,
|
||||||
yang_stmt *resolved,
|
yang_stmt *resolved,
|
||||||
|
|
@ -174,7 +180,8 @@ yang_type_cache_get(yang_type_cache *ycache,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold)
|
yang_type_cache_cp(yang_type_cache **ycnew,
|
||||||
|
yang_type_cache *ycold)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int options;
|
int options;
|
||||||
|
|
@ -207,7 +214,8 @@ yang_type_cache_free(yang_type_cache *ycache)
|
||||||
|
|
||||||
/*! Resolve types: populate type caches */
|
/*! Resolve types: populate type caches */
|
||||||
int
|
int
|
||||||
ys_resolve_type(yang_stmt *ys, void *arg)
|
ys_resolve_type(yang_stmt *ys,
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int options = 0x0;
|
int options = 0x0;
|
||||||
|
|
@ -219,8 +227,9 @@ ys_resolve_type(yang_stmt *ys, void *arg)
|
||||||
|
|
||||||
if (ys->ys_keyword != Y_TYPE)
|
if (ys->ys_keyword != Y_TYPE)
|
||||||
return 0;
|
return 0;
|
||||||
yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
|
if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
|
||||||
&options, &mincv, &maxcv, &pattern, &fraction);
|
&options, &mincv, &maxcv, &pattern, &fraction) < 0)
|
||||||
|
goto done;
|
||||||
if (yang_type_cache_set(&ys->ys_typecache,
|
if (yang_type_cache_set(&ys->ys_typecache,
|
||||||
resolved, options, mincv, maxcv, pattern, fraction) < 0)
|
resolved, options, mincv, maxcv, pattern, fraction) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -229,20 +238,6 @@ ys_resolve_type(yang_stmt *ys, void *arg)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* return 1 if built-in, 0 if not */
|
|
||||||
static int
|
|
||||||
yang_builtin(char *type)
|
|
||||||
{
|
|
||||||
const struct map_str2int *yt;
|
|
||||||
|
|
||||||
/* built-in types */
|
|
||||||
for (yt = &ytmap[0]; yt->ms_str; yt++)
|
|
||||||
if (strcmp(yt->ms_str, type) == 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Translate from a yang type to a cligen variable type
|
/*! Translate from a yang type to a cligen variable type
|
||||||
*
|
*
|
||||||
* Currently many built-in types from RFC6020 and some RFC6991 types.
|
* Currently many built-in types from RFC6020 and some RFC6991 types.
|
||||||
|
|
@ -252,15 +247,15 @@ yang_builtin(char *type)
|
||||||
* Return 0 if no match but set cv_type to CGV_ERR
|
* Return 0 if no match but set cv_type to CGV_ERR
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang2cv_type(char *ytype, enum cv_type *cv_type)
|
yang2cv_type(char *ytype,
|
||||||
|
enum cv_type *cv_type)
|
||||||
{
|
{
|
||||||
const struct map_str2int *yt;
|
int ret;
|
||||||
|
|
||||||
*cv_type = CGV_ERR;
|
*cv_type = CGV_ERR;
|
||||||
/* built-in types */
|
/* built-in types */
|
||||||
for (yt = &ytmap[0]; yt->ms_str; yt++)
|
if ((ret = clicon_str2int(ytmap, ytype)) != -1){
|
||||||
if (strcmp(yt->ms_str, ytype) == 0){
|
*cv_type = ret;
|
||||||
*cv_type = yt->ms_int;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* special derived types */
|
/* special derived types */
|
||||||
|
|
@ -300,14 +295,13 @@ yang2cv_type(char *ytype, enum cv_type *cv_type)
|
||||||
char *
|
char *
|
||||||
cv2yang_type(enum cv_type cv_type)
|
cv2yang_type(enum cv_type cv_type)
|
||||||
{
|
{
|
||||||
const struct map_str2int *yt;
|
|
||||||
char *ytype;
|
char *ytype;
|
||||||
|
const char *str;
|
||||||
|
|
||||||
ytype = "empty";
|
ytype = "empty";
|
||||||
/* built-in types */
|
/* built-in types */
|
||||||
for (yt = &ytmap[0]; yt->ms_str; yt++)
|
if ((str = clicon_int2str(ytmap, cv_type)) != NULL)
|
||||||
if (yt->ms_int == cv_type)
|
return (char*)str;
|
||||||
return yt->ms_str;
|
|
||||||
|
|
||||||
/* special derived types */
|
/* special derived types */
|
||||||
if (cv_type == CGV_IPV4ADDR) /* RFC6991 */
|
if (cv_type == CGV_IPV4ADDR) /* RFC6991 */
|
||||||
|
|
@ -343,7 +337,9 @@ cv2yang_type(enum cv_type cv_type)
|
||||||
* @param[out] cvtype
|
* @param[out] cvtype
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_type2cv(char *origtype, char *restype, enum cv_type *cvtype)
|
clicon_type2cv(char *origtype,
|
||||||
|
char *restype,
|
||||||
|
enum cv_type *cvtype)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
|
|
@ -710,7 +706,7 @@ ys_typedef_up(yang_stmt *ys)
|
||||||
This is a sanity check of base identity of identity-ref and for identity
|
This is a sanity check of base identity of identity-ref and for identity
|
||||||
statements.
|
statements.
|
||||||
|
|
||||||
Return true if node is identityref and is derived from identity_name
|
Return true if node is identityref and is derived from identity_name
|
||||||
The derived-from() function returns true if the (first) node (in
|
The derived-from() function returns true if the (first) node (in
|
||||||
document order in the argument "nodes") is a node of type identityref,
|
document order in the argument "nodes") is a node of type identityref,
|
||||||
and its value is an identity that is derived from the identity
|
and its value is an identity that is derived from the identity
|
||||||
|
|
@ -728,16 +724,14 @@ Return true if node is identityref and is derived from identity_name
|
||||||
Så vad är det denna function ska göra? Svar: 1
|
Så vad är det denna function ska göra? Svar: 1
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
yang_find_identity(yang_stmt *ys, char *identity)
|
yang_find_identity(yang_stmt *ys,
|
||||||
|
char *identity)
|
||||||
{
|
{
|
||||||
char *id;
|
char *id;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
yang_stmt *yimport;
|
|
||||||
yang_spec *yspec;
|
|
||||||
yang_stmt *ymodule;
|
yang_stmt *ymodule;
|
||||||
yang_stmt *yid = NULL;
|
yang_stmt *yid = NULL;
|
||||||
yang_node *yn;
|
yang_node *yn;
|
||||||
yang_stmt *ymod;
|
|
||||||
|
|
||||||
if ((id = strchr(identity, ':')) == NULL)
|
if ((id = strchr(identity, ':')) == NULL)
|
||||||
id = identity;
|
id = identity;
|
||||||
|
|
@ -748,12 +742,8 @@ yang_find_identity(yang_stmt *ys, char *identity)
|
||||||
}
|
}
|
||||||
/* No, now check if identityref is derived from base */
|
/* No, now check if identityref is derived from base */
|
||||||
if (prefix){ /* Go to top and find import that matches */
|
if (prefix){ /* Go to top and find import that matches */
|
||||||
ymod = ys_module(ys);
|
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||||
if ((yimport = ys_module_import(ymod, prefix)) == NULL)
|
|
||||||
goto done;
|
goto done;
|
||||||
yspec = ys_spec(ys);
|
|
||||||
if ((ymodule = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL)
|
|
||||||
goto done; /* unresolved */
|
|
||||||
yid = yang_find((yang_node*)ymodule, Y_IDENTITY, id);
|
yid = yang_find((yang_node*)ymodule, Y_IDENTITY, id);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
@ -845,12 +835,10 @@ yang_type_resolve(yang_stmt *ys,
|
||||||
yang_stmt *ylength;
|
yang_stmt *ylength;
|
||||||
yang_stmt *ypattern;
|
yang_stmt *ypattern;
|
||||||
yang_stmt *yfraction;
|
yang_stmt *yfraction;
|
||||||
yang_stmt *yimport;
|
|
||||||
char *type;
|
char *type;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_node *yn;
|
yang_node *yn;
|
||||||
yang_spec *yspec;
|
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
|
|
@ -879,14 +867,8 @@ yang_type_resolve(yang_stmt *ys,
|
||||||
|
|
||||||
/* Not basic type. Now check if prefix which means we look in other module */
|
/* Not basic type. Now check if prefix which means we look in other module */
|
||||||
if (prefix){ /* Go to top and find import that matches */
|
if (prefix){ /* Go to top and find import that matches */
|
||||||
ymod = ys_module(ys);
|
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||||
if ((yimport = ys_module_import(ymod, prefix)) == NULL){
|
|
||||||
clicon_err(OE_DB, 0, "Prefix %s not defined not found", prefix);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
yspec = ys_spec(ys);
|
|
||||||
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, yimport->ys_argument)) == NULL)
|
|
||||||
goto ok; /* unresolved */
|
|
||||||
if ((rytypedef = yang_find((yang_node*)ymod, Y_TYPEDEF, type)) == NULL)
|
if ((rytypedef = yang_find((yang_node*)ymod, Y_TYPEDEF, type)) == NULL)
|
||||||
goto ok; /* unresolved */
|
goto ok; /* unresolved */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ expectfn(){
|
||||||
# echo "expect:\"$expect\""
|
# echo "expect:\"$expect\""
|
||||||
# echo "match:\"$match\""
|
# echo "match:\"$match\""
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err $expect "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
if [ -n "$expect2" ]; then
|
if [ -n "$expect2" ]; then
|
||||||
match=`echo "$ret" | grep -EZo "$expect2"`
|
match=`echo "$ret" | grep -EZo "$expect2"`
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,12 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit state operation should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>"
|
||||||
|
|
||||||
|
new "netconf get state operation"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get><filter type=\"xpath\" select=\"/interfaces-state\"/></get></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf lock/unlock"
|
new "netconf lock/unlock"
|
||||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
# For memcheck
|
# For memcheck
|
||||||
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||||
clixon_netconf=clixon_netconf
|
clixon_netconf=clixon_netconf
|
||||||
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
cat <<EOF > /tmp/test.yang
|
cat <<EOF > /tmp/test.yang
|
||||||
module ietf-ip{
|
module ietf-ip{
|
||||||
|
|
@ -40,6 +41,12 @@ module ietf-ip{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
container state {
|
||||||
|
config false;
|
||||||
|
leaf-list op {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -74,6 +81,19 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><sourc
|
||||||
new "netconf get leaf-list path"
|
new "netconf get leaf-list path"
|
||||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get (state data XXX should be some)"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "cli set leaf-list"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test set x f e foo" ""
|
||||||
|
|
||||||
|
new "cli show leaf-list"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test show xpath /x/f/e" "<e>foo</e>"
|
||||||
|
|
||||||
|
new "netconf set state data (not allowed)"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value"
|
||||||
|
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
pid=`pgrep clixon_backend`
|
pid=`pgrep clixon_backend`
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Test5: datastore
|
# Test5: datastore tests.
|
||||||
|
# Just run a binary direct to datastore. No clixon.
|
||||||
|
|
||||||
# include err() and new() functions
|
# include err() and new() functions
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
||||||
datastore=datastore_client
|
datastore=datastore_client
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > /tmp/ietf-ip.yang
|
cat <<EOF > /tmp/ietf-ip.yang
|
||||||
module ietf-ip{
|
module ietf-ip{
|
||||||
container x {
|
container x {
|
||||||
|
|
@ -54,7 +54,7 @@ run(){
|
||||||
rm -rf $dir/*
|
rm -rf $dir/*
|
||||||
|
|
||||||
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip"
|
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip"
|
||||||
# echo "conf:$conf"
|
echo "conf:$conf"
|
||||||
new "datastore $name init"
|
new "datastore $name init"
|
||||||
expectfn "$datastore $conf init" ""
|
expectfn "$datastore $conf init" ""
|
||||||
|
|
||||||
|
|
@ -139,8 +139,18 @@ run(){
|
||||||
new "datastore $name create leaf"
|
new "datastore $name create leaf"
|
||||||
expectfn "$datastore $conf put create <config><x><y><a>1</a><b>3</b><c>newentry</c></y></x></config>"
|
expectfn "$datastore $conf put create <config><x><y><a>1</a><b>3</b><c>newentry</c></y></x></config>"
|
||||||
|
|
||||||
#leaf-list
|
new "datastore other db init"
|
||||||
|
expectfn "$datastore -d kalle -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip init"
|
||||||
|
|
||||||
|
new "datastore other db copy"
|
||||||
|
expectfn "$datastore $conf copy kalle" ""
|
||||||
|
|
||||||
|
diff $dir/kalle_db $dir/candidate_db
|
||||||
|
|
||||||
|
new "datastore lock"
|
||||||
|
expectfn "$datastore $conf lock 756" ""
|
||||||
|
|
||||||
|
#leaf-list
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
test/test6.sh
Executable file
64
test/test6.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Test6: Yang specifics: rpc and state info
|
||||||
|
|
||||||
|
# include err() and new() functions
|
||||||
|
. ./lib.sh
|
||||||
|
|
||||||
|
# For memcheck
|
||||||
|
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||||
|
clixon_netconf=clixon_netconf
|
||||||
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
|
cat <<EOF > /tmp/rpc.yang
|
||||||
|
module ietf-ip{
|
||||||
|
rpc fib-route {
|
||||||
|
input {
|
||||||
|
leaf name {
|
||||||
|
type string;
|
||||||
|
mandatory "true";
|
||||||
|
}
|
||||||
|
leaf destination-address {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output {
|
||||||
|
container route {
|
||||||
|
leaf address{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf address{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# kill old backend (if any)
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $clixon_cf -y /tmp/rpc
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "start backend"
|
||||||
|
# start new backend
|
||||||
|
sudo clixon_backend -If $clixon_cf -y /tmp/rpc
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "netconf rpc (notyet)"
|
||||||
|
#expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/rpc" "<rpc><fib-route><name></name></fib-route></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if still alive
|
||||||
|
pid=`pgrep clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
sudo clixon_backend -zf $clixon_cf
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "kill backend"
|
||||||
|
fi
|
||||||
179
yang/clixon-config@2017-07-02.yang
Normal file
179
yang/clixon-config@2017-07-02.yang
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
module clixon-config {
|
||||||
|
|
||||||
|
prefix cc;
|
||||||
|
|
||||||
|
organization
|
||||||
|
"Clicon / Clixon";
|
||||||
|
|
||||||
|
contact
|
||||||
|
"Olof Hagsand <olof@hagsand.se>";
|
||||||
|
|
||||||
|
description
|
||||||
|
"Clixon configuration file
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
Copyright (C) 2009-2017 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 *****";
|
||||||
|
|
||||||
|
revision 2017-07-02 {
|
||||||
|
description
|
||||||
|
"Initial revision";
|
||||||
|
}
|
||||||
|
leaf CLICON_CONFIGFILE{
|
||||||
|
type string;
|
||||||
|
default "sysconfdir/$APPNAME.conf";
|
||||||
|
description "Location of configuration-file for default values (this file)";
|
||||||
|
}
|
||||||
|
leaf CLICON_YANG_DIR {
|
||||||
|
type string;
|
||||||
|
default "prefix/share/$APPNAME/yang";
|
||||||
|
description "Location of YANG module and submodule files. Only if CLICON_DBSPEC_TYPE is YANG";
|
||||||
|
}
|
||||||
|
leaf CLICON_YANG_MODULE_MAIN {
|
||||||
|
type string;
|
||||||
|
default "clicon";
|
||||||
|
description "Option used to construct initial yang file:
|
||||||
|
<module>[@<revision>]
|
||||||
|
This option is only relevant if CLICON_DBSPEC_TYPE is YANG";
|
||||||
|
}
|
||||||
|
leaf CLICON_YANG_MODULE_REVISION {
|
||||||
|
type string;
|
||||||
|
description "Option used to construct initial yang file:
|
||||||
|
<module>[@<revision>]";
|
||||||
|
}
|
||||||
|
leaf CLICON_BACKEND_DIR {
|
||||||
|
type string;
|
||||||
|
default "libdir/$APPNAME/backend";
|
||||||
|
description "Location of backend .so plugins";
|
||||||
|
}
|
||||||
|
leaf CLICON_NETCONF_DIR {
|
||||||
|
type string;
|
||||||
|
default "libdir/$APPNAME/netconf";
|
||||||
|
description "Location of netconf (frontend) .so plugins";
|
||||||
|
}
|
||||||
|
leaf CLICON_RESTCONF_DIR {
|
||||||
|
type string;
|
||||||
|
default "libdir/$APPNAME/restconf";
|
||||||
|
description "Location of restconf (frontend) .so plugins";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_DIR {
|
||||||
|
type string;
|
||||||
|
default "libdir/$APPNAME/cli";
|
||||||
|
description "Location of cli frontend .so plugins";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLISPEC_DIR {
|
||||||
|
type string;
|
||||||
|
default "libdir/$APPNAME/clispec";
|
||||||
|
description "Location of frontend .cli cligen spec files";
|
||||||
|
}
|
||||||
|
leaf CLICON_USE_STARTUP_CONFIG {
|
||||||
|
type int32;
|
||||||
|
default 0;
|
||||||
|
description "Enabled uses \"startup\" configuration on boot";
|
||||||
|
}
|
||||||
|
leaf CLICON_SOCK_FAMILY {
|
||||||
|
type string;
|
||||||
|
default "UNIX";
|
||||||
|
description "Address family for communicating with clixon_backend (UNIX|IPv4|IPv6)";
|
||||||
|
}
|
||||||
|
leaf CLICON_SOCK {
|
||||||
|
type string;
|
||||||
|
default "localstatedir/$APPNAME/$APPNAME.sock";
|
||||||
|
description "If family above is AF_UNIX: Unix socket for communicating with
|
||||||
|
clixon_backend. If family above is AF_INET: IPv4 address";
|
||||||
|
}
|
||||||
|
leaf CLICON_SOCK_PORT {
|
||||||
|
type int32;
|
||||||
|
default 4535;
|
||||||
|
description "Inet socket port for communicating with clixon_backend (only IPv4|IPv6)";
|
||||||
|
}
|
||||||
|
leaf CLICON_BACKEND_PIDFILE {
|
||||||
|
type string;
|
||||||
|
default "localstatedir/$APPNAME/$APPNAME.pidfile";
|
||||||
|
description "Process-id file";
|
||||||
|
}
|
||||||
|
leaf CLICON_SOCK_GROUP {
|
||||||
|
type string;
|
||||||
|
default "clicon";
|
||||||
|
description "Group membership to access clixon_backend unix socket";
|
||||||
|
}
|
||||||
|
leaf CLICON_AUTOCOMMIT {
|
||||||
|
type int32;
|
||||||
|
default 0;
|
||||||
|
description "Set if all configuration changes are committed directly,
|
||||||
|
commit command unnecessary";
|
||||||
|
}
|
||||||
|
leaf CLICON_MASTER_PLUGIN {
|
||||||
|
type string;
|
||||||
|
default "master";
|
||||||
|
description "Name of master plugin (both frontend and backend).
|
||||||
|
Master plugin has special callbacks for frontends.
|
||||||
|
See clicon user manual for more info.";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_MODE {
|
||||||
|
type string;
|
||||||
|
default "base";
|
||||||
|
description "Startup CLI mode. This should match the CLICON_MODE in your startup clispec file";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_GENMODEL {
|
||||||
|
type int32;
|
||||||
|
default 1;
|
||||||
|
description "Generate code for CLI completion of existing db symbols.
|
||||||
|
Add name=\"myspec\" in datamodel spec and reference as @myspec";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_GENMODEL_COMPLETION {
|
||||||
|
type int32;
|
||||||
|
default 0;
|
||||||
|
description "Generate code for CLI completion of existing db symbols";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_GENMODEL_TYPE {
|
||||||
|
type string;
|
||||||
|
default "VARS";
|
||||||
|
description "How to generate and show CLI syntax: VARS|ALL";
|
||||||
|
}
|
||||||
|
leaf CLICON_XMLDB_DIR {
|
||||||
|
type string;
|
||||||
|
default "localstatedir/$APPNAME";
|
||||||
|
description "Directory where \"running\", \"candidate\" and \"startup\" are placed";
|
||||||
|
}
|
||||||
|
leaf CLICON_XMLDB_PLUGIN {
|
||||||
|
type string;
|
||||||
|
default "libdir/xmldb/text.so";
|
||||||
|
description "XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])";
|
||||||
|
}
|
||||||
|
leaf CLICON_CLI_VARONLY {
|
||||||
|
type int32;
|
||||||
|
default 1;
|
||||||
|
description "Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf CLICON_RESTCONF_PATH {
|
||||||
|
type string;
|
||||||
|
default "/www-data/fastcgi_restconf.sock";
|
||||||
|
description "FastCGI unix socket. Should be specified in webserver
|
||||||
|
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;";
|
||||||
|
}
|
||||||
|
}
|
||||||
926
yang/ietf-netconf@2011-06-01.yang
Normal file
926
yang/ietf-netconf@2011-06-01.yang
Normal file
|
|
@ -0,0 +1,926 @@
|
||||||
|
module ietf-netconf {
|
||||||
|
|
||||||
|
// the namespace for NETCONF XML definitions is unchanged
|
||||||
|
// from RFC 4741, which this document replaces
|
||||||
|
namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
|
||||||
|
|
||||||
|
prefix nc;
|
||||||
|
|
||||||
|
import ietf-inet-types {
|
||||||
|
prefix inet;
|
||||||
|
}
|
||||||
|
|
||||||
|
organization
|
||||||
|
"IETF NETCONF (Network Configuration) Working Group";
|
||||||
|
|
||||||
|
contact
|
||||||
|
"WG Web: <http://tools.ietf.org/wg/netconf/>
|
||||||
|
WG List: <netconf@ietf.org>
|
||||||
|
|
||||||
|
WG Chair: Bert Wijnen
|
||||||
|
<bertietf@bwijnen.net>
|
||||||
|
|
||||||
|
WG Chair: Mehmet Ersue
|
||||||
|
<mehmet.ersue@nsn.com>
|
||||||
|
|
||||||
|
Editor: Martin Bjorklund
|
||||||
|
<mbj@tail-f.com>
|
||||||
|
|
||||||
|
Editor: Juergen Schoenwaelder
|
||||||
|
<j.schoenwaelder@jacobs-university.de>
|
||||||
|
|
||||||
|
Editor: Andy Bierman
|
||||||
|
<andy.bierman@brocade.com>";
|
||||||
|
|
||||||
|
description
|
||||||
|
"NETCONF Protocol Data Types and Protocol Operations.
|
||||||
|
|
||||||
|
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||||
|
the document authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or
|
||||||
|
without modification, is permitted pursuant to, and subject
|
||||||
|
to the license terms contained in, the Simplified BSD License
|
||||||
|
set forth in Section 4.c of the IETF Trust's Legal Provisions
|
||||||
|
Relating to IETF Documents
|
||||||
|
(http://trustee.ietf.org/license-info).
|
||||||
|
|
||||||
|
This version of this YANG module is part of RFC 6241; see
|
||||||
|
the RFC itself for full legal notices.";
|
||||||
|
revision 2011-06-01 {
|
||||||
|
description
|
||||||
|
"Initial revision";
|
||||||
|
reference
|
||||||
|
"RFC 6241: Network Configuration Protocol";
|
||||||
|
}
|
||||||
|
|
||||||
|
extension get-filter-element-attributes {
|
||||||
|
description
|
||||||
|
"If this extension is present within an 'anyxml'
|
||||||
|
statement named 'filter', which must be conceptually
|
||||||
|
defined within the RPC input section for the <get>
|
||||||
|
and <get-config> protocol operations, then the
|
||||||
|
following unqualified XML attribute is supported
|
||||||
|
within the <filter> element, within a <get> or
|
||||||
|
<get-config> protocol operation:
|
||||||
|
|
||||||
|
type : optional attribute with allowed
|
||||||
|
value strings 'subtree' and 'xpath'.
|
||||||
|
If missing, the default value is 'subtree'.
|
||||||
|
|
||||||
|
If the 'xpath' feature is supported, then the
|
||||||
|
following unqualified XML attribute is
|
||||||
|
also supported:
|
||||||
|
|
||||||
|
select: optional attribute containing a
|
||||||
|
string representing an XPath expression.
|
||||||
|
The 'type' attribute must be equal to 'xpath'
|
||||||
|
if this attribute is present.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NETCONF capabilities defined as features
|
||||||
|
feature writable-running {
|
||||||
|
description
|
||||||
|
"NETCONF :writable-running capability;
|
||||||
|
If the server advertises the :writable-running
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.2";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature candidate {
|
||||||
|
description
|
||||||
|
"NETCONF :candidate capability;
|
||||||
|
If the server advertises the :candidate
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.3";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature confirmed-commit {
|
||||||
|
if-feature candidate;
|
||||||
|
description
|
||||||
|
"NETCONF :confirmed-commit:1.1 capability;
|
||||||
|
If the server advertises the :confirmed-commit:1.1
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 8.4";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature rollback-on-error {
|
||||||
|
description
|
||||||
|
"NETCONF :rollback-on-error capability;
|
||||||
|
If the server advertises the :rollback-on-error
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.5";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature validate {
|
||||||
|
description
|
||||||
|
"NETCONF :validate:1.1 capability;
|
||||||
|
If the server advertises the :validate:1.1
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.6";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature startup {
|
||||||
|
description
|
||||||
|
"NETCONF :startup capability;
|
||||||
|
If the server advertises the :startup
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.7";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature url {
|
||||||
|
description
|
||||||
|
"NETCONF :url capability;
|
||||||
|
If the server advertises the :url
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.8";
|
||||||
|
}
|
||||||
|
|
||||||
|
feature xpath {
|
||||||
|
description
|
||||||
|
"NETCONF :xpath capability;
|
||||||
|
If the server advertises the :xpath
|
||||||
|
capability for a session, then this feature must
|
||||||
|
also be enabled for that session. Otherwise,
|
||||||
|
this feature must not be enabled.";
|
||||||
|
reference "RFC 6241, Section 8.9";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NETCONF Simple Types
|
||||||
|
|
||||||
|
typedef session-id-type {
|
||||||
|
type uint32 {
|
||||||
|
range "1..max";
|
||||||
|
}
|
||||||
|
description
|
||||||
|
"NETCONF Session Id";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef session-id-or-zero-type {
|
||||||
|
type uint32;
|
||||||
|
description
|
||||||
|
"NETCONF Session Id or Zero to indicate none";
|
||||||
|
}
|
||||||
|
typedef error-tag-type {
|
||||||
|
type enumeration {
|
||||||
|
enum in-use {
|
||||||
|
description
|
||||||
|
"The request requires a resource that
|
||||||
|
already is in use.";
|
||||||
|
}
|
||||||
|
enum invalid-value {
|
||||||
|
description
|
||||||
|
"The request specifies an unacceptable value for one
|
||||||
|
or more parameters.";
|
||||||
|
}
|
||||||
|
enum too-big {
|
||||||
|
description
|
||||||
|
"The request or response (that would be generated) is
|
||||||
|
too large for the implementation to handle.";
|
||||||
|
}
|
||||||
|
enum missing-attribute {
|
||||||
|
description
|
||||||
|
"An expected attribute is missing.";
|
||||||
|
}
|
||||||
|
enum bad-attribute {
|
||||||
|
description
|
||||||
|
"An attribute value is not correct; e.g., wrong type,
|
||||||
|
out of range, pattern mismatch.";
|
||||||
|
}
|
||||||
|
enum unknown-attribute {
|
||||||
|
description
|
||||||
|
"An unexpected attribute is present.";
|
||||||
|
}
|
||||||
|
enum missing-element {
|
||||||
|
description
|
||||||
|
"An expected element is missing.";
|
||||||
|
}
|
||||||
|
enum bad-element {
|
||||||
|
description
|
||||||
|
"An element value is not correct; e.g., wrong type,
|
||||||
|
out of range, pattern mismatch.";
|
||||||
|
}
|
||||||
|
enum unknown-element {
|
||||||
|
description
|
||||||
|
"An unexpected element is present.";
|
||||||
|
}
|
||||||
|
enum unknown-namespace {
|
||||||
|
description
|
||||||
|
"An unexpected namespace is present.";
|
||||||
|
}
|
||||||
|
enum access-denied {
|
||||||
|
description
|
||||||
|
"Access to the requested protocol operation or
|
||||||
|
data model is denied because authorization failed.";
|
||||||
|
}
|
||||||
|
enum lock-denied {
|
||||||
|
description
|
||||||
|
"Access to the requested lock is denied because the
|
||||||
|
lock is currently held by another entity.";
|
||||||
|
}
|
||||||
|
enum resource-denied {
|
||||||
|
description
|
||||||
|
"Request could not be completed because of
|
||||||
|
insufficient resources.";
|
||||||
|
}
|
||||||
|
enum rollback-failed {
|
||||||
|
description
|
||||||
|
"Request to roll back some configuration change (via
|
||||||
|
rollback-on-error or <discard-changes> operations)
|
||||||
|
was not completed for some reason.";
|
||||||
|
|
||||||
|
}
|
||||||
|
enum data-exists {
|
||||||
|
description
|
||||||
|
"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.";
|
||||||
|
}
|
||||||
|
enum data-missing {
|
||||||
|
description
|
||||||
|
"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.";
|
||||||
|
}
|
||||||
|
enum operation-not-supported {
|
||||||
|
description
|
||||||
|
"Request could not be completed because the requested
|
||||||
|
operation is not supported by this implementation.";
|
||||||
|
}
|
||||||
|
enum operation-failed {
|
||||||
|
description
|
||||||
|
"Request could not be completed because the requested
|
||||||
|
operation failed for some reason not covered by
|
||||||
|
any other error condition.";
|
||||||
|
}
|
||||||
|
enum partial-operation {
|
||||||
|
description
|
||||||
|
"This error-tag is obsolete, and SHOULD NOT be sent
|
||||||
|
by servers conforming to this document.";
|
||||||
|
}
|
||||||
|
enum malformed-message {
|
||||||
|
description
|
||||||
|
"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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description "NETCONF Error Tag";
|
||||||
|
reference "RFC 6241, Appendix A";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef error-severity-type {
|
||||||
|
type enumeration {
|
||||||
|
enum error {
|
||||||
|
description "Error severity";
|
||||||
|
}
|
||||||
|
enum warning {
|
||||||
|
description "Warning severity";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description "NETCONF Error Severity";
|
||||||
|
reference "RFC 6241, Section 4.3";
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef edit-operation-type {
|
||||||
|
type enumeration {
|
||||||
|
enum merge {
|
||||||
|
description
|
||||||
|
"The configuration data identified by the
|
||||||
|
element containing this attribute is merged
|
||||||
|
with the configuration at the corresponding
|
||||||
|
level in the configuration datastore identified
|
||||||
|
by the target parameter.";
|
||||||
|
}
|
||||||
|
enum replace {
|
||||||
|
description
|
||||||
|
"The configuration data identified by the element
|
||||||
|
containing this attribute replaces any related
|
||||||
|
configuration in the configuration datastore
|
||||||
|
identified by the target parameter. If no such
|
||||||
|
configuration data exists in the configuration
|
||||||
|
datastore, it is created. Unlike a
|
||||||
|
<copy-config> operation, which replaces the
|
||||||
|
entire target configuration, only the configuration
|
||||||
|
actually present in the config parameter is affected.";
|
||||||
|
}
|
||||||
|
enum create {
|
||||||
|
description
|
||||||
|
"The configuration data identified by the element
|
||||||
|
containing this attribute is added to the
|
||||||
|
configuration if and only if the configuration
|
||||||
|
data does not already exist in the configuration
|
||||||
|
datastore. If the configuration data exists, an
|
||||||
|
<rpc-error> element is returned with an
|
||||||
|
<error-tag> value of 'data-exists'.";
|
||||||
|
}
|
||||||
|
enum delete {
|
||||||
|
description
|
||||||
|
"The configuration data identified by the element
|
||||||
|
containing this attribute is deleted from the
|
||||||
|
configuration if and only if the configuration
|
||||||
|
data currently exists in the configuration
|
||||||
|
datastore. If the configuration data does not
|
||||||
|
exist, an <rpc-error> element is returned with
|
||||||
|
an <error-tag> value of 'data-missing'.";
|
||||||
|
}
|
||||||
|
enum remove {
|
||||||
|
description
|
||||||
|
"The configuration data identified by the element
|
||||||
|
containing this attribute is deleted from the
|
||||||
|
configuration if the configuration
|
||||||
|
data currently exists in the configuration
|
||||||
|
datastore. If the configuration data does not
|
||||||
|
exist, the 'remove' operation is silently ignored
|
||||||
|
by the server.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default "merge";
|
||||||
|
description "NETCONF 'operation' attribute values";
|
||||||
|
reference "RFC 6241, Section 7.2";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NETCONF Standard Protocol Operations
|
||||||
|
|
||||||
|
rpc get-config {
|
||||||
|
description
|
||||||
|
"Retrieve all or part of a specified configuration.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.1";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container source {
|
||||||
|
description
|
||||||
|
"Particular configuration to retrieve.";
|
||||||
|
|
||||||
|
choice config-source {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration to retrieve.";
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config source.
|
||||||
|
This is optional-to-implement on the server because
|
||||||
|
not all servers will support filtering for this
|
||||||
|
datastore.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anyxml filter {
|
||||||
|
description
|
||||||
|
"Subtree or XPath filter to use.";
|
||||||
|
nc:get-filter-element-attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
anyxml data {
|
||||||
|
description
|
||||||
|
"Copy of the source datastore subset that matched
|
||||||
|
the filter criteria (if any). An empty data container
|
||||||
|
indicates that the request did not produce any results.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc edit-config {
|
||||||
|
description
|
||||||
|
"The <edit-config> operation loads all or part of a specified
|
||||||
|
configuration to the specified target configuration.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.2";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container target {
|
||||||
|
description
|
||||||
|
"Particular configuration to edit.";
|
||||||
|
|
||||||
|
choice config-target {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration target.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
if-feature writable-running;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config source.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf default-operation {
|
||||||
|
type enumeration {
|
||||||
|
enum merge {
|
||||||
|
description
|
||||||
|
"The default operation is merge.";
|
||||||
|
}
|
||||||
|
enum replace {
|
||||||
|
description
|
||||||
|
"The default operation is replace.";
|
||||||
|
}
|
||||||
|
enum none {
|
||||||
|
description
|
||||||
|
"There is no default operation.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default "merge";
|
||||||
|
description
|
||||||
|
"The default operation to use.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf test-option {
|
||||||
|
if-feature validate;
|
||||||
|
type enumeration {
|
||||||
|
enum test-then-set {
|
||||||
|
description
|
||||||
|
"The server will test and then set if no errors.";
|
||||||
|
}
|
||||||
|
enum set {
|
||||||
|
description
|
||||||
|
"The server will set without a test first.";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum test-only {
|
||||||
|
description
|
||||||
|
"The server will only test and not set, even
|
||||||
|
if there are no errors.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default "test-then-set";
|
||||||
|
description
|
||||||
|
"The test option to use.";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf error-option {
|
||||||
|
type enumeration {
|
||||||
|
enum stop-on-error {
|
||||||
|
description
|
||||||
|
"The server will stop on errors.";
|
||||||
|
}
|
||||||
|
enum continue-on-error {
|
||||||
|
description
|
||||||
|
"The server may continue on errors.";
|
||||||
|
}
|
||||||
|
enum rollback-on-error {
|
||||||
|
description
|
||||||
|
"The server will roll back on errors.
|
||||||
|
This value can only be used if the 'rollback-on-error'
|
||||||
|
feature is supported.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default "stop-on-error";
|
||||||
|
description
|
||||||
|
"The error option to use.";
|
||||||
|
}
|
||||||
|
|
||||||
|
choice edit-content {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The content for the edit operation.";
|
||||||
|
|
||||||
|
anyxml config {
|
||||||
|
description
|
||||||
|
"Inline Config content.";
|
||||||
|
}
|
||||||
|
leaf url {
|
||||||
|
if-feature url;
|
||||||
|
type inet:uri;
|
||||||
|
description
|
||||||
|
"URL-based config content.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc copy-config {
|
||||||
|
description
|
||||||
|
"Create or replace an entire configuration datastore with the
|
||||||
|
contents of another complete configuration datastore.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.3";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container target {
|
||||||
|
description
|
||||||
|
"Particular configuration to copy to.";
|
||||||
|
|
||||||
|
choice config-target {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration target of the copy operation.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
if-feature writable-running;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config target.
|
||||||
|
This is optional-to-implement on the server.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf url {
|
||||||
|
if-feature url;
|
||||||
|
type inet:uri;
|
||||||
|
description
|
||||||
|
"The URL-based configuration is the config target.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
container source {
|
||||||
|
description
|
||||||
|
"Particular configuration to copy from.";
|
||||||
|
|
||||||
|
choice config-source {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration source for the copy operation.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf url {
|
||||||
|
if-feature url;
|
||||||
|
type inet:uri;
|
||||||
|
description
|
||||||
|
"The URL-based configuration is the config source.";
|
||||||
|
}
|
||||||
|
anyxml config {
|
||||||
|
description
|
||||||
|
"Inline Config content: <config> element. Represents
|
||||||
|
an entire configuration datastore, not
|
||||||
|
a subset of the running datastore.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc delete-config {
|
||||||
|
description
|
||||||
|
"Delete a configuration datastore.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.4";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container target {
|
||||||
|
description
|
||||||
|
"Particular configuration to delete.";
|
||||||
|
|
||||||
|
choice config-target {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration target to delete.";
|
||||||
|
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf url {
|
||||||
|
if-feature url;
|
||||||
|
type inet:uri;
|
||||||
|
description
|
||||||
|
"The URL-based configuration is the config target.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc lock {
|
||||||
|
description
|
||||||
|
"The lock operation allows the client to lock the configuration
|
||||||
|
system of a device.";
|
||||||
|
reference "RFC 6241, Section 7.5";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container target {
|
||||||
|
description
|
||||||
|
"Particular configuration to lock.";
|
||||||
|
|
||||||
|
choice config-target {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration target to lock.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config target.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc unlock {
|
||||||
|
description
|
||||||
|
"The unlock operation is used to release a configuration lock,
|
||||||
|
previously obtained with the 'lock' operation.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.6";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container target {
|
||||||
|
description
|
||||||
|
"Particular configuration to unlock.";
|
||||||
|
|
||||||
|
choice config-target {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration target to unlock.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config target.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config target.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc get {
|
||||||
|
description
|
||||||
|
"Retrieve running configuration and device state information.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.7";
|
||||||
|
|
||||||
|
input {
|
||||||
|
anyxml filter {
|
||||||
|
description
|
||||||
|
"This parameter specifies the portion of the system
|
||||||
|
configuration and state data to retrieve.";
|
||||||
|
nc:get-filter-element-attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output {
|
||||||
|
anyxml data {
|
||||||
|
description
|
||||||
|
"Copy of the running datastore subset and/or state
|
||||||
|
data that matched the filter criteria (if any).
|
||||||
|
An empty data container indicates that the request did not
|
||||||
|
produce any results.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc close-session {
|
||||||
|
description
|
||||||
|
"Request graceful termination of a NETCONF session.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.8";
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc kill-session {
|
||||||
|
description
|
||||||
|
"Force the termination of a NETCONF session.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 7.9";
|
||||||
|
|
||||||
|
input {
|
||||||
|
leaf session-id {
|
||||||
|
type session-id-type;
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"Particular session to kill.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc commit {
|
||||||
|
if-feature candidate;
|
||||||
|
|
||||||
|
description
|
||||||
|
"Commit the candidate configuration as the device's new
|
||||||
|
current configuration.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 8.3.4.1";
|
||||||
|
|
||||||
|
input {
|
||||||
|
leaf confirmed {
|
||||||
|
if-feature confirmed-commit;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"Requests a confirmed commit.";
|
||||||
|
reference "RFC 6241, Section 8.3.4.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf confirm-timeout {
|
||||||
|
if-feature confirmed-commit;
|
||||||
|
type uint32 {
|
||||||
|
range "1..max";
|
||||||
|
}
|
||||||
|
units "seconds";
|
||||||
|
default "600"; // 10 minutes
|
||||||
|
description
|
||||||
|
"The timeout interval for a confirmed commit.";
|
||||||
|
reference "RFC 6241, Section 8.3.4.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf persist {
|
||||||
|
if-feature confirmed-commit;
|
||||||
|
type string;
|
||||||
|
description
|
||||||
|
"This parameter is used to make a confirmed commit
|
||||||
|
persistent. A persistent confirmed commit is not aborted
|
||||||
|
if the NETCONF session terminates. The only way to abort
|
||||||
|
a persistent confirmed commit is to let the timer expire,
|
||||||
|
or to use the <cancel-commit> operation.
|
||||||
|
|
||||||
|
The value of this parameter is a token that must be given
|
||||||
|
in the 'persist-id' parameter of <commit> or
|
||||||
|
<cancel-commit> operations in order to confirm or cancel
|
||||||
|
the persistent confirmed commit.
|
||||||
|
|
||||||
|
The token should be a random string.";
|
||||||
|
reference "RFC 6241, Section 8.3.4.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf persist-id {
|
||||||
|
if-feature confirmed-commit;
|
||||||
|
type string;
|
||||||
|
description
|
||||||
|
"This parameter is given in order to commit a persistent
|
||||||
|
confirmed commit. The value must be equal to the value
|
||||||
|
given in the 'persist' parameter to the <commit> operation.
|
||||||
|
If it does not match, the operation fails with an
|
||||||
|
'invalid-value' error.";
|
||||||
|
reference "RFC 6241, Section 8.3.4.1";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc discard-changes {
|
||||||
|
if-feature candidate;
|
||||||
|
|
||||||
|
description
|
||||||
|
"Revert the candidate configuration to the current
|
||||||
|
running configuration.";
|
||||||
|
reference "RFC 6241, Section 8.3.4.2";
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc cancel-commit {
|
||||||
|
if-feature confirmed-commit;
|
||||||
|
description
|
||||||
|
"This operation is used to cancel an ongoing confirmed commit.
|
||||||
|
If the confirmed commit is persistent, the parameter
|
||||||
|
'persist-id' must be given, and it must match the value of the
|
||||||
|
'persist' parameter.";
|
||||||
|
reference "RFC 6241, Section 8.4.4.1";
|
||||||
|
|
||||||
|
input {
|
||||||
|
leaf persist-id {
|
||||||
|
type string;
|
||||||
|
description
|
||||||
|
"This parameter is given in order to cancel a persistent
|
||||||
|
confirmed commit. The value must be equal to the value
|
||||||
|
given in the 'persist' parameter to the <commit> operation.
|
||||||
|
If it does not match, the operation fails with an
|
||||||
|
'invalid-value' error.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc validate {
|
||||||
|
if-feature validate;
|
||||||
|
|
||||||
|
description
|
||||||
|
"Validates the contents of the specified configuration.";
|
||||||
|
|
||||||
|
reference "RFC 6241, Section 8.6.4.1";
|
||||||
|
|
||||||
|
input {
|
||||||
|
container source {
|
||||||
|
description
|
||||||
|
"Particular configuration to validate.";
|
||||||
|
|
||||||
|
choice config-source {
|
||||||
|
mandatory true;
|
||||||
|
description
|
||||||
|
"The configuration source to validate.";
|
||||||
|
|
||||||
|
leaf candidate {
|
||||||
|
if-feature candidate;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The candidate configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf running {
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The running configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf startup {
|
||||||
|
if-feature startup;
|
||||||
|
type empty;
|
||||||
|
description
|
||||||
|
"The startup configuration is the config source.";
|
||||||
|
}
|
||||||
|
leaf url {
|
||||||
|
if-feature url;
|
||||||
|
type inet:uri;
|
||||||
|
description
|
||||||
|
"The URL-based configuration is the config source.";
|
||||||
|
}
|
||||||
|
anyxml config {
|
||||||
|
description
|
||||||
|
"Inline Config content: <config> element. Represents
|
||||||
|
an entire configuration datastore, not
|
||||||
|
a subset of the running datastore.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue