The Clixon API has been extended with namespaces, or namespace contexts in the following cases:
* CLIspec functions have added namespace parameter:
* `cli_show_config <db> <format> <xpath>` --> `cli_show_config <db> <format> <xpath> <namespace>`
* `cli_copy_config <db> <xpath> ...` --> `cli_copy_config <db> <xpath> <namespace> ...`
* Xpath API
* `xpath_first(x, format, ...)` --> `xpath_first(x, nsc, format, ...)`
* `xpath_vec(x, format, vec, veclen, ...)` --> `xpath_vec(x, nsc, format, vec, veclen, ...)`
* `xpath_vec_flag(x, format, flags, vec, veclen, ...)` --> `xpath_vec_flag(x, format, flags, vec, veclen, ...)`
* `xpath_vec_bool(x, format, ...)` --> `xpath_vec_bool(x, nsc, format, ...)`
* `xpath_vec_ctx(x, xpath, xp)` --> `xpath_vec_ctx(x, nsc, xpath, xp)`
* xmldb_get0 has an added `nsc` parameter:
* `xmldb_get0(h, db, xpath, copy, xret, msd)` --> `xmldb_get0(h, db, nsc, xpath, copy, xret, msd)`
* The plugin statedata callback (ca_statedata) has been extended with an nsc parameter:
* `int example_statedata(clicon_handle h, cvec *nsc, char *xpath, cxobj *xstate);`
* rpc get and get-config api function has an added namespace argument:
* `clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xt);`
* `int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, cxobj **xt);`
This commit is contained in:
parent
73d8e97a01
commit
67b8685bab
78 changed files with 1507 additions and 538 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -69,6 +69,24 @@
|
|||
|
||||
### API changes on existing features (you may need to change your code)
|
||||
|
||||
* The Clixon API has been extended with namespaces, or namespace contexts in the following cases:
|
||||
* CLIspec functions have added namespace parameter:
|
||||
* `cli_show_config <db> <format> <xpath>` --> `cli_show_config <db> <format> <xpath> <namespace>`
|
||||
* `cli_copy_config <db> <xpath> ...` --> `cli_copy_config <db> <xpath> <namespace> ...`
|
||||
* Xpath API
|
||||
* `xpath_first(x, format, ...)` --> `xpath_first(x, nsc, format, ...)`
|
||||
* `xpath_vec(x, format, vec, veclen, ...)` --> `xpath_vec(x, nsc, format, vec, veclen, ...)`
|
||||
* `xpath_vec_flag(x, format, flags, vec, veclen, ...)` --> `xpath_vec_flag(x, format, flags, vec, veclen, ...)`
|
||||
* `xpath_vec_bool(x, format, ...)` --> `xpath_vec_bool(x, nsc, format, ...)`
|
||||
* `xpath_vec_ctx(x, xpath, xp)` --> `xpath_vec_ctx(x, nsc, xpath, xp)`
|
||||
* xmldb_get0 has an added `nsc` parameter:
|
||||
* `xmldb_get0(h, db, xpath, copy, xret, msd)` --> `xmldb_get0(h, db, nsc, xpath, copy, xret, msd)`
|
||||
* The plugin statedata callback (ca_statedata) has been extended with an nsc parameter:
|
||||
* `int example_statedata(clicon_handle h, cvec *nsc, char *xpath, cxobj *xstate);`
|
||||
* rpc get and get-config api function has an added namespace argument:
|
||||
* `clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xt);`
|
||||
* `int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, cxobj **xt);`
|
||||
|
||||
* RESTCONF strict namespace validation of data in POST and PUT.
|
||||
* Accepted:
|
||||
```
|
||||
|
|
@ -191,6 +209,7 @@
|
|||
|
||||
### Minor changes
|
||||
|
||||
* Rewrote `api_path2xpath` to handle namespaces.
|
||||
* `startup_extraxml` triggers unnecessary validation
|
||||
* Renamed startup_db_reset -> xmldb_db_reset (its a general function)
|
||||
* In startup_extraxml(), check if reset callbacks or extraxml file actually makes and changes to the tmp db.
|
||||
|
|
|
|||
60
README.md
60
README.md
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Clixon is a YANG-based configuration manager, with interactive CLI,
|
||||
NETCONF and RESTCONF interfaces, an embedded database and transaction
|
||||
support.
|
||||
mechanism.
|
||||
|
||||
* [Background](#background)
|
||||
* [Frequently asked questions (FAQ)](doc/FAQ.md)
|
||||
|
|
@ -17,7 +17,7 @@ support.
|
|||
* [Extending](#extending)
|
||||
* [Yang](#yang)
|
||||
* [CLI](doc/CLI.md)
|
||||
* [XML and XPATH](#xml)
|
||||
* [XML and XPATH](#xml-and-xpath)
|
||||
* [Netconf](#netconf)
|
||||
* [Restconf](#restconf)
|
||||
* [Datastore](datastore/README.md)
|
||||
|
|
@ -36,10 +36,10 @@ support.
|
|||
## Background
|
||||
|
||||
Clixon was implemented to provide an open-source generic configuration
|
||||
tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while Clixon is a system with configuration database, xml and rest interfaces all defined by Yang. Most of the projects using Clixon are for embedded network and measuring devices. But Clixon can be used for other systems as well due to its modular and pluggable architecture.
|
||||
tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while Clixon is a system with configuration database, XML and REST interfaces all defined by Yang. Most of the projects using Clixon are for embedded network and measuring devices. But Clixon can be used for other systems as well due to its modular and pluggable architecture.
|
||||
|
||||
Users of Clixon currently include:
|
||||
* [Netgate](https://www.netgate.com)
|
||||
* [Netgate](https://www.netgate.com) in particular the [Tnsr product](https://www.tnsr.com/product#architecture)
|
||||
* [CloudMon360](http://cloudmon360.com)
|
||||
* [Grideye](http://hagsand.se/grideye)
|
||||
* [Netclean](https://www.netclean.com/solutions/whitebox) # only CLIgen
|
||||
|
|
@ -122,7 +122,7 @@ You then need to set the following configure option:
|
|||
<CLICON_YANG_REGEXP>libxml2</CLICON_YANG_REGEXP>
|
||||
```
|
||||
|
||||
## XML
|
||||
## XML and XPATH
|
||||
|
||||
Clixon has its own implementation of XML and XPATH implementation.
|
||||
|
||||
|
|
@ -151,6 +151,53 @@ Note that base netconf namespace syntax is not enforced but recommended, which m
|
|||
```
|
||||
All other namespaces are enforced.
|
||||
|
||||
### XPATH and Namespaces
|
||||
|
||||
XPATHs may contain prefixes. Example: `/if:a/if:b`. The prefixes have
|
||||
associated namespaces. For example, `if` may be bound to
|
||||
`urn:ietf:params:xml:ns:yang:ietf-interfaces`. The prefix to namespace binding is called a _namespace context_ (nsc).
|
||||
|
||||
In the Clixon API, there are two variants on namespace contexts: _implicit_ (given by the XML); or _explicit_ given by an external mapping.
|
||||
#### 1. Implicit namespace mapping
|
||||
|
||||
Implicit mapping is typical for basic known XML, where the context is
|
||||
given implicitly by the XML being evaluated. In node comparisons (eg
|
||||
of `if:a`) only name and prefixes are compared.
|
||||
|
||||
Example:
|
||||
```
|
||||
XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
XPATH: /if:a/ip:b
|
||||
```
|
||||
When you call an xpath API function, call it with nsc set to NULL. This is the default.
|
||||
|
||||
#### 2. Explicit namespace mapping
|
||||
|
||||
Explicit binding is typical if the namespace context is independent
|
||||
from the XML. Examples include NETCONF GET using :xpath when the XML
|
||||
is not known so that xpath and XML may use different prefixes for the
|
||||
same namespace. In that case you cannot rely on the prefix but must
|
||||
compare namespaces. The namespace context of the XML is given (by the
|
||||
XML), but the xpath nsc must then be explicitly given in the xpath
|
||||
call. Example:
|
||||
```
|
||||
XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
NETCONF:<get-config><filter select="/x:a/y:b" xmlns:x="urn:example:if" xmlns:y="urn:example:ip/>
|
||||
```
|
||||
Here, x,y are prefixes used for two namespaces that are given by if,ip
|
||||
in the xml. In this case, the namespaces (eg `urn:example:if`) must be
|
||||
compared instead.
|
||||
|
||||
Another case is Yang path expressions.
|
||||
|
||||
#### How to create namespace contexts
|
||||
|
||||
You can create namespace in three ways:
|
||||
* `xml_nsctx_init()` by explicitly giving a default namespace
|
||||
* `xml_nsctx_node()` by copying an XML namespace context from an existing XML node.
|
||||
* `xml_nsctx_yang()` by computing an XML namespace context a yang module import statements.
|
||||
|
||||
|
||||
## Netconf
|
||||
|
||||
Clixon implements the following NETCONF proposals or standards:
|
||||
|
|
@ -177,9 +224,6 @@ Clixon does not support the following netconf features:
|
|||
- edit-config config-text
|
||||
- edit-config operation
|
||||
|
||||
Some other deviations from the RFC:
|
||||
- edit-config xpath select statement does not support namespaces
|
||||
|
||||
## Restconf
|
||||
|
||||
Clixon Restconf is a daemon based on FastCGI C-API. Instructions are available to
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@ client_get_streams(clicon_handle h,
|
|||
/*! Get system state-data, including streams and plugins
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xpath Xpath selection, not used but may be to filter early
|
||||
* @param[in] nsc XML Namespace context for xpath
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 Statedata callback failed (clicon_err called)
|
||||
|
|
@ -225,6 +226,7 @@ client_get_streams(clicon_handle h,
|
|||
static int
|
||||
client_statedata(clicon_handle h,
|
||||
char *xpath,
|
||||
cvec *nsc,
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -251,12 +253,12 @@ client_statedata(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895")){
|
||||
if ((ret = yang_modules_state_get(h, yspec, xpath, 0, xret)) < 0)
|
||||
if ((ret = yang_modules_state_get(h, yspec, xpath, nsc, 0, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = clixon_plugin_statedata(h, yspec, xpath, xret)) < 0)
|
||||
if ((ret = clixon_plugin_statedata(h, yspec, nsc, xpath, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -264,7 +266,7 @@ client_statedata(clicon_handle h,
|
|||
* Actually this is a safety catch, should realy be done in plugins
|
||||
* and modules_state functions.
|
||||
*/
|
||||
if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* If vectors are specified then mark the nodes found and
|
||||
* then filter out everything else,
|
||||
|
|
@ -320,6 +322,7 @@ from_client_get_config(clicon_handle h,
|
|||
size_t xlen;
|
||||
int ret;
|
||||
char *username;
|
||||
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
|
||||
|
||||
username = clicon_username_get(h);
|
||||
if ((db = netconf_db_find(xe, "source")) == NULL){
|
||||
|
|
@ -336,13 +339,22 @@ from_client_get_config(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
||||
xpath="/";
|
||||
/* Create namespace context for xpath from <filter>
|
||||
* The set of namespace declarations are those in scope on the
|
||||
* <filter> element.
|
||||
*/
|
||||
else
|
||||
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Note xret can be pruned by nacm below (and change name),
|
||||
* so zero-copy cant be used
|
||||
* Also, must use external namespace context here due to <filter stmt
|
||||
*/
|
||||
if (xmldb_get(h, db, xpath, &xret) < 0){
|
||||
if (xmldb_get0(h, db, nsc, xpath, 1, &xret, NULL) < 0) {
|
||||
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -351,7 +363,7 @@ from_client_get_config(clicon_handle h,
|
|||
if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* Do NACM validation */
|
||||
if (xpath_vec(xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* NACM datanode/module read validation */
|
||||
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
|
||||
|
|
@ -374,6 +386,8 @@ from_client_get_config(clicon_handle h,
|
|||
xml_free(xnacm);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (xret)
|
||||
|
|
@ -441,14 +455,14 @@ from_client_edit_config(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if ((x = xpath_first(xn, "default-operation")) != NULL){
|
||||
if ((x = xpath_first(xn, NULL, "default-operation")) != NULL){
|
||||
if (xml_operation(xml_body(x), &operation) < 0){
|
||||
if (netconf_invalid_value(cbret, "protocol", "Wrong operation")< 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
if ((xc = xpath_first(xn, "config")) == NULL){
|
||||
if ((xc = xpath_first(xn, NULL, "config")) == NULL){
|
||||
if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -806,24 +820,34 @@ from_client_get(clicon_handle h,
|
|||
cxobj **xvec = NULL;
|
||||
size_t xlen;
|
||||
cxobj *xnacm = NULL;
|
||||
char *username;
|
||||
char *username;
|
||||
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
|
||||
|
||||
username = clicon_username_get(h);
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
||||
xpath="/";
|
||||
/* Create namespace context for xpath from <filter>
|
||||
* The set of namespace declarations are those in scope on the
|
||||
* <filter> element.
|
||||
*/
|
||||
else
|
||||
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Get config
|
||||
* Note xret can be pruned by nacm below and change name and
|
||||
* metrged with state data, so zero-copy cant be used
|
||||
* Also, must use external namespace context here due to <filter stmt
|
||||
*/
|
||||
if (xmldb_get(h, "running", xpath, &xret) < 0){
|
||||
if (xmldb_get0(h, "running", nsc, xpath, 1, &xret, NULL) < 0) {
|
||||
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* Get state data from plugins as defined by plugin_statedata(), if any */
|
||||
clicon_err_reset();
|
||||
if ((ret = client_statedata(h, xpath, &xret)) < 0)
|
||||
if ((ret = client_statedata(h, xpath, nsc, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* Error from callback (error in xret) */
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
|
|
@ -834,7 +858,7 @@ from_client_get(clicon_handle h,
|
|||
if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* Do NACM validation */
|
||||
if (xpath_vec(xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* NACM datanode/module read validation */
|
||||
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
|
||||
|
|
@ -858,6 +882,8 @@ from_client_get(clicon_handle h,
|
|||
xml_free(xnacm);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
|
|
@ -986,10 +1012,13 @@ from_client_create_subscription(clicon_handle h,
|
|||
char *selector = NULL;
|
||||
struct timeval start;
|
||||
struct timeval stop;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if ((x = xpath_first(xe, "//stream")) != NULL)
|
||||
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netmod:notification")) == NULL)
|
||||
goto done;
|
||||
if ((x = xpath_first(xe, nsc, "//stream")) != NULL)
|
||||
stream = xml_find_value(x, "body");
|
||||
if ((x = xpath_first(xe, "//stopTime")) != NULL){
|
||||
if ((x = xpath_first(xe, nsc, "//stopTime")) != NULL){
|
||||
if ((stoptime = xml_find_value(x, "body")) != NULL &&
|
||||
str2time(stoptime, &stop) < 0){
|
||||
if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
|
||||
|
|
@ -997,7 +1026,7 @@ from_client_create_subscription(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
}
|
||||
if ((x = xpath_first(xe, "//startTime")) != NULL){
|
||||
if ((x = xpath_first(xe, nsc, "//startTime")) != NULL){
|
||||
if ((starttime = xml_find_value(x, "body")) != NULL &&
|
||||
str2time(starttime, &start) < 0){
|
||||
if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
|
||||
|
|
@ -1005,7 +1034,7 @@ from_client_create_subscription(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
}
|
||||
if ((xfilter = xpath_first(xe, "//filter")) != NULL){
|
||||
if ((xfilter = xpath_first(xe, nsc, "//filter")) != NULL){
|
||||
if ((ftype = xml_find_value(xfilter, "type")) != NULL){
|
||||
/* Only accept xpath as filter type */
|
||||
if (strcmp(ftype, "xpath") != 0){
|
||||
|
|
@ -1041,6 +1070,8 @@ from_client_create_subscription(clicon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1144,7 +1175,7 @@ from_client_msg(clicon_handle h,
|
|||
goto reply;
|
||||
}
|
||||
|
||||
if ((x = xpath_first(xt, "/rpc")) == NULL){
|
||||
if ((x = xpath_first(xt, NULL, "/rpc")) == NULL){
|
||||
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
|
||||
goto done;
|
||||
goto reply;
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ startup_common(clicon_handle h,
|
|||
if ((msd = modstate_diff_new()) == NULL)
|
||||
goto done;
|
||||
clicon_debug(1, "Reading startup config from %s", db);
|
||||
if (xmldb_get0(h, db, "/", 0, &xt, msd) < 0)
|
||||
if (xmldb_get0(h, db, NULL, "/", 0, &xt, msd) < 0)
|
||||
goto done;
|
||||
/* Clear flags xpath for get */
|
||||
xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||
|
|
@ -404,7 +404,7 @@ from_validate_common(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
/* This is the state we are going to */
|
||||
if (xmldb_get0(h, candidate, "/", 0, &td->td_target, NULL) < 0)
|
||||
if (xmldb_get0(h, candidate, NULL, "/", 0, &td->td_target, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
/* Clear flags xpath for get */
|
||||
|
|
@ -422,7 +422,7 @@ from_validate_common(clicon_handle h,
|
|||
|
||||
/* 2. Parse xml trees
|
||||
* This is the state we are going from */
|
||||
if (xmldb_get0(h, "running", "/", 0, &td->td_src, NULL) < 0)
|
||||
if (xmldb_get0(h, "running", NULL, "/", 0, &td->td_src, NULL) < 0)
|
||||
goto done;
|
||||
/* Clear flags xpath for get */
|
||||
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ clixon_plugin_reset(clicon_handle h,
|
|||
int
|
||||
clixon_plugin_statedata(clicon_handle h,
|
||||
yang_stmt *yspec,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj **xret)
|
||||
{
|
||||
|
|
@ -136,7 +137,7 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
continue;
|
||||
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if (fn(h, xpath, x) < 0)
|
||||
if (fn(h, nsc, xpath, x) < 0)
|
||||
goto fail; /* Dont quit here on user callbacks */
|
||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,8 @@ int backend_plugin_initiate(clicon_handle h);
|
|||
|
||||
int clixon_plugin_reset(clicon_handle h, char *db);
|
||||
|
||||
int clixon_plugin_statedata(clicon_handle h, yang_stmt *yspec, char *xpath, cxobj **xtop);
|
||||
int clixon_plugin_statedata(clicon_handle h, yang_stmt *yspec, cvec *nsc,
|
||||
char *xpath, cxobj **xtop);
|
||||
transaction_data_t * transaction_new(void);
|
||||
int transaction_free(transaction_data_t *);
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ db_merge(clicon_handle h,
|
|||
cxobj *xt = NULL;
|
||||
|
||||
/* Get data as xml from db1 */
|
||||
if (xmldb_get0(h, (char*)db1, NULL, 0, &xt, NULL) < 0)
|
||||
if (xmldb_get0(h, (char*)db1, NULL, NULL, 0, &xt, NULL) < 0)
|
||||
goto done;
|
||||
/* Merge xml into db2. Without commit */
|
||||
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, clicon_username_get(h), cbret);
|
||||
|
|
@ -339,7 +339,7 @@ startup_module_state(clicon_handle h,
|
|||
goto ok;
|
||||
/* Set up cache
|
||||
* Now, access brief module cache with clicon_modst_cache_get(h, 1) */
|
||||
if ((ret = yang_modules_state_get(h, yspec, NULL, 1, &x)) < 0)
|
||||
if ((ret = yang_modules_state_get(h, yspec, NULL, NULL, 1, &x)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -680,15 +680,15 @@ compare_dbs(clicon_handle h,
|
|||
astext = cv_int32_get(cvec_i(argv, 0));
|
||||
else
|
||||
astext = 0;
|
||||
if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
|
||||
if (clicon_rpc_get_config(h, "running", "/", NULL, &xc1) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xc1, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
|
||||
if (clicon_rpc_get_config(h, "candidate", "/", NULL, &xc2) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xc2, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -847,13 +847,13 @@ save_config_file(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
filename = cv_string_get(cv);
|
||||
if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
|
||||
if (clicon_rpc_get_config(h, dbstr,"/", NULL, &xt) < 0)
|
||||
goto done;
|
||||
if (xt == NULL){
|
||||
clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */
|
||||
goto done;
|
||||
}
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -961,7 +961,7 @@ cli_notification_cb(int s,
|
|||
}
|
||||
if (clicon_msg_decode(reply, NULL, &xt) < 0) /* XXX pass yang_spec */
|
||||
goto done;
|
||||
if ((xe = xpath_first(xt, "//event")) != NULL){
|
||||
if ((xe = xpath_first(xt, NULL, "//event")) != NULL){
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xe, x, -1)) != NULL) {
|
||||
switch (format){
|
||||
|
|
@ -1110,14 +1110,15 @@ cli_unlock(clicon_handle h,
|
|||
* @param[in] cvv Vector of variables from CLIgen command-line
|
||||
* @param[in] argv Vector: <db>, <xpath>, <field>, <fromvar>, <tovar>
|
||||
* Explanation of argv fields:
|
||||
* db: Database name, eg candidate|tmp|startup
|
||||
* xpath: XPATH expression with exactly two %s pointing to field and from name
|
||||
* field: Name of list key, eg name
|
||||
* fromvar:Name of variable containing name of object to copy from (given by xpath)
|
||||
* tovar: Name of variable containing name of object to copy to.
|
||||
* db: Database name, eg candidate|tmp|startup
|
||||
* xpath: XPATH expression with exactly two %s pointing to field and from name
|
||||
* namespace: XPATH default namespace
|
||||
* field: Name of list key, eg name
|
||||
* fromvar: Name of variable containing name of object to copy from (given by xpath)
|
||||
* tovar: Name of variable containing name of object to copy to.
|
||||
* @code
|
||||
* cli spec:
|
||||
* copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s='%s']", "from", "n1", "n2");
|
||||
* copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s='%s']", "urn:example:clixon", "from", "n1", "n2");
|
||||
* cli command:
|
||||
* copy snd from to to
|
||||
* @endcode
|
||||
|
|
@ -1133,6 +1134,7 @@ cli_copy_config(clicon_handle h,
|
|||
cxobj *x2 = NULL;
|
||||
cxobj *x;
|
||||
char *xpath;
|
||||
char *namespace;
|
||||
int i;
|
||||
int j;
|
||||
cbuf *cb = NULL;
|
||||
|
|
@ -1144,21 +1146,24 @@ cli_copy_config(clicon_handle h,
|
|||
cg_var *tocv;
|
||||
char *toname;
|
||||
cxobj *xerr;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if (cvec_len(argv) != 5){
|
||||
clicon_err(OE_PLUGIN, 0, "Requires four elements: <db> <xpath> <keyname> <from> <to>");
|
||||
if (cvec_len(argv) != 6){
|
||||
clicon_err(OE_PLUGIN, 0, "Requires 6 elements: <db> <xpath> <namespace> <keyname> <from> <to>");
|
||||
goto done;
|
||||
}
|
||||
/* First argv argument: Database */
|
||||
db = cv_string_get(cvec_i(argv, 0));
|
||||
/* Second argv argument: xpath */
|
||||
xpath = cv_string_get(cvec_i(argv, 1));
|
||||
/* Third argv argument: namespace */
|
||||
namespace = cv_string_get(cvec_i(argv, 2));
|
||||
/* Third argv argument: name of keyname */
|
||||
keyname = cv_string_get(cvec_i(argv, 2));
|
||||
keyname = cv_string_get(cvec_i(argv, 3));
|
||||
/* Fourth argv argument: from variable */
|
||||
fromvar = cv_string_get(cvec_i(argv, 3));
|
||||
fromvar = cv_string_get(cvec_i(argv, 4));
|
||||
/* Fifth argv argument: to variable */
|
||||
tovar = cv_string_get(cvec_i(argv, 4));
|
||||
tovar = cv_string_get(cvec_i(argv, 5));
|
||||
|
||||
/* Get from variable -> cv -> from name */
|
||||
if ((fromcv = cvec_find(cvv, fromvar)) == NULL){
|
||||
|
|
@ -1184,9 +1189,9 @@ cli_copy_config(clicon_handle h,
|
|||
}
|
||||
cprintf(cb, xpath, keyname, fromname);
|
||||
/* 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), namespace, &x1) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(x1, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1204,7 +1209,9 @@ cli_copy_config(clicon_handle h,
|
|||
goto done;
|
||||
xml_name_set(x2, "config");
|
||||
cprintf(cb, "/%s", keyname);
|
||||
if ((x = xpath_first(x2, "%s", cbuf_get(cb))) == NULL){
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
if ((x = xpath_first(x2, nsc, "%s", cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1218,6 +1225,8 @@ cli_copy_config(clicon_handle h,
|
|||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (x1 != NULL)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ yang2cli_var_identityref(yang_stmt *ys,
|
|||
char *helptext,
|
||||
cbuf *cb)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
yang_stmt *ybaseref;
|
||||
yang_stmt *ybaseid;
|
||||
cg_var *cv = NULL;
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ expand_dbvar(void *h,
|
|||
cxobj *xcur;
|
||||
char *xpathcur;
|
||||
char *reason = NULL;
|
||||
char *namespace = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if (argv == NULL || cvec_len(argv) != 2){
|
||||
clicon_err(OE_PLUGIN, 0, "requires arguments: <db> <xmlkeyfmt>");
|
||||
|
|
@ -148,14 +150,14 @@ expand_dbvar(void *h,
|
|||
--> ^/interface/eth0/address/.*$
|
||||
--> /interface/[name="eth0"]/address
|
||||
*/
|
||||
if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0)
|
||||
goto done;
|
||||
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
|
||||
goto done;
|
||||
if (api_path2xpath(api_path, yspec, &xpath, &namespace) < 0)
|
||||
goto done;
|
||||
/* Get configuration */
|
||||
if (clicon_rpc_get_config(h, dbstr, xpath, &xt) < 0)
|
||||
if (clicon_rpc_get_config(h, dbstr, xpath, namespace, &xt) < 0) /* XXX */
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -172,6 +174,9 @@ expand_dbvar(void *h,
|
|||
goto done;
|
||||
if (y==NULL)
|
||||
goto ok;
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Special case for leafref. Detect leafref via Yang-type,
|
||||
* Get Yang path element, tentatively add the new syntax to the whole
|
||||
* tree and apply the path to that.
|
||||
|
|
@ -193,12 +198,12 @@ expand_dbvar(void *h,
|
|||
fprintf(stderr, "%s\n", reason);
|
||||
goto done;
|
||||
}
|
||||
if ((xcur = xpath_first(xt, "%s", xpath)) == NULL){
|
||||
if ((xcur = xpath_first(xt, nsc, "%s", xpath)) == NULL){
|
||||
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (xpath_vec(xcur, "%s", &xvec, &xlen, xpathcur) < 0)
|
||||
if (xpath_vec(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0)
|
||||
goto done;
|
||||
bodystr0 = NULL; /* Assume sorted XML where duplicates are adjacent */
|
||||
for (i = 0; i < xlen; i++) {
|
||||
|
|
@ -217,6 +222,8 @@ expand_dbvar(void *h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (reason)
|
||||
free(reason);
|
||||
if (api_path)
|
||||
|
|
@ -231,6 +238,7 @@ expand_dbvar(void *h,
|
|||
free(xpath);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
expandv_dbvar(void *h,
|
||||
char *name,
|
||||
|
|
@ -241,6 +249,7 @@ expandv_dbvar(void *h,
|
|||
{
|
||||
return expand_dbvar(h, name, cvv, argv, commands, helptexts);
|
||||
}
|
||||
|
||||
/*! List files in a directory
|
||||
*/
|
||||
int
|
||||
|
|
@ -390,10 +399,10 @@ show_yang(clicon_handle h,
|
|||
* Format of argv:
|
||||
* <dbname> "running"|"candidate"|"startup" # note only running state=1
|
||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
||||
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
|
||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name='foo']"
|
||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||
* @code
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name="%s"]","n");
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||
* @endcode
|
||||
* @note if state parameter is set, then db must be running
|
||||
*/
|
||||
|
|
@ -409,16 +418,13 @@ cli_show_config1(clicon_handle h,
|
|||
char *xpath;
|
||||
enum format_enum format;
|
||||
cbuf *cbxpath = NULL;
|
||||
char *attr = NULL;
|
||||
int i;
|
||||
int j;
|
||||
cg_var *cvattr;
|
||||
char *val = NULL;
|
||||
cxobj *xt = NULL;
|
||||
cxobj *xc;
|
||||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
yang_stmt *yspec;
|
||||
char *namespace = NULL;
|
||||
|
||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
||||
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<attr>]", cvec_len(argv));
|
||||
|
|
@ -441,33 +447,12 @@ cli_show_config1(clicon_handle h,
|
|||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Fourth argument is stdarg to xpath format string */
|
||||
if (cvec_len(argv) == 4){
|
||||
attr = cv_string_get(cvec_i(argv, 3));
|
||||
j = 0;
|
||||
for (i=0; i<strlen(xpath); i++)
|
||||
if (xpath[i] == '%')
|
||||
j++;
|
||||
if (j != 1){
|
||||
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'",
|
||||
xpath);
|
||||
goto done;
|
||||
}
|
||||
if ((cvattr = cvec_find(cvv, attr)) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "attr '%s' not found in cligen var list", attr);
|
||||
goto done;
|
||||
}
|
||||
if ((val = cv2str_dup(cvattr)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cv2str_dup");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cbxpath, xpath, val);
|
||||
}
|
||||
else
|
||||
cprintf(cbxpath, "%s", xpath);
|
||||
|
||||
cprintf(cbxpath, "%s", xpath);
|
||||
/* Fourth argument is namespace */
|
||||
if (cvec_len(argv) == 4)
|
||||
namespace = cv_string_get(cvec_i(argv, 3));
|
||||
if (state == 0){ /* Get configuration-only from database */
|
||||
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), &xt) < 0)
|
||||
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), namespace, &xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
else { /* Get configuration and state from database */
|
||||
|
|
@ -475,10 +460,10 @@ cli_show_config1(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_get(h, cbuf_get(cbxpath), &xt) < 0)
|
||||
if (clicon_rpc_get(h, cbuf_get(cbxpath), namespace, &xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -540,9 +525,9 @@ done:
|
|||
* <dbname> "running"|"candidate"|"startup"
|
||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
||||
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
|
||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||
* @code
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name="%s"]","n");
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||
* @endcode
|
||||
* @see cli_show_config_state For config and state data (not only config)
|
||||
*/
|
||||
|
|
@ -565,7 +550,7 @@ cli_show_config(clicon_handle h,
|
|||
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
||||
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
|
||||
* @code
|
||||
* show config id <n:string>, cli_show_config_state("running","xml","iface[name="%s"]","n");
|
||||
* show state id <n:string>, cli_show_config_state("running","xml","iface[name='foo']","urn:example:example");
|
||||
* @endcode
|
||||
* @see cli_show_config For config-only, no state
|
||||
*/
|
||||
|
|
@ -580,9 +565,9 @@ cli_show_config_state(clicon_handle h,
|
|||
/*! Show configuration as text given an xpath
|
||||
* Utility function used by cligen spec file
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] cvv Vector of variables from CLIgen command-line
|
||||
* @param[in] arg A string: <dbname> <xpath>
|
||||
* @note Hardcoded that a variable in cvv is named "xpath"
|
||||
* @param[in] cvv Vector of variables from CLIgen command-line must contain xpath and ns variables
|
||||
* @param[in] argv A string: <dbname>
|
||||
* @note Hardcoded that variable xpath and ns cvv must exist. (kludge)
|
||||
*/
|
||||
int
|
||||
show_conf_xpath(clicon_handle h,
|
||||
|
|
@ -598,6 +583,8 @@ show_conf_xpath(clicon_handle h,
|
|||
cxobj **xv = NULL;
|
||||
size_t xlen;
|
||||
int i;
|
||||
char *namespace = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if (cvec_len(argv) != 1){
|
||||
clicon_err(OE_PLUGIN, 0, "Requires one element to be <dbname>");
|
||||
|
|
@ -611,21 +598,31 @@ show_conf_xpath(clicon_handle h,
|
|||
clicon_err(OE_PLUGIN, 0, "No such db name: %s", str);
|
||||
goto done;
|
||||
}
|
||||
/* Look for xpath in command (kludge: cv must be called "xpath") */
|
||||
cv = cvec_find(cvv, "xpath");
|
||||
xpath = cv_string_get(cv);
|
||||
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
|
||||
|
||||
/* Look for namespace in command (kludge: cv must be called "ns") */
|
||||
cv = cvec_find(cvv, "ns");
|
||||
namespace = cv_string_get(cv);
|
||||
|
||||
if (clicon_rpc_get_config(h, str, xpath, namespace, &xt) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
if (xpath_vec(xt, "%s", &xv, &xlen, xpath) < 0)
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
if (xpath_vec(xt, nsc, "%s", &xv, &xlen, xpath) < 0)
|
||||
goto done;
|
||||
for (i=0; i<xlen; i++)
|
||||
xml_print(stdout, xv[i]);
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xv)
|
||||
free(xv);
|
||||
if (xt)
|
||||
|
|
@ -641,7 +638,7 @@ int cli_show_version(clicon_handle h,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Generic show configuration CLIGEN callback using generated CLI syntax
|
||||
/*! Generic show configuration CLIgen callback using generated CLI syntax
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] state If set, show both config and state, otherwise only config
|
||||
* @param[in] cvv Vector of variables from CLIgen command-line
|
||||
|
|
@ -651,6 +648,7 @@ int cli_show_version(clicon_handle h,
|
|||
* <dbname> "running"|"candidate"|"startup"
|
||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* @note if state parameter is set, then db must be running
|
||||
* @note that first argument is generated by code.
|
||||
*/
|
||||
static int
|
||||
cli_show_auto1(clicon_handle h,
|
||||
|
|
@ -664,12 +662,15 @@ cli_show_auto1(clicon_handle h,
|
|||
// char *api_path = NULL; /* xml key */
|
||||
char *db;
|
||||
char *xpath = NULL;
|
||||
cvec *nsc = NULL;
|
||||
char *formatstr;
|
||||
enum format_enum format = FORMAT_XML;
|
||||
cxobj *xt = NULL;
|
||||
cxobj *xp;
|
||||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
char *namespace = NULL;
|
||||
char *api_path = NULL;
|
||||
|
||||
if (cvec_len(argv) != 3){
|
||||
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format>. (*) generated.");
|
||||
|
|
@ -689,19 +690,20 @@ cli_show_auto1(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
|
||||
// if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
|
||||
// goto done;
|
||||
if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0)
|
||||
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
|
||||
goto done;
|
||||
if (api_path2xpath(api_path, yspec, &xpath, &namespace) < 0)
|
||||
goto done;
|
||||
/* XXX Kludge to overcome a trailing / in show, that I cannot add to
|
||||
* yang2api_path_fmt_1 where it should belong.
|
||||
*/
|
||||
if (xpath[strlen(xpath)-1] == '/')
|
||||
xpath[strlen(xpath)-1] = '\0';
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
|
||||
if (state == 0){ /* Get configuration-only from database */
|
||||
if (clicon_rpc_get_config(h, db, xpath, &xt) < 0)
|
||||
if (clicon_rpc_get_config(h, db, xpath, namespace, &xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* Get configuration and state from database */
|
||||
|
|
@ -709,15 +711,15 @@ cli_show_auto1(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_get(h, xpath, &xt) < 0)
|
||||
if (clicon_rpc_get(h, xpath, namespace, &xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
if ((xp = xpath_first(xt, "%s", xpath)) != NULL)
|
||||
if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL)
|
||||
/* Print configuration according to format */
|
||||
switch (format){
|
||||
case FORMAT_XML:
|
||||
|
|
@ -744,6 +746,10 @@ cli_show_auto1(clicon_handle h,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (api_path)
|
||||
free(api_path);
|
||||
if (xpath)
|
||||
free(xpath);
|
||||
if (xt)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ netconf_hello_dispatch(cxobj *xn)
|
|||
cxobj *xp;
|
||||
int retval = -1;
|
||||
|
||||
if ((xp = xpath_first(xn, "//hello")) != NULL)
|
||||
if ((xp = xpath_first(xn, NULL, "//hello")) != NULL)
|
||||
retval = netconf_hello(xp);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,14 +164,14 @@ netconf_get_target(cxobj *xn,
|
|||
cxobj *x;
|
||||
char *target = NULL;
|
||||
|
||||
if ((x = xpath_first(xn, "%s", path)) != NULL){
|
||||
if (xpath_first(x, "candidate") != NULL)
|
||||
if ((x = xpath_first(xn, NULL, "%s", path)) != NULL){
|
||||
if (xpath_first(x, NULL, "candidate") != NULL)
|
||||
target = "candidate";
|
||||
else
|
||||
if (xpath_first(x, "running") != NULL)
|
||||
if (xpath_first(x, NULL, "running") != NULL)
|
||||
target = "running";
|
||||
else
|
||||
if (xpath_first(x, "startup") != NULL)
|
||||
if (xpath_first(x, NULL, "startup") != NULL)
|
||||
target = "startup";
|
||||
}
|
||||
return target;
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ netconf_input_packet(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
free(str0);
|
||||
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
|
||||
if ((xrpc=xpath_first(xreq, NULL, "//rpc")) != NULL){
|
||||
isrpc++;
|
||||
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
|
||||
goto done;
|
||||
|
|
@ -134,7 +134,7 @@ netconf_input_packet(clicon_handle h,
|
|||
}
|
||||
}
|
||||
else
|
||||
if (xpath_first(xreq, "//hello") != NULL)
|
||||
if (xpath_first(xreq, NULL, "//hello") != NULL)
|
||||
;
|
||||
else{
|
||||
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropped");
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ netconf_get_config(clicon_handle h,
|
|||
cxobj *xconf;
|
||||
|
||||
/* ie <filter>...</filter> */
|
||||
if ((xfilter = xpath_first(xn, "filter")) != NULL)
|
||||
if ((xfilter = xpath_first(xn, NULL, "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)
|
||||
|
|
@ -154,8 +154,8 @@ netconf_get_config(clicon_handle h,
|
|||
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")) != NULL){
|
||||
(xfilterconf = xpath_first(xfilter, NULL, "//configuration"))!= NULL &&
|
||||
(xconf = xpath_first(*xret, NULL, "/rpc-reply/data")) != NULL){
|
||||
/* xml_filter removes parts of xml tree not matching */
|
||||
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
|
||||
xml_filter(xfilterconf, xconf) < 0){
|
||||
|
|
@ -208,7 +208,7 @@ get_edit_opts(cxobj *xn,
|
|||
cxobj *x;
|
||||
char *optstr;
|
||||
|
||||
if ((x = xpath_first(xn, "test-option")) != NULL){
|
||||
if ((x = xpath_first(xn, NULL, "test-option")) != NULL){
|
||||
if ((optstr = xml_body(x)) != NULL){
|
||||
if (strcmp(optstr, "test-then-set") == 0)
|
||||
*testopt = TEST_THEN_SET;
|
||||
|
|
@ -220,7 +220,7 @@ get_edit_opts(cxobj *xn,
|
|||
goto parerr;
|
||||
}
|
||||
}
|
||||
if ((x = xpath_first(xn, "error-option")) != NULL){
|
||||
if ((x = xpath_first(xn, NULL, "error-option")) != NULL){
|
||||
if ((optstr = xml_body(x)) != NULL){
|
||||
if (strcmp(optstr, "stop-on-error") == 0)
|
||||
*erropt = STOP_ON_ERROR;
|
||||
|
|
@ -352,7 +352,7 @@ netconf_get(clicon_handle h,
|
|||
cxobj *xconf;
|
||||
|
||||
/* ie <filter>...</filter> */
|
||||
if ((xfilter = xpath_first(xn, "filter")) != NULL)
|
||||
if ((xfilter = xpath_first(xn, NULL, "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)
|
||||
|
|
@ -366,8 +366,8 @@ netconf_get(clicon_handle h,
|
|||
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")) != NULL){
|
||||
(xfilterconf = xpath_first(xfilter, NULL, "//configuration"))!= NULL &&
|
||||
(xconf = xpath_first(*xret, NULL, "/rpc-reply/data")) != NULL){
|
||||
/* xml_filter removes parts of xml tree not matching */
|
||||
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
|
||||
xml_filter(xfilterconf, xconf) < 0){
|
||||
|
|
@ -428,6 +428,7 @@ netconf_notification_cb(int s,
|
|||
cxobj *xt = NULL; /* top xml */
|
||||
clicon_handle h = (clicon_handle)arg;
|
||||
yang_stmt *yspec = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
/* get msg (this is the reason this function is called) */
|
||||
|
|
@ -444,7 +445,10 @@ netconf_notification_cb(int s,
|
|||
yspec = clicon_dbspec_yang(h);
|
||||
if (clicon_msg_decode(reply, yspec, &xt) < 0)
|
||||
goto done;
|
||||
if ((xn = xpath_first(xt, "notification")) == NULL)
|
||||
|
||||
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netconf:notification:1.0")) == NULL)
|
||||
goto done;
|
||||
if ((xn = xpath_first(xt, nsc, "notification")) == NULL)
|
||||
goto ok;
|
||||
/* create netconf message */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
|
|
@ -460,9 +464,11 @@ netconf_notification_cb(int s,
|
|||
}
|
||||
fflush(stdout);
|
||||
cbuf_free(cb);
|
||||
ok:
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xt != NULL)
|
||||
xml_free(xt);
|
||||
if (reply)
|
||||
|
|
@ -494,7 +500,7 @@ netconf_create_subscription(clicon_handle h,
|
|||
int s;
|
||||
char *ftype;
|
||||
|
||||
if ((xfilter = xpath_first(xn, "//filter")) != NULL){
|
||||
if ((xfilter = xpath_first(xn, NULL, "//filter")) != NULL){
|
||||
if ((ftype = xml_find_value(xfilter, "type")) != NULL){
|
||||
if (strcmp(ftype, "xpath") != 0){
|
||||
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
|
||||
|
|
@ -510,7 +516,7 @@ netconf_create_subscription(clicon_handle h,
|
|||
}
|
||||
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, &s) < 0)
|
||||
goto done;
|
||||
if (xpath_first(*xret, "rpc-reply/rpc-error") != NULL)
|
||||
if (xpath_first(*xret, NULL, "rpc-reply/rpc-error") != NULL)
|
||||
goto ok;
|
||||
if (event_reg_fd(s,
|
||||
netconf_notification_cb,
|
||||
|
|
@ -616,7 +622,7 @@ netconf_application_rpc(clicon_handle h,
|
|||
*/
|
||||
if (0)
|
||||
if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){
|
||||
xoutput=xpath_first(*xret, "/");
|
||||
xoutput=xpath_first(*xret, NULL, "/");
|
||||
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ api_return_err(clicon_handle h,
|
|||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){
|
||||
if ((xtag = xpath_first(xerr, NULL, "//error-tag")) == NULL){
|
||||
notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ api_restconf(clicon_handle h,
|
|||
else{
|
||||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ api_data_get2(clicon_handle h,
|
|||
{
|
||||
int retval = -1;
|
||||
cbuf *cbpath = NULL;
|
||||
char *path;
|
||||
char *xpath = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
yang_stmt *yspec;
|
||||
cxobj *xret = NULL;
|
||||
|
|
@ -197,6 +197,8 @@ api_data_get2(clicon_handle h,
|
|||
int i;
|
||||
cxobj *x;
|
||||
int ret;
|
||||
char *namespace = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
|
|
@ -204,13 +206,13 @@ api_data_get2(clicon_handle h,
|
|||
goto done;
|
||||
cprintf(cbpath, "/");
|
||||
/* We know "data" is element pi-1 */
|
||||
if ((ret = api_path2xpath(yspec, pcvec, pi, cbpath)) < 0)
|
||||
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -218,12 +220,16 @@ api_data_get2(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
path = cbuf_get(cbpath);
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
|
||||
if (clicon_rpc_get(h, path, &xret) < 0){
|
||||
xpath = cbuf_get(cbpath);
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, xpath);
|
||||
/* Create a namespace context for ymod as the default namespace to use with
|
||||
* xpath expressions */
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_get(h, xpath, namespace, &xret) < 0){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -245,7 +251,7 @@ api_data_get2(clicon_handle h,
|
|||
}
|
||||
#endif
|
||||
/* Check if error return */
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -259,7 +265,7 @@ api_data_get2(clicon_handle h,
|
|||
FCGX_FPrintF(r->out, "\r\n");
|
||||
goto ok;
|
||||
}
|
||||
if (path==NULL || strcmp(path,"/")==0){ /* Special case: data root */
|
||||
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cbx, xret, 0, pretty) < 0) /* Dont print top object? */
|
||||
goto done;
|
||||
|
|
@ -270,10 +276,10 @@ api_data_get2(clicon_handle h,
|
|||
}
|
||||
}
|
||||
else{
|
||||
if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0){
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){
|
||||
if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -283,14 +289,14 @@ api_data_get2(clicon_handle h,
|
|||
}
|
||||
if (use_xml){
|
||||
for (i=0; i<xlen; i++){
|
||||
char *prefix, *namespace;
|
||||
char *prefix, *namespace2; /* Same as namespace? */
|
||||
x = xvec[i];
|
||||
/* Some complexities in grafting namespace in existing trees to new */
|
||||
prefix = xml_prefix(x);
|
||||
if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL){
|
||||
if (xml2ns(x, prefix, &namespace) < 0)
|
||||
if (xml2ns(x, prefix, &namespace2) < 0)
|
||||
goto done;
|
||||
if (namespace && xmlns_set(x, prefix, namespace) < 0)
|
||||
if (namespace2 && xmlns_set(x, prefix, namespace2) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (clicon_xml2cbuf(cbx, x, 0, pretty) < 0) /* Dont print top object? */
|
||||
|
|
@ -315,6 +321,8 @@ api_data_get2(clicon_handle h,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (cbpath)
|
||||
|
|
@ -483,7 +491,7 @@ api_data_post(clicon_handle h,
|
|||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -497,7 +505,7 @@ api_data_post(clicon_handle h,
|
|||
if (xml_parse_string(data, NULL, &xdata0) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -510,7 +518,7 @@ api_data_post(clicon_handle h,
|
|||
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -519,7 +527,7 @@ api_data_post(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -534,7 +542,7 @@ api_data_post(clicon_handle h,
|
|||
if (xml_child_nr(xdata0) != 1){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -556,7 +564,7 @@ api_data_post(clicon_handle h,
|
|||
if (ymoddata != ymodapi){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -598,7 +606,7 @@ api_data_post(clicon_handle h,
|
|||
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -612,14 +620,14 @@ api_data_post(clicon_handle h,
|
|||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<discard-changes/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
|
||||
goto done;
|
||||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0) /* Use original xe */
|
||||
goto done;
|
||||
|
|
@ -642,7 +650,7 @@ api_data_post(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
/* If copy-config failed, log and ignore (already committed) */
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
|
||||
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
|
||||
}
|
||||
|
|
@ -831,7 +839,7 @@ api_data_put(clicon_handle h,
|
|||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -846,7 +854,7 @@ api_data_put(clicon_handle h,
|
|||
if (xml_parse_string(data, yspec, &xdata0) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -859,7 +867,7 @@ api_data_put(clicon_handle h,
|
|||
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -868,7 +876,7 @@ api_data_put(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -883,7 +891,7 @@ api_data_put(clicon_handle h,
|
|||
if (xml_child_nr(xdata0) != 1){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -903,7 +911,7 @@ api_data_put(clicon_handle h,
|
|||
if (ymoddata != ymodapi){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -943,7 +951,7 @@ api_data_put(clicon_handle h,
|
|||
if (strcmp(dname, xml_name(xbot))){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -968,7 +976,7 @@ api_data_put(clicon_handle h,
|
|||
if (match_list_keys(ybot, xdata, xbot) < 0){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -992,7 +1000,7 @@ api_data_put(clicon_handle h,
|
|||
if (parbod == NULL || strcmp(parbod, xml_body(xdata))){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1032,7 +1040,7 @@ api_data_put(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -1045,14 +1053,14 @@ api_data_put(clicon_handle h,
|
|||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<discard-changes/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
|
||||
goto done;
|
||||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
|
|
@ -1075,7 +1083,7 @@ api_data_put(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
/* If copy-config failed, log and ignore (already committed) */
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
|
||||
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
|
||||
}
|
||||
|
|
@ -1183,7 +1191,7 @@ api_data_delete(clicon_handle h,
|
|||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1210,7 +1218,7 @@ api_data_delete(clicon_handle h,
|
|||
cprintf(cbx, "</edit-config></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -1224,14 +1232,14 @@ api_data_delete(clicon_handle h,
|
|||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", NACM_RECOVERY_USER);
|
||||
cprintf(cbx, "<discard-changes/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
|
||||
goto done;
|
||||
/* log errors from discard, but ignore */
|
||||
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
|
||||
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
|
||||
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
|
|
@ -1254,7 +1262,7 @@ api_data_delete(clicon_handle h,
|
|||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
/* If copy-config failed, log and ignore (already committed) */
|
||||
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
|
||||
|
||||
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
|
||||
}
|
||||
|
|
@ -1421,7 +1429,7 @@ api_operations_post_input(clicon_handle h,
|
|||
if (xml_parse_string(data, yspec, &xdata) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1434,7 +1442,7 @@ api_operations_post_input(clicon_handle h,
|
|||
if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1443,7 +1451,7 @@ api_operations_post_input(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1476,7 +1484,7 @@ api_operations_post_input(clicon_handle h,
|
|||
else
|
||||
if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1550,7 +1558,7 @@ api_operations_post_output(clicon_handle h,
|
|||
xml_child_nr_type(xret, CX_ELMNT) != 1){
|
||||
if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1587,7 +1595,7 @@ api_operations_post_output(clicon_handle h,
|
|||
(ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if ((xe = xpath_first(xerr, "rpc-reply/rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1720,7 +1728,7 @@ api_operations_post(clicon_handle h,
|
|||
if (oppath == NULL || strcmp(oppath,"/")==0){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1739,7 +1747,7 @@ api_operations_post(clicon_handle h,
|
|||
if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1750,7 +1758,7 @@ api_operations_post(clicon_handle h,
|
|||
if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){
|
||||
if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1779,7 +1787,7 @@ api_operations_post(clicon_handle h,
|
|||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
clicon_err_reset();
|
||||
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1819,7 +1827,7 @@ api_operations_post(clicon_handle h,
|
|||
if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if ((xe = xpath_first(xret, "rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -1851,7 +1859,7 @@ api_operations_post(clicon_handle h,
|
|||
if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
|
||||
goto done;
|
||||
/* Local error: return it and quit */
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -1860,7 +1868,7 @@ api_operations_post(clicon_handle h,
|
|||
else { /* Send to backend */
|
||||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
|
|||
|
|
@ -187,11 +187,11 @@ restconf_stream_cb(int s,
|
|||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((xn = xpath_first(xtop, "notification")) == NULL)
|
||||
if ((xn = xpath_first(xtop, NULL, "notification")) == NULL)
|
||||
goto ok;
|
||||
#ifdef notused
|
||||
xt = xpath_first(xn, "eventTime");
|
||||
if ((xe = xpath_first(xn, "event")) == NULL) /* event can depend on yang? */
|
||||
xt = xpath_first(xn, NULL, "eventTime");
|
||||
if ((xe = xpath_first(xn, NULL, "event")) == NULL) /* event can depend on yang? */
|
||||
goto ok;
|
||||
|
||||
if (xt)
|
||||
|
|
@ -268,7 +268,7 @@ restconf_stream(clicon_handle h,
|
|||
cprintf(cb, "</create-subscription></rpc>]]>]]>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
|
||||
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
@ -416,7 +416,7 @@ api_stream(clicon_handle h,
|
|||
else{
|
||||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ discard("Discard edits (rollback 0)"), discard_changes();
|
|||
compare("Compare running and candidate"), compare_dbs((int32)1);
|
||||
|
||||
show("Show a particular state of the system"){
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression"), show_conf_xpath("candidate");
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||
version("Show version"), cli_show_version("candidate", "text", "/");
|
||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||
|
|
|
|||
|
|
@ -118,17 +118,24 @@ main_commit(clicon_handle h,
|
|||
cxobj **vec = NULL;
|
||||
int i;
|
||||
size_t len;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if (_transaction_log)
|
||||
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
|
||||
|
||||
/* Create namespace context for xpath */
|
||||
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Get all added i/fs */
|
||||
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
||||
if (xpath_vec_flag(target, NULL, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
||||
return -1;
|
||||
if (debug)
|
||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||
xml_print(stdout, vec[i]); /* Print the added interface */
|
||||
// done:
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (vec)
|
||||
free(vec);
|
||||
return 0;
|
||||
|
|
@ -263,6 +270,7 @@ example_copy_extra(clicon_handle h, /* Clicon handle */
|
|||
|
||||
/*! Called to get state data from plugin
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] xstate XML tree, <config/> on entry.
|
||||
* @retval 0 OK
|
||||
|
|
@ -280,16 +288,18 @@ example_copy_extra(clicon_handle h, /* Clicon handle */
|
|||
*/
|
||||
int
|
||||
example_statedata(clicon_handle h,
|
||||
char *xpath,
|
||||
cxobj *xstate)
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj *xstate)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen;
|
||||
size_t xlen = 0;
|
||||
cbuf *cb = cbuf_new();
|
||||
int i;
|
||||
cxobj *xt = NULL;
|
||||
char *name;
|
||||
cvec *nsc1 = NULL;
|
||||
|
||||
if (!_state)
|
||||
goto ok;
|
||||
|
|
@ -298,10 +308,18 @@ example_statedata(clicon_handle h,
|
|||
* state information. In this case adding dummy interface operation state
|
||||
* to configured interfaces.
|
||||
* Get config according to xpath */
|
||||
if (xmldb_get(h, "running", xpath, &xt) < 0)
|
||||
goto done;
|
||||
/* From that subset, only get the names */
|
||||
if (xpath_vec(xt, "/interfaces/interface/name", &xvec, &xlen) < 0)
|
||||
if (xmldb_get0(h, "running", nsc, xpath, 1, &xt, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
/* Here a separate namespace context nsc1 is created. The original nsc
|
||||
* created by the system cannot be used trivially, since we dont know
|
||||
* the prefixes, although we could by a complex mechanism find the prefix
|
||||
* (if it exists) and use that when creating our xpath.
|
||||
* But it is easier creating a new namespace context nsc1.
|
||||
*/
|
||||
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
|
||||
goto done;
|
||||
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
if (xlen){
|
||||
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
|
||||
|
|
@ -322,6 +340,8 @@ example_statedata(clicon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc1)
|
||||
xml_nsctx_free(nsc1);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
|
|
@ -395,7 +415,7 @@ upgrade_2016(clicon_handle h,
|
|||
if ((name = xml_find_body(xi, "name")) == NULL)
|
||||
continue; /* shouldnt happen */
|
||||
/* Get corresponding /interfaces/interface entry */
|
||||
xif = xpath_first(xt, "/interfaces/interface[name=\"%s\"]", name);
|
||||
xif = xpath_first(xt, NULL, "/interfaces/interface[name=\"%s\"]", name);
|
||||
/* - Move /if:interfaces-state/if:interface/if:admin-status to
|
||||
* /if:interfaces/if:interface/ */
|
||||
if ((x = xml_find(xi, "admin-status")) != NULL && xif){
|
||||
|
|
@ -498,7 +518,7 @@ upgrade_2018(clicon_handle h,
|
|||
/* Change type /interfaces/interface/statistics/in-octets to
|
||||
* decimal64 with fraction-digits 3 and divide values with 1000
|
||||
*/
|
||||
if ((x = xpath_first(xi, "statistics/in-octets")) != NULL){
|
||||
if ((x = xpath_first(xi, NULL, "statistics/in-octets")) != NULL){
|
||||
if ((xb = xml_body_get(x)) != NULL){
|
||||
uint64_t u64;
|
||||
cbuf *cb = cbuf_new();
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ nacm_validate(clicon_handle h,
|
|||
if (_transaction_log){
|
||||
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
|
||||
if (_transaction_error_toggle==0 &&
|
||||
xpath_first(transaction_target(td), "%s", _transaction_xpath)){
|
||||
xpath_first(transaction_target(td), NULL, "%s", _transaction_xpath)){
|
||||
_transaction_error_toggle=1; /* toggle if triggered */
|
||||
clicon_err(OE_XML, 0, "User error");
|
||||
return -1; /* induce fail */
|
||||
|
|
@ -116,7 +116,7 @@ nacm_commit(clicon_handle h,
|
|||
if (_transaction_log){
|
||||
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
|
||||
if (_transaction_error_toggle==1 &&
|
||||
xpath_first(target, "%s", _transaction_xpath)){
|
||||
xpath_first(target, NULL, "%s", _transaction_xpath)){
|
||||
_transaction_error_toggle=0; /* toggle if triggered */
|
||||
clicon_err(OE_XML, 0, "User error");
|
||||
return -1; /* induce fail */
|
||||
|
|
@ -154,6 +154,7 @@ nacm_abort(clicon_handle h,
|
|||
|
||||
/*! Called to get NACM state data
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] xtop XML tree, <config/> on entry.
|
||||
* @retval 0 OK
|
||||
|
|
@ -164,6 +165,7 @@ nacm_abort(clicon_handle h,
|
|||
*/
|
||||
int
|
||||
nacm_statedata(clicon_handle h,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj *xstate)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
|||
/* Show eth0 interfaces config using XPATH */
|
||||
if (clicon_rpc_get_config(h, "running",
|
||||
"/interfaces/interface[name='eth0']",
|
||||
"urn:example:clixon",
|
||||
&xret) < 0)
|
||||
goto done;
|
||||
xml_print(stdout, xret);
|
||||
|
|
@ -104,7 +105,7 @@ example_client_rpc(clicon_handle h,
|
|||
/* Send to backend */
|
||||
if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Get configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ debug("Debugging parts of the system"), cli_debug_cli((int32)1);{
|
|||
}
|
||||
copy("Copy and create a new object") {
|
||||
interface("Copy interface"){
|
||||
(<name:string>|<name:string expand_dbvar("candidate","/ietf-interfaces:interfaces/interface=%s/name")>("name of interface to copy from")) to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
|
||||
(<name:string>|<name:string expand_dbvar("candidate","/ietf-interfaces:interfaces/interface=%s/name")>("name of interface to copy from")) to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","urn:ietf:params:xml:ns:yang:ietf-interfaces","name","name","toname");
|
||||
}
|
||||
}
|
||||
discard("Discard edits (rollback 0)"), discard_changes();
|
||||
compare("Compare running and candidate"), compare_dbs((int32)1);
|
||||
|
||||
show("Show a particular state of the system"){
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression"), show_conf_xpath("candidate");
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||
version("Show version"), cli_show_version("candidate", "text", "/");
|
||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@
|
|||
*/
|
||||
#undef RPC_USERNAME_ASSERT
|
||||
|
||||
/* If rpc call does not have a namespace (eg using xmlns) then use the default
|
||||
* NETCONF namespace (see rfc6241 3.1)
|
||||
* Undefine it if you want to ensure strict namespace assignment on all netconf and
|
||||
* XML statements.
|
||||
*/
|
||||
#define USE_NETCONF_NS_AS_DEFAULT
|
||||
|
||||
/* Make namespace check on RESTCONF PUT and POST -d data
|
||||
* Should be defined according to standard
|
||||
* Undefine it if you want allow no namespace (pick first name it finds in list
|
||||
* of loaded modules).
|
||||
*/
|
||||
#define RESTCONF_NS_DATA_CHECK
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
#include <clixon/clixon_netconf_lib.h>
|
||||
#include <clixon/clixon_nacm.h>
|
||||
#include <clixon/clixon_xml_changelog.h>
|
||||
#include <clixon/clixon_xml_nsctx.h>
|
||||
|
||||
/*
|
||||
* Global variables generated by Makefile
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ int xmldb_connect(clicon_handle h);
|
|||
int xmldb_disconnect(clicon_handle h);
|
||||
/* in clixon_datastore_read.[ch] */
|
||||
int xmldb_get(clicon_handle h, const char *db, char *xpath, cxobj **xtop);
|
||||
int xmldb_get0(clicon_handle h, const char *db, char *xpath, int copy, cxobj **xtop, modstate_diff_t *msd);
|
||||
int xmldb_get0(clicon_handle h, const char *db,
|
||||
cvec *nc, char *xpath,
|
||||
int copy, cxobj **xtop, modstate_diff_t *msd);
|
||||
int xmldb_get0_clear(clicon_handle h, cxobj *x);
|
||||
int xmldb_get0_free(clicon_handle h, cxobj **xp);
|
||||
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret); /* in clixon_datastore_write.[ch] */
|
||||
|
|
|
|||
|
|
@ -119,7 +119,16 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
|||
typedef int (plgauth_t)(clicon_handle, void *);
|
||||
|
||||
typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system status */
|
||||
typedef int (plgstatedata_t)(clicon_handle h, char *xpath, cxobj *xtop);
|
||||
|
||||
/* Plugin statedata
|
||||
* @param[in] Clicon handle
|
||||
* @param[in] xpath Part of state requested
|
||||
* @param[in] nsc XPATH namespace context.
|
||||
* @param[in] xtop XML tree where statedata is added
|
||||
* @retval -1 Fatal error
|
||||
* @retval 0 OK
|
||||
*/
|
||||
typedef int (plgstatedata_t)(clicon_handle h, cvec *nsc, char *xpath, cxobj *xtop);
|
||||
|
||||
typedef void *transaction_data;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
|
|||
int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
|
||||
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
|
||||
int clicon_rpc_generate_error(char *format, cxobj *xerr);
|
||||
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret);
|
||||
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xret);
|
||||
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
|
||||
char *xml);
|
||||
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_lock(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_get(clicon_handle h, char *xpath, char *namespace, cxobj **xret);
|
||||
int clicon_rpc_close_session(clicon_handle h);
|
||||
int clicon_rpc_kill_session(clicon_handle h, int session_id);
|
||||
int clicon_rpc_validate(clicon_handle h, char *db);
|
||||
|
|
|
|||
|
|
@ -41,12 +41,11 @@
|
|||
/*
|
||||
* Constants
|
||||
*/
|
||||
/* If rpc call does not have a namespace (eg w xmlns) then use the default NETCONF
|
||||
* namespace (rfc6241 3.1)
|
||||
|
||||
/* Default NETCONF namespace (see rfc6241 3.1)
|
||||
* See USE_NETCONF_NS_AS_DEFAULT for use of this namespace as default
|
||||
*/
|
||||
#define DEFAULT_XML_RPC_NAMESPACE "urn:ietf:params:xml:ns:netconf:base:1.0"
|
||||
/* default namespace statement, such as in <rpc xmlns="..."> */
|
||||
#define DEFAULT_XMLNS "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\""
|
||||
#define NETCONF_BASE_NAMESPACE "urn:ietf:params:xml:ns:netconf:base:1.0"
|
||||
|
||||
/*
|
||||
* Types
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@ int xml_sanity(cxobj *x, void *arg);
|
|||
int xml_non_config_data(cxobj *xt, void *arg);
|
||||
int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_stmt *yspec);
|
||||
int xml_spec_populate(cxobj *x, void *arg);
|
||||
int api_path2xpath(yang_stmt *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||
int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, char **namespace);
|
||||
int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, char **namespace);
|
||||
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||
yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp);
|
||||
|
||||
|
|
|
|||
59
lib/clixon/clixon_xml_nsctx.h
Normal file
59
lib/clixon/clixon_xml_nsctx.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
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 *****
|
||||
|
||||
* XML support functions.
|
||||
* @see https://www.w3.org/TR/2009/REC-xml-names-20091208/
|
||||
*/
|
||||
#ifndef _CLIXON_XML_NSCTX_H
|
||||
#define _CLIXON_XML_NSCTX_H
|
||||
|
||||
/*
|
||||
* An xml namespace context is a cligen variable vector containing a list of
|
||||
* <prefix,namespace> pairs.
|
||||
* It is encoded in a cvv as a list of string values, where the c name is the
|
||||
* prefix and the string values are the namespace URI.
|
||||
* The default namespace is decoded as having the name NULL
|
||||
*/
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
char *xml_nsctx_get(cvec *nsc, char *prefix);
|
||||
int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix);
|
||||
int xml_nsctx_set(cvec *nsc, char *prefix, char *namespace);
|
||||
cvec *xml_nsctx_init(char *prefix, char *namespace);
|
||||
int xml_nsctx_node(cxobj *x, cvec **ncp);
|
||||
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
|
||||
int xml_nsctx_free(cvec *ncs);
|
||||
|
||||
#endif /* _CLIXON_XML_NSCTX_H */
|
||||
|
|
@ -80,24 +80,26 @@ enum axis_type{
|
|||
*/
|
||||
extern const map_str2int xpopmap[];
|
||||
|
||||
extern int xpatherrordiff;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5)));
|
||||
int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags,
|
||||
cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6)));
|
||||
cxobj *xpath_first(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6)));
|
||||
int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags,
|
||||
cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 7)));
|
||||
cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
|
||||
#else
|
||||
int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...);
|
||||
int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags,
|
||||
cxobj ***vec, size_t *veclen, ...);
|
||||
cxobj *xpath_first(cxobj *xcur, char *format, ...);
|
||||
int xpath_vec_bool(cxobj *xcur, char *format, ...);
|
||||
|
||||
int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...);
|
||||
int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags,
|
||||
cxobj ***vec, size_t *veclen, ...);
|
||||
cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...);
|
||||
int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...);
|
||||
#endif
|
||||
int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp);
|
||||
|
||||
int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp);
|
||||
|
||||
#endif /* _CLIXON_XPATH_H */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ int yang_modules_init(clicon_handle h);
|
|||
char *yang_modules_revision(clicon_handle h);
|
||||
|
||||
int yang_modules_state_get(clicon_handle h, yang_stmt *yspec, char *xpath,
|
||||
int brief, cxobj **xret);
|
||||
cvec *nsc, int brief, cxobj **xret);
|
||||
|
||||
int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
|||
clixon_string.c clixon_regex.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
|
||||
clixon_yang_cardinality.c clixon_xml_changelog.c \
|
||||
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
|
||||
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
|
||||
#include "clixon_datastore.h"
|
||||
#include "clixon_datastore_read.h"
|
||||
|
|
@ -257,7 +258,7 @@ text_read_modstate(clicon_handle h,
|
|||
if ((name = xml_find_body(xm, "name")) == NULL)
|
||||
continue;
|
||||
/* 3a) There is no such module in the system */
|
||||
if ((xs = xpath_first(xmcache, "module[name=\"%s\"]", name)) == NULL){
|
||||
if ((xs = xpath_first(xmcache, NULL, "module[name=\"%s\"]", name)) == NULL){
|
||||
// fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name);
|
||||
if ((xm2 = xml_dup(xm)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -385,6 +386,7 @@ xmldb_readfile(clicon_handle h,
|
|||
* This is a clixon datastore plugin of the the xmldb api
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
|
|
@ -395,6 +397,7 @@ xmldb_readfile(clicon_handle h,
|
|||
static int
|
||||
xmldb_get_nocache(clicon_handle h,
|
||||
const char *db,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
|
|
@ -417,7 +420,7 @@ xmldb_get_nocache(clicon_handle h,
|
|||
goto done;
|
||||
/* Here xt looks like: <config>...</config> */
|
||||
/* Given the xpath, return a vector of matches in xvec */
|
||||
if (xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
|
||||
/* If vectors are specified then mark the nodes found with all ancestors
|
||||
|
|
@ -467,6 +470,7 @@ xmldb_get_nocache(clicon_handle h,
|
|||
* This is a clixon datastore plugin of the the xmldb api
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
|
|
@ -475,11 +479,12 @@ xmldb_get_nocache(clicon_handle h,
|
|||
* @see xmldb_get the generic API function
|
||||
*/
|
||||
static int
|
||||
xmldb_get_cache(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
xmldb_get_cache(clicon_handle h,
|
||||
const char *db,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -521,7 +526,7 @@ xmldb_get_cache(clicon_handle h,
|
|||
*/
|
||||
|
||||
/* Here xt looks like: <config>...</config> */
|
||||
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
|
||||
/* Make new tree by copying top-of-tree from x0t to x1t */
|
||||
|
|
@ -565,6 +570,7 @@ xmldb_get_cache(clicon_handle h,
|
|||
* This is a clixon datastore plugin of the the xmldb api
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
|
|
@ -573,11 +579,12 @@ xmldb_get_cache(clicon_handle h,
|
|||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xmldb_get_zerocopy(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
xmldb_get_zerocopy(clicon_handle h,
|
||||
const char *db,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -612,7 +619,7 @@ xmldb_get_zerocopy(clicon_handle h,
|
|||
else
|
||||
x0t = de->de_xml;
|
||||
/* Here xt looks like: <config>...</config> */
|
||||
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* Iterate through the match vector
|
||||
* For every node found in x0, mark the tree up to t1
|
||||
|
|
@ -656,7 +663,7 @@ xmldb_get(clicon_handle h,
|
|||
char *xpath,
|
||||
cxobj **xret)
|
||||
{
|
||||
return xmldb_get0(h, db, xpath, 1, xret, NULL);
|
||||
return xmldb_get0(h, db, NULL, xpath, 1, xret, NULL);
|
||||
}
|
||||
|
||||
/*! Zero-copy variant of get content of database
|
||||
|
|
@ -669,6 +676,7 @@ xmldb_get(clicon_handle h,
|
|||
* freeing tree must be made after use.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
|
|
@ -677,17 +685,19 @@ xmldb_get(clicon_handle h,
|
|||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (xmldb_get0(xh, "running", "/interface[name="eth"]", 0, &xt, NULL) < 0)
|
||||
* if (xmldb_get0(xh, "running", nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0)
|
||||
* err;
|
||||
* ...
|
||||
* xmldb_get0_clear(h, xt); # Clear tree from default values and flags
|
||||
* xmldb_get0_free(h, &xt); # Free tree
|
||||
* @endcode
|
||||
* @see xml_nsctx_node to get a XML namespace context from XML tree
|
||||
* @see xmldb_get for a copy version (old-style)
|
||||
*/
|
||||
int
|
||||
xmldb_get0(clicon_handle h,
|
||||
const char *db,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
int copy,
|
||||
cxobj **xret,
|
||||
|
|
@ -701,7 +711,7 @@ xmldb_get0(clicon_handle h,
|
|||
* Add default values in copy
|
||||
* Copy deleted by xmldb_free
|
||||
*/
|
||||
retval = xmldb_get_nocache(h, db, xpath, xret, msd);
|
||||
retval = xmldb_get_nocache(h, db, nsc, xpath, xret, msd);
|
||||
break;
|
||||
case DATASTORE_CACHE_ZEROCOPY:
|
||||
/* Get cache (file if empty) mark xpath match in original tree
|
||||
|
|
@ -709,7 +719,7 @@ xmldb_get0(clicon_handle h,
|
|||
* Default values and markings removed in xmldb_clear
|
||||
*/
|
||||
if (!copy){
|
||||
retval = xmldb_get_zerocopy(h, db, xpath, xret, msd);
|
||||
retval = xmldb_get_zerocopy(h, db, nsc, xpath, xret, msd);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
|
@ -718,7 +728,7 @@ xmldb_get0(clicon_handle h,
|
|||
* Add default values in copy, return copy
|
||||
* Copy deleted by xmldb_free
|
||||
*/
|
||||
retval = xmldb_get_cache(h, db, xpath, xret, msd);
|
||||
retval = xmldb_get_cache(h, db, nsc, xpath, xret, msd);
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include "clixon_nacm.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
#include "clixon_datastore.h"
|
||||
|
|
@ -614,6 +615,7 @@ xmldb_put(clicon_handle h,
|
|||
cxobj *x;
|
||||
int permit = 0; /* nacm permit all */
|
||||
char *format;
|
||||
cvec *nsc = NULL; /* nacm namespace context */
|
||||
|
||||
if (cbret == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "cbret is NULL");
|
||||
|
|
@ -656,8 +658,11 @@ xmldb_put(clicon_handle h,
|
|||
else if (strcmp(mode, "internal")==0)
|
||||
xnacm0 = x0;
|
||||
}
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-netconf-acm")) == NULL)
|
||||
goto done;
|
||||
if (xnacm0 != NULL &&
|
||||
(xnacm = xpath_first(xnacm0, "nacm")) != NULL){
|
||||
(xnacm = xpath_first(xnacm0, nsc, "nacm")) != NULL){
|
||||
/* Pre-NACM access step, if permit, then dont do any nacm checks in
|
||||
* text_modify_* below */
|
||||
if ((permit = nacm_access(mode, xnacm, username)) < 0)
|
||||
|
|
@ -744,6 +749,8 @@ xmldb_put(clicon_handle h,
|
|||
done:
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (dbfile)
|
||||
free(dbfile);
|
||||
if (cb)
|
||||
|
|
|
|||
|
|
@ -68,8 +68,12 @@
|
|||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_datastore.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_nacm.h"
|
||||
|
||||
/* NACM namespace for use with xml namespace contexts and xpath */
|
||||
#define NACM_NS "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"
|
||||
|
||||
/*! Match nacm access operations according to RFC8341 3.4.4.
|
||||
* Incoming RPC Message Validation Step 7 (c)
|
||||
* The rule's "access-operations" leaf has the "exec" bit set or
|
||||
|
|
@ -191,7 +195,11 @@ nacm_rpc(char *rpc,
|
|||
char *gname;
|
||||
char *action;
|
||||
int match= 0;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
/* 3. If the requested operation is the NETCONF <close-session>
|
||||
protocol operation, then the protocol operation is permitted.
|
||||
*/
|
||||
|
|
@ -204,8 +212,9 @@ nacm_rpc(char *rpc,
|
|||
transport layer.) */
|
||||
if (username == NULL)
|
||||
goto step10;
|
||||
|
||||
/* User's group */
|
||||
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
goto done;
|
||||
/* 5. If no groups are found, continue with step 10. */
|
||||
if (glen == 0)
|
||||
|
|
@ -214,14 +223,14 @@ nacm_rpc(char *rpc,
|
|||
configuration. If a rule-list's "group" leaf-list does not
|
||||
match any of the user's groups, proceed to the next rule-list
|
||||
entry. */
|
||||
if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
goto done;
|
||||
for (i=0; i<rlistlen; i++){
|
||||
rlist = rlistvec[i];
|
||||
/* Loop through user's group to find match in this rule-list */
|
||||
for (j=0; j<glen; j++){
|
||||
gname = xml_find_body(gvec[j], "name");
|
||||
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
|
||||
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
|
||||
break; /* found */
|
||||
}
|
||||
if (j==glen) /* not found */
|
||||
|
|
@ -230,7 +239,7 @@ nacm_rpc(char *rpc,
|
|||
until a rule that matches the requested access operation is
|
||||
found.
|
||||
*/
|
||||
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
|
||||
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
|
||||
goto done;
|
||||
for (j=0; j<rlen; j++){
|
||||
xrule = rvec[j];
|
||||
|
|
@ -283,6 +292,8 @@ nacm_rpc(char *rpc,
|
|||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (gvec)
|
||||
free(gvec);
|
||||
if (rlistvec)
|
||||
|
|
@ -321,7 +332,11 @@ nacm_rule_datanode(cxobj *xt,
|
|||
char *module;
|
||||
cxobj *xpath; /* xpath match */
|
||||
cxobj *xp; /* parent */
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
||||
* the YANG module where the requested data node is defined. */
|
||||
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
|
||||
|
|
@ -375,7 +390,7 @@ nacm_rule_datanode(cxobj *xt,
|
|||
}
|
||||
/* Here module is matched, now check for path if any NYI */
|
||||
if (path){
|
||||
if ((xpath = xpath_first(xt, "%s", path)) == NULL)
|
||||
if ((xpath = xpath_first(xt, nsc, "%s", path)) == NULL)
|
||||
goto nomatch;
|
||||
/* The requested node xr is the node specified by the path or is a
|
||||
* descendant node of the path:
|
||||
|
|
@ -390,6 +405,8 @@ nacm_rule_datanode(cxobj *xt,
|
|||
match:
|
||||
retval = 1;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
return retval;
|
||||
nomatch:
|
||||
retval = 0;
|
||||
|
|
@ -403,7 +420,8 @@ nacm_rule_datanode(cxobj *xt,
|
|||
* @param[in] glen Length of gvec
|
||||
* @param[in] rlistvec NACM rule-list entries
|
||||
* @param[in] rlistlen Length of rlistvec
|
||||
* @param[out] xrulep If set, then points to matching rule
|
||||
* @param[in] nsc NACM namespace context for xpaths
|
||||
* @param[out] xrulep If set, then points to matching rule
|
||||
*/
|
||||
static int
|
||||
nacm_data_read_xr(cxobj *xt,
|
||||
|
|
@ -412,6 +430,7 @@ nacm_data_read_xr(cxobj *xt,
|
|||
size_t glen,
|
||||
cxobj **rlistvec,
|
||||
size_t rlistlen,
|
||||
cvec *nsc,
|
||||
cxobj **xrulep)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -428,7 +447,7 @@ nacm_data_read_xr(cxobj *xt,
|
|||
/* Loop through user's group to find match in this rule-list */
|
||||
for (j=0; j<glen; j++){
|
||||
gname = xml_find_body(gvec[j], "name");
|
||||
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
|
||||
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
|
||||
break; /* found */
|
||||
}
|
||||
if (j==glen) /* not found */
|
||||
|
|
@ -437,7 +456,7 @@ nacm_data_read_xr(cxobj *xt,
|
|||
until a rule that matches the requested access operation is
|
||||
found. (see 6 sub rules in nacm_rule_datanode
|
||||
*/
|
||||
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
|
||||
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
|
||||
goto done;
|
||||
for (j=0; j<rlen; j++){ /* Loop through rules */
|
||||
xrule = rvec[j];
|
||||
|
|
@ -556,7 +575,11 @@ nacm_datanode_read(cxobj *xt,
|
|||
char *read_default = NULL;
|
||||
cxobj *xrule;
|
||||
char *action;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
/* 3. Check all the "group" entries to see if any of them contain a
|
||||
"user-name" entry that equals the username for the session
|
||||
making the request. (If the "enable-external-groups" leaf is
|
||||
|
|
@ -565,7 +588,7 @@ nacm_datanode_read(cxobj *xt,
|
|||
if (username == NULL)
|
||||
goto step9;
|
||||
/* User's group */
|
||||
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
goto done;
|
||||
/* 4. If no groups are found (glen=0), continue and check read-default
|
||||
in step 11. */
|
||||
|
|
@ -573,7 +596,7 @@ nacm_datanode_read(cxobj *xt,
|
|||
configuration. If a rule-list's "group" leaf-list does not
|
||||
match any of the user's groups, proceed to the next rule-list
|
||||
entry. */
|
||||
if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
goto done;
|
||||
/* read-default has default permit so should never be NULL */
|
||||
if ((read_default = xml_find_body(xnacm, "read-default")) == NULL){
|
||||
|
|
@ -587,7 +610,7 @@ nacm_datanode_read(cxobj *xt,
|
|||
xrule = NULL;
|
||||
/* Skip if no groups */
|
||||
if (glen && nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
|
||||
&xrule) < 0)
|
||||
nsc, &xrule) < 0)
|
||||
goto done;
|
||||
if (xrule){ /* xrule match requested node xr */
|
||||
if ((action = xml_find_body(xrule, "action")) == NULL)
|
||||
|
|
@ -628,6 +651,8 @@ nacm_datanode_read(cxobj *xt,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (gvec)
|
||||
free(gvec);
|
||||
if (rlistvec)
|
||||
|
|
@ -673,7 +698,11 @@ nacm_datanode_write(cxobj *xt,
|
|||
int match = 0;
|
||||
char *action;
|
||||
char *write_default = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
if (xnacm == NULL)
|
||||
goto permit;
|
||||
/* 3. Check all the "group" entries to see if any of them contain a
|
||||
|
|
@ -684,7 +713,7 @@ nacm_datanode_write(cxobj *xt,
|
|||
if (username == NULL)
|
||||
goto step9;
|
||||
/* User's group */
|
||||
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
goto done;
|
||||
/* 4. If no groups are found, continue with step 9. */
|
||||
if (glen == 0)
|
||||
|
|
@ -693,19 +722,19 @@ nacm_datanode_write(cxobj *xt,
|
|||
configuration. If a rule-list's "group" leaf-list does not
|
||||
match any of the user's groups, proceed to the next rule-list
|
||||
entry. */
|
||||
if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
goto done;
|
||||
for (i=0; i<rlistlen; i++){
|
||||
rlist = rlistvec[i];
|
||||
/* Loop through user's group to find match in this rule-list */
|
||||
for (j=0; j<glen; j++){
|
||||
gname = xml_find_body(gvec[j], "name");
|
||||
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
|
||||
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
|
||||
break; /* found */
|
||||
}
|
||||
if (j==glen) /* not found */
|
||||
continue;
|
||||
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
|
||||
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
|
||||
goto done;
|
||||
/* 6. For each rule-list entry found, process all rules, in order,
|
||||
until a rule that matches the requested access operation is
|
||||
|
|
@ -765,6 +794,8 @@ nacm_datanode_write(cxobj *xt,
|
|||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (gvec)
|
||||
free(gvec);
|
||||
if (rlistvec)
|
||||
|
|
@ -811,8 +842,11 @@ nacm_access(char *mode,
|
|||
cxobj *xnacm0 = NULL;
|
||||
char *enabled;
|
||||
cxobj *x;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
if (mode == NULL || strcmp(mode, "disabled") == 0)
|
||||
goto permit;
|
||||
/* 0. If nacm-mode is external, get NACM defintion from separet tree,
|
||||
|
|
@ -828,7 +862,7 @@ nacm_access(char *mode,
|
|||
* RFC8341 3.4 */
|
||||
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
|
||||
operation is permitted. */
|
||||
if ((x = xpath_first(xnacm, "enable-nacm")) == NULL)
|
||||
if ((x = xpath_first(xnacm, nsc, "enable-nacm")) == NULL)
|
||||
goto permit;
|
||||
enabled = xml_body(x);
|
||||
if (strcmp(enabled, "true") != 0)
|
||||
|
|
@ -840,6 +874,8 @@ nacm_access(char *mode,
|
|||
|
||||
retval = 0; /* not permitted yet. continue with next NACM step */
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (retval != 0 && xnacm0)
|
||||
xml_free(xnacm0);
|
||||
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||
|
|
@ -882,7 +918,10 @@ nacm_access_pre(clicon_handle h,
|
|||
cxobj *x;
|
||||
cxobj *xnacm0 = NULL;
|
||||
cxobj *xnacm = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
goto done;
|
||||
if ((mode = clicon_option_str(h, "CLICON_NACM_MODE")) != NULL){
|
||||
if (strcmp(mode, "external")==0){
|
||||
if ((x = clicon_nacm_ext(h)))
|
||||
|
|
@ -890,7 +929,7 @@ nacm_access_pre(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
else if (strcmp(mode, "internal")==0){
|
||||
if (xmldb_get(h, "running", "nacm", &xnacm0) < 0)
|
||||
if (xmldb_get0(h, "running", nsc, "nacm", 1, &xnacm0, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -898,7 +937,7 @@ nacm_access_pre(clicon_handle h,
|
|||
if (xnacm0 == NULL)
|
||||
goto permit;
|
||||
/* If config does not exist then the operation is permitted(?) */
|
||||
if ((xnacm = xpath_first(xnacm0, "nacm")) == NULL)
|
||||
if ((xnacm = xpath_first(xnacm0, nsc, "nacm")) == NULL)
|
||||
goto permit;
|
||||
if (xml_rootchild_node(xnacm0, xnacm) < 0)
|
||||
goto done;
|
||||
|
|
@ -910,6 +949,8 @@ nacm_access_pre(clicon_handle h,
|
|||
xnacm = NULL;
|
||||
}
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xnacm0)
|
||||
xml_free(xnacm0);
|
||||
else if (xnacm)
|
||||
|
|
|
|||
|
|
@ -1274,13 +1274,13 @@ netconf_err2cb(cxobj *xerr,
|
|||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((x=xpath_first(xerr, "error-type"))!=NULL)
|
||||
if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
|
||||
if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-message"))!=NULL)
|
||||
if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-info"))!=NULL)
|
||||
if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL)
|
||||
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
|
||||
*cberr = cb;
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -73,8 +73,15 @@
|
|||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
/*! Clixon configuration namespace
|
||||
* Probably should be defined somewhere else or extracted from yang
|
||||
* @see clixon-config.yang
|
||||
*/
|
||||
#define CLIXON_CONF_NS "http://clicon.org/config"
|
||||
|
||||
/* Mapping between Clicon startup modes string <--> constants,
|
||||
see clixon-config.yang type startup_mode */
|
||||
static const map_str2int startup_mode_map[] = {
|
||||
|
|
@ -165,6 +172,7 @@ parse_configfile(clicon_handle h,
|
|||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
int ret;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if (filename == NULL || !strlen(filename)){
|
||||
clicon_err(OE_UNIX, 0, "Not specified");
|
||||
|
|
@ -191,25 +199,12 @@ parse_configfile(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
/* Hard-coded config for < 3.10 and clixon-config for >= 3.10 */
|
||||
if ((xc = xpath_first(xt, "clixon-config")) == NULL){
|
||||
/* Backward compatible code to accept "config" as top-level symbol.
|
||||
This cannot be controlled by config option due to bootstrap */
|
||||
#if 0
|
||||
if ((xc = xpath_first(xt, "config")) != NULL){
|
||||
if (xml_name_set(xc, "clixon-config") < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: <clixon-config xmlns=\"http://clicon.org/config\" (See Changelog in Clixon 3.10)>", filename);
|
||||
if ((nsc = xml_nsctx_init(NULL, CLIXON_CONF_NS)) == NULL)
|
||||
goto done;
|
||||
if ((xc = xpath_first(xt, nsc, "clixon-config")) == NULL){
|
||||
clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: <clixon-config xmlns=\"%s\" (See Changelog in Clixon 3.10)>", filename, CLIXON_CONF_NS);
|
||||
|
||||
goto done;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0)
|
||||
goto done;
|
||||
|
|
@ -249,6 +244,8 @@ parse_configfile(clicon_handle h,
|
|||
*xconfig = xt;
|
||||
xt = NULL;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xret)
|
||||
|
|
@ -364,7 +361,7 @@ clicon_options_main(clicon_handle h,
|
|||
if (xml_rootchild(xconfig, 0, &xconfig) < 0)
|
||||
goto done;
|
||||
if (xml_spec(xconfig) == NULL){
|
||||
clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: <clixon-config xmlns=\"http://clicon.org/config\"> or clixon-config.yang not found?", configfile);
|
||||
clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: <clixon-config xmlns=\"%s\"> or clixon-config.yang not found?", configfile, CLIXON_CONF_NS);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,15 +253,16 @@ clicon_rpc_generate_error(char *prefix,
|
|||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @param[in] xpath XPath (or "")
|
||||
* @param[in] namespace Namespace associated w xpath
|
||||
* @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_config(h, "running", "/", &xt) < 0)
|
||||
* if (clicon_rpc_get_config(h, "running", "/hello/world", "urn:example:hello", &xt) < 0)
|
||||
* err;
|
||||
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
* clicon_rpc_generate_error("", xerr);
|
||||
* err;
|
||||
* }
|
||||
|
|
@ -274,6 +275,7 @@ int
|
|||
clicon_rpc_get_config(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
char *namespace,
|
||||
cxobj **xt)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -288,18 +290,25 @@ clicon_rpc_get_config(clicon_handle h,
|
|||
cprintf(cb, "<rpc");
|
||||
if ((username = clicon_username_get(h)) != NULL)
|
||||
cprintf(cb, " username=\"%s\"", username);
|
||||
if (namespace)
|
||||
cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cb, "><get-config><source><%s/></source>", db);
|
||||
if (xpath && strlen(xpath))
|
||||
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
|
||||
if (xpath && strlen(xpath)){
|
||||
if (namespace)
|
||||
cprintf(cb, "<nc:filter nc:type=\"xpath\" nc:select=\"%s\" xmlns=\"%s\"/>",
|
||||
xpath, namespace);
|
||||
else /* XXX shouldnt happen */
|
||||
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
|
||||
}
|
||||
cprintf(cb, "</get-config></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)
|
||||
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
|
||||
xd = xml_parent(xd); /* point to rpc-reply */
|
||||
else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL)
|
||||
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL)
|
||||
if ((xd = xml_new("data", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xt){
|
||||
|
|
@ -347,7 +356,7 @@ clicon_rpc_edit_config(clicon_handle h,
|
|||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc %s", DEFAULT_XMLNS);
|
||||
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
if ((username = clicon_username_get(h)) != NULL)
|
||||
cprintf(cb, " username=\"%s\"", username);
|
||||
cprintf(cb, "><edit-config><target><%s/></target>", db);
|
||||
|
|
@ -360,7 +369,7 @@ clicon_rpc_edit_config(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Editing configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -406,7 +415,7 @@ clicon_rpc_copy_config(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Copying configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -445,7 +454,7 @@ clicon_rpc_delete_config(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Deleting configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -480,7 +489,7 @@ clicon_rpc_lock(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Locking configuration", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -514,7 +523,7 @@ clicon_rpc_unlock(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Configuration unlock", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -528,17 +537,20 @@ clicon_rpc_unlock(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Get database configuration and state data
|
||||
* @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
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xpath XPath in a filter stmt (or NULL/"" for no filter)
|
||||
* @param[in] namespace Namespace associated w xpath
|
||||
* @param[out] xt XML tree. Free with xml_free.
|
||||
* Either <config> or <rpc-error>.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error, fatal or xml
|
||||
* @note if xpath is set but namespace is NULL, the default, netconf base
|
||||
* namespace will be used which is most probably wrong.
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* if (clicon_rpc_get(h, "/", &xt) < 0)
|
||||
* if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", &xt) < 0)
|
||||
* err;
|
||||
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
|
||||
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
|
||||
* clicon_rpc_generate_error(xerr);
|
||||
* err;
|
||||
* }
|
||||
|
|
@ -550,6 +562,7 @@ clicon_rpc_unlock(clicon_handle h,
|
|||
int
|
||||
clicon_rpc_get(clicon_handle h,
|
||||
char *xpath,
|
||||
char *namespace,
|
||||
cxobj **xt)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -564,18 +577,25 @@ clicon_rpc_get(clicon_handle h,
|
|||
cprintf(cb, "<rpc");
|
||||
if ((username = clicon_username_get(h)) != NULL)
|
||||
cprintf(cb, " username=\"%s\"", username);
|
||||
if (namespace)
|
||||
cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(cb, "><get>");
|
||||
if (xpath && strlen(xpath))
|
||||
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
|
||||
if (xpath && strlen(xpath)) {
|
||||
if (namespace)
|
||||
cprintf(cb, "<nc:filter nc:type=\"xpath\" nc:select=\"%s\" xmlns=\"%s\"/>",
|
||||
xpath, namespace);
|
||||
else /* XXX shouldnt happen */
|
||||
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)
|
||||
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
|
||||
xd = xml_parent(xd); /* point to rpc-reply */
|
||||
else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL)
|
||||
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL)
|
||||
if ((xd = xml_new("data", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xt){
|
||||
|
|
@ -614,7 +634,7 @@ clicon_rpc_close_session(clicon_handle h)
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Close session", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -649,7 +669,7 @@ clicon_rpc_kill_session(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Kill session", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -683,7 +703,7 @@ clicon_rpc_validate(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -715,7 +735,7 @@ clicon_rpc_commit(clicon_handle h)
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -747,7 +767,7 @@ clicon_rpc_discard_changes(clicon_handle h)
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Discard changes", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -792,7 +812,7 @@ clicon_rpc_create_subscription(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Create subscription", xerr);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -827,11 +847,11 @@ clicon_rpc_debug(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error("Debug",xerr);
|
||||
goto done;
|
||||
}
|
||||
if (xpath_first(xret, "//rpc-reply/ok") == NULL){
|
||||
if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){
|
||||
clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -514,7 +514,7 @@ stream_notify1(clicon_handle h,
|
|||
else{ /* xpath match */
|
||||
if (ss->ss_xpath == NULL ||
|
||||
strlen(ss->ss_xpath)==0 ||
|
||||
xpath_first(xevent, "%s", ss->ss_xpath) != NULL)
|
||||
xpath_first(xevent, NULL, "%s", ss->ss_xpath) != NULL)
|
||||
if ((*ss->ss_fn)(h, 0, xevent, ss->ss_arg) < 0)
|
||||
goto done;
|
||||
ss = NEXTQ(struct stream_subscription *, ss);
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#include "clixon_xml_map.h" /* xml_spec_populate */
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
|
||||
/*
|
||||
* Constants
|
||||
|
|
@ -130,7 +131,7 @@ struct xml{
|
|||
reference, dont free */
|
||||
cg_var *x_cv; /* Cached value as cligen variable
|
||||
(eg xml_cmp) */
|
||||
char *x_ns_cache; /* Cached namespace */
|
||||
cvec *x_ns_cache; /* Cached vector of namespaces */
|
||||
int _x_vector_i; /* internal use: xml_child_each */
|
||||
int _x_i; /* internal use for sorting:
|
||||
see xml_enumerate and xml_cmp */
|
||||
|
|
@ -229,6 +230,47 @@ xml_prefix_set(cxobj *xn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Get cached namespace
|
||||
* @param[in] x XML node
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @retval ns Cached namespace
|
||||
* @retval NULL No namespace found (not cached or not found)
|
||||
* @note may want to distinguish between not set cache and no namespace?
|
||||
*/
|
||||
static char*
|
||||
nscache_get(cxobj *x,
|
||||
char *prefix)
|
||||
{
|
||||
if (x->x_ns_cache != NULL)
|
||||
return xml_nsctx_get(x->x_ns_cache, prefix);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Set cached namespace. Replace if necessary
|
||||
* @param[in] x XML node
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
nscache_set(cxobj *x,
|
||||
char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (x->x_ns_cache == NULL){
|
||||
if ((x->x_ns_cache = xml_nsctx_init(prefix, namespace)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
return xml_nsctx_set(x->x_ns_cache, prefix, namespace);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Given an xml tree return URI namespace recursively : default or localname given
|
||||
*
|
||||
* Given an XML tree and a prefix (or NULL) return URI namespace.
|
||||
|
|
@ -238,7 +280,7 @@ xml_prefix_set(cxobj *xn,
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see xmlns_check XXX can these be merged?
|
||||
* @see xml2ns_set cache is set
|
||||
* @see xmlns_set cache is set
|
||||
* @note, this function uses a cache.
|
||||
*/
|
||||
int
|
||||
|
|
@ -250,13 +292,13 @@ xml2ns(cxobj *x,
|
|||
char *ns = NULL;
|
||||
cxobj *xp;
|
||||
|
||||
if ((ns = x->x_ns_cache) != NULL)
|
||||
if ((ns = nscache_get(x, prefix)) != NULL)
|
||||
goto ok;
|
||||
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
|
||||
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
|
||||
else /* xmlns="<uri>" */
|
||||
else{ /* xmlns="<uri>" */
|
||||
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
|
||||
|
||||
}
|
||||
/* namespace not found, try parent */
|
||||
if (ns == NULL){
|
||||
if ((xp = xml_parent(x)) != NULL){
|
||||
|
|
@ -264,15 +306,15 @@ xml2ns(cxobj *x,
|
|||
goto done;
|
||||
}
|
||||
/* If no parent, return default namespace if defined */
|
||||
#if defined(DEFAULT_XML_RPC_NAMESPACE)
|
||||
#ifdef USE_NETCONF_NS_AS_DEFAULT
|
||||
else
|
||||
ns = DEFAULT_XML_RPC_NAMESPACE;
|
||||
ns = NETCONF_BASE_NAMESPACE;
|
||||
#endif
|
||||
}
|
||||
if (ns && (x->x_ns_cache = strdup(ns)) == NULL){
|
||||
clicon_err(OE_XML, errno, "strdup");
|
||||
/* Set default namespace cache (since code is at this point,
|
||||
* no cache was found */
|
||||
if (ns && nscache_set(x, prefix, ns) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
if (namespace)
|
||||
*namespace = ns;
|
||||
|
|
@ -311,12 +353,8 @@ xmlns_set(cxobj *x,
|
|||
if (xml_value_set(xa, ns) < 0)
|
||||
goto done;
|
||||
/* (re)set namespace cache (as used in xml2ns) */
|
||||
if (x->x_ns_cache)
|
||||
free(x->x_ns_cache);
|
||||
if ((x->x_ns_cache = strdup(ns)) == NULL){
|
||||
clicon_err(OE_XML, errno, "strdup");
|
||||
if (ns && nscache_set(x, prefix, ns) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1406,7 +1444,7 @@ xml_free(cxobj *x)
|
|||
if (x->x_cv)
|
||||
cv_free(x->x_cv);
|
||||
if (x->x_ns_cache)
|
||||
free(x->x_ns_cache);
|
||||
xml_nsctx_free(x->x_ns_cache);
|
||||
free(x);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xml_changelog.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
|
|
@ -77,6 +78,7 @@ static int
|
|||
changelog_rename(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
cvec *nsc,
|
||||
char *tag)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -87,7 +89,7 @@ changelog_rename(clicon_handle h,
|
|||
clicon_err(OE_XML, 0, "tag required");
|
||||
goto done;
|
||||
}
|
||||
if (xpath_vec_ctx(xw, tag, &xctx) < 0)
|
||||
if (xpath_vec_ctx(xw, nsc, tag, &xctx) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xctx, &str) < 0)
|
||||
goto done;
|
||||
|
|
@ -192,12 +194,13 @@ static int
|
|||
changelog_move(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
cvec *nsc,
|
||||
char *dst)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xp; /* destination parent node */
|
||||
|
||||
if ((xp = xpath_first(xt, "%s", dst)) == NULL){
|
||||
if ((xp = xpath_first(xt, nsc, "%s", dst)) == NULL){
|
||||
clicon_err(OE_XML, 0, "path required");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -235,7 +238,11 @@ changelog_op(clicon_handle h,
|
|||
int ret;
|
||||
xp_ctx *xctx = NULL;
|
||||
int i;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* Get namespace context from changelog item */
|
||||
if (xml_nsctx_node(xi, &nsc) < 0)
|
||||
goto done;
|
||||
if ((op = xml_find_body(xi, "op")) == NULL)
|
||||
goto ok;
|
||||
/* get common variables that may be used in the operations below */
|
||||
|
|
@ -246,13 +253,13 @@ changelog_op(clicon_handle h,
|
|||
if ((wxpath = xml_find_body(xi, "where")) == NULL)
|
||||
goto ok;
|
||||
/* Get vector of target nodes meeting the where requirement */
|
||||
if (xpath_vec(xt, "%s", &wvec, &wlen, wxpath) < 0)
|
||||
if (xpath_vec(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0)
|
||||
goto done;
|
||||
for (i=0; i<wlen; i++){
|
||||
xw = wvec[i];
|
||||
/* If 'when' exists and is false, skip this target */
|
||||
if (whenxpath){
|
||||
if (xpath_vec_ctx(xw, whenxpath, &xctx) < 0)
|
||||
if (xpath_vec_ctx(xw, nsc, whenxpath, &xctx) < 0)
|
||||
goto done;
|
||||
if ((ret = ctx2boolean(xctx)) < 0)
|
||||
goto done;
|
||||
|
|
@ -265,7 +272,7 @@ changelog_op(clicon_handle h,
|
|||
}
|
||||
/* Now switch on operation */
|
||||
if (strcmp(op, "rename") == 0){
|
||||
ret = changelog_rename(h, xt, xw, tag);
|
||||
ret = changelog_rename(h, xt, xw, nsc, tag);
|
||||
}
|
||||
else if (strcmp(op, "replace") == 0){
|
||||
ret = changelog_replace(h, xt, xw, xnew);
|
||||
|
|
@ -277,7 +284,7 @@ changelog_op(clicon_handle h,
|
|||
ret = changelog_delete(h, xt, xw);
|
||||
}
|
||||
else if (strcmp(op, "move") == 0){
|
||||
ret = changelog_move(h, xt, xw, dst);
|
||||
ret = changelog_move(h, xt, xw, nsc, dst);
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_XML, 0, "Unknown operation: %s", op);
|
||||
|
|
@ -288,10 +295,11 @@ changelog_op(clicon_handle h,
|
|||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (wvec)
|
||||
free(wvec);
|
||||
if (xctx)
|
||||
|
|
@ -320,7 +328,7 @@ changelog_iterate(clicon_handle h,
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
if (xpath_vec(xch, "step", &vec, &veclen) < 0)
|
||||
if (xpath_vec(xch, NULL, "step", &vec, &veclen) < 0)
|
||||
goto done;
|
||||
/* Iterate through changelog items */
|
||||
for (i=0; i<veclen; i++){
|
||||
|
|
@ -384,7 +392,7 @@ xml_changelog_upgrade(clicon_handle h,
|
|||
* - find all changelogs in the interval: [from, to]
|
||||
* - note it t=0 then no changelog is applied
|
||||
*/
|
||||
if (xpath_vec(xchlog, "changelog[namespace=\"%s\"]",
|
||||
if (xpath_vec(xchlog, NULL, "changelog[namespace=\"%s\"]",
|
||||
&vec, &veclen, namespace) < 0)
|
||||
goto done;
|
||||
/* Get all changelogs in the interval [from,to]*/
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_log.h"
|
||||
|
|
@ -252,6 +253,14 @@ xml2cli(FILE *f,
|
|||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
* From rfc7950 Sec 9.9.2
|
||||
* The "path" XPath expression is evaluated in the following context,
|
||||
* in addition to the definition in Section 6.4.1:
|
||||
* o If the "path" statement is defined within a typedef, the context
|
||||
* node is the leaf or leaf-list node in the data tree that
|
||||
* references the typedef.
|
||||
* o Otherwise, the context node is the node in the data tree for which
|
||||
* the "path" statement is defined.
|
||||
*/
|
||||
static int
|
||||
validate_leafref(cxobj *xt,
|
||||
|
|
@ -266,6 +275,7 @@ validate_leafref(cxobj *xt,
|
|||
size_t xlen = 0;
|
||||
char *leafrefbody;
|
||||
char *leafbody;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
if ((leafrefbody = xml_body(xt)) == NULL)
|
||||
goto ok;
|
||||
|
|
@ -274,7 +284,10 @@ validate_leafref(cxobj *xt,
|
|||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (xpath_vec(xt, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
|
||||
/* XXX see comment above regarding typeref or not */
|
||||
if (xml_nsctx_yang(ytype, &nsc) < 0)
|
||||
goto done;
|
||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
|
||||
goto done;
|
||||
for (i = 0; i < xlen; i++) {
|
||||
x = xvec[i];
|
||||
|
|
@ -291,6 +304,8 @@ validate_leafref(cxobj *xt,
|
|||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
|
|
@ -1231,7 +1246,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
if (yc->ys_keyword != Y_MUST)
|
||||
continue;
|
||||
xpath = yc->ys_argument; /* "must" has xpath argument */
|
||||
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
|
||||
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
|
||||
|
|
@ -1244,7 +1259,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
|
||||
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
|
||||
xpath = yc->ys_argument; /* "when" has xpath argument */
|
||||
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
|
||||
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
|
|
@ -1768,7 +1783,6 @@ yang2api_path_fmt(yang_stmt *ys,
|
|||
* @param[in] cvv cligen variable vector, one for every wildchar in
|
||||
* api_path_fmt
|
||||
* @param[out] api_path api_path, eg /aaa/17. Free after use
|
||||
* @param[out] yang_arg yang-stmt argument name. Free after use
|
||||
* @note first and last elements of cvv are not used,..
|
||||
* @see api_path_fmt2xpath
|
||||
* @example
|
||||
|
|
@ -1866,7 +1880,9 @@ api_path_fmt2api_path(char *api_path_fmt,
|
|||
* api_path_fmt: /subif-entry=%s,%s/subid
|
||||
* cvv: foo
|
||||
* xpath: /subif-entry[if-name=foo]/subid"
|
||||
*
|
||||
* @example
|
||||
* api_path_fmt: /a:b/c
|
||||
* xpath : /b/c prefix:a
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||
*/
|
||||
int
|
||||
|
|
@ -2259,13 +2275,14 @@ xml_spec_populate(cxobj *x,
|
|||
/*! Translate from restconf api-path in cvv form to xml xpath
|
||||
* eg a/b=c -> a/[b=c]
|
||||
* eg example:a/b -> ex:a/b
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] cvv api-path as cvec
|
||||
* @param[in] offset Offset of cvec, where api-path starts
|
||||
* @param[out] xpath The xpath as cbuf variable string, must be initializeed
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
* @param[in] api_path api-path as cvec
|
||||
* @param[in] offset Offset of cvec, where api-path starts
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in,out] xpath The xpath as cbuf (must be created and may have content)
|
||||
* @param[out] namespace Namespace of xpath (direct pointer don't free)
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
*
|
||||
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||
* @note Not proper namespace translation from api-path 2 xpath
|
||||
|
|
@ -2279,7 +2296,7 @@ xml_spec_populate(cxobj *x,
|
|||
* cvec *cvv = NULL;
|
||||
* if (str2cvec("www.foo.com/restconf/a/b=c", '/', '=', &cvv) < 0)
|
||||
* err;
|
||||
* if ((ret = api_path2xpath(yspec, cvv, 0, cxpath)) < 0)
|
||||
* if ((ret = api_path2xpath(yspec, cvv, 0, cxpath, NULL)) < 0)
|
||||
* err;
|
||||
* if (ret == 0){
|
||||
* ... access error string in clicon_err_reason
|
||||
|
|
@ -2293,10 +2310,11 @@ xml_spec_populate(cxobj *x,
|
|||
* @see api_path2xml For api-path to xml tree
|
||||
*/
|
||||
int
|
||||
api_path2xpath(yang_stmt *yspec,
|
||||
cvec *cvv,
|
||||
int offset,
|
||||
cbuf *xpath)
|
||||
api_path2xpath_cvv(cvec *api_path,
|
||||
int offset,
|
||||
yang_stmt *yspec,
|
||||
cbuf *xpath,
|
||||
char **namespace)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -2306,13 +2324,13 @@ api_path2xpath(yang_stmt *yspec,
|
|||
char *name = NULL;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
yang_stmt *y = NULL;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *ymod = NULL;
|
||||
char *val;
|
||||
char *v;
|
||||
cg_var *cvi;
|
||||
|
||||
for (i=offset; i<cvec_len(cvv); i++){
|
||||
cv = cvec_i(cvv, i);
|
||||
for (i=offset; i<cvec_len(api_path); i++){
|
||||
cv = cvec_i(api_path, i);
|
||||
nodeid = cv_name_get(cv);
|
||||
if (nodeid_split(nodeid, &prefix, &name) < 0)
|
||||
goto done;
|
||||
|
|
@ -2368,6 +2386,9 @@ api_path2xpath(yang_stmt *yspec,
|
|||
name = NULL;
|
||||
}
|
||||
} /* for */
|
||||
/* return values: yang module */
|
||||
if (namespace)
|
||||
*namespace = yang_find_mynamespace(ymod);
|
||||
retval = 1; /* OK */
|
||||
done:
|
||||
if (prefix)
|
||||
|
|
@ -2380,6 +2401,48 @@ api_path2xpath(yang_stmt *yspec,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Translate from restconf api-path to xml xpath as cbuf and yang module
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||
* @retval -1 Fatal error, clicon_err called
|
||||
*/
|
||||
int
|
||||
api_path2xpath(char *api_path,
|
||||
yang_stmt *yspec,
|
||||
char **xpathp,
|
||||
char **namespace)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *cvv = NULL; /* api-path vector */
|
||||
cbuf *xpath = NULL; /* xpath as cbuf (sub-function uses that) */
|
||||
|
||||
/* Split api-path into cligen variable vector */
|
||||
if (str2cvec(api_path, '/', '=', &cvv) < 0)
|
||||
goto done;
|
||||
if ((xpath = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((retval = api_path2xpath_cvv(cvv, 0, yspec, xpath, namespace)) < 0)
|
||||
goto done;
|
||||
if (retval == 0)
|
||||
goto fail;
|
||||
/* prepare output xpath parameter */
|
||||
if (xpathp)
|
||||
if ((*xpathp = strdup(cbuf_get(xpath))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
if (xpath)
|
||||
cbuf_free(xpath);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0; /* Validation failed */
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create xml tree from api-path as vector
|
||||
* @param[in] vec APIpath as char* vector
|
||||
* @param[in] nvec Length of vec
|
||||
|
|
@ -2711,7 +2774,7 @@ xml2xpath(cxobj *x,
|
|||
xt = xml_parent(xt);
|
||||
xcp = xml_parent(xt);
|
||||
xml_parent_set(xt, NULL);
|
||||
x2 = xpath_first(xt, "%s", xpath); /* +1: skip first / */
|
||||
x2 = xpath_first(xt, NULL, "%s", xpath); /* +1: skip first / */
|
||||
xml_parent_set(xt, xcp);
|
||||
assert(x2 && x==x2);
|
||||
if (x==x2)
|
||||
|
|
|
|||
355
lib/src/clixon_xml_nsctx.c
Normal file
355
lib/src/clixon_xml_nsctx.c
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 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 *****
|
||||
|
||||
* XML support functions.
|
||||
* @see https://www.w3.org/TR/2009/REC-xml-names-20091208
|
||||
* An xml namespace context is a cligen variable vector containing a list of
|
||||
* <prefix,namespace> pairs.
|
||||
* It is encoded in a cvv as a list of string values, where the c name is the
|
||||
* prefix and the string values are the namespace URI.
|
||||
* The default namespace is decoded as having the name NULL
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clixon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
|
||||
/*! Get namespace given prefix (or NULL for default) from namespace context
|
||||
* @param[in] cvv Namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @retval ns Cached namespace
|
||||
* @retval NULL No namespace found (not cached or not found)
|
||||
*/
|
||||
char*
|
||||
xml_nsctx_get(cvec *cvv,
|
||||
char *prefix)
|
||||
{
|
||||
cg_var *cv;
|
||||
|
||||
if ((cv = cvec_find(cvv, prefix)) != NULL)
|
||||
return cv_string_get(cv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Reverse get prefix given namespace
|
||||
* @param[in] cvv Namespace context
|
||||
* @param[in] ns Namespace
|
||||
* @param[out] prefix Prefix (direct pointer)
|
||||
* @retval 0 No prefix found
|
||||
* @retval 1 Prefix found
|
||||
* @note NULL is a valid prefix (default)
|
||||
*/
|
||||
int
|
||||
xml_nsctx_get_prefix(cvec *cvv,
|
||||
char *namespace,
|
||||
char **prefix)
|
||||
{
|
||||
cg_var *cv = NULL;
|
||||
char *ns = NULL;
|
||||
|
||||
while ((cv = cvec_each(cvv, cv)) != NULL){
|
||||
if ((ns = cv_string_get(cv)) != NULL &&
|
||||
strcmp(ns, namespace) == 0){
|
||||
*prefix = cv_name_get(cv); /* can be NULL */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
*prefix = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Set or replace namespace in namespace context
|
||||
* @param[in] cvv Namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
xml_nsctx_set(cvec *cvv,
|
||||
char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv;
|
||||
|
||||
if ((cv = cvec_find(cvv, prefix)) != NULL) /* found, replace that */
|
||||
cv_string_set(cv, namespace);
|
||||
else /* cvec exists, but not prefix */
|
||||
cvec_add_string(cvv, prefix, namespace);
|
||||
retval = 0;
|
||||
// done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create and initialize XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
* @code
|
||||
* cvec *nsc = NULL;
|
||||
* if ((nsc = xml_nsctx_init(NULL, "urn:example:example")) == NULL)
|
||||
* err;
|
||||
* ...
|
||||
* xml_nsctx_free(nsc);
|
||||
* @endcode
|
||||
* @see xml_nsctx_node Use namespace context of an existing XML node
|
||||
* @see xml_nsctx_free Free the reutned handle
|
||||
*/
|
||||
cvec *
|
||||
xml_nsctx_init(char *prefix,
|
||||
char *namespace)
|
||||
{
|
||||
cvec *cvv = NULL;
|
||||
|
||||
if ((cvv = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_nsctx_set(cvv, prefix, namespace) < 0)
|
||||
goto done;
|
||||
done:
|
||||
return cvv;
|
||||
}
|
||||
|
||||
static int
|
||||
xml_nsctx_node1(cxobj *xn,
|
||||
cvec *ncs)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xa = NULL;
|
||||
char *pf; /* prefix */
|
||||
char *nm; /* name */
|
||||
char *val; /* value */
|
||||
cxobj *xp; /* parent */
|
||||
|
||||
/* xmlns:t="<ns1>" prefix:xmlns, name:t
|
||||
* xmlns="<ns2>" prefix:NULL name:xmlns
|
||||
*/
|
||||
while ((xa = xml_child_each(xn, xa, CX_ATTR)) != NULL){
|
||||
pf = xml_prefix(xa);
|
||||
nm = xml_name(xa);
|
||||
if (pf == NULL){
|
||||
if (strcmp(nm, "xmlns")==0 && /* set default namespace context */
|
||||
xml_nsctx_get(ncs, NULL) == NULL){
|
||||
val = xml_value(xa);
|
||||
if (xml_nsctx_set(ncs, NULL, val) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (strcmp(pf, "xmlns")==0 && /* set prefixed namespace context */
|
||||
xml_nsctx_get(ncs, nm) == NULL){
|
||||
val = xml_value(xa);
|
||||
if (xml_nsctx_set(ncs, nm, val) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if ((xp = xml_parent(xn)) == NULL){
|
||||
#ifdef USE_NETCONF_NS_AS_DEFAULT
|
||||
/* If not default namespace defined, use the base netconf ns as default */
|
||||
if (xml_nsctx_get(ncs, NULL) == NULL)
|
||||
if (xml_nsctx_set(ncs, NULL, NETCONF_BASE_NAMESPACE) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
if (xml_nsctx_node1(xp, ncs) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create and initialize XML namespace from XML node context
|
||||
* Fully explore all prefix:namespace pairs from context of one node
|
||||
* @param[in] xn XML node
|
||||
* @param[out] ncp XML namespace context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *x; // must initialize
|
||||
* cvec *nsc = NULL;
|
||||
* if (xml_nsctx_node(x, &nsc) < 0)
|
||||
* err
|
||||
* ...
|
||||
* xml_nsctx_free(nsc)
|
||||
* @endcode
|
||||
* @see xml_nsctx_init
|
||||
* @see xml_nsctx_free Free the reutned handle
|
||||
*/
|
||||
int
|
||||
xml_nsctx_node(cxobj *xn,
|
||||
cvec **ncp)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *nc = NULL;
|
||||
|
||||
if ((nc = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml_nsctx_node1(xn, nc) < 0)
|
||||
goto done;
|
||||
*ncp = nc;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create and initialize XML namespace from Yang node
|
||||
* Primary use is Yang path statements, eg leafrefs and others
|
||||
* Fully explore all prefix:namespace pairs from context of one node
|
||||
* @param[in] xn XML node
|
||||
* @param[out] ncp XML namespace context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* yang_stmt *y; // must initialize
|
||||
* cvec *nsc = NULL;
|
||||
* if (xml_nsctx_yang(y, &nsc) < 0)
|
||||
* err
|
||||
* ...
|
||||
* xml_nsctx_free(nsc)
|
||||
* @endcode
|
||||
* @see RFC7950 Sections 6.4.1 (and 9.9.2?)
|
||||
*/
|
||||
int
|
||||
xml_nsctx_yang(yang_stmt *yn,
|
||||
cvec **ncp)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *nc = NULL;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ymod; /* yang main module/submodule node */
|
||||
yang_stmt *yp; /* yang prefix node */
|
||||
yang_stmt *ym; /* yang imported module */
|
||||
yang_stmt *yns; /* yang namespace */
|
||||
yang_stmt *y;
|
||||
char *name;
|
||||
char *namespace;
|
||||
char *prefix;
|
||||
char *mynamespace;
|
||||
char *myprefix;
|
||||
|
||||
if ((nc = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if ((myprefix = yang_find_myprefix(yn)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "My yang prefix not found");
|
||||
goto done;
|
||||
}
|
||||
if ((mynamespace = yang_find_mynamespace(yn)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "My yang namespace not found");
|
||||
goto done;
|
||||
}
|
||||
/* Add my prefix and default namespace (from real module) */
|
||||
if (xml_nsctx_set(nc, NULL, mynamespace) < 0)
|
||||
goto done;
|
||||
if (xml_nsctx_set(nc, myprefix, mynamespace) < 0)
|
||||
goto done;
|
||||
/* Find top-most module or sub-module and get prefixes from that */
|
||||
if ((ymod = ys_module(yn)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
yspec = yang_parent_get(ymod); /* Assume yspec exists */
|
||||
|
||||
/* Iterate over module and register all import prefixes
|
||||
*/
|
||||
y = NULL;
|
||||
while ((y = yn_each(ymod, y)) != NULL) {
|
||||
if (yang_keyword_get(y) == Y_IMPORT){
|
||||
if ((name = yang_argument_get(y)) == NULL)
|
||||
continue; /* Just skip - shouldnt happen) */
|
||||
if ((yp = yang_find(y, Y_PREFIX, NULL)) == NULL)
|
||||
continue;
|
||||
if ((prefix = yang_argument_get(yp)) == NULL)
|
||||
continue;
|
||||
if ((ym = yang_find(yspec, Y_MODULE, name)) == NULL)
|
||||
continue;
|
||||
if ((yns = yang_find(ym, Y_NAMESPACE, NULL)) == NULL)
|
||||
continue;
|
||||
if ((namespace = yang_argument_get(yns)) == NULL)
|
||||
continue;
|
||||
if (xml_nsctx_set(nc, prefix, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
*ncp = nc;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Free XML namespace context
|
||||
* @param[in] prefix Namespace prefix, or NULL for default
|
||||
* @param[in] namespace Cached namespace to set (assume non-null?)
|
||||
* @retval nsc Return namespace context in form of a cvec
|
||||
* @retval NULL Error
|
||||
*/
|
||||
int
|
||||
xml_nsctx_free(cvec *ncs)
|
||||
{
|
||||
cvec *cvv = (cvec*)ncs;
|
||||
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -32,6 +32,56 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
|
||||
*
|
||||
* Some notes on namespace extensions in Netconf/Yang
|
||||
* RFC6241 8.9.1
|
||||
* The set of namespace declarations are those in scope on the <filter> element.
|
||||
* <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
|
||||
* <get-config>
|
||||
* <filter xmlns:t="http://example.com/schema/1.2/config"
|
||||
* type="xpath"
|
||||
* select="/t:top/t:users/t:user[t:name='fred']"/>
|
||||
* </get-config>
|
||||
* We need to add namespace context to the cpath tree, typically in eval. How do
|
||||
* we do that?
|
||||
* One observation is that the namespace context is static, so it can not be a part
|
||||
* of the xpath-tree, which is context-dependent.
|
||||
* Best is to send it as a (read-only) parameter to the xp_eval family of functions
|
||||
* as an exlicit namespace context.
|
||||
* For that you need an API to get/set namespaces: clixon_xml_nscache.c?
|
||||
* Then you need to fix API functions and this is the real work:
|
||||
* - Replace all existing functions or create new?
|
||||
* - Expose explicit namespace parameter, or xml object, or default namespace?
|
||||
*
|
||||
* On namespaces and xpath
|
||||
* =======================
|
||||
* XPATHs may contain prefixes such as /if:a/if:b
|
||||
* XPATHs excecutes in a "namespace context" (nsc)
|
||||
* The namespace context is either:
|
||||
* (1) the same as the xml that is evaluated, typical for basic XML, or
|
||||
* (2) separate from the XML that is evaluated. typical netconf and yang.
|
||||
* 1. Same nsc as XML
|
||||
* This happens in base XML (not yang), where the nsc is given implicitly by
|
||||
* the XML being evaluated. In node comparisons (eg of ip:a) only name and
|
||||
* prefixes are compared.
|
||||
* XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
* XPATH: /if:a/ip:b
|
||||
* When you call an xpath function, then call it with nsc=NULL.
|
||||
* 2. Separate nsc.
|
||||
* This happens if the namespace context is independent from the XML. It can
|
||||
* happen for example in NETCONF GET using :xpath when the XML is not known
|
||||
* so that xpath and xml may use different prefixes for the same namespace.
|
||||
* In that case you cannot rely on the prefix but must compare namespaces.
|
||||
* The namespace context of the XML is given (by the XML), but the xpath
|
||||
* nsc must then be explicitly given in the xpath call.
|
||||
* Example:
|
||||
* XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
|
||||
* NETCONF:<get-config><filter select="/x:a/y:b"
|
||||
* xmlns:x="urn:example:if" xmlns:y="urn:example:ip/>
|
||||
* Here, x,y are prefixes used for two namespaces that are given by if,ip in
|
||||
* the xml. In this case, the namespaces (eg urn:example:if) must be compared
|
||||
* instead.
|
||||
* Another case is Yang path expressions.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
@ -62,6 +112,7 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_parse.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
|
|
@ -69,6 +120,9 @@
|
|||
/*
|
||||
* Variables
|
||||
*/
|
||||
/* XXX assert break if xpath dont match. Set to 0 if ypu want it to pass */
|
||||
int xpatherrordiff=1;
|
||||
|
||||
/* Mapping between XPATH operator string <--> int */
|
||||
const map_str2int xpopmap[] = {
|
||||
{"and", XO_AND},
|
||||
|
|
@ -141,8 +195,7 @@ xpath_tree_print(cbuf *cb,
|
|||
}
|
||||
|
||||
static int
|
||||
xpath_tree_free(
|
||||
xpath_tree *xs)
|
||||
xpath_tree_free(xpath_tree *xs)
|
||||
{
|
||||
if (xs->xs_s0)
|
||||
free(xs->xs_s0);
|
||||
|
|
@ -156,45 +209,118 @@ xpath_tree_free(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @retval -1 Error XXX: retval -1 not properly handled
|
||||
* @retval 0 No match
|
||||
* @retval 1 Match
|
||||
*/
|
||||
static int
|
||||
nodetest_eval_node(cxobj *x,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name1 = xml_name(x);
|
||||
char *prefix1 = xml_prefix(x);
|
||||
char *nsxml = NULL; /* xml body namespace */
|
||||
char *nsxpath = NULL; /* xpath context namespace */
|
||||
char *prefix2 = NULL;
|
||||
char *name2 = NULL;
|
||||
|
||||
/* Namespaces is s0, name is s1 */
|
||||
if (strcmp(xs->xs_s1, "*")==0)
|
||||
return 1;
|
||||
/* get namespace of xml tree */
|
||||
if (xml2ns(x, prefix1, &nsxml) < 0)
|
||||
goto done;
|
||||
prefix2 = xs->xs_s0;
|
||||
name2 = xs->xs_s1;
|
||||
/* Before going into namespaces, check name equality and filter out noteq */
|
||||
if (strcmp(name1, name2) != 0){
|
||||
retval = 0; /* no match */
|
||||
goto done;
|
||||
}
|
||||
/* here names are equal
|
||||
* Now look for namespaces
|
||||
* 1) prefix1 and prefix2 point to same namespace <<-- try this first
|
||||
* 2) prefix1 is equal to prefix2 <<-- then try this
|
||||
* (1) is strict yang xml
|
||||
* (2) without yang
|
||||
*/
|
||||
if (nsc != NULL) { /* solution (1) */
|
||||
nsxpath = xml_nsctx_get(nsc, prefix2);
|
||||
if (nsxml != NULL && nsxpath != NULL)
|
||||
retval = (strcmp(nsxml, nsxpath) == 0);
|
||||
else
|
||||
retval = (nsxml == nsxpath); /* True only if both are NULL */
|
||||
}
|
||||
else{ /* solution (2) */
|
||||
if (prefix1 == NULL && prefix2 == NULL)
|
||||
retval = 1;
|
||||
else if (prefix1 == NULL || prefix2 == NULL)
|
||||
retval = 0;
|
||||
else
|
||||
retval = strcmp(prefix1, prefix2) == 0;
|
||||
}
|
||||
/* If retval == 0 here, then there is name match, but not ns match */
|
||||
if (retval == 0){
|
||||
fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
|
||||
name1, nsxml,
|
||||
name2, nsxpath);
|
||||
if (xpatherrordiff)
|
||||
assert(retval == 1);
|
||||
}
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Make a nodetest
|
||||
* @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
|
||||
* @retval 0 Match
|
||||
* @retval 1 No match
|
||||
* @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @retval -1 Error
|
||||
* @retval 0 No match
|
||||
* @retval 1 Match
|
||||
* - node() is true for any node of any type whatsoever.
|
||||
* - text() is true for any text node.
|
||||
*/
|
||||
static int
|
||||
nodetest_eval(cxobj *x,
|
||||
xpath_tree *xs)
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = 0; /* NB: no match is default (not error) */
|
||||
char *fn;
|
||||
|
||||
if (xs->xs_type == XP_NODE){
|
||||
/* Namespaces is s0, name is s1 */
|
||||
if (strcmp(xs->xs_s1, "*")==0)
|
||||
return 1;
|
||||
else if (strcmp(xml_name(x), xs->xs_s1)==0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
if (xs->xs_type == XP_NODE)
|
||||
retval = nodetest_eval_node(x, xs, nsc);
|
||||
else if (xs->xs_type == XP_NODE_FN){
|
||||
fn = xs->xs_s0;
|
||||
if (strcmp(fn, "node")==0)
|
||||
return 1;
|
||||
retval = 1;
|
||||
else if (strcmp(fn, "text")==0)
|
||||
return 1;
|
||||
retval = 1;
|
||||
}
|
||||
return 0;
|
||||
/* note, retval set by previous statement */
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] xn
|
||||
* @param[in] nodetest XPATH stack
|
||||
* @param[in] node_type
|
||||
* @param[in] flags
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[out] vec0
|
||||
* @param[out] vec0len
|
||||
*/
|
||||
int
|
||||
nodetest_recursive(cxobj *xn,
|
||||
nodetest_recursive(cxobj *xn,
|
||||
xpath_tree *nodetest,
|
||||
int node_type,
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
int node_type,
|
||||
uint16_t flags,
|
||||
cvec *nsc,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xsub;
|
||||
|
|
@ -203,14 +329,14 @@ nodetest_recursive(cxobj *xn,
|
|||
|
||||
xsub = NULL;
|
||||
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
|
||||
if (nodetest_eval(xsub, nodetest) == 1){
|
||||
if (nodetest_eval(xsub, nodetest, nsc) == 1){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
|
||||
if (flags==0x0 || xml_flag(xsub, flags))
|
||||
if (cxvec_append(xsub, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
// continue; /* Dont go deeper */
|
||||
}
|
||||
if (nodetest_recursive(xsub, nodetest, node_type, flags, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -220,12 +346,13 @@ nodetest_recursive(cxobj *xn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
|
||||
static int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
|
||||
|
||||
/*! Evaluate xpath step rule of an XML tree
|
||||
*
|
||||
* @param[in] xc0 Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[out] xrp Resulting context
|
||||
*
|
||||
* - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
|
||||
|
|
@ -237,6 +364,7 @@ static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
|
|||
static int
|
||||
xp_eval_step(xp_ctx *xc0,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -263,7 +391,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
if (xc->xc_descendant){
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
xc->xc_descendant = 0;
|
||||
|
|
@ -280,7 +408,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
/* xs->xs_c0 is nodetest */
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest))
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1)
|
||||
if (cxvec_append(x, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -292,7 +420,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
case A_DESCENDANT_OR_SELF:
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, &vec, &veclen) < 0)
|
||||
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
|
|
@ -331,7 +459,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
break;
|
||||
}
|
||||
if (xs->xs_c1){
|
||||
if (xp_eval(xc, xs->xs_c1, xrp) < 0)
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
|
|
@ -351,6 +479,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
* pred -> pred expr
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[out] xrp Resulting context
|
||||
*
|
||||
* A predicate filters a node-set with respect to an axis to produce a new
|
||||
|
|
@ -371,6 +500,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
static int
|
||||
xp_eval_predicate(xp_ctx *xc,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -386,7 +516,7 @@ xp_eval_predicate(xp_ctx *xc,
|
|||
goto done;
|
||||
}
|
||||
else{ /* eval previous predicates */
|
||||
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xs->xs_c1){
|
||||
|
|
@ -415,7 +545,7 @@ xp_eval_predicate(xp_ctx *xc,
|
|||
* evaluated with that node as the context node */
|
||||
if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
|
||||
goto done;
|
||||
if (xp_eval(xcc, xs->xs_c1, &xrc) < 0)
|
||||
if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0)
|
||||
goto done;
|
||||
if (xcc)
|
||||
ctx_free(xcc);
|
||||
|
|
@ -834,13 +964,16 @@ xp_union(xp_ctx *xc1,
|
|||
* Each node in that set is used as a context node for the following step.
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xp_eval(xp_ctx *xc,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
xp_ctx **xrp)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
|
|
@ -880,12 +1013,12 @@ xp_eval(xp_ctx *xc,
|
|||
|
||||
break;
|
||||
case XP_STEP: /* XP_NODE is first argument -not called explicitly */
|
||||
if (xp_eval_step(xc, xs, xrp) < 0)
|
||||
if (xp_eval_step(xc, xs, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XP_PRED:
|
||||
if (xp_eval_predicate(xc, xs, xrp) < 0)
|
||||
if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
|
|
@ -895,7 +1028,7 @@ xp_eval(xp_ctx *xc,
|
|||
/* Eval first child c0
|
||||
*/
|
||||
if (xs->xs_c0){
|
||||
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Actions between first and second child
|
||||
|
|
@ -973,7 +1106,7 @@ xp_eval(xp_ctx *xc,
|
|||
* Note, some operators like locationpath, need transitive context (use_xr0)
|
||||
*/
|
||||
if (xs->xs_c1)
|
||||
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, &xr1) < 0)
|
||||
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0)
|
||||
goto done;
|
||||
/* Actions after second child
|
||||
*/
|
||||
|
|
@ -1032,19 +1165,20 @@ xp_eval(xp_ctx *xc,
|
|||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
return retval;
|
||||
}
|
||||
} /* xp_eval */
|
||||
|
||||
/*! Given XML tree and xpath, returns xpath context
|
||||
/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
|
||||
* This is a raw form of xpath where you can do type conversion, etc,
|
||||
* not just a nodeset.
|
||||
* @param[in] xcur XML-tree where to search
|
||||
* @param[in] xpath String with XPATH 1.0 syntax
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[out] xrp Return XPATH context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* xp_ctx *xc = NULL;
|
||||
* if (xpath_vec_ctx(x, xpath, &xc) < 0)
|
||||
* if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
|
||||
* err;
|
||||
* if (xc)
|
||||
* ctx_free(xc);
|
||||
|
|
@ -1052,6 +1186,7 @@ xp_eval(xp_ctx *xc,
|
|||
*/
|
||||
int
|
||||
xpath_vec_ctx(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *xpath,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
|
|
@ -1084,7 +1219,7 @@ xpath_vec_ctx(cxobj *xcur,
|
|||
xc.xc_initial = xcur;
|
||||
if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0)
|
||||
goto done;
|
||||
if (xp_eval(&xc, xy.xy_top, xrp) < 0)
|
||||
if (xp_eval(&xc, xy.xy_top, nsc, xrp) < 0)
|
||||
goto done;
|
||||
if (xc.xc_nodeset){
|
||||
free(xc.xc_nodeset);
|
||||
|
|
@ -1102,23 +1237,27 @@ xpath_vec_ctx(cxobj *xcur,
|
|||
|
||||
/*! Xpath nodeset function where only the first matching entry is returned
|
||||
* args:
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @retval xml-tree of first match
|
||||
* @retval NULL Error or not found
|
||||
* @param[in] xcur XML tree where to search
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] format string with XPATH syntax
|
||||
* @retval xml-tree XML tree of first match
|
||||
* @retval NULL Error or not found
|
||||
*
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
|
||||
* cvec *nsc; // namespace context
|
||||
* if ((x = xpath_first(xtop, nsc, "//symbol/foo")) != NULL) {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
* @note the returned pointer points into the original tree so should not be freed fter use.
|
||||
* @note return value does not see difference between error and not found
|
||||
* @see also xpath_vec.
|
||||
* @experimental
|
||||
*/
|
||||
cxobj *
|
||||
xpath_first(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
|
|
@ -1144,9 +1283,8 @@ xpath_first(cxobj *xcur,
|
|||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
|
||||
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
|
||||
cx = xr->xc_nodeset[0];
|
||||
done:
|
||||
|
|
@ -1157,18 +1295,21 @@ xpath_first(cxobj *xcur,
|
|||
return cx;
|
||||
}
|
||||
|
||||
|
||||
/*! Given XML tree and xpath, returns nodeset as xml node vector
|
||||
* If result is not nodeset, return empty nodeset
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath stdarg string with XPATH 1.0 syntax
|
||||
* @param[in] format stdarg string with XPATH 1.0 syntax
|
||||
* @param[in] nsc XML Namespace context for XPATH
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cvec *nsc; // namespace context
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
|
|
@ -1176,9 +1317,11 @@ xpath_first(cxobj *xcur,
|
|||
* }
|
||||
* free(vec);
|
||||
* @endcode
|
||||
* @note Namespace prefix checking is not properly implemented
|
||||
*/
|
||||
int
|
||||
xpath_vec(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
cxobj ***vec,
|
||||
size_t *veclen,
|
||||
|
|
@ -1208,7 +1351,7 @@ xpath_vec(cxobj *xcur,
|
|||
va_end(ap);
|
||||
*vec=NULL;
|
||||
*veclen = 0;
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr && xr->xc_type == XT_NODESET){
|
||||
*vec = xr->xc_nodeset;
|
||||
|
|
@ -1227,6 +1370,7 @@ xpath_vec(cxobj *xcur,
|
|||
/* Xpath that returns a vector of matches (only nodes marked with flags)
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
|
|
@ -1235,7 +1379,8 @@ xpath_vec(cxobj *xcur,
|
|||
* @code
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* cvec *nsc; // namespace context (not NULL)
|
||||
* if (xpath_vec_flag(xcur, nsc, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
|
|
@ -1249,8 +1394,9 @@ xpath_vec(cxobj *xcur,
|
|||
*/
|
||||
int
|
||||
xpath_vec_flag(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
uint16_t flags,
|
||||
uint16_t flags,
|
||||
cxobj ***vec,
|
||||
size_t *veclen,
|
||||
...)
|
||||
|
|
@ -1281,7 +1427,7 @@ xpath_vec_flag(cxobj *xcur,
|
|||
va_end(ap);
|
||||
*vec=NULL;
|
||||
*veclen = 0;
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr && xr->xc_type == XT_NODESET){
|
||||
for (i=0; i<xr->xc_size; i++){
|
||||
|
|
@ -1303,14 +1449,16 @@ xpath_vec_flag(cxobj *xcur,
|
|||
/*! Given XML tree and xpath, returns boolean
|
||||
* Returns true if the nodeset is non-empty
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath stdarg string with XPATH 1.0 syntax
|
||||
* @retval 1 True
|
||||
* @retval 0 False
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
xpath_vec_bool(cxobj *xcur,
|
||||
char *format,
|
||||
xpath_vec_bool(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1335,7 +1483,7 @@ xpath_vec_bool(cxobj *xcur,
|
|||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
|
||||
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
|
||||
goto done;
|
||||
if (xr)
|
||||
retval = ctx2boolean(xr);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
|
||||
* This file defines XPATH contexts using in traversing the XPATH parse tree.
|
||||
* This file defines XPATH contexts used in traversing the XPATH parse tree.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
|
||||
*/
|
||||
#ifndef _CLIXON_XPATH_PARSE_H_
|
||||
#define _CLIXON_XPATH_PARSE_H_
|
||||
|
|
@ -50,7 +51,7 @@ enum xp_type{
|
|||
XP_ABSPATH,
|
||||
XP_RELLOCPATH,
|
||||
XP_STEP,
|
||||
XP_NODE,
|
||||
XP_NODE, /* s0 is namespace prefix, s1 is name */
|
||||
XP_NODE_FN,
|
||||
XP_PRED,
|
||||
XP_PRI0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -789,12 +789,12 @@ yang_find_mynamespace(yang_stmt *ys)
|
|||
char *namespace = NULL;
|
||||
|
||||
if ((ymod = ys_real_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
clicon_err(OE_YANG, ENOENT, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL)
|
||||
goto done;
|
||||
namespace = ynamespace->ys_argument;
|
||||
namespace = yang_argument_get(ynamespace);
|
||||
done:
|
||||
return namespace;
|
||||
}
|
||||
|
|
@ -1176,17 +1176,17 @@ yang_find_module_by_prefix(yang_stmt *ys,
|
|||
}
|
||||
yimport = NULL;
|
||||
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
|
||||
if (yimport->ys_keyword != Y_IMPORT &&
|
||||
yimport->ys_keyword != Y_INCLUDE)
|
||||
if (yang_keyword_get(yimport) != Y_IMPORT &&
|
||||
yang_keyword_get(yimport) != Y_INCLUDE)
|
||||
continue;
|
||||
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||
strcmp(yang_argument_get(yprefix), prefix) == 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (yimport){
|
||||
if ((ymod = yang_find(yspec, Y_MODULE, yimport->ys_argument)) == NULL &&
|
||||
(ymod = yang_find(yspec, Y_SUBMODULE, yimport->ys_argument)) == NULL){
|
||||
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL &&
|
||||
(ymod = yang_find(yspec, Y_SUBMODULE, yang_argument_get(yimport))) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
|
||||
prefix);
|
||||
yimport = NULL;
|
||||
|
|
@ -1222,7 +1222,7 @@ yang_find_module_by_namespace(yang_stmt *yspec,
|
|||
return ymod;
|
||||
}
|
||||
|
||||
/*! Given a yang spec and a module name, return yang module
|
||||
/*! Given a yang spec and a module name, return yang module or submodule
|
||||
*
|
||||
* @param[in] yspec A yang specification
|
||||
* @param[in] name Name of module
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ struct yang_stmt{
|
|||
char *ys_argument; /* String / argument depending on keyword */
|
||||
int ys_flags; /* Flags according to YANG_FLAG_* above */
|
||||
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
|
||||
nodes can belong to other
|
||||
nodes can belong to other
|
||||
modules than the ancestor module */
|
||||
|
||||
char *ys_extra; /* For unknown */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -69,6 +69,7 @@
|
|||
#include "clixon_file.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_options.h"
|
||||
|
|
@ -253,6 +254,7 @@ yms_build(clicon_handle h,
|
|||
* @param[in] h Clicon handle
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] xpath XML Xpath
|
||||
* @param[in] nsc XML Namespace context for xpath
|
||||
* @param[in] brief Just name, revision and uri (no cache)
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @retval -1 Error (fatal)
|
||||
|
|
@ -281,6 +283,7 @@ int
|
|||
yang_modules_state_get(clicon_handle h,
|
||||
yang_stmt *yspec,
|
||||
char *xpath,
|
||||
cvec *nsc,
|
||||
int brief,
|
||||
cxobj **xret)
|
||||
{
|
||||
|
|
@ -302,7 +305,7 @@ yang_modules_state_get(clicon_handle h,
|
|||
/* xc is also original tree, need to copy it */
|
||||
if ((xw = xml_wrap(xc, "top")) == NULL)
|
||||
goto done;
|
||||
if (xpath_first(xw, "%s", xpath)){
|
||||
if (xpath_first(xw, NULL, "%s", xpath)){
|
||||
if ((x = xml_dup(xc)) == NULL) /* Make copy and use below */
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -334,7 +337,7 @@ yang_modules_state_get(clicon_handle h,
|
|||
if ((x = xml_wrap(x, "top")) < 0)
|
||||
goto done;
|
||||
/* extract xpath part of module-state tree */
|
||||
if (xpath_vec(x, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
if (xpath_vec(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
if (xvec != NULL){
|
||||
for (i=0; i<xlen; i++)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ stop_backend(){
|
|||
|
||||
# Wait for restconf to stop sending 502 Bad Gateway
|
||||
wait_backend(){
|
||||
reply=$(echo '<rpc message-id="101" xmlns="http://clicon.org/lib"><ping/></rpc>]]>]]>' | clixon_netconf -qef $cfg 2> /dev/null)
|
||||
reply=$(echo '<rpc message-id="101"><ping xmlns="http://clicon.org/lib"/></rpc>]]>]]>' | clixon_netconf -qef $cfg 2> /dev/null)
|
||||
let i=0;
|
||||
while [[ $reply != "<rpc-reply"* ]]; do
|
||||
sleep 1
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ new "cli configure ip type"
|
|||
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type ex:eth" 0 "^$"
|
||||
|
||||
new "cli show xpath description"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" 0 "<description>mydesc</description>"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces" 0 "<description>mydesc</description>"
|
||||
|
||||
new "cli delete description"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o delete interfaces interface eth/0/0 description mydesc" 0 ""
|
||||
|
||||
new "cli show xpath no description"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces" 0 "^$"
|
||||
|
||||
new "cli copy interface"
|
||||
expectfn "$clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99" 0 "^$"
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
# reply since the modules change so often
|
||||
new "netconf schema resource, RFC 7895"
|
||||
ret=$($clixon_netconf -qf $cfg<<EOF
|
||||
<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>
|
||||
<rpc><get><filter type="xpath" select="l:modules-state/l:module" xmlns:l="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>
|
||||
EOF
|
||||
)
|
||||
#echo $ret
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
#!/bin/bash
|
||||
# XML Insert unit test
|
||||
# XML Insert elements and test if they are sorted according to yang
|
||||
# First a list with 0-5 base elements, insert in different places
|
||||
# Second varying yangs: container, leaf, list, leaf-list, choice, user-order list
|
||||
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
: ${clixon_util_insert:=clixon_util_insert}
|
||||
|
||||
OPTS="-D $DBG -s"
|
||||
OPTS="-D $DBG"
|
||||
|
||||
APPNAME=example
|
||||
|
||||
|
|
@ -63,7 +62,7 @@ testrun(){
|
|||
err "length of retval is zero"
|
||||
fi
|
||||
# echo "rs:$rs"
|
||||
# echo "r0:$r0"
|
||||
# echo "r0:$r0"
|
||||
# Check they are equal
|
||||
if [[ "$r0" != "$rs" ]]; then
|
||||
err "$rs" "$r0"
|
||||
|
|
|
|||
|
|
@ -45,21 +45,29 @@ module example{
|
|||
}
|
||||
container default-address {
|
||||
leaf absname {
|
||||
description "Absolute references existing interfaces in if module";
|
||||
type leafref {
|
||||
path "/ip:interfaces/ip:interface/ip:name";
|
||||
path "/if:interfaces/if:interface/if:name";
|
||||
}
|
||||
}
|
||||
leaf relname {
|
||||
type leafref {
|
||||
path "../../interfaces/interface/name";
|
||||
path "../../if:interfaces/if:interface/if:name";
|
||||
}
|
||||
}
|
||||
leaf address {
|
||||
description "From RFC7950 9.9.5";
|
||||
type leafref {
|
||||
path "../../interfaces/interface[name = current()/../relname]"
|
||||
+ "/ipv4/address/ip";
|
||||
path "../../if:interfaces/if:interface[if:name = current()/../relname]"
|
||||
+ "/if:ipv4/if:address/if:ip";
|
||||
}
|
||||
}
|
||||
leaf wrong {
|
||||
description "References leading nowhere in yang";
|
||||
type leafref {
|
||||
path "/ip:interfaces/ip:interface/ip:name";
|
||||
}
|
||||
}
|
||||
}
|
||||
list sender{
|
||||
key name;
|
||||
|
|
@ -75,6 +83,36 @@ module example{
|
|||
}
|
||||
EOF
|
||||
|
||||
BASEXML=$(cat <<EOF
|
||||
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
<interface>
|
||||
<name>eth0</name>
|
||||
<type>ex:eth</type>
|
||||
<ipv4>
|
||||
<address>
|
||||
<ip>192.0.2.1</ip>
|
||||
<prefix-length>24</prefix-length>
|
||||
</address>
|
||||
<address>
|
||||
<ip>192.0.2.2</ip>
|
||||
<prefix-length>24</prefix-length>
|
||||
</address>
|
||||
</ipv4>
|
||||
</interface>
|
||||
<interface>
|
||||
<name>lo</name>
|
||||
<type>ex:lo</type>
|
||||
<ipv4>
|
||||
<address>
|
||||
<ip>127.0.0.1</ip>
|
||||
<prefix-length>32</prefix-length>
|
||||
</address>
|
||||
</ipv4>
|
||||
</interface>
|
||||
</interfaces>
|
||||
EOF
|
||||
)
|
||||
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
|
|
@ -91,33 +129,40 @@ if [ $BE -ne 0 ]; then
|
|||
fi
|
||||
|
||||
new "leafref base config"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name><type>ex:eth</type><ipv4><address><ip>192.0.2.1</ip><prefix-length>24</prefix-length></address><address><ip>192.0.2.2</ip><prefix-length>24</prefix-length></address></ipv4></interface><interface><name>lo</name><type>ex:lo</type><ipv4><address><ip>127.0.0.1</ip><prefix-length>32</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$BASEXML</config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "leafref get config"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' "^<rpc-reply><data>$BASEXML</data></rpc-reply>]]>]]>"
|
||||
|
||||
new "leafref base commit"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "leafref get config"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
||||
|
||||
new "leafref add wrong ref"
|
||||
new "leafref add non-existing ref"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "leafref validate"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
||||
#new "leafref wrong ref"
|
||||
#expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><wrong>eth3</wrong><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "leafref discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "leafref add correct absref"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><absname>eth0</absname></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "leafref validate (ok)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
||||
|
||||
new "leafref add correct relref"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><relname>eth0</relname></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
# XXX add address
|
||||
new "leafref validate (ok)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
||||
|
||||
new "leafref add correct address"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><address>192.0.2.1</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "leafref validate (ok)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ EOF
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
new "start backend -s startup -f $cfg"
|
||||
start_backend -s startup -f $cfg
|
||||
else
|
||||
new "Restart backend as eg follows: -Ff $cfg -s startup"
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ module nacm-example{
|
|||
EOF
|
||||
|
||||
cat <<EOF > $nacmfile
|
||||
<nacm>
|
||||
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||
<enable-nacm>true</enable-nacm>
|
||||
<read-default>permit</read-default>
|
||||
<write-default>deny</write-default>
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-ex
|
|||
'
|
||||
|
||||
new "admin read netconf ok"
|
||||
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/translate"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon" /></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "admin read element ok"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value": "val42"}
|
||||
|
|
@ -193,7 +193,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-e
|
|||
'
|
||||
|
||||
new "limit read netconf ok"
|
||||
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/translate"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><k>key42</k><value>val42</value></translate><translate xmlns="urn:example:clixon"><k>key43</k><value>val43</value></translate></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "limit read element ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value": "val42"}
|
||||
|
|
@ -217,7 +217,7 @@ new "guest read fail"
|
|||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
|
||||
new "guest read netconf fail"
|
||||
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/translate"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
||||
new "guest read element fail"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
|
||||
# Too many quotes, (single inside double inside single) need to fool bash
|
||||
cat <<EOF > $tmp # new
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name='eth/0/0']"/></get-config></rpc>]]>]]>
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/if:interfaces/if:interface[if:name='eth/0/0']" xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces"/></get-config></rpc>]]>]]>
|
||||
EOF
|
||||
|
||||
new "Check eth/0/0 added using xpath"
|
||||
|
|
@ -98,7 +98,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
|
||||
# Too many quotes
|
||||
cat <<EOF > $tmp # new
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name='eth1']/enabled"/></get-config></rpc>]]>]]>
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/if:interfaces/if:interface[if:name='eth1']/if:enabled" xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces"/></get-config></rpc>]]>]]>
|
||||
EOF
|
||||
|
||||
new "netconf get config xpath"
|
||||
|
|
@ -106,7 +106,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfa
|
|||
|
||||
# Too many quotes
|
||||
cat <<EOF > $tmp # new
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name='eth1']/enabled/../.."/></get-config></rpc>]]>]]>
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/if:interfaces/if:interface[if:name='eth1']/if:enabled/../.." xmlns:if="urn:ietf:params:xml:ns:yang:ietf-interfaces"/></get-config></rpc>]]>]]>
|
||||
EOF
|
||||
|
||||
new "netconf get config xpath parent"
|
||||
|
|
@ -162,7 +162,7 @@ new "netconf edit state operation should fail"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e0</name><oper-status>up</oper-status></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>State data not allowed</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf get state operation"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/interfaces\"/></get></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf lock/unlock"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ cat <<EOF > $fyang
|
|||
module order-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:order";
|
||||
prefix ex;
|
||||
prefix exo;
|
||||
import clixon-example { /* for state callback */
|
||||
prefix ex;
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ fi
|
|||
# STATE (should not be ordered)
|
||||
new "state data (should be unordered: 42,41,43)"
|
||||
cat <<EOF > $tmp
|
||||
<rpc><get><filter type="xpath" select="state"/></get></rpc>]]>]]>
|
||||
<rpc><get><filter type="xpath" select="ex:state" xmlns:ex="urn:example:clixon" /></get></rpc>]]>]]>
|
||||
EOF
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '<rpc-reply><data><state xmlns="urn:example:clixon"><op>42</op><op>41</op><op>43</op></state></data></rpc-reply>]]>]]>'
|
||||
|
||||
|
|
@ -177,16 +177,16 @@ new "verify running from start, should be: c,l,y0,y1,y2,y3; y1 and y3 sorted."
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><c xmlns="urn:example:order"><d>hej</d></c><l xmlns="urn:example:order">hopp</l><y0 xmlns="urn:example:order">d</y0><y0 xmlns="urn:example:order">b</y0><y0 xmlns="urn:example:order">c</y0><y0 xmlns="urn:example:order">a</y0><y1 xmlns="urn:example:order">a</y1><y1 xmlns="urn:example:order">b</y1><y1 xmlns="urn:example:order">c</y1><y1 xmlns="urn:example:order">d</y1><y2 xmlns="urn:example:order"><k>d</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>a</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>bar</a></y2><y3 xmlns="urn:example:order"><k>a</k><a>bar</a></y3><y3 xmlns="urn:example:order"><k>b</k><a>bar</a></y3><y3 xmlns="urn:example:order"><k>c</k><a>bar</a></y3><y3 xmlns="urn:example:order"><k>d</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "get each ordered-by user leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='a']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>a</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/exo:y2[exo:k='a']\" xmlns:exo=\"urn:example:order\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>a</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "get each ordered-by user leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='a']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:order"><k>a</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/exo:y3[exo:k='a']\" xmlns:exo=\"urn:example:order\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:order"><k>a</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "get each ordered-by user leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='b']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>b</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/exo:y2[exo:k='b']\" xmlns:exo=\"urn:example:order\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>b</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "get each ordered-by user leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='b']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:order"><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/exo:y3[exo:k='b']\" xmlns:exo=\"urn:example:order\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:order"><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "delete candidate"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -209,7 +209,7 @@ new "netconf commit"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "verify leaf-list user order in running (as entered: c,b,a,0)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source><filter type="xpath" select="/y0"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y0 xmlns="urn:example:order">c</y0><y0 xmlns="urn:example:order">b</y0><y0 xmlns="urn:example:order">a</y0><y0 xmlns="urn:example:order">0</y0></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source><filter type="xpath" select="/exo:y0" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y0 xmlns="urn:example:order">c</y0><y0 xmlns="urn:example:order">b</y0><y0 xmlns="urn:example:order">a</y0><y0 xmlns="urn:example:order">0</y0></data></rpc-reply>]]>]]>$'
|
||||
|
||||
# LISTS
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ new "add one entry to list user order"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order"><k>a</k><a>fie</a></y2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "verify list user order (as entered)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/y2"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>foo</a></y2><y2 xmlns="urn:example:order"><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:y2" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>foo</a></y2><y2 xmlns="urn:example:order"><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "Overwrite existing ordered-by user y2->c"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
|
|
@ -238,7 +238,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</y2></config></edit-config></rpc>]]>]]>'
|
||||
|
||||
new "Tests for no duplicates."
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/y2"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>c</k><a>newc</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>newb</a></y2><y2 xmlns="urn:example:order"><k>a</k><a>newa</a></y2></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:y2" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 xmlns="urn:example:order"><k>c</k><a>newc</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>newb</a></y2><y2 xmlns="urn:example:order"><k>a</k><a>newa</a></y2></data></rpc-reply>]]>]]>$'
|
||||
|
||||
#-- order by type rather than strings.
|
||||
# there are three leaf-lists:strings, ints, and decimal64, and two lists:
|
||||
|
|
@ -252,7 +252,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check string order (1,10,2)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/types/strings"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><strings>1</strings><strings>10</strings><strings>2</strings></types></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:types/exo:strings" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><strings>1</strings><strings>10</strings><strings>2</strings></types></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "put leaf-list int (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
|
|
@ -260,7 +260,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check leaf-list int order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/types/ints"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><ints>1</ints><ints>2</ints><ints>10</ints></types></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:types/exo:ints" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><ints>1</ints><ints>2</ints><ints>10</ints></types></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "put list int (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
|
|
@ -268,7 +268,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check list int order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/types/listints"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><listints><a>1</a></listints><listints><a>2</a></listints><listints><a>10</a></listints></types></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:types/exo:listints" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><listints><a>1</a></listints><listints><a>2</a></listints><listints><a>10</a></listints></types></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "put leaf-list decimal64 (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
|
|
@ -276,7 +276,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check leaf-list decimal64 order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/types/decs"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><decs>1.0</decs><decs>2.0</decs><decs>10.0</decs></types></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:types/exo:decs" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><decs>1.0</decs><decs>2.0</decs><decs>10.0</decs></types></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "put list decimal64 (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
|
|
@ -284,7 +284,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
|||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check list decimal64 order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/types/listdecs"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><listdecs><a>1.0</a></listdecs><listdecs><a>2.0</a></listdecs><listdecs><a>10.0</a></listdecs></types></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/exo:types/exo:listdecs" xmlns:exo="urn:example:order"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><types xmlns="urn:example:order"><listdecs><a>1.0</a></listdecs><listdecs><a>2.0</a></listdecs><listdecs><a>10.0</a></listdecs></types></data></rpc-reply>]]>]]>$'
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
|||
: ${format:=xml}
|
||||
|
||||
# Number of list/leaf-list entries in file
|
||||
: ${perfnr:=5000}
|
||||
: ${perfnr:=1000}
|
||||
|
||||
# Number of requests made get/put
|
||||
: ${perfreq:=100}
|
||||
|
|
@ -27,7 +27,7 @@ cat <<EOF > $fyang
|
|||
module scaling{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:clixon";
|
||||
prefix ip;
|
||||
prefix ex;
|
||||
container x {
|
||||
list y {
|
||||
key "a";
|
||||
|
|
@ -120,7 +120,7 @@ expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>
|
|||
new "netconf get $perfreq small config"
|
||||
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
||||
rnd=$(( ( RANDOM % $perfnr ) ))
|
||||
echo "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=$rnd][b=$rnd]\" /></get-config></rpc>]]>]]>"
|
||||
echo "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/ex:x/ex:y[ex:a=$rnd][ex:b=$rnd]\" xmlns:ex=\"urn:example:clixon\"/></get-config></rpc>]]>]]>"
|
||||
done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
|
||||
|
||||
# NETCONF add
|
||||
|
|
|
|||
|
|
@ -83,23 +83,22 @@ new "netconf commit large config"
|
|||
expecteof "/usr/bin/time -f %e $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# START actual tests
|
||||
|
||||
# Having a large db, get single entries many times
|
||||
# NETCONF get
|
||||
new "netconf get test single req"
|
||||
sel="/ietf-interfaces:interfaces/interface[name='e1']"
|
||||
msg="<rpc><get><filter type=\"xpath\" select=\"$sel\" /></get></rpc>]]>]]>"
|
||||
sel="/if:interfaces/if:interface[if:name='e1']"
|
||||
msg="<rpc><get><filter type=\"xpath\" select=\"$sel\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf get $perfreq single reqs"
|
||||
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
||||
rnd=$(( ( RANDOM % $perfnr ) ))
|
||||
sel="/ietf-interfaces:interfaces/interface[name='e$rnd']"
|
||||
echo "<rpc><get><filter type=\"xpath\" select=\"$sel\" /></get></rpc>]]>]]>"
|
||||
sel="/if:interfaces/if:interface[if:name='e$rnd']"
|
||||
echo "<rpc><get><filter type=\"xpath\" select=\"$sel\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>"
|
||||
done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
|
||||
|
||||
# RESTCONF get
|
||||
new "restconf get test single req"
|
||||
new "restconf get test single req XXX"
|
||||
expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface": [{"name": "e1","type": "ex:eth","enabled": true,"oper-status": "up"}]}
|
||||
'
|
||||
|
||||
|
|
@ -129,7 +128,7 @@ done } 2>&1 | awk '/real/ {print $2}'
|
|||
|
||||
# Get config in one large get
|
||||
new "netconf get large config"
|
||||
/usr/bin/time -f %e echo "<rpc><get> <filter type=\"xpath\" select=\"/ietf-interfaces:interfaces\" /></get></rpc>]]>]]>" | $clixon_netconf -qf $cfg > /tmp/netconf
|
||||
/usr/bin/time -f %e echo "<rpc><get> <filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>" | $clixon_netconf -qf $cfg > /tmp/netconf
|
||||
|
||||
new "restconf get large config"
|
||||
/usr/bin/time -f %e curl -sG http://localhost/restconf/data/ietf-interfaces:interfaces | wc
|
||||
|
|
|
|||
|
|
@ -129,10 +129,10 @@ wait_restconf
|
|||
new "1. Netconf RFC5277 stream testing"
|
||||
# 1.1 Stream discovery
|
||||
new "netconf event stream discovery RFC5277 Sec 3.2.5"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get><filter type="xpath" select="n:netconf/n:streams" xmlns:n="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
|
||||
|
||||
new "netconf event stream discovery RFC8040 Sec 6.2"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support><access><encoding>xml</encoding><location>https://localhost/streams/EXAMPLE</location></access></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get><filter type="xpath" select="r:restconf-state/r:streams" xmlns:r="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support><access><encoding>xml</encoding><location>https://localhost/streams/EXAMPLE</location></access></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
|
||||
|
||||
#
|
||||
# 1.2 Netconf stream subscription
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ EOF
|
|||
|
||||
# Type tests.
|
||||
# Parameters:
|
||||
# 1: dbcache true/false
|
||||
# 1: dbcache: cache, nocache, cache-zerocopy
|
||||
testrun(){
|
||||
dbcache=$1
|
||||
new "test params: -f $cfg # dbcache: $dbcache"
|
||||
|
|
@ -216,7 +216,7 @@ testrun(){
|
|||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||
<CLICON_XMLDB_CACHE>$dbcache</CLICON_XMLDB_CACHE>
|
||||
<CLICON_DATASTORE_CACHE>$dbcache</CLICON_DATASTORE_CACHE>
|
||||
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
|
@ -614,9 +614,12 @@ EOF
|
|||
}
|
||||
|
||||
# Run without db cache
|
||||
testrun false
|
||||
testrun nocache
|
||||
|
||||
# Run with db cache
|
||||
testrun true
|
||||
testrun cache
|
||||
|
||||
# Run with
|
||||
testrun cache-zerocopy
|
||||
|
||||
rm -rf $dir
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ EOF
|
|||
|
||||
# Changelog of example-a:
|
||||
cat <<EOF > $changelog
|
||||
<changelogs xmlns="http://clicon.org/xml-changelog">
|
||||
<changelogs xmlns="http://clicon.org/xml-changelog" xmlns:a="urn:example:a" xmlns:b="urn:example:b" >
|
||||
<changelog>
|
||||
<namespace>urn:example:b</namespace>
|
||||
<revfrom>2017-12-01</revfrom>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ new "xpath ../type='rt:static'"
|
|||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 "../type='rt:static'" "^bool:true$"
|
||||
|
||||
new "xpath rib-name != ../../name"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "rib-name != ../../name" "^bool:true$"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "rib-name != ../name" "^bool:true$"
|
||||
|
||||
new "xpath routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" "^bool:true$"
|
||||
|
|
|
|||
|
|
@ -193,16 +193,16 @@ new "netconf commit 2nd"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get config xpath"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2][c=5]\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y></x></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/c:x/c:y[c:a=1][c:b=2][c:c=5]\" xmlns:c=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y></x></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf edit leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/f/e"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/c:x/c:f/c:e" xmlns:c="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf get leaf-list path"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e='hej']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x xmlns=\"urn:example:clixon\"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/c:x/c:f[c:e='hej']\" xmlns:c=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x xmlns=\"urn:example:clixon\"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get (should be some)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x>'
|
||||
|
|
@ -211,7 +211,7 @@ new "cli set leaf-list"
|
|||
expectfn "$clixon_cli -1f $cfg set x f e foo" 0 ""
|
||||
|
||||
new "cli show leaf-list"
|
||||
expectfn "$clixon_cli -1f $cfg show xpath /x/f/e" 0 "<e>foo</e>"
|
||||
expectfn "$clixon_cli -1f $cfg show xpath /x/f/e urn:example:clixon" 0 "<e>foo</e>"
|
||||
|
||||
new "netconf set state data (not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><state xmlns="urn:example:clixon"><op>42</op></state></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>State data not allowed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||
|
|
@ -220,10 +220,10 @@ new "netconf set presence and not present"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get presence only"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/presence"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><presence/></x></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/c:x/c:presence" xmlns:c="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><presence/></x></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf get presence only"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/nopresence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/c:x/c:nopresence" xmlns:c="urn:example:clixon"/></get-config></rpc>]]>]]>' "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if ((xb = xpath_first(x0, "%s", xpath)) == NULL){
|
||||
if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){
|
||||
clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
if (xml_apply(xi, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if ((xi = xpath_first(xi, "%s", xpath)) == NULL){
|
||||
if ((xi = xpath_first(xi, NULL, "%s", xpath)) == NULL){
|
||||
clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath);
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,9 +191,10 @@ main(int argc, char **argv)
|
|||
fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If xpath0 given, position current x */
|
||||
if (xpath0){
|
||||
if ((x = xpath_first(x0, "%s", xpath0)) == NULL){
|
||||
if ((x = xpath_first(x0, NULL, "%s", xpath0)) == NULL){
|
||||
fprintf(stderr, "Error: xpath0 returned NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -201,8 +202,8 @@ main(int argc, char **argv)
|
|||
else
|
||||
x = x0;
|
||||
|
||||
/* Parse XML */
|
||||
if (xpath_vec_ctx(x, xpath, &xc) < 0)
|
||||
/* Parse XML (use nsc == NULL to indicate dont use) */
|
||||
if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
|
||||
return -1;
|
||||
/* Print results */
|
||||
cb = cbuf_new();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue