Merge branch 'master' of https://github.com/clicon/clixon
This commit is contained in:
commit
af720e8f28
58 changed files with 1527 additions and 770 deletions
32
CHANGELOG.md
32
CHANGELOG.md
|
|
@ -16,6 +16,8 @@
|
|||
* Added clixon_util_regexp utility function
|
||||
* Added extensive regexp test [test/test_pattern.sh] for both posix and libxml2
|
||||
* Added regex cache to type resolution
|
||||
* Yang "refine" feature supported
|
||||
* According to RFC 7950 7.13.2
|
||||
* Yang "min-element" and "max-element" feature supported
|
||||
* According to RFC 7950 7.7.4 and 7.7.5
|
||||
* See (tests)[test/test_minmax.sh]
|
||||
|
|
@ -68,6 +70,29 @@
|
|||
### API changes on existing features (you may need to change your code)
|
||||
|
||||
* All hash_ functions have been prefixed with `clicon_` to avoid name collision with other packages (frr)
|
||||
* RESTCONF strict namespace validation of data in POST and PUT.
|
||||
* Accepted:
|
||||
```
|
||||
curl -X PUT http://localhost/restconf/data/mod:a -d {"mod:a":"x"}
|
||||
```
|
||||
* Not accepted (must prefix "a" with module):
|
||||
```
|
||||
curl -X PUT http://localhost/restconf/data/mod:a -d {"a":"x"}
|
||||
```
|
||||
* Undefine `RESTCONF_NS_DATA_CHECK` in include/clixon_custom.h to disable strict check.
|
||||
* Many validation functions have changed error parameter from cbuf to xml tree.
|
||||
* XML trees are more flexible for utility tools
|
||||
* If you use these(mostly internal), you need to change the error function: `generic_validate, from_validate_common, xml_yang_validate_all_top, xml_yang_validate_all, xml_yang_validate_add, xml_yang_validate_rpc, xml_yang_validate_list_key_only`
|
||||
* Replaced `CLIXON_DATADIR` with two configurable options defining where Clixon installs Yang files.
|
||||
* use `--with-yang-installdir=DIR` to install Clixon yang files in DIR
|
||||
* use `--with-std-yang-installdir=DIR` to install standard yang files that Clixon may use in DIR
|
||||
* Default is (as before) `/usr/local/share/clixon`
|
||||
* New clixon-config@2019-06-05.yang revision
|
||||
* Added: `CLICON_YANG_REGEXP, CLICON_CLI_TAB_MODE, CLICON_CLI_HIST_FILE, CLICON_CLI_HIST_SIZE, CLICON_XML_CHANGELOG, CLICON_XML_CHANGELOG_FILE`.
|
||||
* Renamed: `CLICON_XMLDB_CACHE` to `CLICON_DATASTORE_CACHE` and type changed.
|
||||
* Deleted: `CLICON_XMLDB_PLUGIN, CLICON_USE_STARTUP_CONFIG`;
|
||||
* New clixon-lib@2019-06-05.yang revision
|
||||
* Added: ping rpc added (for liveness)
|
||||
* Added compiled regexp parameter as part of internal yang type resolution functions
|
||||
* `yang_type_resolve()`, `yang_type_get()`
|
||||
* All internal `ys_populate_*()` functions (except ys_populate()) have switched parameters: `clicon_handle, yang_stmt *)`
|
||||
|
|
@ -167,6 +192,11 @@
|
|||
|
||||
### Minor changes
|
||||
|
||||
* `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.
|
||||
* Print CLICON_YANG_DIR and CLICON_FEATURE lists on startup with debug flag
|
||||
* Extended `util/clixon_util_xml` with yang and validate functionality so it can be used as a stand-alone utility for validating XML/JSON files
|
||||
* JSON parse and print improvements
|
||||
* Integrated parsing with namespace translation and yang spec lookup
|
||||
* Added CLIgen tab-modes in config option CLICON_CLI_TAB_MODE, which means you can control the behaviour of `<tab>` in the CLI.
|
||||
|
|
@ -212,6 +242,8 @@
|
|||
|
||||
### Corrected Bugs
|
||||
|
||||
* Fixed a problem with some netconf error messages caused restconf daemon to exit due to no XML encoding
|
||||
* Check cligen tab mode, dont start if CLICON_CLI_TAB_MODE is undefined
|
||||
* Startup transactions did not mark added tree with XML_FLAG_ADD as it should.
|
||||
* Restconf PUT different keys detected (thanks @dcornejo) and fixed
|
||||
* This was accepted but shouldn't be: `PUT http://restconf/data/A=hello/B -d '{"B":"goodbye"}'`
|
||||
|
|
|
|||
42
README.md
42
README.md
|
|
@ -10,7 +10,7 @@ support.
|
|||
* [Frequently asked questions (FAQ)](doc/FAQ.md)
|
||||
* [Hello world](example/hello/README.md)
|
||||
* [Changelog](CHANGELOG.md)
|
||||
* [Installation](#installation)
|
||||
* [Installation](doc/INSTALL.md)
|
||||
* [Licenses](#licenses)
|
||||
* [Support](#support)
|
||||
* [Dependencies](#dependencies)
|
||||
|
|
@ -47,21 +47,6 @@ Users of Clixon currently include:
|
|||
|
||||
See also [Clicon project page](http://clicon.org).
|
||||
|
||||
Clixon runs on Linux, [FreeBSD port](https://www.freshports.org/devel/clixon) and Mac/Apple. CPU architecures include x86_64, i686, ARM32.
|
||||
|
||||
## Installation
|
||||
|
||||
A typical installation is as follows:
|
||||
```
|
||||
configure # Configure clixon to platform
|
||||
make # Compile
|
||||
sudo make install # Install libs, binaries, and config-files
|
||||
sudo make install-include # Install include files (for compiling)
|
||||
```
|
||||
|
||||
One [example application](example/README.md) is provided, a IETF IP YANG datamodel with
|
||||
generated CLI, Netconf and restconf interface.
|
||||
|
||||
## Licenses
|
||||
|
||||
Clixon is open-source and dual licensed. Either Apache License, Version 2.0 or GNU
|
||||
|
|
@ -113,7 +98,7 @@ Clixon follows:
|
|||
However, the following YANG syntax modules are not implemented (reference to RFC7950 in parenthesis):
|
||||
- deviation (7.20.3)
|
||||
- action (7.15)
|
||||
- refine (7.13.2)
|
||||
- augment in a uses sub-clause (7.17) (module-level augment is implemented)
|
||||
- status (7.21.2)
|
||||
- extension (7.19)
|
||||
- YIN (13)
|
||||
|
|
@ -149,23 +134,22 @@ The standards covered include:
|
|||
Not supported:
|
||||
- !DOCTYPE (ie DTD)
|
||||
|
||||
Historically, Clixon has not until 3.9 made strict namespace
|
||||
enforcing. For example, the following non-strict netconf was
|
||||
previously accepted:
|
||||
```
|
||||
<rpc><my-own-method/></rpc>
|
||||
```
|
||||
In 3.9, the same statement should be, for example:
|
||||
```
|
||||
<rpc><my-own-method xmlns="urn:example:my-own"/></rpc>
|
||||
```
|
||||
Note that base netconf syntax is still not enforced but recommended:
|
||||
The following xpath axes are supported:
|
||||
- CHILD, DESCENDANT, DESCENDANT_OR_SELF, SELF, and PARENT
|
||||
|
||||
The following xpath axes are _not_ supported:
|
||||
- PRECEEDING, PRECEEDING_SIBLING, NAMESPACE, FOLLOWING_SIBLING, FOLLOWING, ANCESTOR,ANCESTOR_OR_SELF, ATTRIBUTE
|
||||
|
||||
Note that base netconf namespace syntax is not enforced but recommended, which means that the following two expressions are treated equivalently:
|
||||
```
|
||||
<rpc>
|
||||
<my-own-method xmlns="urn:example:my-own"/>
|
||||
</rpc>
|
||||
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
|
||||
<my-own-method xmlns="urn:example:my-own"/>
|
||||
</rpc>
|
||||
```
|
||||
|
||||
All other namespaces are enforced.
|
||||
|
||||
## Netconf
|
||||
|
||||
|
|
|
|||
|
|
@ -411,6 +411,7 @@ from_client_edit_config(clicon_handle h,
|
|||
cbuf *cbx = NULL; /* Assist cbuf */
|
||||
int ret;
|
||||
char *username;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
username = clicon_username_get(h);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
|
|
@ -470,10 +471,13 @@ from_client_edit_config(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
/* xmldb_put (difflist handling) requires list keys */
|
||||
if ((ret = xml_yang_validate_list_key_only(h, xc, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_list_key_only(h, xc, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
}
|
||||
/* Cant do this earlier since we dont have a yang spec to
|
||||
* the upper part of the tree, until we get the "config" tree.
|
||||
*/
|
||||
|
|
@ -493,6 +497,8 @@ from_client_edit_config(clicon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
|
||||
|
|
@ -1121,6 +1127,7 @@ from_client_msg(clicon_handle h,
|
|||
yang_stmt *ye;
|
||||
yang_stmt *ymod;
|
||||
cxobj *xnacm = NULL;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
|
|
@ -1147,10 +1154,13 @@ from_client_msg(clicon_handle h,
|
|||
* maybe not necessary since it should be */
|
||||
if (xml_spec_populate_rpc(h, x, yspec) < 0)
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_rpc(h, x, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_rpc(h, x, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto reply;
|
||||
}
|
||||
xe = NULL;
|
||||
username = xml_find_value(x, "username");
|
||||
/* May be used by callbacks, etc */
|
||||
|
|
@ -1222,6 +1232,8 @@ from_client_msg(clicon_handle h,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cbret)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
* are if code comes via XML/NETCONF.
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] td Transaction data
|
||||
* @param[out] cbret Cligen buffer containing netconf error (if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval -1 Error
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
|
|
@ -90,7 +90,7 @@ static int
|
|||
generic_validate(clicon_handle h,
|
||||
yang_stmt *yspec,
|
||||
transaction_data_t *td,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x1;
|
||||
|
|
@ -100,7 +100,7 @@ generic_validate(clicon_handle h,
|
|||
int ret;
|
||||
|
||||
/* All entries */
|
||||
if ((ret = xml_yang_validate_all_top(h, td->td_target, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all_top(h, td->td_target, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -109,7 +109,7 @@ generic_validate(clicon_handle h,
|
|||
x1 = td->td_scvec[i]; /* source changed */
|
||||
x2 = td->td_tcvec[i]; /* target changed */
|
||||
/* Should this be recursive? */
|
||||
if ((ret = xml_yang_validate_add(h, x2, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_add(h, x2, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -119,7 +119,7 @@ generic_validate(clicon_handle h,
|
|||
x1 = td->td_dvec[i];
|
||||
ys = xml_spec(x1);
|
||||
if (ys && yang_mandatory(ys) && yang_config(ys)==0){
|
||||
if (netconf_missing_element(cbret, "protocol", xml_name(x1), "Missing mandatory variable") < 0)
|
||||
if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "Missing mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ generic_validate(clicon_handle h,
|
|||
/* added entries */
|
||||
for (i=0; i<td->td_alen; i++){
|
||||
x2 = td->td_avec[i];
|
||||
if ((ret = xml_yang_validate_add(h, x2, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_add(h, x2, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -175,6 +175,7 @@ startup_common(clicon_handle h,
|
|||
modstate_diff_t *msd = NULL;
|
||||
cxobj *xt = NULL;
|
||||
cxobj *x;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
/* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with
|
||||
* potentially non-matching module-state in msd
|
||||
|
|
@ -225,11 +226,13 @@ startup_common(clicon_handle h,
|
|||
/* 5. Make generic validation on all new or changed data.
|
||||
Note this is only call that uses 3-values */
|
||||
clicon_debug(1, "Validating startup %s", db);
|
||||
if ((ret = generic_validate(h, yspec, td, cbret)) < 0)
|
||||
if ((ret = generic_validate(h, yspec, td, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail; /* STARTUP_INVALID */
|
||||
|
||||
}
|
||||
/* 6. Call plugin transaction validate callbacks */
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
|
|
@ -240,6 +243,8 @@ startup_common(clicon_handle h,
|
|||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (msd)
|
||||
|
|
@ -374,6 +379,7 @@ startup_commit(clicon_handle h,
|
|||
* and call application callback validations.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] candidate The candidate database. The wanted backend state
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
|
|
@ -385,7 +391,7 @@ static int
|
|||
from_validate_common(clicon_handle h,
|
||||
char *candidate,
|
||||
transaction_data_t *td,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -409,7 +415,7 @@ from_validate_common(clicon_handle h,
|
|||
* But xml_diff requires some basic validation, at least check that yang-specs
|
||||
* have been assigned
|
||||
*/
|
||||
if ((ret = xml_yang_validate_all_top(h, td->td_target, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all_top(h, td->td_target, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -462,7 +468,7 @@ from_validate_common(clicon_handle h,
|
|||
|
||||
/* 5. Make generic validation on all new or changed data.
|
||||
Note this is only call that uses 3-values */
|
||||
if ((ret = generic_validate(h, yspec, td, cbret)) < 0)
|
||||
if ((ret = generic_validate(h, yspec, td, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -503,6 +509,7 @@ candidate_commit(clicon_handle h,
|
|||
int retval = -1;
|
||||
transaction_data_t *td = NULL;
|
||||
int ret;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
/* 1. Start transaction */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
|
|
@ -511,10 +518,13 @@ candidate_commit(clicon_handle h,
|
|||
/* Common steps (with validate). Load candidate and running and compute diffs
|
||||
* Note this is only call that uses 3-values
|
||||
*/
|
||||
if ((ret = from_validate_common(h, candidate, td, cbret)) < 0)
|
||||
if ((ret = from_validate_common(h, candidate, td, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 7. Call plugin transaction commit callbacks */
|
||||
if (plugin_transaction_commit(h, td) < 0)
|
||||
|
|
@ -563,6 +573,8 @@ candidate_commit(clicon_handle h,
|
|||
xmldb_get0_free(h, &td->td_src);
|
||||
transaction_free(td);
|
||||
}
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
@ -725,6 +737,7 @@ from_client_validate(clicon_handle h,
|
|||
transaction_data_t *td = NULL;
|
||||
int ret;
|
||||
char *db;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if ((db = netconf_db_find(xe, "source")) == NULL){
|
||||
if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
|
||||
|
|
@ -737,9 +750,15 @@ from_client_validate(clicon_handle h,
|
|||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
/* Common steps (with commit) */
|
||||
if ((ret = from_validate_common(h, db, td, cbret)) < 1){
|
||||
if ((ret = from_validate_common(h, db, td, &xret)) < 1){
|
||||
/* A little complex due to several sources of validation fails or errors.
|
||||
* (1) xerr is set -> translate to cbret; (2) cbret set use that; otherwise
|
||||
* use clicon_err. */
|
||||
if (xret && clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
plugin_transaction_abort(h, td);
|
||||
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
|
||||
if (!cbuf_len(cbret) &&
|
||||
netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -773,6 +792,8 @@ from_client_validate(clicon_handle h,
|
|||
xmldb_get0_free(h, &td->td_src);
|
||||
transaction_free(td);
|
||||
}
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
} /* from_client_validate */
|
||||
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ main(int argc,
|
|||
switch (startup_mode){
|
||||
case SM_INIT: /* Scratch running and start from empty */
|
||||
/* [Delete and] create running db */
|
||||
if (startup_db_reset(h, "running") < 0)
|
||||
if (xmldb_db_reset(h, "running") < 0)
|
||||
goto done;
|
||||
case SM_NONE: /* Fall through *
|
||||
* Load plugins and call plugin_init() */
|
||||
|
|
|
|||
|
|
@ -70,22 +70,6 @@
|
|||
#include "backend_commit.h"
|
||||
#include "backend_startup.h"
|
||||
|
||||
/*! Create an XML database. If it exists already, delete it before creating
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||
*/
|
||||
int
|
||||
startup_db_reset(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
if (xmldb_exists(h, db) == 1){
|
||||
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
||||
return -1;
|
||||
}
|
||||
if (xmldb_create(h, db) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Merge db1 into db2 without commit
|
||||
* @retval -1 Error
|
||||
|
|
@ -235,34 +219,45 @@ startup_extraxml(clicon_handle h,
|
|||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
char *db = "tmp";
|
||||
char *tmp_db = "tmp";
|
||||
int ret;
|
||||
cxobj *xt = NULL; /* Potentially upgraded XML */
|
||||
|
||||
/* Clear tmp db */
|
||||
if (startup_db_reset(h, db) < 0)
|
||||
if (xmldb_db_reset(h, tmp_db) < 0)
|
||||
goto done;
|
||||
/* Application may define extra xml in its reset function*/
|
||||
if (clixon_plugin_reset(h, db) < 0)
|
||||
if (clixon_plugin_reset(h, tmp_db) < 0)
|
||||
goto done;
|
||||
/* Extra XML can also be added via file */
|
||||
if (file){
|
||||
/* Parse and load file into tmp db */
|
||||
if ((ret = load_extraxml(h, file, db, cbret)) < 0)
|
||||
if ((ret = load_extraxml(h, file, tmp_db, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
/*
|
||||
* Check if tmp db is empty.
|
||||
* It should be empty if extra-xml is null and reset plugins did nothing
|
||||
* then skip validation.
|
||||
*/
|
||||
if (xmldb_get(h, tmp_db, NULL, &xt) < 0)
|
||||
goto done;
|
||||
if (xt==NULL || xml_child_nr(xt)==0)
|
||||
goto ok;
|
||||
xml_free(xt);
|
||||
xt = NULL;
|
||||
/* Validate the tmp db and return possibly upgraded xml in xt
|
||||
*/
|
||||
if ((ret = startup_validate(h, db, &xt, cbret)) < 0)
|
||||
if ((ret = startup_validate(h, tmp_db, &xt, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if (xt==NULL || xml_child_nr(xt)==0)
|
||||
goto ok;
|
||||
/* Merge tmp into running (no commit) */
|
||||
if ((ret = db_merge(h, db, "running", cbret)) < 0)
|
||||
if ((ret = db_merge(h, tmp_db, "running", cbret)) < 0)
|
||||
goto fail;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -270,7 +265,7 @@ startup_extraxml(clicon_handle h,
|
|||
retval = 1;
|
||||
done:
|
||||
xmldb_get0_free(h, &xt);
|
||||
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
||||
if (xmldb_delete(h, tmp_db) != 0 && errno != ENOENT)
|
||||
return -1;
|
||||
return retval;
|
||||
fail:
|
||||
|
|
@ -306,7 +301,7 @@ startup_failsafe(clicon_handle h)
|
|||
/* Copy original running to tmp as backup (restore if error) */
|
||||
if (xmldb_copy(h, "running", "tmp") < 0)
|
||||
goto done;
|
||||
if (startup_db_reset(h, "running") < 0)
|
||||
if (xmldb_db_reset(h, "running") < 0)
|
||||
goto done;
|
||||
ret = candidate_commit(h, db, cbret);
|
||||
if (ret != 1)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int startup_db_reset(clicon_handle h, char *db);
|
||||
int startup_mode_startup(clicon_handle h, char *db, cbuf *cbret);
|
||||
int startup_extraxml(clicon_handle h, char *file, cbuf *cbret);
|
||||
int startup_failsafe(clicon_handle h);
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ main(int argc, char **argv)
|
|||
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
|
||||
struct passwd *pw;
|
||||
char *str;
|
||||
int tabmode;
|
||||
|
||||
/* Defaults */
|
||||
once = 0;
|
||||
|
|
@ -542,7 +543,11 @@ main(int argc, char **argv)
|
|||
clicon_log(LOG_WARNING, "No such cli mode: %s (Specify cli mode with CLICON_CLI_MODE in config file or -m <mode> on command line", cli_syntax_mode(h));
|
||||
|
||||
/* CLIgen tab mode, ie how <tab>s behave */
|
||||
cligen_tabmode_set(cli_cligen(h), clicon_cli_tab_mode(h));
|
||||
if ((tabmode = clicon_cli_tab_mode(h)) < 0){
|
||||
fprintf(stderr, "FATAL: CLICON_CLI_TAB_MODE not set\n");
|
||||
goto done;
|
||||
}
|
||||
cligen_tabmode_set(cli_cligen(h), tabmode);
|
||||
|
||||
if (logclisyntax)
|
||||
cli_logsyntax_set(h, logclisyntax);
|
||||
|
|
|
|||
|
|
@ -125,9 +125,10 @@ netconf_input_packet(clicon_handle h,
|
|||
isrpc++;
|
||||
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_rpc(h, xrpc, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_rpc(h, xrpc, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_xml2cbuf(cbret, xret, 0, 0);
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -547,6 +547,7 @@ netconf_application_rpc(clicon_handle h,
|
|||
yang_stmt *yinput;
|
||||
yang_stmt *youtput;
|
||||
cxobj *xoutput;
|
||||
cxobj *xerr = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cbuf *cbret = NULL;
|
||||
int ret;
|
||||
|
|
@ -587,15 +588,13 @@ netconf_application_rpc(clicon_handle h,
|
|||
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_all_top(h, xn, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all_top(h, xn, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret > 0 && (ret = xml_yang_validate_add(h, xn, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
if ((ret = xml_yang_validate_add(h, xn, cbret)) < 0)
|
||||
if (clicon_xml2cbuf(cbret, xerr, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -622,15 +621,13 @@ netconf_application_rpc(clicon_handle h,
|
|||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
||||
if ((ret = xml_yang_validate_all_top(h, xoutput, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all_top(h, xoutput, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret > 0 && (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
|
||||
goto ok;
|
||||
}
|
||||
if ((ret = xml_yang_validate_add(h, xoutput, cbret)) < 0)
|
||||
if (clicon_xml2cbuf(cbret, xerr, 0, 0) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -641,6 +638,8 @@ netconf_application_rpc(clicon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cbret)
|
||||
|
|
|
|||
|
|
@ -437,13 +437,17 @@ api_data_post(clicon_handle h,
|
|||
{
|
||||
int retval = -1;
|
||||
enum operation_type op = OP_CREATE;
|
||||
cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
|
||||
cxobj *xdata; /* -d data (without top symbol)*/
|
||||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
cxobj *x;
|
||||
yang_stmt *y = NULL;
|
||||
cxobj *xtop = NULL; /* top of api-path */
|
||||
cxobj *xbot = NULL; /* bottom of api-path */
|
||||
yang_stmt *ybot = NULL; /* yang of xbot */
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
|
||||
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
|
||||
#endif
|
||||
yang_stmt *yspec;
|
||||
cxobj *xa;
|
||||
cxobj *xret = NULL;
|
||||
|
|
@ -469,8 +473,12 @@ api_data_post(clicon_handle h,
|
|||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
goto done;
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
if (ybot)
|
||||
ymodapi=ys_module(ybot);
|
||||
#endif
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -486,7 +494,7 @@ api_data_post(clicon_handle h,
|
|||
}
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (xml_parse_string(data, NULL, &xdata) < 0){
|
||||
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){
|
||||
|
|
@ -499,7 +507,7 @@ api_data_post(clicon_handle h,
|
|||
}
|
||||
}
|
||||
else {
|
||||
if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){
|
||||
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){
|
||||
|
|
@ -523,7 +531,7 @@ api_data_post(clicon_handle h,
|
|||
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
*/
|
||||
if (xml_child_nr(xdata) != 1){
|
||||
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){
|
||||
|
|
@ -534,15 +542,46 @@ api_data_post(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
x = xml_child_i(xdata,0);
|
||||
xdata = xml_child_i(xdata0,0);
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
/* If the api-path (above) defines a module, then xdata must have a prefix
|
||||
* and it match the module defined in api-path.
|
||||
* In a POST, maybe there are cornercases where xdata (which is a child) and
|
||||
* xbot (which is the parent) may have non-matching namespaces?
|
||||
* This does not apply if api-path is / (no module)
|
||||
*/
|
||||
if (ys_module_by_xml(yspec, xdata, &ymoddata) < 0)
|
||||
goto done;
|
||||
if (ymoddata && ymodapi){
|
||||
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){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (debug){
|
||||
cbuf *ccc=cbuf_new();
|
||||
if (clicon_xml2cbuf(ccc, xe, 0, 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s XE:%s", __FUNCTION__, cbuf_get(ccc));
|
||||
}
|
||||
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
#endif /* RESTCONF_NS_DATA_CHECK */
|
||||
|
||||
/* Add operation (create/replace) as attribute */
|
||||
if ((xa = xml_new("operation", x, NULL)) == NULL)
|
||||
if ((xa = xml_new("operation", xdata, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
goto done;
|
||||
/* Replace xbot with x, ie bottom of api-path with data */
|
||||
if (xml_addsub(xbot, x) < 0)
|
||||
if (xml_addsub(xbot, xdata) < 0)
|
||||
goto done;
|
||||
/* Create text buffer for transfer to backend */
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
|
|
@ -626,8 +665,8 @@ api_data_post(clicon_handle h,
|
|||
xml_free(xretdis);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (xdata0)
|
||||
xml_free(xdata0);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
|
|
@ -636,11 +675,14 @@ api_data_post(clicon_handle h,
|
|||
|
||||
/*! Check matching keys
|
||||
*
|
||||
* Check that x1 and x2 are of type list/leaf-list and share the same key statements
|
||||
* I.e that if x1=<list><key>b</key></list> then x2 = <list><key>b</key></list> as
|
||||
* well. Otherwise return -1.
|
||||
* @param[in] y Yang statement, should be list or leaf-list
|
||||
* @param[in] xdata XML data tree
|
||||
* @param[in] xapipath XML api-path tree
|
||||
* @param[in] x1 First XML tree (eg data)
|
||||
* @param[in] x2 Second XML tree (eg api-path)
|
||||
* @retval 0 Yes, keys match
|
||||
* @retval -1 No keys do not match
|
||||
* @retval -1 No, keys do not match
|
||||
* If the target resource represents a YANG leaf-list, then the PUT
|
||||
* method MUST NOT change the value of the leaf-list instance.
|
||||
*
|
||||
|
|
@ -651,17 +693,17 @@ api_data_post(clicon_handle h,
|
|||
*/
|
||||
static int
|
||||
match_list_keys(yang_stmt *y,
|
||||
cxobj *xdata,
|
||||
cxobj *xapipath)
|
||||
cxobj *x1,
|
||||
cxobj *x2)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
char *keyname;
|
||||
cxobj *xkeya; /* xml key object in api-path */
|
||||
cxobj *xkeyd; /* xml key object in data */
|
||||
char *keya;
|
||||
char *keyd;
|
||||
cxobj *xkey1; /* xml key object of x1 */
|
||||
cxobj *xkey2; /* xml key object of x2 */
|
||||
char *key1;
|
||||
char *key2;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
switch (yang_keyword_get(y)){
|
||||
|
|
@ -670,29 +712,30 @@ match_list_keys(yang_stmt *y,
|
|||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if ((xkeya = xml_find(xapipath, keyname)) == NULL)
|
||||
if ((xkey2 = xml_find(x2, keyname)) == NULL)
|
||||
goto done; /* No key in api-path */
|
||||
if ((keya = xml_body(xkeya)) == NULL)
|
||||
if ((key2 = xml_body(xkey2)) == NULL)
|
||||
goto done;
|
||||
if ((xkeyd = xml_find(xdata, keyname)) == NULL)
|
||||
if ((xkey1 = xml_find(x1, keyname)) == NULL)
|
||||
goto done; /* No key in data */
|
||||
if ((keyd = xml_body(xkeyd)) == NULL)
|
||||
if ((key1 = xml_body(xkey1)) == NULL)
|
||||
goto done;
|
||||
if (strcmp(keya, keyd) != 0)
|
||||
if (strcmp(key2, key1) != 0)
|
||||
goto done; /* keys dont match */
|
||||
}
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
if ((keya = xml_body(xapipath)) == NULL)
|
||||
if ((key2 = xml_body(x2)) == NULL)
|
||||
goto done; /* No key in api-path */
|
||||
if ((keyd = xml_body(xdata)) == NULL)
|
||||
if ((key1 = xml_body(x1)) == NULL)
|
||||
goto done; /* No key in data */
|
||||
if (strcmp(keya, keyd) != 0)
|
||||
if (strcmp(key2, key1) != 0)
|
||||
goto done; /* keys dont match */
|
||||
break;
|
||||
default:
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
|
|
@ -738,13 +781,17 @@ api_data_put(clicon_handle h,
|
|||
int retval = -1;
|
||||
enum operation_type op = OP_REPLACE;
|
||||
int i;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
|
||||
cxobj *xdata; /* -d data (without top symbol)*/
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
cxobj *xtop = NULL; /* top of api-path */
|
||||
cxobj *xbot = NULL; /* bottom of api-path */
|
||||
yang_stmt *ybot = NULL; /* yang of xbot */
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */
|
||||
yang_stmt *ymoddata = NULL; /* yang module of data (-d) */
|
||||
#endif
|
||||
cxobj *xparent;
|
||||
cxobj *x;
|
||||
yang_stmt *y = NULL;
|
||||
yang_stmt *yp; /* yang parent */
|
||||
yang_stmt *yspec;
|
||||
cxobj *xa;
|
||||
|
|
@ -757,6 +804,7 @@ api_data_put(clicon_handle h,
|
|||
char *username;
|
||||
int ret;
|
||||
char *namespace0;
|
||||
char *dname;
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__, api_path0, data);
|
||||
|
|
@ -773,8 +821,12 @@ api_data_put(clicon_handle h,
|
|||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
if (api_path){
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0)
|
||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0)
|
||||
goto done;
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
if (ybot)
|
||||
ymodapi=ys_module(ybot);
|
||||
#endif
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -788,9 +840,10 @@ api_data_put(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (xml_parse_string(data, NULL, &xdata) < 0){
|
||||
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){
|
||||
|
|
@ -803,7 +856,7 @@ api_data_put(clicon_handle h,
|
|||
}
|
||||
}
|
||||
else{
|
||||
if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){
|
||||
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){
|
||||
|
|
@ -827,7 +880,7 @@ api_data_put(clicon_handle h,
|
|||
/* The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
*/
|
||||
if (xml_child_nr(xdata) != 1){
|
||||
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){
|
||||
|
|
@ -838,34 +891,56 @@ api_data_put(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
x = xml_child_i(xdata,0);
|
||||
xdata = xml_child_i(xdata0,0);
|
||||
#ifdef RESTCONF_NS_DATA_CHECK
|
||||
/* If the api-path (above) defines a module, then xdata must have a prefix
|
||||
* and it match the module defined in api-path
|
||||
* This does not apply if api-path is / (no module)
|
||||
*/
|
||||
if (ys_module_by_xml(yspec, xdata, &ymoddata) < 0)
|
||||
goto done;
|
||||
if (ymoddata && ymodapi){
|
||||
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){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
#endif /* RESTCONF_NS_DATA_CHECK */
|
||||
|
||||
/* Add operation (create/replace) as attribute */
|
||||
if ((xa = xml_new("operation", x, NULL)) == NULL)
|
||||
if ((xa = xml_new("operation", xdata, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
goto done;
|
||||
#if 0
|
||||
if (debug){
|
||||
cbuf *ccc=cbuf_new();
|
||||
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
|
||||
}
|
||||
#endif
|
||||
/* Replace xparent with x, ie bottom of api-path with data */
|
||||
if (api_path==NULL && strcmp(xml_name(x),"data")==0){
|
||||
if (xml_addsub(NULL, x) < 0)
|
||||
|
||||
/* Top-of tree, no api-path
|
||||
* Replace xparent with x, ie bottom of api-path with data
|
||||
*/
|
||||
dname = xml_name(xdata);
|
||||
if (api_path==NULL && strcmp(dname,"data")==0){
|
||||
if (xml_addsub(NULL, xdata) < 0)
|
||||
goto done;
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
xtop = x;
|
||||
xtop = xdata;
|
||||
xml_name_set(xtop, "config");
|
||||
}
|
||||
else {
|
||||
clicon_debug(1, "%s x:%s xbot:%s",__FUNCTION__, xml_name(x), xml_name(xbot));
|
||||
/* There is an api-path that defines an element in the datastore tree.
|
||||
* Not top-of-tree.
|
||||
*/
|
||||
clicon_debug(1, "%s x:%s xbot:%s",__FUNCTION__, dname, xml_name(xbot));
|
||||
|
||||
/* Check same symbol in api-path as data */
|
||||
if (strcmp(xml_name(x), xml_name(xbot))){
|
||||
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){
|
||||
|
|
@ -884,27 +959,13 @@ api_data_put(clicon_handle h,
|
|||
* That is why the conditional is somewhat hairy
|
||||
*/
|
||||
xparent = xml_parent(xbot);
|
||||
#if 1
|
||||
if (debug){
|
||||
cbuf *ccc=cbuf_new();
|
||||
if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s AAA XPATH:%s", __FUNCTION__, cbuf_get(ccc));
|
||||
}
|
||||
if (debug){
|
||||
cbuf *ccc=cbuf_new();
|
||||
if (clicon_xml2cbuf(ccc, xdata, 0, 0) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s DATA:%s", __FUNCTION__, cbuf_get(ccc));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (y){
|
||||
yp = yang_parent_get(y);
|
||||
if (((yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST) &&
|
||||
match_list_keys(y, x, xbot) < 0) ||
|
||||
(yp && yang_keyword_get(yp) == Y_LIST &&
|
||||
match_list_keys(yp, xml_parent(x), xparent) < 0)){
|
||||
if (ybot){
|
||||
/* Ensure list keys match between uri and data. That is:
|
||||
* If data is on the form: -d {"a":{"k":1}} where a is list or leaf-list
|
||||
* then uri-path must be ../a=1
|
||||
* match_list_key() checks if this is true
|
||||
*/
|
||||
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){
|
||||
|
|
@ -914,15 +975,40 @@ api_data_put(clicon_handle h,
|
|||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
|
||||
}
|
||||
/* Ensure keys in lists are not changed. That is:
|
||||
* If data is on the form: -d {"k":1} and its parent is a list "a"
|
||||
* then the uri-path must be "../a=1 (you cannot change a's key)"
|
||||
*/
|
||||
if ((yp = yang_parent_get(ybot)) != NULL &&
|
||||
yang_keyword_get(yp) == Y_LIST){
|
||||
if ((ret = yang_key_match(yp, dname)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){ /* Match: xdata is a key */
|
||||
char *parbod = xml_find_body(xparent, dname);
|
||||
/* Check if the key is different from the one in uri-path,
|
||||
* or does not exist
|
||||
*/
|
||||
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){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xml_purge(xbot);
|
||||
if (xml_addsub(xparent, x) < 0)
|
||||
if (xml_addsub(xparent, xdata) < 0)
|
||||
goto done;
|
||||
/* If we already have that default namespace, remove it in child */
|
||||
if ((xa = xml_find_type(x, NULL, "xmlns", CX_ATTR)) != NULL){
|
||||
if ((xa = xml_find_type(xdata, NULL, "xmlns", CX_ATTR)) != NULL){
|
||||
if (xml2ns(xparent, NULL, &namespace0) < 0)
|
||||
goto done;
|
||||
/* Set xmlns="" default namespace attribute (if diff from default) */
|
||||
|
|
@ -945,6 +1031,7 @@ api_data_put(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 (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
|
|
@ -1010,8 +1097,8 @@ api_data_put(clicon_handle h,
|
|||
xml_free(xretdis);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (xdata0)
|
||||
xml_free(xdata0);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
|
|
@ -1454,14 +1541,9 @@ api_operations_post_output(clicon_handle h,
|
|||
cxobj *xa; /* xml attribute (xmlns) */
|
||||
cxobj *x;
|
||||
cxobj *xok;
|
||||
cbuf *cbret = NULL;
|
||||
int isempty;
|
||||
|
||||
// clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Validate that exactly only <rpc-reply> tag */
|
||||
if ((xoutput = xml_child_i_type(xret, 0, CX_ELMNT)) == NULL ||
|
||||
strcmp(xml_name(xoutput),"rpc-reply") != 0 ||
|
||||
|
|
@ -1499,14 +1581,12 @@ api_operations_post_output(clicon_handle h,
|
|||
#if 0
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_all(xoutput, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all(xoutput, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 1 &&
|
||||
(ret = xml_yang_validate_add(h, xoutput, cbret)) < 0)
|
||||
(ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (xml_parse_string(cbuf_get(cbret), yspec, &xerr) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xerr, "rpc-reply/rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
|
|
@ -1551,8 +1631,6 @@ api_operations_post_output(clicon_handle h,
|
|||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval: %d", __FUNCTION__, retval);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
return retval;
|
||||
|
|
@ -1738,14 +1816,12 @@ api_operations_post(clicon_handle h,
|
|||
/* 6. Validate incoming RPC and fill in defaults */
|
||||
if (xml_spec_populate_rpc(h, xtop, yspec) < 0) /* */
|
||||
goto done;
|
||||
if ((ret = xml_yang_validate_rpc(h, xtop, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
|
||||
goto done;
|
||||
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) == NULL){
|
||||
if ((xe = xpath_first(xret, "rpc-error")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
47
configure
vendored
47
configure
vendored
|
|
@ -621,7 +621,8 @@ ac_includes_default="\
|
|||
|
||||
ac_subst_vars='LTLIBOBJS
|
||||
LIBOBJS
|
||||
CLIXON_DATADIR
|
||||
STD_YANG_INSTALLDIR
|
||||
YANG_INSTALLDIR
|
||||
EGREP
|
||||
GREP
|
||||
LEXLIB
|
||||
|
|
@ -637,7 +638,6 @@ wwwuser
|
|||
wwwdir
|
||||
enable_stdyangs
|
||||
with_restconf
|
||||
RANLIB
|
||||
SH_SUFFIX
|
||||
CLIXON_DEFAULT_CONFIG
|
||||
INSTALLFLAGS
|
||||
|
|
@ -718,6 +718,8 @@ with_restconf
|
|||
with_wwwuser
|
||||
with_configfile
|
||||
with_libxml2
|
||||
with_yang_installdir
|
||||
with_std_yang_installdir
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
|
|
@ -1367,6 +1369,8 @@ Optional Packages:
|
|||
--with-wwwuser=<user> Set www user different from www-data
|
||||
--with-configfile=FILE set default path to config file
|
||||
--with-libxml2 use gnome/libxml2 regex engine
|
||||
--with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon)
|
||||
--with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon)
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
|
|
@ -2463,7 +2467,6 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
|
|||
|
||||
|
||||
|
||||
|
||||
# If yes, compile apps/restconf
|
||||
|
||||
wwwdir=/www-data
|
||||
|
|
@ -4493,13 +4496,39 @@ fi
|
|||
done
|
||||
|
||||
|
||||
# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile
|
||||
# This directory should most probably be included in each application,
|
||||
# so each application designer may need to place CLIXON_DATADIR in their config
|
||||
# (last in yang dir list):
|
||||
# <CLIXON-YANG-DIR>$CLIXON_DATADIR</CLIXON-YANG_DIR>
|
||||
# YANG_INSTALLDIR is where clixon installs the Clixon yang files
|
||||
# (the files in in yang/clixon)
|
||||
# Each application designer may need to place CLIXON_YANG_DIR in their config:
|
||||
# <CLIXON-YANG-DIR>$YANG_INSTALLDIR</CLIXON-YANG_DIR>
|
||||
|
||||
CLIXON_DATADIR="${prefix}/share/clixon"
|
||||
# Check whether --with-yang-installdir was given.
|
||||
if test "${with_yang_installdir+set}" = set; then :
|
||||
withval=$with_yang_installdir; YANG_INSTALLDIR="$withval"
|
||||
else
|
||||
YANG_INSTALLDIR="${prefix}/share/clixon"
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Clixon yang files are installed in ${YANG_INSTALLDIR}" >&5
|
||||
$as_echo "Clixon yang files are installed in ${YANG_INSTALLDIR}" >&6; }
|
||||
|
||||
# STD_YANG_INSTALLDIR is where clixon installs standard yang files
|
||||
# (the files in in yang/standard)
|
||||
# that Clixon needs to run (or examples rely on). These may be retreived from
|
||||
# elsewhere (eg yangmodels repo)
|
||||
|
||||
# Check whether --with-std-yang-installdir was given.
|
||||
if test "${with_std_yang_installdir+set}" = set; then :
|
||||
withval=$with_std_yang_installdir; STD_YANG_INSTALLDIR="$withval"
|
||||
else
|
||||
STD_YANG_INSTALLDIR="${prefix}/share/clixon"
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&5
|
||||
$as_echo "Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&6; }
|
||||
|
||||
# Default location for config file
|
||||
|
||||
|
|
|
|||
31
configure.ac
31
configure.ac
|
|
@ -89,7 +89,6 @@ AC_SUBST(INSTALLFLAGS)
|
|||
AC_SUBST(CLIXON_DEFAULT_CONFIG)
|
||||
AC_SUBST(LIBS)
|
||||
AC_SUBST(SH_SUFFIX)
|
||||
AC_SUBST(RANLIB)
|
||||
AC_SUBST(with_restconf) # If yes, compile apps/restconf
|
||||
AC_SUBST(enable_stdyangs)
|
||||
AC_SUBST(wwwdir,/www-data)
|
||||
|
|
@ -232,13 +231,29 @@ fi
|
|||
#
|
||||
AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort)
|
||||
|
||||
# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile
|
||||
# This directory should most probably be included in each application,
|
||||
# so each application designer may need to place CLIXON_DATADIR in their config
|
||||
# (last in yang dir list):
|
||||
# <CLIXON-YANG-DIR>$CLIXON_DATADIR</CLIXON-YANG_DIR>
|
||||
AC_SUBST(CLIXON_DATADIR)
|
||||
CLIXON_DATADIR="${prefix}/share/clixon"
|
||||
# YANG_INSTALLDIR is where clixon installs the Clixon yang files
|
||||
# (the files in in yang/clixon)
|
||||
# Each application designer may need to place YANG_INSTALLDIR in their config:
|
||||
# <CLICON_YANG_DIR>$YANG_INSTALLDIR</CLICON_YANG_DIR>
|
||||
AC_ARG_WITH(yang-installdir,
|
||||
[ --with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon) ],
|
||||
[YANG_INSTALLDIR="$withval"],
|
||||
[YANG_INSTALLDIR="${prefix}/share/clixon"]
|
||||
)
|
||||
AC_SUBST(YANG_INSTALLDIR)
|
||||
AC_MSG_RESULT(Clixon yang files are installed in ${YANG_INSTALLDIR})
|
||||
|
||||
# STD_YANG_INSTALLDIR is where clixon installs standard yang files
|
||||
# (the files in in yang/standard)
|
||||
# that Clixon needs to run (or examples rely on). These may be retreived from
|
||||
# elsewhere (eg yangmodels repo)
|
||||
AC_ARG_WITH(std-yang-installdir,
|
||||
[ --with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon) ],
|
||||
[STD_YANG_INSTALLDIR="$withval"],
|
||||
[STD_YANG_INSTALLDIR="${prefix}/share/clixon"]
|
||||
)
|
||||
AC_SUBST(STD_YANG_INSTALLDIR)
|
||||
AC_MSG_RESULT(Standard yang files are installed in ${STD_YANG_INSTALLDIR})
|
||||
|
||||
# Default location for config file
|
||||
AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${CLIXON_DEFAULT_CONFIG}",[Location for apps to find default config file])
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ The following configuration file options control the loading of Yang files:
|
|||
- `CLICON_YANG_MODULE_REVISION` : Specifies a revision to the main module.
|
||||
- `CLICON_YANG_MAIN_DIR` - Load all yang modules in this directory.
|
||||
|
||||
Note that the special `CLIXON_DATADIR`, by default `/usr/local/share/clixon` should be included in the yang dir path for Clixon system files to be found.
|
||||
Note that the special `YANG_INSTALLDIR`, by default `/usr/local/share/clixon` should be included in the yang dir path for Clixon system files to be found.
|
||||
|
||||
You can combine the options, however, if there are different variants
|
||||
of the same module, more specific options override less
|
||||
|
|
|
|||
47
doc/INSTALL.md
Normal file
47
doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Building Clixon
|
||||
|
||||
Clixon runs on Linux, [FreeBSD port](https://www.freshports.org/devel/clixon) and Mac/Apple. CPU architecures include x86_64, i686, ARM32.
|
||||
|
||||
## Ubuntu Linux
|
||||
|
||||
### Installing dependencies
|
||||
|
||||
Install packages
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install flex bison fcgi-dev curl-dev
|
||||
```
|
||||
|
||||
Install and build CLIgen
|
||||
```
|
||||
git clone https://github.com/olofhagsand/cligen.git
|
||||
cd cligen;
|
||||
configure;
|
||||
make;
|
||||
make install
|
||||
```
|
||||
|
||||
Add a user group, using groupadd and usermod:
|
||||
```
|
||||
sudo groupadd clicon #
|
||||
sudo usermod -a -G clicon <user>
|
||||
sudo usermod -a -G clicon www-data
|
||||
```
|
||||
|
||||
|
||||
### Build from source
|
||||
```
|
||||
configure # Configure clixon to platform
|
||||
make # Compile
|
||||
sudo make install # Install libs, binaries, and config-files
|
||||
sudo make install-include # Install include files (for compiling)
|
||||
```
|
||||
|
||||
## Alpine Linux
|
||||
Docker is used to build Alpine Linux
|
||||
### Build docker image
|
||||
|
||||
## FreeBSD
|
||||
### Package install
|
||||
### Build from source
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ libdir = @exec_prefix@/lib
|
|||
APPNAME = hello
|
||||
|
||||
# Here is where example yang appears
|
||||
CLIXON_DATADIR = @CLIXON_DATADIR@
|
||||
YANG_INSTALLDIR = @YANG_INSTALLDIR@
|
||||
# Install here if you want default clixon location:
|
||||
CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ install: $(YANGSPECS) $(CLISPECS) $(PLUGINS) $(APPNAME).xml
|
|||
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/clispec
|
||||
install -m 0644 $(CLISPECS) $(DESTDIR)$(libdir)/$(APPNAME)/clispec
|
||||
install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(YANG_INSTALLDIR)
|
||||
install -d -m 0755 $(DESTDIR)$(localstatedir)/$(APPNAME)
|
||||
|
||||
# Uncomment for installing config file in /usr/local/etc instead
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ libdir = @exec_prefix@/lib
|
|||
|
||||
APPNAME = example
|
||||
# Here is where example yang appears
|
||||
CLIXON_DATADIR = @CLIXON_DATADIR@
|
||||
YANG_INSTALLDIR = @YANG_INSTALLDIR@
|
||||
# Install here if you want default clixon location:
|
||||
CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ install: $(YANGSPECS) $(CLISPECS) $(PLUGINS) $(APPNAME).xml
|
|||
install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir)
|
||||
# install -m 0644 $(APPNAME).xml $(DESTDIR)$(CLIXON_DEFAULT_CONFIG)
|
||||
install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(YANG_INSTALLDIR)
|
||||
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli
|
||||
install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli
|
||||
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend
|
||||
|
|
|
|||
|
|
@ -41,6 +41,6 @@
|
|||
*/
|
||||
#undef RPC_USERNAME_ASSERT
|
||||
|
||||
/* Use new xml_insert code on sorted xml lists
|
||||
/* Make namespace check on RESTCONF PUT and POST -d data
|
||||
*/
|
||||
#define USE_XML_INSERT
|
||||
#define RESTCONF_NS_DATA_CHECK
|
||||
|
|
|
|||
|
|
@ -62,5 +62,7 @@ int xmldb_islocked(clicon_handle h, const char *db);
|
|||
int xmldb_exists(clicon_handle h, const char *db);
|
||||
int xmldb_delete(clicon_handle h, const char *db);
|
||||
int xmldb_create(clicon_handle h, const char *db);
|
||||
/* utility functions */
|
||||
int xmldb_db_reset(clicon_handle h, char *db);
|
||||
|
||||
#endif /* _CLIXON_DATASTORE_H */
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Netconf library functions. See RFC6241
|
||||
*
|
||||
* Functions to generate a netconf error message come in two forms: xml-tree and
|
||||
* cbuf. XML tree is preferred.
|
||||
*/
|
||||
#ifndef _CLIXON_NETCONF_LIB_H
|
||||
#define _CLIXON_NETCONF_LIB_H
|
||||
|
|
@ -60,16 +61,18 @@ int netconf_lock_denied(cbuf *cb, char *info, char *message);
|
|||
int netconf_resource_denied(cbuf *cb, char *type, char *message);
|
||||
int netconf_rollback_failed(cbuf *cb, char *type, char *message);
|
||||
int netconf_data_exists(cbuf *cb, char *message);
|
||||
int netconf_data_missing(cbuf *cb, char *message);
|
||||
int netconf_data_missing(cbuf *cb, char *missing_choice, char *message);
|
||||
int netconf_data_missing_xml(cxobj **xret, char *missing_choice, char *message);
|
||||
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
|
||||
int netconf_operation_failed(cbuf *cb, char *type, char *message);
|
||||
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
|
||||
int netconf_malformed_message(cbuf *cb, char *message);
|
||||
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
||||
int netconf_data_not_unique(cbuf *cb, cxobj *x, cvec *cvk);
|
||||
int netconf_minmax_elements(cbuf *cb, cxobj *x, int max);
|
||||
int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
|
||||
int netconf_minmax_elements_xml(cxobj **xret, cxobj *x, int max);
|
||||
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
|
||||
int netconf_module_load(clicon_handle h);
|
||||
char *netconf_db_find(cxobj *xn, char *name);
|
||||
int netconf_err2cb(cxobj *xerr, cbuf **cberr);
|
||||
|
||||
#endif /* _CLIXON_NETCONF_LIB_H */
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@ int xml2txt(FILE *f, cxobj *x, int level);
|
|||
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
|
||||
int xml_yang_root(cxobj *x, cxobj **xr);
|
||||
int xmlns_assign(cxobj *x);
|
||||
int xml_yang_validate_rpc(clicon_handle h, cxobj *xrpc, cbuf *cbret);
|
||||
int xml_yang_validate_list_key_only(clicon_handle h, cxobj *xt, cbuf *cbret);
|
||||
int xml_yang_validate_add(clicon_handle h, cxobj *xt, cbuf *cbret);
|
||||
int xml_yang_validate_all(clicon_handle h, cxobj *xt, cbuf *cbret);
|
||||
int xml_yang_validate_all_top(clicon_handle h, cxobj *xt, cbuf *cbret);
|
||||
int xml_yang_validate_rpc(clicon_handle h, cxobj *xrpc, cxobj **xret);
|
||||
int xml_yang_validate_list_key_only(clicon_handle h, cxobj *xt, cxobj **xret);
|
||||
int xml_yang_validate_add(clicon_handle h, cxobj *xt, cxobj **xret);
|
||||
int xml_yang_validate_all(clicon_handle h, cxobj *xt, cxobj **xret);
|
||||
int xml_yang_validate_all_top(clicon_handle h, cxobj *xt, cxobj **xret);
|
||||
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
||||
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
#include "clixon_datastore_write.h"
|
||||
#include "clixon_datastore_read.h"
|
||||
|
||||
|
||||
/*! Translate from symbolic database name to actual filename in file-system
|
||||
* @param[in] th text handle handle
|
||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||
|
|
@ -446,3 +447,21 @@ xmldb_create(clicon_handle h,
|
|||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create an XML database. If it exists already, delete it before creating
|
||||
* Utility function.
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||
*/
|
||||
int
|
||||
xmldb_db_reset(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
if (xmldb_exists(h, db) == 1){
|
||||
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
||||
return -1;
|
||||
}
|
||||
if (xmldb_create(h, db) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -641,7 +641,8 @@ xmldb_get_zerocopy(clicon_handle h,
|
|||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0)
|
||||
* err;
|
||||
|
|
|
|||
|
|
@ -154,17 +154,10 @@ text_modify(clicon_handle h,
|
|||
goto fail;
|
||||
permit = 1;
|
||||
}
|
||||
// int iamkey=0;
|
||||
|
||||
#ifdef USE_XML_INSERT
|
||||
/* Add new xml node but without parent - insert when node fully
|
||||
copied (see changed conditional below) */
|
||||
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
#else
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
#endif
|
||||
changed++;
|
||||
|
||||
/* Copy xmlns attributes */
|
||||
|
|
@ -210,16 +203,14 @@ text_modify(clicon_handle h,
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef USE_XML_INSERT
|
||||
if (changed){
|
||||
if (xml_insert(x0p, x0) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case OP_DELETE:
|
||||
if (x0==NULL){
|
||||
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
|
||||
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -295,17 +286,12 @@ text_modify(clicon_handle h,
|
|||
goto fail;
|
||||
permit = 1;
|
||||
}
|
||||
#ifdef USE_XML_INSERT
|
||||
/* Add new xml node but without parent - insert when node fully
|
||||
* copied (see changed conditional below)
|
||||
* Note x0 may dangle cases if exit before changed conditional
|
||||
*/
|
||||
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
#else
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
#endif
|
||||
changed++;
|
||||
/* Copy xmlns attributes */
|
||||
x1a = NULL;
|
||||
|
|
@ -367,16 +353,14 @@ text_modify(clicon_handle h,
|
|||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
#ifdef USE_XML_INSERT
|
||||
if (changed){
|
||||
if (xml_insert(x0p, x0) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case OP_DELETE:
|
||||
if (x0==NULL){
|
||||
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
|
||||
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -396,17 +380,11 @@ text_modify(clicon_handle h,
|
|||
break;
|
||||
} /* CONTAINER switch op */
|
||||
} /* else Y_CONTAINER */
|
||||
#ifndef USE_XML_INSERT
|
||||
if (changed)
|
||||
xml_sort(x0p, h);
|
||||
#endif
|
||||
retval = 1;
|
||||
done:
|
||||
#ifdef USE_XML_INSERT
|
||||
/* Remove dangling added objects */
|
||||
if (changed && x0 && xml_parent(x0)==NULL)
|
||||
xml_purge(x0);
|
||||
#endif
|
||||
if (x0vec)
|
||||
free(x0vec);
|
||||
return retval;
|
||||
|
|
@ -488,7 +466,7 @@ text_modify_top(clicon_handle h,
|
|||
I.e., curl -u andy:bar -sS -X DELETE http://localhost/restconf/data
|
||||
*/
|
||||
case OP_DELETE:
|
||||
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
|
||||
if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Netconf library functions. See RFC6241
|
||||
* Functions to generate a netconf error message come in two forms: xml-tree and
|
||||
* cbuf. XML tree is preferred.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -63,6 +65,8 @@
|
|||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
|
||||
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
|
||||
|
|
@ -70,7 +74,7 @@
|
|||
* The request requires a resource that already is in use.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
int
|
||||
netconf_in_use(cbuf *cb,
|
||||
|
|
@ -109,7 +113,7 @@ netconf_in_use(cbuf *cb,
|
|||
* The request specifies an unacceptable value for one or more parameters.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
int
|
||||
netconf_invalid_value(cbuf *cb,
|
||||
|
|
@ -149,7 +153,7 @@ netconf_invalid_value(cbuf *cb,
|
|||
* too large for the implementation to handle.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "transport", "rpc", "application", "protocol"
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
int
|
||||
netconf_too_big(cbuf *cb,
|
||||
|
|
@ -189,7 +193,7 @@ netconf_too_big(cbuf *cb,
|
|||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||
* @param[in] info bad-attribute or bad-element xml
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
int
|
||||
netconf_missing_attribute(cbuf *cb,
|
||||
|
|
@ -230,7 +234,7 @@ netconf_missing_attribute(cbuf *cb,
|
|||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||
* @param[in] info bad-attribute or bad-element xml
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
int
|
||||
netconf_bad_attribute(cbuf *cb,
|
||||
|
|
@ -313,7 +317,7 @@ netconf_unknown_attribute(cbuf *cb,
|
|||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] tag Error tag
|
||||
* @param[in] element bad-element xml
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
*/
|
||||
static int
|
||||
netconf_common_xml(cxobj **xret,
|
||||
|
|
@ -325,6 +329,7 @@ netconf_common_xml(cxobj **xret,
|
|||
{
|
||||
int retval =-1;
|
||||
cxobj *xerr;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
|
|
@ -340,11 +345,17 @@ netconf_common_xml(cxobj **xret,
|
|||
"<error-severity>error</error-severity>",
|
||||
type, tag, infotag, element, infotag) < 0)
|
||||
goto done;
|
||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
message) < 0)
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
encstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -551,7 +562,7 @@ netconf_access_denied(cbuf *cb,
|
|||
* authorization failed.
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] type Error type: "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
* if (netconf_access_denied_xml(&xret, "protocol", "Unauthorized") < 0)
|
||||
|
|
@ -567,6 +578,7 @@ netconf_access_denied_xml(cxobj **xret,
|
|||
{
|
||||
int retval =-1;
|
||||
cxobj *xerr;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
|
|
@ -580,11 +592,17 @@ netconf_access_denied_xml(cxobj **xret,
|
|||
"<error-tag>access-denied</error-tag>"
|
||||
"<error-severity>error</error-severity>", type) < 0)
|
||||
goto done;
|
||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
message) < 0)
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
encstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -752,36 +770,79 @@ netconf_data_exists(cbuf *cb,
|
|||
* does not exist. For example, a "delete" operation was attempted on
|
||||
* data that does not exist.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatiry choice
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
int
|
||||
netconf_data_missing(cbuf *cb,
|
||||
char *missing_choice,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if (netconf_data_missing_xml(&xret, missing_choice, message) < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A
|
||||
*
|
||||
* Request could not be completed because the relevant data model content
|
||||
* does not exist. For example, a "delete" operation was attempted on
|
||||
* data that does not exist.
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] missing_choice If set, see RFC7950: 15.6 violates mandatiry choice
|
||||
* @param[in] message Error message
|
||||
*/
|
||||
int
|
||||
netconf_data_missing_xml(cxobj **xret,
|
||||
char *missing_choice,
|
||||
char *message)
|
||||
{
|
||||
int retval = -1;
|
||||
char *encstr = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
||||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL,
|
||||
"<error-type>application</error-type>"
|
||||
"<error-tag>data-missing</error-tag>"
|
||||
"<error-tag>data-missing</error-tag>") < 0)
|
||||
goto done;
|
||||
if (missing_choice) /* NYI: RFC7950: 15.6 <error-path> */
|
||||
if (xml_parse_va(&xerr, NULL,
|
||||
"<error-app-tag>missing-choice</error-app-tag>"
|
||||
"<error-info><missing-choice>%s</missing-choice></error-info>",
|
||||
missing_choice) < 0)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL,
|
||||
"<error-severity>error</error-severity>") < 0)
|
||||
goto err;
|
||||
goto done;
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
goto done;
|
||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
||||
goto err;
|
||||
if (xml_parse_va(&xerr, NULL,
|
||||
"<error-message>%s</error-message>", encstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
||||
|
|
@ -858,7 +919,7 @@ netconf_operation_failed(cbuf *cb,
|
|||
* some reason not covered by any other error condition.
|
||||
* @param[out] xret Error XML tree
|
||||
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
* if (netconf_operation_failed_xml(&xret, "protocol", "Unauthorized") < 0)
|
||||
|
|
@ -874,6 +935,7 @@ netconf_operation_failed_xml(cxobj **xret,
|
|||
{
|
||||
int retval =-1;
|
||||
cxobj *xerr;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
|
|
@ -887,11 +949,17 @@ netconf_operation_failed_xml(cxobj **xret,
|
|||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-severity>error</error-severity>", type) < 0)
|
||||
goto done;
|
||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
message) < 0)
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
encstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -929,7 +997,7 @@ netconf_malformed_message(cbuf *cb,
|
|||
* For example, the message is not well-formed XML or it uses an
|
||||
* invalid character set.
|
||||
* @param[out] xret Error XML tree
|
||||
* @param[in] message Error message
|
||||
* @param[in] message Error message (will be XML encoded)
|
||||
* @note New in :base:1.1
|
||||
* @code
|
||||
* cxobj *xret = NULL;
|
||||
|
|
@ -945,6 +1013,7 @@ netconf_malformed_message_xml(cxobj **xret,
|
|||
{
|
||||
int retval =-1;
|
||||
cxobj *xerr;
|
||||
char *encstr = NULL;
|
||||
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
|
|
@ -958,11 +1027,17 @@ netconf_malformed_message_xml(cxobj **xret,
|
|||
"<error-tag>malformed-message</error-tag>"
|
||||
"<error-severity>error</error-severity>") < 0)
|
||||
goto done;
|
||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
message) < 0)
|
||||
if (message){
|
||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||
encstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (encstr)
|
||||
free(encstr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -970,79 +1045,97 @@ netconf_malformed_message_xml(cxobj **xret,
|
|||
*
|
||||
* A NETCONF operation would result in configuration data where a
|
||||
* "unique" constraint is invalidated.
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] x List element containing duplicate
|
||||
* @param[in] cvk List of comonents in x that are non-unique
|
||||
* @see RFC7950 Sec 15.1
|
||||
*/
|
||||
int
|
||||
netconf_data_not_unique(cbuf *cb,
|
||||
netconf_data_not_unique_xml(cxobj **xret,
|
||||
cxobj *x,
|
||||
cvec *cvk)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cvi = NULL;
|
||||
cxobj *xi;
|
||||
cxobj *xerr;
|
||||
cxobj *xinfo;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>protocol</error-type>"
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
||||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-type>protocol</error-type>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-app-tag>data-not-unique</error-app-tag>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-info>") < 0)
|
||||
goto err;
|
||||
"<error-severity>error</error-severity>") < 0)
|
||||
goto done;
|
||||
if (cvec_len(cvk)){
|
||||
if ((xinfo = xml_new("error-info", xerr, NULL)) == NULL)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
if ((xi = xml_find(x, cv_string_get(cvi))) == NULL)
|
||||
continue; /* ignore, shouldnt happen */
|
||||
cprintf(cb, "<non-unique>");
|
||||
clicon_xml2cbuf(cb, xi, 0, 0);
|
||||
cprintf(cb, "</non-unique>");
|
||||
if (xml_parse_va(&xinfo, NULL, "<non-unique>%s</non-unique>", cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
cbuf_reset(cb);
|
||||
}
|
||||
}
|
||||
if (cprintf(cb, "</error-info></rpc-error></rpc-reply>") <0)
|
||||
goto err;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Create Netconf too-many/few-elements err msg according to RFC 7950 15.2/15.3
|
||||
*
|
||||
* A NETCONF operation would result in configuration data where a
|
||||
list or a leaf-list would have too many entries, the following error
|
||||
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @param[in] x List element containing duplicate
|
||||
* @param[in] max If set, return too-many, otherwise too-few
|
||||
* @see RFC7950 Sec 15.1
|
||||
*/
|
||||
int
|
||||
netconf_minmax_elements(cbuf *cb,
|
||||
netconf_minmax_elements_xml(cxobj **xret,
|
||||
cxobj *x,
|
||||
int max)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xerr;
|
||||
|
||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>protocol</error-type>"
|
||||
if (*xret == NULL){
|
||||
if ((*xret = xml_new("rpc-reply", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
||||
goto done;
|
||||
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_parse_va(&xerr, NULL, "<error-type>protocol</error-type>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-app-tag>too-%s-elements</error-app-tag>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-path>%s</error-path>"
|
||||
"</rpc-error></rpc-reply>",
|
||||
"<error-path>%s</error-path>",
|
||||
max?"many":"few",
|
||||
xml_name(x)) < 0) /* XXX should be xml2xpath */
|
||||
goto err;
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
err:
|
||||
clicon_err(OE_XML, errno, "cprintf");
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/*! Help function: merge - check yang - if error make netconf errmsg
|
||||
* @param[in] x XML tree
|
||||
* @param[in] yspec Yang spec
|
||||
|
|
@ -1156,3 +1249,41 @@ netconf_db_find(cxobj *xn,
|
|||
return db;
|
||||
}
|
||||
|
||||
/*! Generate netconf error msg to cbuf to use in string printout or logs
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||
* @param[out] cberr Translation from netconf err to cbuf. Free with cbuf_free.
|
||||
* @retval 0 OK, with cberr set
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cbuf *cb = NULL;
|
||||
* if (netconf_err2cb(xerr, &cb) < 0)
|
||||
* err;
|
||||
* printf("%s", cbuf_get(cb));
|
||||
* @endcode
|
||||
* @see clicon_rpc_generate_error
|
||||
*/
|
||||
int
|
||||
netconf_err2cb(cxobj *xerr,
|
||||
cbuf **cberr)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *x;
|
||||
|
||||
if ((cb = cbuf_new()) ==NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((x=xpath_first(xerr, "error-type"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-message"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-info"))!=NULL)
|
||||
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
|
||||
*cberr = cb;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
#include "clixon_data.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
/* Mapping between Clicon startup modes string <--> constants,
|
||||
|
|
@ -89,6 +90,7 @@ static const map_str2int startup_mode_map[] = {
|
|||
* @param[in] dbglevel Debug level
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note CLICON_FEATURE and CLICON_YANG_DIR are treated specially since they are lists
|
||||
*/
|
||||
int
|
||||
clicon_option_dump(clicon_handle h,
|
||||
|
|
@ -101,6 +103,7 @@ clicon_option_dump(clicon_handle h,
|
|||
void *val;
|
||||
size_t klen;
|
||||
size_t vlen;
|
||||
cxobj *x = NULL;
|
||||
|
||||
if (clicon_hash_keys(hash, &keys, &klen) < 0)
|
||||
goto done;
|
||||
|
|
@ -115,6 +118,21 @@ clicon_option_dump(clicon_handle h,
|
|||
else
|
||||
clicon_debug(dbglevel, "%s = NULL", keys[i]);
|
||||
}
|
||||
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree
|
||||
* Since they are lists they are placed in the config tree.
|
||||
*/
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(x), "CLICON_YANG_DIR") != 0)
|
||||
continue;
|
||||
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
|
||||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(x), "CLICON_FEATURE") != 0)
|
||||
continue;
|
||||
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (keys)
|
||||
|
|
@ -145,6 +163,7 @@ parse_configfile(clicon_handle h,
|
|||
char *body;
|
||||
clicon_hash_t *copt = clicon_options(h);
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
int ret;
|
||||
|
||||
if (filename == NULL || !strlen(filename)){
|
||||
|
|
@ -194,13 +213,11 @@ parse_configfile(clicon_handle h,
|
|||
}
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0)
|
||||
goto done;
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = xml_yang_validate_add(h, xc, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_add(h, xc, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (netconf_err2cb(xret, &cbret) < 0)
|
||||
goto done;
|
||||
clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -234,6 +251,8 @@ parse_configfile(clicon_handle h,
|
|||
done:
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (f)
|
||||
|
|
@ -243,6 +262,7 @@ parse_configfile(clicon_handle h,
|
|||
|
||||
/*! Add configuration option overriding file setting
|
||||
* Add to clicon_options hash, and to clicon_conf_xml tree
|
||||
* Assumes clicon_conf_xml_set has been called
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Name of configuration option (see clixon-config.yang)
|
||||
* @param[in] value String value
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
#include "clixon_proto.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_err_string.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_proto_client.h"
|
||||
|
||||
/*! Send internal netconf rpc from client to backend
|
||||
|
|
@ -224,29 +225,22 @@ clicon_rpc_netconf_xml(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Generate and log clicon error function call from Netconf error message
|
||||
* @param[in] prefix Print this string (if given) before: "<prefix>: <error>"
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||
*/
|
||||
int
|
||||
clicon_rpc_generate_error(char *format,
|
||||
clicon_rpc_generate_error(char *prefix,
|
||||
cxobj *xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *x;
|
||||
|
||||
if ((cb = cbuf_new()) ==NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
if (netconf_err2cb(xerr, &cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((x=xpath_first(xerr, "error-type"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-message"))!=NULL)
|
||||
cprintf(cb, "%s ", xml_body(x));
|
||||
if ((x=xpath_first(xerr, "error-info"))!=NULL)
|
||||
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
|
||||
clicon_log(LOG_ERR, "%s: %s", format, cbuf_get(cb));
|
||||
if (prefix)
|
||||
clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb));
|
||||
else
|
||||
clicon_log(LOG_ERR, "%s", cbuf_get(cb));
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xml_changelog.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
|
|
@ -424,8 +425,9 @@ clixon_xml_changelog_init(clicon_handle h)
|
|||
int fd = -1;
|
||||
cxobj *xt = NULL;
|
||||
yang_stmt *yspec;
|
||||
cbuf *cbret = NULL;
|
||||
int ret;
|
||||
cxobj *xret = NULL;
|
||||
cbuf *cbret = NULL;
|
||||
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((filename = clicon_option_str(h, "CLICON_XML_CHANGELOG_FILE")) != NULL){
|
||||
|
|
@ -437,15 +439,13 @@ clixon_xml_changelog_init(clicon_handle h)
|
|||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
if ((ret = xml_yang_validate_all(h, xt, &xret)) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((ret = xml_yang_validate_all(h, xt, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret==1 && (ret = xml_yang_validate_add(h, xt, cbret)) < 0)
|
||||
if (ret==1 && (ret = xml_yang_validate_add(h, xt, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (netconf_err2cb(xret, &cbret) < 0)
|
||||
goto done;
|
||||
clicon_err(OE_YANG, 0, "validation failed: %s", cbuf_get(cbret));
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -455,12 +455,14 @@ clixon_xml_changelog_init(clicon_handle h)
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ xml2cli(FILE *f,
|
|||
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
|
||||
* @param[in] xt XML leaf node of type leafref
|
||||
* @param[in] ytype Yang type statement belonging to the XML node
|
||||
* @param[out] cbret Error buffer
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
|
|
@ -256,7 +256,7 @@ xml2cli(FILE *f,
|
|||
static int
|
||||
validate_leafref(cxobj *xt,
|
||||
yang_stmt *ytype,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ypath;
|
||||
|
|
@ -270,7 +270,7 @@ validate_leafref(cxobj *xt,
|
|||
if ((leafrefbody = xml_body(xt)) == NULL)
|
||||
goto ok;
|
||||
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
|
||||
if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -284,12 +284,12 @@ validate_leafref(cxobj *xt,
|
|||
break;
|
||||
}
|
||||
if (i==xlen){
|
||||
if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
|
||||
if (netconf_bad_element_xml(xret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
retval = 1;
|
||||
done:
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
|
|
@ -312,7 +312,7 @@ validate_leafref(cxobj *xt,
|
|||
* @param[in] xt XML leaf node of type identityref
|
||||
* @param[in] ys Yang spec of leaf
|
||||
* @param[in] ytype Yang type field of type identityref
|
||||
* @param[out] cbret Error buffer
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
|
|
@ -324,7 +324,8 @@ static int
|
|||
validate_identityref(cxobj *xt,
|
||||
yang_stmt *ys,
|
||||
yang_stmt *ytype,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
char *node;
|
||||
|
|
@ -350,13 +351,13 @@ validate_identityref(cxobj *xt,
|
|||
}
|
||||
/* This is the type's base reference */
|
||||
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
|
||||
if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* This is the actual base identity */
|
||||
if ((ybaseid = yang_find_identity(ybaseref, yang_argument_get(ybaseref))) == NULL){
|
||||
if (netconf_missing_element(cbret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -367,7 +368,7 @@ validate_identityref(cxobj *xt,
|
|||
cbuf_reset(cb);
|
||||
cprintf(cb, "Identityref validation failed, %s not derived from %s",
|
||||
node, yang_argument_get(ybaseid));
|
||||
if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0)
|
||||
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -413,7 +414,9 @@ xml_yang_root(cxobj *x,
|
|||
}
|
||||
|
||||
/*! Validate an RPC node
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xrpc XML node to be validated
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
|
|
@ -455,7 +458,7 @@ xml_yang_root(cxobj *x,
|
|||
int
|
||||
xml_yang_validate_rpc(clicon_handle h,
|
||||
cxobj *xrpc,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yn=NULL; /* rpc name */
|
||||
|
|
@ -469,15 +472,15 @@ xml_yang_validate_rpc(clicon_handle h,
|
|||
/* xn is name of rpc, ie <rcp><xn/></rpc> */
|
||||
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
|
||||
if ((yn = xml_spec(xn)) == NULL){
|
||||
if (netconf_unknown_element(cbret, "application", xml_name(xn), NULL) < 0)
|
||||
if (netconf_unknown_element_xml(xret, "application", xml_name(xn), NULL) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((retval = xml_yang_validate_all(h, xn, cbret)) < 1)
|
||||
if ((retval = xml_yang_validate_all(h, xn, xret)) < 1)
|
||||
goto done; /* error or validation fail */
|
||||
if ((retval = xml_yang_validate_add(h, xn, cbret)) < 1)
|
||||
if ((retval = xml_yang_validate_add(h, xn, xret)) < 1)
|
||||
goto done; /* error or validation fail */
|
||||
if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
|
||||
if (xml_apply0(xn, CX_ELMNT, xml_default, h) < 0)
|
||||
goto done;
|
||||
}
|
||||
// ok: /* pass validation */
|
||||
|
|
@ -492,7 +495,7 @@ xml_yang_validate_rpc(clicon_handle h,
|
|||
/*! Check if an xml node is a part of a choice and have >1 siblings
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[in] yt xt:s yang statement
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
|
|
@ -501,7 +504,7 @@ xml_yang_validate_rpc(clicon_handle h,
|
|||
static int
|
||||
check_choice(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y;
|
||||
|
|
@ -552,7 +555,7 @@ check_choice(cxobj *xt,
|
|||
continue; /* not choice */
|
||||
break;
|
||||
}
|
||||
if (netconf_bad_element(cbret, "application", xml_name(x), "Element in choice statement already exists") < 0)
|
||||
if (netconf_bad_element_xml(xret, "application", xml_name(x), "Element in choice statement already exists") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
} /* while */
|
||||
|
|
@ -569,7 +572,7 @@ check_choice(cxobj *xt,
|
|||
/*! Check if an xml node lacks mandatory children
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[in] yt xt:s yang statement
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
|
|
@ -577,7 +580,8 @@ check_choice(cxobj *xt,
|
|||
static int
|
||||
check_mandatory(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -600,7 +604,7 @@ check_mandatory(cxobj *xt,
|
|||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
|
||||
if (netconf_missing_element(cbret, "application", keyname, "Mandatory key") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -623,7 +627,7 @@ check_mandatory(cxobj *xt,
|
|||
break; /* got it */
|
||||
}
|
||||
if (x == NULL){
|
||||
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -640,17 +644,7 @@ check_mandatory(cxobj *xt,
|
|||
if (x == NULL){
|
||||
/* @see RFC7950: 15.6 Error Message for Data That Violates
|
||||
* a Mandatory "choice" Statement */
|
||||
if (cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-type>application</error-type>"
|
||||
"<error-tag>data-missing</error-tag>"
|
||||
"<error-app-tag>missing-choice</error-app-tag>"
|
||||
#ifdef NYI
|
||||
// "<error-path></error-path>"
|
||||
#endif
|
||||
"<error-info><missing-choice>%s</missing-choice></error-info>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"</rpc-error></rpc-reply>",
|
||||
yc->ys_argument) <0)
|
||||
if (netconf_data_missing_xml(xret, yc->ys_argument, NULL) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -667,10 +661,14 @@ check_mandatory(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
*/
|
||||
static int
|
||||
check_list_key(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
@ -690,7 +688,7 @@ check_list_key(cxobj *xt,
|
|||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
|
||||
if (netconf_missing_element(cbret, "application", keyname, "Mandatory key") < 0)
|
||||
if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -739,7 +737,7 @@ check_insert_duplicate(char **vec,
|
|||
* @param[in] xt The parent of x
|
||||
* @param[in] y Its yang spec (Y_LIST)
|
||||
* @param[in] yu A yang unique spec (Y_UNIQUE)
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
|
|
@ -750,7 +748,8 @@ check_unique_list(cxobj *x,
|
|||
cxobj *xt,
|
||||
yang_stmt *y,
|
||||
yang_stmt *yu,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *cvk; /* unique vector */
|
||||
|
|
@ -784,7 +783,7 @@ check_unique_list(cxobj *x,
|
|||
if (cvi==NULL){
|
||||
/* Last element (i) is newly inserted, see if it is already there */
|
||||
if (check_insert_duplicate(vec, i, vlen) < 0){
|
||||
if (netconf_data_not_unique(cbret, x, cvk) < 0)
|
||||
if (netconf_data_not_unique_xml(xret, x, cvk) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -807,7 +806,7 @@ check_unique_list(cxobj *x,
|
|||
* @param[in] x One x (the last) of a specific lis
|
||||
* @param[in] y Yang spec of x
|
||||
* @param[in] nr Number of elements (like x) in thlist
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
|
|
@ -817,7 +816,7 @@ static int
|
|||
check_min_max(cxobj *x,
|
||||
yang_stmt *y,
|
||||
int nr,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ymin; /* yang min */
|
||||
|
|
@ -827,7 +826,7 @@ check_min_max(cxobj *x,
|
|||
if ((ymin = yang_find(y, Y_MIN_ELEMENTS, NULL)) != NULL){
|
||||
cv = yang_cv_get(ymin);
|
||||
if (nr < cv_uint32_get(cv)){
|
||||
if (netconf_minmax_elements(cbret, x, 0) < 0)
|
||||
if (netconf_minmax_elements_xml(xret, x, 0) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -836,7 +835,7 @@ check_min_max(cxobj *x,
|
|||
cv = yang_cv_get(ymax);
|
||||
if (cv_uint32_get(cv) > 0 && /* 0 means unbounded */
|
||||
nr > cv_uint32_get(cv)){
|
||||
if (netconf_minmax_elements(cbret, x, 1) < 0)
|
||||
if (netconf_minmax_elements_xml(xret, x, 1) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -851,9 +850,9 @@ check_min_max(cxobj *x,
|
|||
|
||||
/*! Detect unique constraint for duplicates from parent node and minmax
|
||||
* @param[in] xt XML parent (may have lists w unique constraints as child)
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval 0 Validation failed (xret set)
|
||||
* @retval -1 Error
|
||||
* Assume xt:s children are sorted and yang populated.
|
||||
* The function does two different things of the children of an XML node:
|
||||
|
|
@ -890,7 +889,7 @@ check_min_max(cxobj *x,
|
|||
*/
|
||||
static int
|
||||
check_list_unique_minmax(cxobj *xt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = NULL;
|
||||
|
|
@ -932,7 +931,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
}
|
||||
else {
|
||||
/* Check if the list length violates min/max */
|
||||
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
|
||||
if ((ret = check_min_max(xp, yp, nr, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -953,7 +952,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
do {
|
||||
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
|
||||
/* Check if the list length violates min/max */
|
||||
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
|
||||
if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -973,7 +972,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
* its first element x, its yang spec y, its parent xt, and
|
||||
* a unique yang spec yu,
|
||||
*/
|
||||
if ((ret = check_unique_list(x, xt, y, yu, cbret)) < 0)
|
||||
if ((ret = check_unique_list(x, xt, y, yu, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -984,7 +983,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
*/
|
||||
if (yp){
|
||||
/* Check if the list length violates min/max */
|
||||
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
|
||||
if ((ret = check_min_max(xp, yp, nr, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -996,7 +995,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
do {
|
||||
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
|
||||
/* Check if the list length violates min/max */
|
||||
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
|
||||
if ((ret = check_min_max(xt, ye, 0, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1014,14 +1013,14 @@ check_list_unique_minmax(cxobj *xt,
|
|||
* 1. Check if mandatory leafs present as subs.
|
||||
* 2. Check leaf values, eg int ranges and string regexps.
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* cbuf *cbret = cbuf_new();
|
||||
* if ((ret = xml_yang_validate_add(h, x, cbret)) < 0)
|
||||
* cbuf *xret = NULL;
|
||||
* if ((ret = xml_yang_validate_add(h, x, &xret)) < 0)
|
||||
* err;
|
||||
* if (ret == 0)
|
||||
* fail;
|
||||
|
|
@ -1033,7 +1032,7 @@ check_list_unique_minmax(cxobj *xt,
|
|||
int
|
||||
xml_yang_validate_add(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv = NULL;
|
||||
|
|
@ -1047,11 +1046,11 @@ xml_yang_validate_add(clicon_handle h,
|
|||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
|
||||
if ((ret = check_choice(xt, yt, cbret)) < 0)
|
||||
if ((ret = check_choice(xt, yt, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if ((ret = check_mandatory(xt, yt, cbret)) < 0)
|
||||
if ((ret = check_mandatory(xt, yt, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1073,21 +1072,21 @@ xml_yang_validate_add(clicon_handle h,
|
|||
* are considered as "" */
|
||||
cvtype = cv_type_get(cv);
|
||||
if (cv_isint(cvtype) || cvtype == CGV_BOOL || cvtype == CGV_DEC64){
|
||||
if (netconf_bad_element(cbret, "application", yt->ys_argument, "Invalid NULL value") < 0)
|
||||
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, "Invalid NULL value") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (cv_parse1(body, cv, &reason) != 1){
|
||||
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
|
||||
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, reason) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ys_cv_validate(h, cv, yt, &reason)) != 1){
|
||||
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
|
||||
if (netconf_bad_element_xml(xret, "application", yt->ys_argument, reason) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1098,7 +1097,7 @@ xml_yang_validate_add(clicon_handle h,
|
|||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_add(h, x, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_add(h, x, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1116,11 +1115,12 @@ xml_yang_validate_add(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Some checks done only at edit_config, eg keys in lists
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_list_key_only(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yt; /* yang spec of xt going in */
|
||||
|
|
@ -1130,14 +1130,14 @@ xml_yang_validate_list_key_only(clicon_handle h,
|
|||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
|
||||
if ((ret = check_list_key(xt, yt, cbret)) < 0)
|
||||
if ((ret = check_list_key(xt, yt, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_list_key_only(h, x, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_list_key_only(h, x, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1154,17 +1154,18 @@ xml_yang_validate_list_key_only(clicon_handle h,
|
|||
/*! Validate a single XML node with yang specification for all (not only added) entries
|
||||
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @param[out] xret Error XML tree (if retval=0). Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *x;
|
||||
* cbuf *cbret = cbuf_new();
|
||||
* if ((ret = xml_yang_validate_all(x, cbret)) < 0)
|
||||
* cbuf *xret = NULL;
|
||||
* if ((ret = xml_yang_validate_all(h, x, &xret)) < 0)
|
||||
* err;
|
||||
* if (ret == 0)
|
||||
* fail;
|
||||
* xml_free(xret);
|
||||
* @endcode
|
||||
* @see xml_yang_validate_add
|
||||
* @see xml_yang_validate_rpc
|
||||
|
|
@ -1173,7 +1174,7 @@ xml_yang_validate_list_key_only(clicon_handle h,
|
|||
int
|
||||
xml_yang_validate_all(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys; /* yang node */
|
||||
|
|
@ -1188,7 +1189,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
and !Node has a config sub-statement and it is false */
|
||||
ys=xml_spec(xt);
|
||||
if (ys==NULL){
|
||||
if (netconf_unknown_element(cbret, "application", xml_name(xt), NULL) < 0)
|
||||
if (netconf_unknown_element_xml(xret, "application", xml_name(xt), NULL) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1207,12 +1208,16 @@ xml_yang_validate_all(clicon_handle h,
|
|||
*/
|
||||
if ((yc = yang_find(ys, Y_TYPE, NULL)) != NULL){
|
||||
if (strcmp(yc->ys_argument, "leafref") == 0){
|
||||
if (validate_leafref(xt, yc, cbret) < 0)
|
||||
if ((ret = validate_leafref(xt, yc, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
else if (strcmp(yc->ys_argument, "identityref") == 0){
|
||||
if (validate_identityref(xt, ys, yc, cbret) < 0)
|
||||
if ((ret = validate_identityref(xt, ys, yc, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1230,7 +1235,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
goto done;
|
||||
if (!nr){
|
||||
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
|
||||
if (netconf_operation_failed(cbret, "application",
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
ye?ye->ys_argument:"must xpath validation failed") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
|
|
@ -1242,7 +1247,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
if (netconf_operation_failed(cbret, "application",
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
"when xpath validation failed") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
|
|
@ -1251,7 +1256,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_all(h, x, cbret)) < 0)
|
||||
if ((ret = xml_yang_validate_all(h, x, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1259,7 +1264,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
/* Check unique and min-max after choice test for example*/
|
||||
if (yang_config(ys) != 0){
|
||||
/* Checks if next level contains any unique list constraints */
|
||||
if ((ret = check_list_unique_minmax(xt, cbret)) < 0)
|
||||
if ((ret = check_list_unique_minmax(xt, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1274,24 +1279,25 @@ xml_yang_validate_all(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
||||
* @param[out] xret Error XML tree (if ret == 0). Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval 0 Validation failed (xret set)
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
xml_yang_validate_all_top(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cbuf *cbret)
|
||||
cxobj **xret)
|
||||
{
|
||||
int ret;
|
||||
cxobj *x;
|
||||
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_all(h, x, cbret)) < 1)
|
||||
if ((ret = xml_yang_validate_all(h, x, xret)) < 1)
|
||||
return ret;
|
||||
}
|
||||
if ((ret = check_list_unique_minmax(xt, cbret)) < 1)
|
||||
if ((ret = check_list_unique_minmax(xt, xret)) < 1)
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -2079,13 +2085,8 @@ xml_default(cxobj *xt,
|
|||
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
|
||||
if (!xml_find(xt, y->ys_argument)){
|
||||
|
||||
#ifdef USE_XML_INSERT
|
||||
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
|
||||
goto done;
|
||||
#else
|
||||
if ((xc = xml_new(y->ys_argument, xt, y)) == NULL)
|
||||
goto done;
|
||||
#endif
|
||||
xml_flag_set(xc, XML_FLAG_DEFAULT);
|
||||
if ((xb = xml_new("body", xc, NULL)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -2098,18 +2099,12 @@ xml_default(cxobj *xt,
|
|||
goto done;
|
||||
free(str);
|
||||
added++;
|
||||
#ifdef USE_XML_INSERT
|
||||
if (xml_insert(xt, xc) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef USE_XML_INSERT
|
||||
if (added)
|
||||
xml_sort(xt, NULL);
|
||||
#endif
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -2206,7 +2201,9 @@ xml_spec_populate_rpc(clicon_handle h,
|
|||
if (yrpc){
|
||||
xml_spec_set(x, yrpc);
|
||||
if ((yi = yang_find(yrpc, Y_INPUT, NULL)) != NULL){
|
||||
/* kludge rpc -> input XXX THIS HIDES AN ERROR IN xml_spec_populate */
|
||||
/* xml_spec_populate need to have parent with yang spec for
|
||||
* recursive population to work. Therefore, assign input yang
|
||||
* to rpc level although not 100% intuitive */
|
||||
xml_spec_set(x, yi);
|
||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
|
@ -2497,7 +2494,7 @@ api_path2xml_vec(char **vec,
|
|||
else{
|
||||
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
||||
goto done;
|
||||
if (nvalvec > cvec_len(cvk)){
|
||||
if (nvalvec != cvec_len(cvk)){
|
||||
clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,7 +301,38 @@ ys_free1(yang_stmt *ys)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Free a tree of yang statements recursively */
|
||||
/*! Remove child i from parent yp (dont free)
|
||||
* @param[in] yp Parent node
|
||||
* @param[in] i Order of child to remove
|
||||
* @retval NULL No such node, nothing done
|
||||
* @retval yc returned orphaned yang node
|
||||
* @see ys_free Deallocate yang node
|
||||
* @note Do not call this in a loop of yang children (unless you know what you are doing)
|
||||
*/
|
||||
static yang_stmt *
|
||||
ys_prune(yang_stmt *yp,
|
||||
int i)
|
||||
{
|
||||
size_t size;
|
||||
yang_stmt *yc = NULL;
|
||||
|
||||
if (i >= yp->ys_len)
|
||||
goto done;
|
||||
size = (yp->ys_len - i - 1)*sizeof(struct yang_stmt *);
|
||||
yc = yp->ys_stmt[i];
|
||||
memmove(&yp->ys_stmt[i],
|
||||
&yp->ys_stmt[i+1],
|
||||
size);
|
||||
yc = yp->ys_stmt[yp->ys_len--] = NULL;;
|
||||
done:
|
||||
return yc;
|
||||
}
|
||||
|
||||
/*! Free a yang statement tree recursively
|
||||
* @param[in] ys Yang node to remove and all its children recursively
|
||||
* @note does not remove yang node from tree
|
||||
* @see ys_prune Remove from parent
|
||||
*/
|
||||
int
|
||||
ys_free(yang_stmt *ys)
|
||||
{
|
||||
|
|
@ -1363,7 +1394,6 @@ ys_populate_leaf(clicon_handle h,
|
|||
/* 3b. If not default value, indicate empty cv. */
|
||||
cv_flag_set(cv, V_UNSET); /* no value (no default) */
|
||||
}
|
||||
|
||||
/* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
|
||||
if (yparent && yparent->ys_keyword == Y_LIST){
|
||||
if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
|
||||
|
|
@ -1822,9 +1852,16 @@ ys_populate_unknown(clicon_handle h,
|
|||
|
||||
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
|
||||
*
|
||||
* We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree
|
||||
* See ys_parse_sub for first pass and what can be assumed
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] h Clicon handle
|
||||
* Preferably run this command using yang_apply
|
||||
* Done in 2nd pass after complete parsing to be sure to have a complete
|
||||
* parse-tree
|
||||
|
||||
* After this pass, cv:s are set for LEAFs and LEAF-LISTs
|
||||
* @see ys_parse_sub for first pass and what can be assumed
|
||||
* @see ys_populate2 for after grouping expand and augment
|
||||
* (there may be more functions (all?) that may be moved to ys_populate2)
|
||||
*/
|
||||
int
|
||||
ys_populate(yang_stmt *ys,
|
||||
|
|
@ -1834,11 +1871,6 @@ ys_populate(yang_stmt *ys,
|
|||
clicon_handle h = (clicon_handle)arg;
|
||||
|
||||
switch(ys->ys_keyword){
|
||||
case Y_LEAF:
|
||||
case Y_LEAF_LIST:
|
||||
if (ys_populate_leaf(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (ys_populate_list(h, ys) < 0)
|
||||
goto done;
|
||||
|
|
@ -1851,11 +1883,6 @@ ys_populate(yang_stmt *ys,
|
|||
if (ys_populate_length(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_MANDATORY: /* call yang_mandatory() to check if set */
|
||||
case Y_CONFIG:
|
||||
if (ys_parse(ys, CGV_BOOL) == NULL)
|
||||
goto done;
|
||||
break;
|
||||
case Y_TYPE:
|
||||
if (ys_populate_type(h, ys) < 0)
|
||||
goto done;
|
||||
|
|
@ -1880,8 +1907,41 @@ ys_populate(yang_stmt *ys,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Run after grouping expand and augment
|
||||
* @see ys_populate run before grouping expand and augment
|
||||
*/
|
||||
static int
|
||||
ys_populate2(yang_stmt *ys,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
clicon_handle h = (clicon_handle)arg;
|
||||
|
||||
switch(ys->ys_keyword){
|
||||
case Y_LEAF:
|
||||
case Y_LEAF_LIST:
|
||||
if (ys_populate_leaf(h, ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_MANDATORY: /* call yang_mandatory() to check if set */
|
||||
case Y_CONFIG:
|
||||
if (ys_parse(ys, CGV_BOOL) == NULL)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Resolve a grouping name from a point in the yang tree
|
||||
* @retval 0 OK, but ygrouping determines if a grouping was resolved or not
|
||||
* @param[in] ys Yang statement of "uses" statement doing the lookup
|
||||
* @param[in] prefix Prefix of grouping to look for
|
||||
* @param[in] name Name of grouping to look for
|
||||
* @param[out] ygrouping0 A found grouping yang structure as result
|
||||
* @retval 0 OK, ygrouping may be NULL
|
||||
* @retval -1 Error, with clicon_err called
|
||||
*/
|
||||
static int
|
||||
|
|
@ -1999,23 +2059,94 @@ yang_augment_spec(yang_stmt *ysp,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Macro expansion of grouping/uses done in step 2 of yang parsing
|
||||
NOTE
|
||||
RFC6020 says this:
|
||||
Identifiers appearing inside the grouping are resolved relative to the scope in which the
|
||||
grouping is defined, not where it is used. Prefix mappings, type names, grouping
|
||||
names, and extension usage are evaluated in the hierarchy where the
|
||||
"grouping" statement appears.
|
||||
But it will be very difficult to generate keys etc with this semantics. So for now I
|
||||
macro-expand them
|
||||
/*! Given a refine node, perform the refinement action on the target refine node
|
||||
* The RFC is somewhat complicate in the rules for refine.
|
||||
* Most nodes will be replaced, but some are added
|
||||
* @param[in] yr Refine node
|
||||
* @param[in] yt Refine target node (will be modified)
|
||||
* @see RFC7950 Sec 7.13.2
|
||||
* There may be some missed cornercases
|
||||
*/
|
||||
static int
|
||||
yang_expand(yang_stmt *yn)
|
||||
ys_do_refine(yang_stmt *yr,
|
||||
yang_stmt *yt)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yrc; /* refine child */
|
||||
yang_stmt *yrc1;
|
||||
yang_stmt *ytc; /* target child */
|
||||
enum rfc_6020 keyw;
|
||||
int i;
|
||||
|
||||
/* Loop through refine node children. First if remove do that first
|
||||
* In some cases remove a set of nodes.
|
||||
*/
|
||||
yrc = NULL;
|
||||
while ((yrc = yn_each(yr, yrc)) != NULL) {
|
||||
keyw = yang_keyword_get(yrc);
|
||||
switch (keyw){
|
||||
case Y_DEFAULT: /* remove old, add new */
|
||||
case Y_DESCRIPTION:
|
||||
case Y_REFERENCE:
|
||||
case Y_CONFIG:
|
||||
case Y_MANDATORY:
|
||||
case Y_PRESENCE:
|
||||
case Y_MIN_ELEMENTS:
|
||||
case Y_MAX_ELEMENTS:
|
||||
case Y_EXTENSION:
|
||||
/* Remove old matching, dont increment due to prune in loop */
|
||||
for (i=0; i<yt->ys_len; ){
|
||||
ytc = yt->ys_stmt[i];
|
||||
if (keyw != yang_keyword_get(ytc)){
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
ys_prune(yt, i);
|
||||
ys_free(ytc);
|
||||
}
|
||||
/* fall through and add if not found */
|
||||
case Y_MUST: /* keep old, add new */
|
||||
case Y_IF_FEATURE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Second, add the node(s) */
|
||||
yrc = NULL;
|
||||
while ((yrc = yn_each(yr, yrc)) != NULL) {
|
||||
keyw = yang_keyword_get(yrc);
|
||||
/* Make copy */
|
||||
if ((yrc1 = ys_dup(yrc)) == NULL)
|
||||
goto done;
|
||||
if (yn_insert(yt, yrc1) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Macro expansion of grouping/uses done in step 2 of yang parsing
|
||||
* RFC7950:
|
||||
* Identifiers appearing inside the grouping are resolved
|
||||
* relative to the scope in which the grouping is defined, not where it is
|
||||
* used. Prefix mappings, type names, grouping names, and extension usage are
|
||||
* evaluated in the hierarchy where the "grouping" statement appears.
|
||||
* The identifiers defined in the grouping are not bound to a namespace
|
||||
* until the contents of the grouping are added to the schema tree via a
|
||||
* "uses" statement that does not appear inside a "grouping" statement,
|
||||
* at which point they are bound to the namespace of the current module.
|
||||
*/
|
||||
static int
|
||||
yang_expand_grouping(yang_stmt *yn)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *ygrouping;
|
||||
yang_stmt *yg;
|
||||
yang_stmt *ygrouping; /* grouping original */
|
||||
yang_stmt *ygrouping2; /* grouping copy */
|
||||
yang_stmt *yg; /* grouping child */
|
||||
yang_stmt *yr; /* refinement */
|
||||
int glen;
|
||||
int i;
|
||||
int j;
|
||||
|
|
@ -2034,27 +2165,32 @@ yang_expand(yang_stmt *yn)
|
|||
prefix = yarg_prefix(ys); /* And this its prefix */
|
||||
if (ys_grouping_resolve(ys, prefix, name, &ygrouping) < 0)
|
||||
goto done;
|
||||
|
||||
if (prefix){
|
||||
free(prefix);
|
||||
prefix = NULL;
|
||||
}
|
||||
if (ygrouping == NULL){
|
||||
clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
|
||||
__FUNCTION__, ys->ys_argument, ys_module(ys)->ys_argument);
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
if (prefix)
|
||||
free(prefix); /* XXX move up */
|
||||
/* Check mark flag to see if this grouping (itself) has been expanded
|
||||
If not, this needs to be done before we can insert it into
|
||||
the 'uses' place */
|
||||
if ((ygrouping->ys_flags & YANG_FLAG_MARK) == 0){
|
||||
if (yang_expand(ygrouping) < 0)
|
||||
if (yang_expand_grouping(ygrouping) < 0)
|
||||
goto done;
|
||||
ygrouping->ys_flags |= YANG_FLAG_MARK; /* Mark as expanded */
|
||||
}
|
||||
/* Make a copy of the grouping, then make refinements to this copy
|
||||
*/
|
||||
if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
|
||||
goto done;
|
||||
/* Replace ys with ygrouping,...
|
||||
* First enlarge parent vector
|
||||
*/
|
||||
glen = ygrouping->ys_len;
|
||||
glen = ygrouping2->ys_len;
|
||||
/*
|
||||
* yn is parent: the children of ygrouping replaces ys.
|
||||
* Is there a case when glen == 0? YES AND THIS BREAKS
|
||||
|
|
@ -2064,7 +2200,7 @@ yang_expand(yang_stmt *yn)
|
|||
yn->ys_len += glen - 1;
|
||||
if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yn->ys_len)*sizeof(yang_stmt *))) == 0){
|
||||
clicon_err(OE_YANG, errno, "realloc");
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
/* Then move all existing elements up from i+1 (not uses-stmt) */
|
||||
if (size)
|
||||
|
|
@ -2072,16 +2208,42 @@ yang_expand(yang_stmt *yn)
|
|||
&yn->ys_stmt[i+1],
|
||||
size);
|
||||
}
|
||||
|
||||
/* Iterate through refinements and modify grouping copy
|
||||
* See RFC 7950 7.13.2 yrt is the refine target node
|
||||
*/
|
||||
yr = NULL;
|
||||
while ((yr = yn_each(ys, yr)) != NULL) {
|
||||
yang_stmt *yrt; /* refine target node */
|
||||
if (yang_keyword_get(yr) != Y_REFINE)
|
||||
continue;
|
||||
/* Find a node */
|
||||
if (yang_desc_schema_nodeid(ygrouping2,
|
||||
yang_argument_get(yr),
|
||||
-1,
|
||||
&yrt) < 0)
|
||||
goto done;
|
||||
/* Not found, try next */
|
||||
if (yrt == NULL)
|
||||
continue;
|
||||
/* Do the actual refinement */
|
||||
if (ys_do_refine(yr, yrt) < 0)
|
||||
goto done;
|
||||
/* RFC: The argument is a string that identifies a node in the
|
||||
* grouping. I interpret that as only one node --> break */
|
||||
break;
|
||||
}
|
||||
/* Then copy and insert each child element */
|
||||
for (j=0; j<glen; j++){
|
||||
if ((yg = ys_dup(ygrouping->ys_stmt[j])) == NULL)
|
||||
goto done;
|
||||
yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
|
||||
yn->ys_stmt[i+j] = yg;
|
||||
yg->ys_parent = yn;
|
||||
}
|
||||
/* XXX: refine */
|
||||
/* Remove 'uses' node */
|
||||
ys_free(ys);
|
||||
/* Remove the grouping copy */
|
||||
ygrouping2->ys_len = 0;
|
||||
ys_free(ygrouping2);
|
||||
break; /* Note same child is re-iterated since it may be changed */
|
||||
default:
|
||||
i++;
|
||||
|
|
@ -2091,7 +2253,7 @@ yang_expand(yang_stmt *yn)
|
|||
/* Second pass since length may have changed */
|
||||
for (i=0; i<yn->ys_len; i++){
|
||||
ys = yn->ys_stmt[i];
|
||||
if (yang_expand(ys) < 0)
|
||||
if (yang_expand_grouping(ys) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -2441,8 +2603,8 @@ ys_schemanode_check(yang_stmt *ys,
|
|||
if (yang_desc_schema_nodeid(yp, ys->ys_argument, -1, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %d %s",
|
||||
ys->ys_keyword,
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
|
||||
yang_key2str(ys->ys_keyword),
|
||||
ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -2609,15 +2771,20 @@ yang_parse_post(clicon_handle h,
|
|||
|
||||
/* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
||||
for (i=modnr; i<yspec->ys_len; i++){
|
||||
if (yang_expand(yspec->ys_stmt[i]) < 0)
|
||||
if (yang_expand_grouping(yspec->ys_stmt[i]) < 0)
|
||||
goto done;
|
||||
yang_apply(yspec->ys_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
||||
}
|
||||
|
||||
/* 7: Top-level augmentation of all modules XXX: only new modules? */
|
||||
/* 7: Top-level augmentation of all modules. (Augment also in uses) */
|
||||
if (yang_augment_spec(yspec, modnr) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4: Go through parse tree and do 2nd step populate (eg default) */
|
||||
for (i=modnr; i<yspec->ys_len; i++)
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate2, (void*)h) < 0)
|
||||
goto done;
|
||||
|
||||
/* 8: sanity check of schemanode references, need more here */
|
||||
for (i=modnr; i<yspec->ys_len; i++)
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
|
||||
|
|
@ -2829,19 +2996,13 @@ yang_spec_load_dir(clicon_handle h,
|
|||
* This is a failsafe in case anything else fails
|
||||
*/
|
||||
if (revm && rev0){
|
||||
int size;
|
||||
if (revm > rev0) /* Loaded module is older or eq -> remove ym */
|
||||
ym = ym0;
|
||||
for (j=0; j<yspec->ys_len; j++)
|
||||
if (yspec->ys_stmt[j] == ym)
|
||||
break;
|
||||
size = (yspec->ys_len - j - 1)*sizeof(struct yang_stmt *);
|
||||
memmove(&yspec->ys_stmt[j],
|
||||
&yspec->ys_stmt[j+1],
|
||||
size);
|
||||
ys_prune(yspec, j);
|
||||
ys_free(ym);
|
||||
yspec->ys_len--;
|
||||
yspec->ys_stmt[yspec->ys_len] = NULL;
|
||||
}
|
||||
}
|
||||
if (yang_parse_post(h, yspec, modnr) < 0)
|
||||
|
|
@ -3101,7 +3262,7 @@ yang_abs_schema_nodeid(yang_stmt *yspec,
|
|||
* @param[out] yres First yang node matching schema nodeid
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error, with clicon_err called
|
||||
* @see yang_schema_nodeid
|
||||
* @see yang_abs_schema_nodeid
|
||||
* Used in yang: unique, refine, uses augment
|
||||
*/
|
||||
int
|
||||
|
|
@ -3429,6 +3590,7 @@ yang_arg2cvec(yang_stmt *ys,
|
|||
|
||||
/*! Check if yang node yn has key-stmt as child which matches name
|
||||
*
|
||||
* The function looks at the LIST argument string (not actual children)
|
||||
* @param[in] yn Yang node with sub-statements (look for a key child)
|
||||
* @param[in] name Check if this name (eg "b") is a key in the yang key statement
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# Run, eg as:
|
||||
# ./all.sh 2>&1 | tee test.log # break on first test
|
||||
|
||||
# Pattern to run tests, default is all, but you may want to narrow it down
|
||||
: ${pattern:=test_*.sh}
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
echo "usage: $0 # detailed logs and stopon first error"
|
||||
exit -1
|
||||
|
|
@ -9,7 +12,7 @@ fi
|
|||
|
||||
err=0
|
||||
testnr=0
|
||||
for test in test_*.sh; do
|
||||
for test in $pattern; do
|
||||
if [ $testnr != 0 ]; then echo; fi
|
||||
testfile=$test
|
||||
. ./$test
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
# Run valgrind leak test for cli, restconf, netconf or background.
|
||||
# Stop on first error
|
||||
|
||||
# Pattern to run tests, default is all, but you may want to narrow it down
|
||||
: ${pattern:=test_*.sh}
|
||||
|
||||
# Run valgrindtest once, args:
|
||||
# what: (cli|netconf|restconf|backend)* # no args means all
|
||||
memonce(){
|
||||
|
|
@ -45,7 +48,7 @@ memonce(){
|
|||
esac
|
||||
|
||||
err=0
|
||||
for test in test_*.sh; do
|
||||
for test in $pattern; do
|
||||
if [ $testnr != 0 ]; then echo; fi
|
||||
testfile=$test
|
||||
. ./$test
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_FEATURE>a:test</CLICON_FEATURE>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
|
|
@ -66,11 +67,11 @@ module ietf-interfaces {
|
|||
}
|
||||
}
|
||||
grouping endpoint {
|
||||
description "A reusable endpoint group.";
|
||||
leaf mip {
|
||||
description "A reusable endpoint group. From rf7950 Sec 7.12.2";
|
||||
leaf ip {
|
||||
type string;
|
||||
}
|
||||
leaf mport {
|
||||
leaf port {
|
||||
type uint16;
|
||||
}
|
||||
}
|
||||
|
|
@ -98,12 +99,14 @@ module example-augment {
|
|||
identity you {
|
||||
base my-type;
|
||||
}
|
||||
grouping mypoint {
|
||||
description "A reusable endpoint group.";
|
||||
leaf ip {
|
||||
grouping localgroup {
|
||||
description "Local grouping defining lid and lport";
|
||||
leaf lid {
|
||||
description "this will be kept as-is";
|
||||
type string;
|
||||
}
|
||||
leaf port {
|
||||
leaf lport {
|
||||
description "this will be refined";
|
||||
type uint16;
|
||||
}
|
||||
}
|
||||
|
|
@ -124,12 +127,14 @@ module example-augment {
|
|||
}
|
||||
}
|
||||
uses if:endpoint {
|
||||
description "Use an external grouping defining ip and port";
|
||||
refine port {
|
||||
default 80;
|
||||
}
|
||||
}
|
||||
uses mypoint {
|
||||
refine mport {
|
||||
uses localgroup {
|
||||
description "Use a local grouping defining lip and lport";
|
||||
refine lport {
|
||||
default 8080;
|
||||
}
|
||||
}
|
||||
|
|
@ -137,37 +142,35 @@ module example-augment {
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg -y $fyang
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
fi
|
||||
|
||||
# mandatory-leaf See RFC7950 Sec 7.17
|
||||
# Error1: the xml should have xmlns for "mymod"
|
||||
# XMLNS_YANG_ONLY must be undeffed
|
||||
new "netconf set interface with augmented type and mandatory leaf"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
<interface xmlns:mymod="urn:example:augment">
|
||||
<name>e1</name>
|
||||
<type>mymod:some-new-iftype</type>
|
||||
<mymod:mandatory-leaf>true</mymod:mandatory-leaf>
|
||||
</interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate ok"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
new "netconf verify get with refined ports"
|
||||
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 xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mandatory-leaf>true</mandatory-leaf><port>80</port><lport>8080</lport></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf set identity defined in other"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
<interface xmlns:mymod="urn:example:augment">
|
||||
<name>e2</name>
|
||||
<type>fddi</type>
|
||||
|
|
@ -176,10 +179,10 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><can
|
|||
</interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate ok"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf set identity defined in main"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
<interface xmlns:mymod="urn:example:augment">
|
||||
<name>e3</name>
|
||||
<type>fddi</type>
|
||||
|
|
@ -188,9 +191,11 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><can
|
|||
</interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate ok"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "discard"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ if [ $BE -ne 0 ]; then
|
|||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
new "cli configure top"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||
|
|
@ -108,80 +109,82 @@ cat <<EOF > $fyang
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
fi
|
||||
|
||||
new "Set crypto to aes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">aes</crypto></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">aes</crypto></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf validate "
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Set crypto to mc:aes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:aes</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:aes</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Set crypto to des:des3"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Set crypto to mc:foo"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:foo</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:foo</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Set crypto to des:des3 using xmlns"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:des="urn:example:des">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:des="urn:example:des">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
if false; then
|
||||
# XXX this is not supported
|
||||
#new "Set crypto to x:des3 using xmlns"
|
||||
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:x="urn:example:des">x:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
new "Set crypto to x:des3 using xmlns"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:x="urn:example:des">x:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
fi # not supported
|
||||
|
||||
new "Set crypto to foo:bar"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">foo:bar</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">foo:bar</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
new "netconf validate (expect fail)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "cli set crypto to mc:aes"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto mc:aes" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o set crypto mc:aes" 0 "^$"
|
||||
|
||||
new "cli validate"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
|
||||
|
||||
new "cli set crypto to aes"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto aes" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o set crypto aes" 0 "^$"
|
||||
|
||||
new "cli validate"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
|
||||
|
||||
new "cli set crypto to des:des3"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto des:des3" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o set crypto des:des3" 0 "^$"
|
||||
|
||||
new "cli validate"
|
||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$"
|
||||
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -149,7 +149,8 @@ new "start restconf daemon (-a is enable http basic auth)"
|
|||
start_restconf -f $cfg -- -a
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
wait_backend
|
||||
wait_restconf
|
||||
|
||||
new "auth get"
|
||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data": {"clixon-example:state": {"op": ["42","41","43"]}}}
|
||||
|
|
@ -183,10 +184,10 @@ new "admin edit nacm"
|
|||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"nacm-example:x": 1}' http://localhost/restconf/data/nacm-example:x)" 0 ""
|
||||
|
||||
new "limited edit nacm"
|
||||
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"nacm-example:x": 2}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
|
||||
new "guest edit nacm"
|
||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"nacm-example:x": 3}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||
|
||||
new "cli show conf as admin"
|
||||
expectfn "$clixon_cli -1 -U andy -l o -f $cfg show conf" 0 "^x 1;$"
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ new "commit it"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "enable nacm"
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
|
||||
#--------------- nacm enabled
|
||||
|
||||
|
|
@ -256,7 +256,7 @@ expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml
|
|||
#------------------ Set read-default permit
|
||||
|
||||
new "admin set read-default permit"
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"read-default": "permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 ""
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:read-default": "permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 ""
|
||||
|
||||
new "limit read ok"
|
||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate": [{"k": "key42","value": "val42"},{ "k": "key43","value": "val43"}]}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ nacm(){
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "enable nacm"
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
}
|
||||
|
||||
#--------------- enable nacm
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ new "commit it"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "enable nacm"
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
|
||||
|
||||
#--------------- nacm enabled
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ if [ $BE -ne 0 ]; then
|
|||
start_backend -s init -f $cfg -- -s
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
new "netconf hello"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_CONFIGFILE>/tmp/conf_yang.xml</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
|
|
@ -149,16 +150,16 @@ cat <<EOF > $dbdir/running_db
|
|||
</config>
|
||||
EOF
|
||||
|
||||
new "test params: -s running -f $cfg -y $fyang -- -s"
|
||||
new "test params: -s running -f $cfg -- -s"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg -y $fyang
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend"
|
||||
start_backend -s running -f $cfg -y $fyang -- -s
|
||||
start_backend -s running -f $cfg -- -s
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
|
|
@ -169,75 +170,75 @@ new "state data (should be unordered: 42,41,43)"
|
|||
cat <<EOF > $tmp
|
||||
<rpc><get><filter type="xpath" select="state"/></get></rpc>]]>]]>
|
||||
EOF
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '<rpc-reply><data><state xmlns="urn:example:clixon"><op>42</op><op>41</op><op>43</op></state></data></rpc-reply>]]>]]>'
|
||||
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>]]>]]>'
|
||||
|
||||
# Check as file
|
||||
new "verify running from start, should be: c,l,y0,y1,y2,y3; y1 and y3 sorted."
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>]]>]]>$'
|
||||
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 -y $fyang" 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=\"/y2[k='a']\"/></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 -y $fyang" 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=\"/y3[k='a']\"/></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 -y $fyang" 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=\"/y2[k='b']\"/></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 -y $fyang" 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=\"/y3[k='b']\"/></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 -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# LEAF_LISTS
|
||||
|
||||
new "add two entries (c,b) to leaf-list user order"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">c</y0><y0 xmlns="urn:example:order">b</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">c</y0><y0 xmlns="urn:example:order">b</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "add one entry (a) to leaf-list user order"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">a</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">a</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "add one entry (0) to leaf-list user order after commit"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">0</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:order">0</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
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 -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
# LISTS
|
||||
|
||||
new "add two entries to list user order"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>foo</a></y2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:order"><k>b</k><a>foo</a></y2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "add one entry to list user order"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>]]>]]>$"
|
||||
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 -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "Overwrite existing ordered-by user y2->c"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
<k>c</k><a>newc</a>
|
||||
</y2></config></edit-config></rpc>]]>]]>'
|
||||
|
||||
new "Overwrite existing ordered-by user y2->b"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
<k>b</k><a>newb</a>
|
||||
</y2></config></edit-config></rpc>]]>]]>'
|
||||
|
||||
new "Overwrite existing ordered-by user y2->a"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:order">
|
||||
<k>a</k><a>newa</a>
|
||||
</y2></config></edit-config></rpc>]]>]]>'
|
||||
|
||||
new "Tests for no duplicates."
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/y2"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 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="/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>]]>]]>$'
|
||||
|
||||
#-- order by type rather than strings.
|
||||
# there are three leaf-lists:strings, ints, and decimal64, and two lists:
|
||||
|
|
@ -246,44 +247,44 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><cand
|
|||
# The check is to write the entries as: 10,2,1, and then expect them to
|
||||
# get back as 1,2,10 (if typed).
|
||||
new "put strings (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
<strings>10</strings><strings>2</strings><strings>1</strings>
|
||||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check string order (1,10,2)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "put leaf-list int (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
<ints>10</ints><ints>2</ints><ints>1</ints>
|
||||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check leaf-list int order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "put list int (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
<listints><a>10</a></listints><listints><a>2</a></listints><listints><a>1</a></listints>
|
||||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check list int order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "put leaf-list decimal64 (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
<decs>10.0</decs><decs>2.0</decs><decs>1.0</decs>
|
||||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check leaf-list decimal64 order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "put list decimal64 (10,2,1)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><types xmlns="urn:example:order">
|
||||
<listdecs><a>10.0</a></listdecs><listdecs><a>2.0</a></listdecs><listdecs><a>1.0</a></listdecs>
|
||||
</types></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "check list decimal64 order (1,2,10)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ if [ $BE -ne 0 ]; then
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
sudo pkill clixon_backend # to be sure
|
||||
new "start backend -s init -f $cfg -- -s"
|
||||
start_backend -s init -f $cfg -- -s
|
||||
fi
|
||||
|
|
@ -57,8 +58,6 @@ new "waiting"
|
|||
wait_backend
|
||||
wait_restconf
|
||||
|
||||
new "restconf tests"
|
||||
|
||||
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" 0 "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
|
||||
<Link rel='restconf' href='/restconf'/>
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ fyang=$dir/restconf.yang
|
|||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/var</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>$dir/restconf.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
|
|
@ -66,7 +66,7 @@ module example{
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
|
|
@ -74,28 +74,30 @@ if [ $BE -ne 0 ]; then
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
sudo pkill clixon_backend # to be sure
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
fi
|
||||
|
||||
new "kill old restconf daemon"
|
||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon"
|
||||
start_restconf -f $cfg -y $fyang
|
||||
start_restconf -f $cfg
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
wait_restconf
|
||||
|
||||
new "restconf tests"
|
||||
|
||||
new "restconf POST tree without key"
|
||||
expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"type":"regular"}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "name"},"error-severity": "error","error-message": "Mandatory key"}}}
'
|
||||
|
||||
new "restconf POST initial tree"
|
||||
expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
|
||||
|
||||
new "restconf POST top without namespace"
|
||||
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "cont1"},"error-severity": "error","error-message": "Unassigned yang spec"}}}'
|
||||
|
||||
new "restconf GET datastore initial"
|
||||
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
||||
|
||||
|
|
@ -114,17 +116,19 @@ new "restconf GET if-type"
|
|||
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1/interface=local0/type" 0 '{"example:type": "regular"}'
|
||||
|
||||
new "restconf POST interface without mandatory type"
|
||||
expectfn 'curl -s -X POST http://localhost/restconf/data/example:cont1 -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/example:cont1' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "type"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
||||
expectfn 'curl -s -X POST http://localhost/restconf/data/example:cont1 -d {"example:interface":{"name":"TEST"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "type"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
||||
|
||||
new "restconf POST interface without mandatory key"
|
||||
expectfn 'curl -s -X POST http://localhost/restconf/data/example:cont1 -d {"interface":{"type":"regular"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "name"},"error-severity": "error","error-message": "Mandatory key"}}}
'
|
||||
expectfn 'curl -s -X POST http://localhost/restconf/data/example:cont1 -d {"example:interface":{"type":"regular"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "name"},"error-severity": "error","error-message": "Mandatory key"}}}
'
|
||||
|
||||
new "restconf POST interface"
|
||||
expectfn 'curl -s -X POST -d {"example:interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/example:cont1' 0 ""
|
||||
|
||||
# XXX should it be example:interface?
|
||||
new "restconf POST interface without namespace"
|
||||
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST2","type":"eth0"}} http://localhost/restconf/data/example:cont1' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "Data is not prefixed with matching namespace"}}}'
|
||||
|
||||
new "restconf POST again"
|
||||
expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||
expecteq "$(curl -s -X POST -d '{"example:interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||
|
||||
new "restconf POST from top"
|
||||
expecteq "$(curl -s -X POST -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||
|
|
@ -163,16 +167,22 @@ new "restconf PUT initial datastore again"
|
|||
expectfn 'curl -s -X PUT -d {"data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
|
||||
|
||||
new "restconf PUT change interface"
|
||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/example:cont1/interface=local0' 0 ""
|
||||
expectfn 'curl -s -X PUT -d {"example:interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/example:cont1/interface=local0' 0 ""
|
||||
|
||||
new "restconf GET datastore atm"
|
||||
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}'
|
||||
|
||||
new "restconf PUT add interface"
|
||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 ""
|
||||
expectfn 'curl -s -X PUT -d {"example:interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 ""
|
||||
|
||||
new "restconf PUT change key error"
|
||||
expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
expectfn 'curl -is -X PUT -d {"example:interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT change type to eth0 (non-key sub-element to list)"
|
||||
expectfn 'curl -s -X PUT -d {"example:type":"eth0"} http://localhost/restconf/data/example:cont1/interface=local0/type' 0 ""
|
||||
|
||||
new "restconf GET datastore eth"
|
||||
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1/interface=local0" 0 '{"example:interface": \[{"name": "local0","type": "eth0"}\]}'
|
||||
|
||||
#--------------- json type tests
|
||||
new "restconf POST type x3"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ fyang=$dir/list.yang
|
|||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/var</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
|
|
@ -32,13 +31,30 @@ module list{
|
|||
prefix ex;
|
||||
container c{
|
||||
list a{
|
||||
key b;
|
||||
key "b c";
|
||||
leaf b{
|
||||
type string;
|
||||
}
|
||||
leaf c{
|
||||
type string;
|
||||
}
|
||||
leaf nonkey{
|
||||
description "non-key element";
|
||||
type string;
|
||||
}
|
||||
list e{
|
||||
description "A list in a list";
|
||||
key "f";
|
||||
leaf f{
|
||||
type string;
|
||||
}
|
||||
leaf nonkey{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf-list f{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf-list d{
|
||||
type string;
|
||||
|
|
@ -55,6 +71,8 @@ if [ $BE -ne 0 ]; then
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
sudo pkill clixon_backend # to be sure
|
||||
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
fi
|
||||
|
|
@ -69,17 +87,36 @@ new "waiting"
|
|||
wait_backend
|
||||
wait_restconf
|
||||
|
||||
new "restconf PUT add list entry"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x -d {"list:a":{"b":"x","c":"0"}}' 0 ''
|
||||
new "restconf PUT add whole list entry"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y -d {"list:a":{"b":"x","c":"y","nonkey":"0"}}' 0 ''
|
||||
|
||||
new "restconf PUT change regular list entry"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x -d {"list:a":{"b":"x","c":"z"}}' 0 ''
|
||||
new "restconf PUT add whole list entry XML"
|
||||
expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xx</b><c>xy</c><nonkey>0</nonkey></a>' http://localhost/restconf/data/list:c/a=xx,xy)" 0 ''
|
||||
|
||||
new "restconf PUT change list key entry (expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x -d {"list:a":{"b":"y"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT change actual list key entry (expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x/b -d {"b":"y"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
new "restconf PUT change whole list entry (same keys)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y -d {"list:a":{"b":"x","c":"y","nonkey":"z"}}' 0 ''
|
||||
|
||||
new "restconf PUT change whole list entry (no namespace)(expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y -d {"a":{"b":"x","c":"y","nonkey":"z"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "Data is not prefixed with matching namespace"}}}'
|
||||
|
||||
new "restconf PUT change list entry (wrong keys)(expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y -d {"list:a":{"b":"y","c":"x"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT change list entry (wrong keys)(expect fail) XML"
|
||||
expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xy</b><c>xz</c><nonkey>0</nonkey></a>' http://localhost/restconf/data/list:c/a=xx,xy)" 0 '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>api-path keys do not match data keys</error-message></error></errors>
'
|
||||
|
||||
new "restconf PUT change list entry (just one key)(expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x -d {"list:a":{"b":"x"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "List key a length mismatch"}}}
'
|
||||
|
||||
new "restconf PUT sub non-key"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/nonkey -d {"list:nonkey":"u"}' 0 ''
|
||||
|
||||
new "restconf PUT sub key same value"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/b -d {"list:b":"x"}' 0 ''
|
||||
|
||||
new "restconf PUT just key other value (should fail)ZX"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x/b -d {"b":"y"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "List key a length mismatch"}}}'
|
||||
|
||||
new "restconf PUT add leaf-list entry"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/d=x -d {"list:d":"x"}' 0 ''
|
||||
|
|
@ -87,6 +124,31 @@ expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/d=x -d {"list:d":
|
|||
new "restconf PUT change leaf-list entry (expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/d=x -d {"list:d":"y"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT list-list"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/e=z -d {"list:e":{"f":"z","nonkey":"0"}}' 0 ''
|
||||
|
||||
new "restconf PUT change list-lst entry (wrong keys)(expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/e=z -d {"list:e":{"f":"wrong","nonley":"0"}}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT list-list sub non-key"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/e=z/nonkey -d {"list:nonkey":"u"}' 0 ''
|
||||
|
||||
new "restconf PUT list-list single first key"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x/e=z/f -d {"f":"z"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "List key a length mismatch"}}}'
|
||||
|
||||
new "restconf PUT list-list just key ok"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/e=z/f -d {"list:f":"z"}' 0 ''
|
||||
|
||||
new "restconf PUT list-list just key just key wrong value (should fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/e=z/f -d {"list:f":"wrong"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
new "restconf PUT add list+leaf-list entry"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/f=u -d {"list:f":"u"}' 0 ''
|
||||
|
||||
new "restconf PUT change list+leaf-list entry (expect fail)"
|
||||
expectfn 'curl -s -X PUT http://localhost/restconf/data/list:c/a=x,y/f=u -d {"list:f":"w"}' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
||||
|
|
|
|||
|
|
@ -77,11 +77,11 @@ expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0","y":"99"}}' htt
|
|||
|
||||
new "restconf example rpc json/json"
|
||||
# XXX example:input example:output
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}}
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+json' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}}
|
||||
'
|
||||
|
||||
new "restconf example rpc xml/json"
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}}
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+json' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output": {"x": "0","y": "42"}}
|
||||
'
|
||||
|
||||
new "restconf example rpc json/xml"
|
||||
|
|
@ -92,6 +92,13 @@ new "restconf example rpc xml/xml"
|
|||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/clixon-example:example)" 0 '<output xmlns="urn:example:clixon"><x>0</x><y>42</y></output>
|
||||
'
|
||||
|
||||
new "restconf example rpc xml in w json encoding (expect fail)"
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/clixon-example:example)" 0 "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message> on line 1: syntax error at or before: '<'</error-message></error></errors>
"
|
||||
|
||||
|
||||
new "restconf example rpc json in xml encoding (expect fail)"
|
||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message>xml_parse: line 0: syntax error: at or before: "</error-message></error></errors>
'
|
||||
|
||||
new "netconf example rpc"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
|
|
@ -101,7 +102,7 @@ cat <<EOF > $fyang
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
|
|
@ -109,15 +110,15 @@ if [ $BE -ne 0 ]; then
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
fi
|
||||
|
||||
new "kill old restconf daemon"
|
||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
new "start restconf daemon"
|
||||
start_restconf -f $cfg -y $fyang
|
||||
start_restconf -f $cfg
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
|
|
@ -128,35 +129,35 @@ 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 -y $fyang" 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="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>]]>]]>'
|
||||
|
||||
new "netconf event stream discovery RFC8040 Sec 6.2"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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="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>]]>]]>'
|
||||
|
||||
#
|
||||
# 1.2 Netconf stream subscription
|
||||
new "netconf EXAMPLE subscription"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
expectwait "$clixon_netconf -qf $cfg" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
|
||||
new "netconf subscription with empty startTime"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime/></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail:'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime/></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail:'
|
||||
|
||||
new "netconf EXAMPLE subscription with simple filter"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><filter type="xpath" select="event"/></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
expectwait "$clixon_netconf -qf $cfg" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><filter type="xpath" select="event"/></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
|
||||
new "netconf EXAMPLE subscription with filter classifier"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
expectwait "$clixon_netconf -qf $cfg" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||
|
||||
new "netconf NONEXIST subscription"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
|
||||
expectwait "$clixon_netconf -qf $cfg" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
|
||||
|
||||
new "netconf EXAMPLE subscription with wrong date"
|
||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail:' 0
|
||||
expectwait "$clixon_netconf -qf $cfg" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail:' 0
|
||||
|
||||
#new "netconf EXAMPLE subscription with replay"
|
||||
#NOW=$(date +"%Y-%m-%dT%H:%M:%S")
|
||||
#sleep 10
|
||||
#expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><startTime>$NOW</startTime></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' 10
|
||||
#expectwait "$clixon_netconf -qf $cfg" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><startTime>$NOW</startTime></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' 10
|
||||
sleep 2
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ checklog(){
|
|||
s=$1 # statement
|
||||
l0=$2 # linenr
|
||||
new "Check $s in log"
|
||||
# echo "grep \"transaction_log $s\" $flog"
|
||||
t=$(grep -n "transaction_log $s" $flog)
|
||||
if [ -z "$t" ]; then
|
||||
echo -e "\e[31m\nError in Test$testnr [$testname]:"
|
||||
|
|
@ -124,7 +125,7 @@ if [ $BE -ne 0 ]; then
|
|||
sleep $RCWAIT
|
||||
fi
|
||||
|
||||
let nr=1
|
||||
let nr=0
|
||||
|
||||
new "Basic transaction to add top-level x"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config><x xmlns='urn:example:clixon'><y><a>$nr</a></y></x></config></edit-config></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
|
|
@ -76,7 +77,7 @@ module example{
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
|
|
@ -84,21 +85,21 @@ if [ $BE -ne 0 ]; then
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
fi
|
||||
|
||||
new "cli set transitive string"
|
||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle x" 0 "^$"
|
||||
expectfn "$clixon_cli -1f $cfg -l o set c talle x" 0 "^$"
|
||||
|
||||
new "cli set transitive union"
|
||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 33" 0 "^$"
|
||||
expectfn "$clixon_cli -1f $cfg -l o set c ulle 33" 0 "^$"
|
||||
|
||||
new "cli set transitive union error"
|
||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle kalle" 255 '^CLI syntax error: "set c ulle kalle": Unknown command$'
|
||||
expectfn "$clixon_cli -1f $cfg -l o set c ulle kalle" 255 '^CLI syntax error: "set c ulle kalle": Unknown command$'
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
|
|
@ -89,56 +90,56 @@ module $APPNAME{
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg -y $fyang
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
fi
|
||||
|
||||
new "when: add static route"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: validate ok"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: add direct route"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when get config"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "when: validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: add interface"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>1500</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>1500</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: validate ok"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: add atm interface"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: atm validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: add eth interface"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "must: eth validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# 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_xml:=clixon_util_xml}
|
||||
: ${clixon_util_xml:=clixon_util_xml -o} # -o is output
|
||||
|
||||
new "xml parse"
|
||||
expecteof "$clixon_util_xml" 0 "<a><b/></a>" "^<a><b/></a>$"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
|
|
@ -141,35 +142,35 @@ module $APPNAME{
|
|||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg -y $fyang"
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg -y $fyang
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg -y $fyang"
|
||||
start_backend -s init -f $cfg -y $fyang
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
new "cli defined extension"
|
||||
expectfn "$clixon_cli -1f $cfg -y $fyang show version" 0 "3."
|
||||
expectfn "$clixon_cli -1f $cfg show version" 0 "3."
|
||||
|
||||
new "empty values in leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e>a</e></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e>a</e></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "empty values in leaf-list2"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e/></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e/></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get config"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e/><e>a</e></f></x></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e/><e>a</e></f></x></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
|
||||
#new "cli not defined extension"
|
||||
|
|
@ -179,106 +180,106 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]
|
|||
#expectfn "$clixon_cli -1f $cfg -y $fyangerr show version" 0 "Yang error: Extension ex:not-defined not found"
|
||||
|
||||
new "netconf schema resource, RFC 7895"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace><conformance-type>implement</conformance-type></module>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace><conformance-type>implement</conformance-type></module>'
|
||||
|
||||
new "netconf edit config"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf commit"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# text empty type in running
|
||||
new "netconf commit 2nd"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get config xpath"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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=\"/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>]]>]]>$'
|
||||
|
||||
new "netconf edit leaf-list"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>]]>]]>$"
|
||||
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 -y $fyang" 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="/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>]]>]]>$'
|
||||
|
||||
new "netconf get leaf-list path"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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=\"/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>]]>]]>$"
|
||||
|
||||
new "netconf get (should be some)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>'
|
||||
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>'
|
||||
|
||||
new "cli set leaf-list"
|
||||
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
|
||||
expectfn "$clixon_cli -1f $cfg set x f e foo" 0 ""
|
||||
|
||||
new "cli show leaf-list"
|
||||
expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "<e>foo</e>"
|
||||
expectfn "$clixon_cli -1f $cfg show xpath /x/f/e" 0 "<e>foo</e>"
|
||||
|
||||
new "netconf set state data (not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>]]>]]>$'
|
||||
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>]]>]]>$'
|
||||
|
||||
new "netconf set presence and not present"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 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>]]>]]>$"
|
||||
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 -y $fyang" 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="/x/presence"/></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 -y $fyang" 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="/x/nopresence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf anyxml"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><any><foo><bar a="nisse"/></foo></any></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><any><foo><bar a="nisse"/></foo></any></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate anyxml"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf delete candidate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
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>]]>]]>$"
|
||||
|
||||
# Check 3-keys
|
||||
new "netconf add one 3-key entry"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf check add one 3-key"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></data></rpc-reply>]]>]]>'
|
||||
|
||||
new "netconf add another (with same 1st key)"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf check add another"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
|
||||
new "netconf replace first"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf check replace"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
|
||||
new "netconf delete first"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y operation="remove"><a>1</a><b>1</b><c>1</c></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y operation="remove"><a>1</a><b>1</b><c>1</c></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf check delete"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||
|
||||
# clear db for next test
|
||||
new "netconf delete candidate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
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>]]>]]>$"
|
||||
|
||||
new "netconf commit empty candidate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconfig config submodule"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf submodule get config"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf submodule validate"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf submodule discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
if [ $BE -eq 0 ]; then
|
||||
exit # BE
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
#include "clixon/clixon.h"
|
||||
|
||||
/*
|
||||
* Turn this on to get a json parse and pretty print test program
|
||||
* JSON parse and pretty print test program
|
||||
* Usage: xpath
|
||||
* read json from input
|
||||
* Example compile:
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@
|
|||
* XML support functions.
|
||||
* @see https://www.w3.org/TR/2008/REC-xml-20081126
|
||||
* https://www.w3.org/TR/2009/REC-xml-names-20091208
|
||||
* The function can do yang validation, process xml and json, etc.
|
||||
* On success, nothing is printed and exitcode 0
|
||||
* On failure, an error is printed on stderr and exitcode != 0
|
||||
* Failure error prints are different, it would be nice to make them more
|
||||
* uniform. (see clicon_rpc_generate_error)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -49,7 +54,9 @@
|
|||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -69,12 +76,20 @@
|
|||
static int
|
||||
usage(char *argv0)
|
||||
{
|
||||
fprintf(stderr, "usage:%s [options]\n"
|
||||
fprintf(stderr, "usage:%s [options] with xml on stdin\n"
|
||||
"where options are\n"
|
||||
"\t-h \t\tHelp\n"
|
||||
"\t-D <level> \tDebug\n"
|
||||
"\t-f <file>\tXML input file (overrides stdin)\n"
|
||||
"\t-J \t\tInput as JSON\n"
|
||||
"\t-j \t\tOutput as JSON\n"
|
||||
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr, std(o)ut (stderr is default)\n",
|
||||
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr, std(o)ut (stderr is default)\n"
|
||||
"\t-o \t\tOutput the file\n"
|
||||
"\t-v \t\tValidate the result in terms of Yang model (requires -y)\n"
|
||||
"\t-p \t\tPretty-print output\n"
|
||||
"\t-y <filename> \tYang filename or dir (load all files)\n"
|
||||
"\t-Y <dir> \tYang dirs (can be several)\n"
|
||||
,
|
||||
argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -89,11 +104,33 @@ main(int argc,
|
|||
cbuf *cb = cbuf_new();
|
||||
int c;
|
||||
int logdst = CLICON_LOG_STDERR;
|
||||
int json = 0;
|
||||
int jsonin = 0;
|
||||
int jsonout = 0;
|
||||
char *input_filename = NULL;
|
||||
char *yang_file_dir = NULL;
|
||||
yang_stmt *yspec = NULL;
|
||||
cxobj *xerr = NULL; /* malloced must be freed */
|
||||
int ret;
|
||||
int pretty = 0;
|
||||
int validate = 0;
|
||||
int output = 0;
|
||||
clicon_handle h;
|
||||
struct stat st;
|
||||
int fd = 0; /* stdin */
|
||||
cxobj *xcfg = NULL;
|
||||
cbuf *cbret = NULL;
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
|
||||
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
goto done;
|
||||
|
||||
xcfg = xml_new("clixon-config", NULL, NULL);
|
||||
clicon_conf_xml_set(h, xcfg);
|
||||
optind = 1;
|
||||
opterr = 0;
|
||||
while ((c = getopt(argc, argv, "hD:jl:")) != -1)
|
||||
while ((c = getopt(argc, argv, "hD:f:Jjl:pvoy:Y:")) != -1)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
|
|
@ -102,37 +139,131 @@ main(int argc,
|
|||
if (sscanf(optarg, "%d", &debug) != 1)
|
||||
usage(argv[0]);
|
||||
break;
|
||||
case 'f':
|
||||
input_filename = optarg;
|
||||
break;
|
||||
case 'J':
|
||||
jsonin++;
|
||||
break;
|
||||
case 'j':
|
||||
json++;
|
||||
jsonout++;
|
||||
break;
|
||||
case 'l': /* Log destination: s|e|o|f */
|
||||
if ((logdst = clicon_log_opt(optarg[0])) < 0)
|
||||
usage(argv[0]);
|
||||
break;
|
||||
case 'o':
|
||||
output++;
|
||||
break;
|
||||
case 'v':
|
||||
validate++;
|
||||
break;
|
||||
case 'p':
|
||||
pretty++;
|
||||
break;
|
||||
case 'y':
|
||||
yang_file_dir = optarg;
|
||||
break;
|
||||
case 'Y':
|
||||
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
if (validate && !yang_file_dir){
|
||||
fprintf(stderr, "-v requires -y\n");
|
||||
usage(argv[0]);
|
||||
}
|
||||
clicon_log_init(__FILE__, debug?LOG_DEBUG:LOG_INFO, logdst);
|
||||
if (xml_parse_file(0, "</config>", NULL, &xt) < 0){
|
||||
fprintf(stderr, "xml parse error %s\n", clicon_err_reason);
|
||||
/* 1. Parse yang */
|
||||
if (yang_file_dir){
|
||||
if ((yspec = yspec_new()) == NULL)
|
||||
goto done;
|
||||
if (stat(yang_file_dir, &st) < 0){
|
||||
clicon_err(OE_YANG, errno, "%s not found", yang_file_dir);
|
||||
goto done;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)){
|
||||
if (yang_spec_load_dir(h, yang_file_dir, yspec) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
if (yang_spec_parse_file(h, yang_file_dir, yspec) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (input_filename){
|
||||
if ((fd = open(input_filename, O_RDONLY)) < 0){
|
||||
clicon_err(OE_YANG, errno, "open(%s)", input_filename);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* 2. Parse data (xml/json) */
|
||||
if (jsonin){
|
||||
if ((ret = json_parse_file(fd, yspec, &xt, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
clicon_rpc_generate_error("util_xml", xerr);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (xml_parse_file(fd, "</config>", NULL, &xt) < 0){
|
||||
fprintf(stderr, "xml parse error: %s\n", clicon_err_reason);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump data structures (for debug) */
|
||||
if (debug){
|
||||
cbuf_reset(cb);
|
||||
xmltree2cbuf(cb, xt, 0);
|
||||
fprintf(stderr, "%s\n", cbuf_get(cb));
|
||||
cbuf_reset(cb);
|
||||
}
|
||||
|
||||
/* 3. Validate data (if yspec) */
|
||||
if (validate){
|
||||
xc = xml_child_i(xt, 0);
|
||||
/* Populate */
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
/* Sort */
|
||||
if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
|
||||
goto done;
|
||||
/* Add default values */
|
||||
if (xml_apply(xc, CX_ELMNT, xml_default, h) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xc, -1, xml_sort_verify, h) < 0)
|
||||
clicon_log(LOG_NOTICE, "%s: sort verify failed", __FUNCTION__);
|
||||
if ((ret = xml_yang_validate_all_top(h, xc, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret > 0 && (ret = xml_yang_validate_add(h, xc, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (netconf_err2cb(xerr, &cbret) < 0)
|
||||
goto done;
|
||||
fprintf(stderr, "xml validation error: %s\n", cbuf_get(cbret));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* 4. Output data (xml/json) */
|
||||
if (output){
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
|
||||
if (json)
|
||||
xml2json_cbuf(cb, xc, 0); /* print xml */
|
||||
if (jsonout)
|
||||
xml2json_cbuf(cb, xc, pretty); /* print xml */
|
||||
else
|
||||
clicon_xml2cbuf(cb, xc, 0, 0); /* print xml */
|
||||
clicon_xml2cbuf(cb, xc, 0, pretty); /* print xml */
|
||||
fprintf(stdout, "%s", cbuf_get(cb));
|
||||
fflush(stdout);
|
||||
#if 0
|
||||
cbuf_reset(cb);
|
||||
xmltree2cbuf(cb, xt, 0); /* dump data structures */
|
||||
fprintf(stderr, "%s\n", cbuf_get(cb));
|
||||
#endif
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ includedir = @includedir@
|
|||
datarootdir = @datarootdir@
|
||||
enable_stdyangs = @enable_stdyangs@
|
||||
|
||||
CLIXON_DATADIR = @CLIXON_DATADIR@
|
||||
|
||||
SUBDIRS = clixon
|
||||
# See configure.ac
|
||||
ifeq ($(enable_stdyangs),yes)
|
||||
|
|
|
|||
|
|
@ -38,10 +38,11 @@ bindir = @bindir@
|
|||
includedir = @includedir@
|
||||
datarootdir = @datarootdir@
|
||||
|
||||
CLIXON_DATADIR = @CLIXON_DATADIR@
|
||||
# See also STD_YANG_INSTALLDIR for the standard yang files
|
||||
YANG_INSTALLDIR = @YANG_INSTALLDIR@
|
||||
|
||||
YANGSPECS = clixon-config@2019-03-05.yang
|
||||
YANGSPECS += clixon-lib@2019-01-02.yang
|
||||
YANGSPECS = clixon-config@2019-06-05.yang
|
||||
YANGSPECS += clixon-lib@2019-06-05.yang
|
||||
YANGSPECS += clixon-rfc5277@2008-07-01.yang
|
||||
YANGSPECS += clixon-xml-changelog@2019-03-21.yang
|
||||
|
||||
|
|
@ -55,11 +56,11 @@ distclean: clean
|
|||
rm -f Makefile *~ .depend
|
||||
|
||||
install: $(YANGSPECS)
|
||||
install -d -m 0755 $(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -d -m 0755 $(DESTDIR)$(YANG_INSTALLDIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(YANG_INSTALLDIR)
|
||||
|
||||
uninstall:
|
||||
(cd $(DESTDIR)$(CLIXON_DATADIR); rm -rf *.yang)
|
||||
(cd $(DESTDIR)$(YANG_INSTALLDIR); rm -rf *.yang)
|
||||
|
||||
install-include:
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ module clixon-config {
|
|||
|
||||
***** END LICENSE BLOCK *****";
|
||||
|
||||
revision 2019-06-05 {
|
||||
description
|
||||
"Added: CLICON_YANG_REGEXP, CLICON_CLI_TAB_MODE,
|
||||
CLICON_CLI_HIST_FILE, CLICON_CLI_HIST_SIZE,
|
||||
CLICON_XML_CHANGELOG, CLICON_XML_CHANGELOG_FILE;
|
||||
Renamed CLICON_XMLDB_CACHE to CLICON_DATASTORE_CACHE (changed type)
|
||||
Deleted: CLICON_XMLDB_PLUGIN, CLICON_USE_STARTUP_CONFIG";
|
||||
}
|
||||
revision 2019-03-05{
|
||||
description
|
||||
"Changed URN. Changed top-level symbol to clixon-config.
|
||||
|
|
@ -197,7 +205,7 @@ module clixon-config {
|
|||
"Yang directory path for finding module and submodule files.
|
||||
A list of these options should be in the configuration.
|
||||
When loading a Yang module, Clixon searches this list in the order
|
||||
they appear. Ensure that CLIXON_DATADIR(default
|
||||
they appear. Ensure that YANG_INSTALLDIR(default
|
||||
/usr/local/share/clixon) is present in the path";
|
||||
}
|
||||
leaf CLICON_YANG_MAIN_FILE {
|
||||
|
|
@ -427,14 +435,6 @@ module clixon-config {
|
|||
description
|
||||
"Directory where \"running\", \"candidate\" and \"startup\" are placed.";
|
||||
}
|
||||
leaf CLICON_XMLDB_PLUGIN {
|
||||
type string;
|
||||
status obsolete;
|
||||
description
|
||||
"XMLDB datastore plugin filename
|
||||
(see datastore/ and clixon_xml_db.[ch])
|
||||
Obsolete: Merged with libclixon in 3.10";
|
||||
}
|
||||
leaf CLICON_DATASTORE_CACHE {
|
||||
type datastore_cache;
|
||||
default cache;
|
||||
|
|
@ -40,6 +40,10 @@ module clixon-lib {
|
|||
|
||||
***** END LICENSE BLOCK *****";
|
||||
|
||||
revision 2019-06-05 {
|
||||
description
|
||||
"ping rpc added for liveness";
|
||||
}
|
||||
revision 2019-01-02 {
|
||||
description
|
||||
"Released in Clixon 3.9";
|
||||
|
|
@ -38,8 +38,8 @@ bindir = @bindir@
|
|||
includedir = @includedir@
|
||||
datarootdir = @datarootdir@
|
||||
|
||||
# Could place them in separate standards dir?
|
||||
CLIXON_DATADIR = @CLIXON_DATADIR@
|
||||
# See also YANG_INSTALLDIR for the clixon-specific yang files
|
||||
STD_YANG_INSTALLDIR = @STD_YANG_INSTALLDIR@
|
||||
|
||||
YANGSPECS = iana-if-type@2014-05-08.yang
|
||||
YANGSPECS += ietf-interfaces@2018-02-20.yang
|
||||
|
|
@ -61,11 +61,11 @@ distclean: clean
|
|||
rm -f Makefile *~ .depend
|
||||
|
||||
install: $(YANGSPECS)
|
||||
install -d -m 0755 $(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(CLIXON_DATADIR)
|
||||
install -d -m 0755 $(DESTDIR)$(STD_YANG_INSTALLDIR)
|
||||
install -m 0644 $(YANGSPECS) $(DESTDIR)$(STD_YANG_INSTALLDIR)
|
||||
|
||||
uninstall:
|
||||
(cd $(DESTDIR)$(CLIXON_DATADIR); rm -rf *.yang)
|
||||
(cd $(DESTDIR)$(STD_YANG_INSTALLDIR); rm -rf *.yang)
|
||||
|
||||
install-include:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue