diff --git a/.gitignore b/.gitignore
index cd4446bc..1d8e315b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,10 +14,9 @@ lib/Makefile
lib/*/Makefile
autom4te.cache/
-clixon.conf.cpp
-clixon.mk
config.log
config.status
+TAGS
apps/backend/clixon_backend
apps/backend/test
@@ -45,5 +44,5 @@ build-root/*.tar.xz
build-root/*.rpm
build-root/rpmbuild
-test/public
+test/site.sh
doc/html
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 160f6254..dd5694ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,47 +1,105 @@
# Clixon Changelog
-## 3.9.0 (Preliminary Target: 31 December 2018)
+## 3.9.0 (Preliminary Target: Mid-January 2019)
### Planned new features
-* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
+* [Roadmap](ROADMAP.md)
### Major New features
-
-* NACM extension (RFC8341)
- * NACM module support (RFC8341 A1+A2)
- * Recovery user "_nacm_recovery" added.
- * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
- * Example user changed adm1 to andy to comply with RFC8341 example
+* Correct XML namespace handling
+ * XML multiple modules was based on non-strict semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208) as well as strict Netconf and Restconf namespace handling, which causes problems with overlapping names and false positives, and most importantly, with standard conformance.
+ * There are still the following non-strict namespace handling:
+ * Everything in ietf-netconf base syntax with namespace `urn:ietf:params:xml:ns:netconf:base:1.0` is default and need not be explicitly given
+ * edit-config xpath select statement does not support namespaces
+ * notifications do not support namespaces.
+ * Below see netconf old (but wrong) netconf RPC:
+ ```
+
+
+
+
+
+ ipv4
+
+
+ ```
+ This is the currently correct Netconf RPC:
+ ```
+ # xmlns may be ommitted
+
+
+
+
+ ipv4
+
+
+ ```
+ * Another example for restconf rpc with new correct syntax. Note that while Netconf uses xmlns attribute syntax, Restconf uses module name prefix. First the request:
+ ```
+ POST http://localhost/restconf/operations/example:example)
+ Content-Type: application/yang-data+json
+ {
+ "example:input":{
+ "x":0
+ }
+ }
+ ```
+ then the reply:
+ ```
+ HTTP/1.1 200 OK
+ {
+ "example:output": {
+ "x": "0",
+ "y": "42"
+ }
+ }
+ ```
+ * To keep previous non-strict namespace handling (backwards compatible), set CLICON_XML_NS_STRICT to false.
+ * See https://github.com/clicon/clixon/issues/49
* Yang code upgrade (RFC7950)
* YANG parser cardinality checked (https://github.com/clicon/clixon/issues/48)
* See https://github.com/clicon/clixon/issues/84
* RPC method input parameters validated
* see https://github.com/clicon/clixon/issues/47
* Support of submodule, include and belongs-to.
- * Openconfig yang specs parsed: https://github.com/openconfig/public
+ * Parsing of standard yang files supported, such as:
+ * https://github.com/openconfig/public - except [https://github.com/clicon/clixon/issues/60].
+ * See [test/test_openconfig.sh]
+ * https://github.com/YangModels/yang - except vendor-specific specs
+ * See [test/test_yangmodels.sh]
* Improved "unknown" handling
+ * More precise Yang validation and better error messages
+ * Example: adding bad-, missing-, or unknown-element error messages, instead of operation-failed.
+ * Validation of mandatory choice and recursive mandatory containers
* Yang load file configure options changed
* `CLICON_YANG_DIR` is changed from a single directory to a path of directories
* Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
* CLICON_YANG_MAIN_FILE Provides a filename with a single module filename.
* CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
-* Correct XML namespace handling
- * XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration:
-```
- # Wrong but accepted
- # Correct
-
-
-```
- * To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default)
- * XML to JSON translator support for mapping xmlns attribute to module name prefix.
- * Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0"
- * See https://github.com/clicon/clixon/issues/49
-
+* NACM extension (RFC8341)
+ * NACM module support (RFC8341 A1+A2)
+ * Recovery user "_nacm_recovery" added.
+ * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
+ * Example user changed adm1 to andy to comply with RFC8341 example
+* Added -o "=" command-line option to all programs: backend, cli, netconf, restconf.
+ * Any config option from file can be overrided by giving them on command-line.
+
### API changes on existing features (you may need to change your code)
+* Date-and-time type now properly uses ISO 8601 UTC timezone designators.
+ * Eg 2008-09-21T18:57:21.003456 is changed to 2008-09-21T18:57:21.003456Z
+* Renamed yang file `ietf-netconf-notification@2008-07-01.yang` to `clixon-rfc5277`.
+ * Fixed validation problems, see [https://github.com/clicon/clixon/issues/62]
+ * Name confusion, the file is manually constructed from the rfc.
+ * Changed prefix to `ncevent`
+* Stricter YANG choice validation leads to enforcement of structures like: `choice c{ mandatory true; leaf x` statements. `x` was not previously enforced.
+* Many hand-crafted validation messages have been removed and replaced with generic validations, which may lead to changed rpc-error messages.
+* CLICON_XML_SORT option (in clixon-config.yang) has been removed and set to true permanently. Unsorted XML lists leads to slower performance and old obsolete code can be removed.
+* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.
+* Removed `delete-config` support for candidate db since it is not supported in RFC6241.
+* Switched the order of `error-type` and `error-tag` in all netconf and restconf error messages to comply to RFC order.
* Yang parser is stricter (see above) which may break parsing of existing yang specs.
* XML namespace handling is corrected (see above)
- * For backward compatibility set config option CLICON_XML_NS_ITERATE
+ * For backward compatibility set config option CLICON_XML_NS_LOOSE
* Yang parser functions have changed signatures. Please check the source if you call these functions.
* Add `/usr/local/share/clixon ` to your configuration file, or corresponding CLICON_DATADIR directory for Clixon system yang files.
* Change all @datamodel:tree to @datamodel in all CLI specification files
@@ -49,6 +107,13 @@
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
### Minor changes
+* Cligen uses posix regex while yang uses XSD. It differs in some aspects. A translator function has been added for `\d` -> `[0-9]` translation, there may be more.
+* Added new clixon-lib yang module for internal netconf protocol. Currently only extends the standard with a debug RPC.
+* Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK.
+ * This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath`
+* Added new xml functions for specific types: `xml_child_nr_notype`, `xml_child_nr_notype`, `xml_child_i_type`, `xml_find_type`.
+* Added example_rpc RPC to example backend
+* Renamed xml_namespace[_set]() to xml_prefix[_set]()
* Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3)
* Removed return value ymodp from yang parse functions (eg yang_parse()).
@@ -60,8 +125,15 @@
* x:des3
```
diff --git a/DEVELOP.md b/DEVELOP.md
index fb66e641..74d7f4ef 100644
--- a/DEVELOP.md
+++ b/DEVELOP.md
@@ -97,6 +97,9 @@ EOF
## New release
What to think about when doing a new release.
+* valgrind for memory leaks
+* New clixon-config.yang revision?
+Tagging:
* git merge --no-ff develop
* change CLIXON_VERSION in configure.ac
* git tag -a
+```
+In 3.9, the same statement should be, for example:
+```
+
+```
+Note that base netconf syntax is still not enforced but recommended:
+```
+
+
+
+```
+
Yang
====
YANG and XML is the heart of Clixon. Yang modules are used as a
@@ -125,8 +142,10 @@ However, the following YANG syntax modules are not implemented:
- belongs-to
Restrictions on Yang types are as follows:
-- The range statement does not support multiple values (RFC7895 sec 9.2.4)
+- The range statement for built-in integers does not support multiple values (RFC7950 9.2.4)
+- The length statement for built-in strings does not support multiple values (RFC7950 9.4.4)
- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60)
+- default values on leaf-lists (RFC7950 7.7.2)
Netconf
=======
@@ -152,6 +171,9 @@ Clixon does not support the following netconf features:
- edit-config config-text
- edit-config operation
+Some other deviations from the RFC:
+- edit-config xpath select statement does not support namespaces
+
Restconf
========
Clixon Restconf is a daemon based on FastCGI C-API. Instructions are available to
diff --git a/ROADMAP.md b/ROADMAP.md
index e0dd98f3..3e91e1a3 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,27 +1,42 @@
# Clixon roadmap
-Not in prio order (yet)
+## High prio
+- NACM (RFC 8341)
+ - Module rules (done)
+ - Data node rules (read/create/delete/update/execute)
+- Special handling of the initial startup transaction to avoid exit at startup
+ - Possibly - draft-wu-netconf-restconf-factory-restore-03
+- Handle revisions to data model.
+ - Possibly draft-wang-netmod-module-revision-management-01
+- XML [Namespace handling](https://github.com/clicon/clixon/issues/49)
-- XML
- - [Namespace handling](https://github.com/clicon/clixon/issues/49)
+## Medium prio:
+- Input validation on custom RPCs/ (done)
+ - [Sanity checks](https://github.com/clicon/clixon/issues/47)
+- Support for XML regex's.
+ - Currently Posix extended regular expressions
+- Support a plugin callback that is invoked when copy-config is called.
+- Preserve CLI command history across sessions. The up/down arrows
+
+## Low prio:
+- Provide a client library to access netconf APIs provided by system services.
+ - Netconf backend (Clixon acts as netconf controller)
+- Support for restconf call-home (RFC 8071)
+
+Not prioritized:
+- Support for restconf PATCH method
- NETCONF
- Support for additional Netconf [edit-config modes](https://github.com/clicon/clixon/issues/53)
- Netconf [framing](https://github.com/clicon/clixon/issues/50)
- - [Sanity checks](https://github.com/clicon/clixon/issues/47)
- [Child ordering](https://github.com/clicon/clixon/issues/22)
-- Netconf backend (Clixon acts as netconf controller)
- Restconf
- Query parameters
-- NACM (RFC 8341) is somewhat limited
- - Extend with data node access (read/create/delete/update/execute)
- Streams (netconf and restconf)
- Extend native stream mode with external persistent timeseries database, eg influxdb.
- Jenkins CI/CD and webhooks
- YANG
- - [Cardinality](https://github.com/clicon/clixon/issues/48)
- RFC 6022 [NETCONF monitoring](https://github.com/clicon/clixon/issues/39)
- - Factory default Setting - draft-wu-netconf-restconf-factory-restore-03
- - Deviation, belongs-to, min/max-elements, action, unique
+ - Deviation, min/max-elements, action, unique
- Containers
- [Docker improvements](https://github.com/clicon/clixon/issues/44)
- Kubernetes Helm chart definition
diff --git a/apps/Makefile.in b/apps/Makefile.in
index bfcc31cf..d8125aa0 100644
--- a/apps/Makefile.in
+++ b/apps/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/apps/backend/Makefile.in b/apps/backend/Makefile.in
index 4d57dfc8..b3700e33 100644
--- a/apps/backend/Makefile.in
+++ b/apps/backend/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 3b79eab2..f8766206 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -292,7 +292,7 @@ client_get_streams(clicon_handle h,
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 OK
- * @retval 1 Statedata callback failed
+ * @retval 1 Statedata callback failed (clicon_err called)
*/
static int
client_statedata(clicon_handle h,
@@ -309,15 +309,15 @@ client_statedata(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
- if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- (retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
- goto done;
- if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
- (retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
- goto done;
- if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
- (retval = yang_modules_state_get(h, yspec, xret)) != 0)
- goto done;
+ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
+ if ((retval = client_get_streams(h, yspec, xpath, "clixon-rfc5277", "netconf", xret)) != 0)
+ goto done;
+ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040"))
+ if ((retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
+ goto done;
+ if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895"))
+ if ((retval = yang_modules_state_get(h, yspec, xret)) != 0)
+ goto done;
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
goto done;
/* Code complex to filter out anything that is outside of xpath */
@@ -339,7 +339,7 @@ client_statedata(clicon_handle h,
/* reset flag */
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
- retval = 0;
+ retval = 0; /* OK */
done:
if (xvec)
free(xvec);
@@ -363,7 +363,6 @@ from_client_get(clicon_handle h,
char *xpath = "/";
cxobj *xret = NULL;
int ret;
- cbuf *cbx = NULL; /* Assist cbuf */
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
@@ -379,33 +378,24 @@ from_client_get(clicon_handle h,
clicon_err_reset();
if ((ret = client_statedata(h, xpath, &xret)) < 0)
goto done;
- if (ret == 0){ /* OK */
- cprintf(cbret, "");
- if (xret==NULL)
- cprintf(cbret, " ");
- else{
- if (xml_name_set(xret, "data") < 0)
- goto done;
- if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
- goto done;
- }
- cprintf(cbret, " ");
- }
- else { /* 1 Error from callback */
- if ((cbx = cbuf_new()) == NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
+ if (ret == 1){ /* Error from callback (error in xret) */
+ if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
goto done;
- }
- cprintf(cbx, "Internal error:%s", clicon_err_reason);
- if (netconf_operation_failed(cbret, "rpc", cbuf_get(cbx))< 0)
- goto done;
- clicon_log(LOG_NOTICE, "%s Error in backend_statedata_call:%s", __FUNCTION__, xml_name(xe));
+ goto ok;
}
+ cprintf(cbret, ""); /* OK */
+ if (xret==NULL)
+ cprintf(cbret, " ");
+ else{
+ if (xml_name_set(xret, "data") < 0)
+ goto done;
+ if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
+ goto done;
+ }
+ cprintf(cbret, " ");
ok:
retval = 0;
done:
- if (cbx)
- cbuf_free(cbx);
if (xret)
xml_free(xret);
return retval;
@@ -433,14 +423,16 @@ from_client_edit_config(clicon_handle h,
int non_config = 0;
yang_spec *yspec;
cbuf *cbx = NULL; /* Assist cbuf */
+ int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9");
goto done;
}
if ((target = netconf_db_find(xn, "target")) == NULL){
- clicon_err(OE_XML, 0, "db not found");
- goto done;
+ if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
+ goto done;
+ goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@@ -468,12 +460,12 @@ from_client_edit_config(clicon_handle h,
}
}
if ((xc = xpath_first(xn, "config")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "config ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
goto done;
goto ok;
}
else{
- /* yang spec may be set to anyxml by ingress yang check,...*/
+ /* yang spec may be set to anyxmly by ingress yang check,...*/
if (xml_spec(xc) != NULL)
xml_spec_set(xc, NULL);
/* Populate XML with Yang spec (why not do this in parser?)
@@ -491,24 +483,27 @@ from_client_edit_config(clicon_handle h,
/* Cant do this earlier since we dont have a yang spec to
* the upper part of the tree, until we get the "config" tree.
*/
- if (xml_child_sort && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
+ if (xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
- if (xmldb_put(h, target, operation, xc, cbret) < 0){
+ if ((ret = xmldb_put(h, target, operation, xc, cbret)) < 0){
clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
+ if (ret == 0)
+ goto ok;
}
+ assert(cbuf_len(cbret) == 0);
+ cprintf(cbret, " ");
ok:
- if (!cbuf_len(cbret))
- cprintf(cbret, " ");
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
return retval;
+
} /* from_client_edit_config */
/*! Internal message: Lock database
@@ -530,7 +525,7 @@ from_client_lock(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "target ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@@ -589,7 +584,7 @@ from_client_unlock(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "target ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@@ -651,7 +646,7 @@ from_client_kill_session(clicon_handle h,
if ((x = xml_find(xe, "session-id")) == NULL ||
(str = xml_find_value(x, "body")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "session-id ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0)
goto done;
goto ok;
}
@@ -709,7 +704,7 @@ from_client_copy_config(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((source = netconf_db_find(xe, "source")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "source ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done;
goto ok;
}
@@ -724,7 +719,7 @@ from_client_copy_config(clicon_handle h,
goto ok;
}
if ((target = netconf_db_find(xe, "target")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "target ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@@ -777,7 +772,7 @@ from_client_delete_config(clicon_handle h,
if ((target = netconf_db_find(xe, "target")) == NULL||
strcmp(target, "running")==0){
- if (netconf_missing_element(cbret, "protocol", "target ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@@ -856,7 +851,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//stopTime")) != NULL){
if ((stoptime = xml_find_value(x, "body")) != NULL &&
str2time(stoptime, &stop) < 0){
- if (netconf_bad_element(cbret, "application", "stopTime ", "Expected timestamp") < 0)
+ if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
goto done;
goto ok;
}
@@ -864,7 +859,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//startTime")) != NULL){
if ((starttime = xml_find_value(x, "body")) != NULL &&
str2time(starttime, &start) < 0){
- if (netconf_bad_element(cbret, "application", "startTime ", "Expected timestamp") < 0)
+ if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
goto done;
goto ok;
}
@@ -925,7 +920,7 @@ from_client_debug(clicon_handle h,
char *valstr;
if ((valstr = xml_find_body(xe, "level")) == NULL){
- if (netconf_missing_element(cbret, "application", "level ", NULL) < 0)
+ if (netconf_missing_element(cbret, "application", "level", NULL) < 0)
goto done;
goto ok;
}
@@ -971,6 +966,7 @@ from_client_msg(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
pid = ce->ce_pid;
+ yspec = clicon_dbspec_yang(h);
/* Return netconf message. Should be filled in by the dispatch(sub) functions
* as wither rpc-error or by positive response.
*/
@@ -978,28 +974,26 @@ from_client_msg(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- if (clicon_msg_decode(msg, &xt) < 0){
+ if (clicon_msg_decode(msg, yspec, &xt) < 0){
if (netconf_malformed_message(cbret, "XML parse error")< 0)
goto done;
goto reply;
}
- /* Get yang spec */
- yspec = clicon_dbspec_yang(h); /* XXX maybe move to clicon_msg_decode? */
+
if ((x = xpath_first(xt, "/rpc")) == NULL){
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
goto done;
goto reply;
}
- /* Populate incoming XML tree with yang */
+ /* Populate incoming XML tree with yang -
+ * should really have been dealt with by decode above
+ * maybe not necessary since it should be */
if (xml_spec_populate_rpc(h, x, yspec) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_rpc(x, cbret)) < 0)
goto done;
- if ((ret = xml_yang_validate_rpc(x)) < 0)
- goto done;
- if (ret == 0){
- if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
- goto done;
+ if (ret == 0)
goto reply;
- }
xe = NULL;
username = xml_find_value(x, "username");
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
@@ -1059,7 +1053,7 @@ from_client_msg(clicon_handle h,
}
else if (strcmp(rpc, "validate") == 0){
if ((db = netconf_db_find(xe, "source")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "source ", NULL) < 0)
+ if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done;
goto reply;
}
diff --git a/apps/backend/backend_client.h b/apps/backend/backend_client.h
index 81620d4a..c9a1da11 100644
--- a/apps/backend/backend_client.h
+++ b/apps/backend/backend_client.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index 3aa2f82d..8c406d69 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -81,78 +81,108 @@
* 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)
+ * @retval -1 Error
+ * @retval 0 Validation failed (with cbret set)
+ * @retval 1 Validation OK
*/
static int
generic_validate(yang_spec *yspec,
- transaction_data_t *td)
+ transaction_data_t *td,
+ cbuf *cbret)
{
int retval = -1;
cxobj *x1;
cxobj *x2;
yang_stmt *ys;
int i;
+ int ret;
/* All entries */
- if (xml_apply(td->td_target, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
+ if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0)
goto done;
-
+ if (ret == 0)
+ goto fail;
/* changed entries */
for (i=0; itd_clen; i++){
x1 = td->td_scvec[i]; /* source changed */
x2 = td->td_tcvec[i]; /* target changed */
- if (xml_yang_validate_add(x2, NULL) < 0)
+ /* Should this be recursive? */
+ if ((ret = xml_yang_validate_add(x2, cbret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
}
/* deleted entries */
for (i=0; itd_dlen; i++){
x1 = td->td_dvec[i];
ys = xml_spec(x1);
- if (ys && yang_mandatory(ys)){
- clicon_err(OE_CFG, 0,"Removed mandatory variable: %s",
- xml_name(x1));
- goto done;
+ if (ys && yang_mandatory(ys) && yang_config(ys)==0){
+ if (netconf_missing_element(cbret, "protocol", xml_name(x1), "Missing mandatory variable") < 0)
+ goto done;
+ goto fail;
}
}
/* added entries */
for (i=0; itd_alen; i++){
x2 = td->td_avec[i];
- if (xml_apply0(x2, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_add, NULL) < 0)
+ if ((ret = xml_yang_validate_add(x2, cbret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
}
- retval = 0;
+ // ok:
+ retval = 1;
done:
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! Common code of candidate_validate and candidate_commit
* @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state
- * @retval 0 OK
- * @retval -1 Fatal error or validation fail
- * @note Need to differentiate between error and validation fail
+ * @retval -1 Error - or validation failed (but cbret not set)
+ * @retval 0 Validation failed (with cbret set)
+ * @retval 1 Validation OK
+ * @note Need to differentiate between error and validation fail
+ * (only done for generic_validate)
*/
static int
validate_common(clicon_handle h,
char *candidate,
- transaction_data_t *td)
+ transaction_data_t *td,
+ cbuf *cbret)
{
int retval = -1;
yang_spec *yspec;
int i;
cxobj *xn;
-
+ int ret;
+
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
- /* 2. Parse xml trees */
+ /* 2. Parse xml trees
+ * This is the state we are going from */
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
goto done;
+ /* This is the state we are going to */
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
goto done;
+ /* Validate the target state. It is not completely clear this should be done
+ * here. It is being made in generic_validate below.
+ * But xml_diff requires some basic validation, at least check that yang-specs
+ * have been assigned
+ */
+ if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
@@ -193,9 +223,12 @@ validate_common(clicon_handle h,
if (plugin_transaction_begin(h, td) < 0)
goto done;
- /* 5. Make generic validation on all new or changed data. */
- if (generic_validate(yspec, td) < 0)
+ /* 5. Make generic validation on all new or changed data.
+ Note this is only call that uses 3-values */
+ if ((ret = generic_validate(yspec, td, cbret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
/* 6. Call plugin transaction validate callbacks */
if (plugin_transaction_validate(h, td) < 0)
@@ -204,9 +237,12 @@ validate_common(clicon_handle h,
/* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0)
goto done;
- retval = 0;
+ retval = 1;
done:
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! Do a diff between candidate and running, then start a commit transaction
@@ -216,57 +252,69 @@ validate_common(clicon_handle h,
* do something more drastic?
* @param[in] h Clicon handle
* @param[in] candidate A candidate database, not necessarily "candidate"
- * @retval 0 OK
- * @retval -1 Fatal error or validation fail
+ * @retval -1 Error - or validation failed
+ * @retval 0 Validation failed (with cbret set)
+ * @retval 1 Validation OK
* @note Need to differentiate between error and validation fail
+ * (only done for validate_common)
*/
int
candidate_commit(clicon_handle h,
- char *candidate)
+ char *candidate,
+ cbuf *cbret)
{
- int retval = -1;
+ int retval = -1;
transaction_data_t *td = NULL;
+ int ret;
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
- /* Common steps (with validate) */
- if (validate_common(h, candidate, td) < 0)
+ /* Common steps (with validate). Load candidate and running and compute diffs
+ * Note this is only call that uses 3-values
+ */
+ if ((ret = validate_common(h, candidate, td, cbret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
/* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0)
goto done;
/* Optionally write (potentially modified) tree back to candidate */
- if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
- if (xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL) < 0)
+ if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
+ if ((ret = xmldb_put(h, candidate, OP_REPLACE, td->td_target, cbret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
+ }
/* 8. Success: Copy candidate to running
*/
-
if (xmldb_copy(h, candidate, "running") < 0)
goto done;
/* 9. Call plugin transaction end callbacks */
plugin_transaction_end(h, td);
-
/* 8. Copy running back to candidate in case end functions updated running */
if (xmldb_copy(h, "running", candidate) < 0){
/* ignore errors or signal major setback ? */
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
goto done;
}
- retval = 0;
+ retval = 1;
done:
- /* In case of failure, call plugin transaction termination callbacks */
- if (retval < 0 && td)
+ /* In case of failure (or error), call plugin transaction termination callbacks */
+ if (retval < 1 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! Commit changes from candidate to running
@@ -283,6 +331,7 @@ from_client_commit(clicon_handle h,
int retval = -1;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
+ int ret;
/* Check if target locked by other client */
piddb = xmldb_islocked(h, "running");
@@ -296,9 +345,10 @@ from_client_commit(clicon_handle h,
goto done;
goto ok;
}
- if (candidate_commit(h, "candidate") < 0){ /* Assume validation fail, nofatal */
+ if ((ret = candidate_commit(h, "candidate", cbret)) < 0){ /* Assume validation fail, nofatal */
clicon_debug(1, "Commit candidate failed");
- if (netconf_invalid_value(cbret, "protocol", clicon_err_reason)< 0)
+ if (ret < 0)
+ if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
goto ok;
}
@@ -367,6 +417,7 @@ from_client_validate(clicon_handle h,
{
int retval = -1;
transaction_data_t *td = NULL;
+ int ret;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
if (netconf_invalid_value(cbret, "protocol", "No such database")< 0)
@@ -379,17 +430,20 @@ from_client_validate(clicon_handle h,
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with commit) */
- if (validate_common(h, db, td) < 0){
+ if ((ret = validate_common(h, db, td, cbret)) < 1){
clicon_debug(1, "Validate %s failed", db);
- /* XXX: candidate_validate should have proper error handling */
- if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
- goto done;
+ if (ret < 0){
+ if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
+ goto done;
+ }
goto ok;
}
/* Optionally write (potentially modified) tree back to candidate */
- if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
- if (xmldb_put(h, "candidate", OP_REPLACE, td->td_target, NULL) < 0)
- goto done;
+ if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
+ if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target, cbret)) < 0)
+ goto done;
+ goto ok;
+ }
cprintf(cbret, " ");
ok:
retval = 0;
diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h
index 6be05858..3af2461a 100644
--- a/apps/backend/backend_commit.h
+++ b/apps/backend/backend_commit.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -43,6 +43,6 @@
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
int from_client_commit(clicon_handle h, int pid, cbuf *cbret);
int from_client_discard_changes(clicon_handle h, int pid, cbuf *cbret);
-int candidate_commit(clicon_handle h, char *db);
+int candidate_commit(clicon_handle h, char *db, cbuf *cbret);
#endif /* _BACKEND_COMMIT_H_ */
diff --git a/apps/backend/backend_handle.h b/apps/backend/backend_handle.h
index 8eb3e073..f2acea6f 100644
--- a/apps/backend/backend_handle.h
+++ b/apps/backend/backend_handle.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index ec4a99d7..5fbfd6e8 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -73,7 +73,7 @@
#include "backend_handle.h"
/* Command line options to be passed to getopt(3) */
-#define BACKEND_OPTS "hD:f:l:d:b:Fza:u:P:1s:c:g:y:x:" /* substitute s: for IRCc:r */
+#define BACKEND_OPTS "hD:f:l:d:b:Fza:u:P:1s:c:g:y:x:o:"
#define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log"
@@ -154,7 +154,8 @@ usage(clicon_handle h,
"\t-g \tClient membership required to this group (default: %s)\n"
"\t-y \tLoad yang spec file (override yang main module)\n"
- "\t-x \tXMLDB plugin\n",
+ "\t-x \tXMLDB plugin\n"
+ "\t-o \"=\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
plgdir ? plgdir : "none",
confsock ? confsock : "none",
@@ -176,22 +177,24 @@ db_reset(clicon_handle h,
}
/*! Merge db1 into db2 without commit
+ * @retval -1 Error
+ * @retval 0 Validation failed (with cbret set)
+ * @retval 1 Validation OK
*/
static int
db_merge(clicon_handle h,
const char *db1,
- const char *db2)
+ const char *db2,
+ cbuf *cbret)
{
- int retval = -1;
- cxobj *xt = NULL;
-
+ int retval = -1;
+ cxobj *xt = NULL;
+
/* Get data as xml from db1 */
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
goto done;
/* Merge xml into db2. Without commit */
- if (xmldb_put(h, (char*)db2, OP_MERGE, xt, NULL) < 0)
- goto done;
- retval = 0;
+ retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, cbret);
done:
if (xt)
xml_free(xt);
@@ -266,7 +269,7 @@ nacm_load_external(clicon_handle h)
}
if ((yspec = yspec_new()) == NULL)
goto done;
- if (yang_parse(h, NULL, "ietf-netconf-acm", NULL, yspec) < 0)
+ if (yang_spec_parse_module(h, "ietf-netconf-acm", NULL, yspec) < 0)
goto done;
fd = fileno(f);
/* Read configfile */
@@ -288,18 +291,22 @@ nacm_load_external(clicon_handle h)
}
/*! Merge xml in filename into database
+ * @retval -1 Error
+ * @retval 0 Validation failed (with cbret set)
+ * @retval 1 Validation OK
*/
static int
load_extraxml(clicon_handle h,
char *filename,
- const char *db)
+ const char *db,
+ cbuf *cbret)
{
int retval = -1;
cxobj *xt = NULL;
int fd = -1;
-
+
if (filename == NULL)
- return 0;
+ return 1;
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
@@ -310,9 +317,7 @@ load_extraxml(clicon_handle h,
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
- if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
- goto done;
- retval = 0;
+ retval = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret);
done:
if (fd != -1)
close(fd);
@@ -385,8 +390,13 @@ static int
startup_mode_running(clicon_handle h,
char *extraxml_file)
{
- int retval = -1;
+ int retval = -1;
+ cbuf *cbret = NULL;
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
/* Stash original running to candidate for later commit */
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
@@ -399,34 +409,46 @@ startup_mode_running(clicon_handle h,
/* Application may define extra xml in its reset function*/
if (clixon_plugin_reset(h, "tmp") < 0)
goto done;
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = 0;
/* Get application extra xml from file */
- if (load_extraxml(h, extraxml_file, "tmp") < 0)
- goto done;
+ if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
+ goto fail;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
/* Commit original running. Assume -1 is validate fail */
- if (candidate_commit(h, "candidate") < 0){
- /* (1) We cannot differentiate between fatal errors and validation
- * failures
- * (2) If fatal error, we should exit
- * (3) If validation fails we cannot continue. How could we?
- * (4) Need to restore the running db since we destroyed it above
- */
- clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting.", __FUNCTION__);
- /* Reinstate original */
- if (xmldb_copy(h, "candidate", "running") < 0)
- goto done;
- goto done;
- }
+ if (candidate_commit(h, "candidate", cbret) < 1)
+ goto fail;
/* Merge user reset state and extra xml file (no commit) */
- if (db_merge(h, "tmp", "running") < 0)
- goto done;
+ if (db_merge(h, "tmp", "running", cbret) < 1)
+ goto fail;
retval = 0;
done:
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
+ if (cbret)
+ cbuf_free(cbret);
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval;
+ fail:
+ /* (1) We cannot differentiate between fatal errors and validation
+ * failures
+ * (2) If fatal error, we should exit
+ * (3) If validation fails we cannot continue. How could we?
+ * (4) Need to restore the running db since we destroyed it above
+ */
+ if (strlen(cbuf_get(cbret)))
+ clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
+ __FUNCTION__, cbuf_get(cbret));
+ else
+ clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
+ __FUNCTION__, clicon_err_reason);
+ /* Reinstate original */
+ if (xmldb_copy(h, "candidate", "running") < 0)
+ goto done;
+ goto done;
}
/*! Clixon startup startup mode: Commit startup configuration into running state
@@ -452,10 +474,16 @@ startup -------------------------+--|
*/
static int
startup_mode_startup(clicon_handle h,
- char *extraxml_file)
+ char *extraxml_file)
{
int retval = -1;
+ cbuf *cbret = NULL;
+ /* Create return buffer for netconf xml errors */
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
/* Stash original running to backup */
if (xmldb_copy(h, "running", "backup") < 0)
goto done;
@@ -472,33 +500,46 @@ startup_mode_startup(clicon_handle h,
/* Application may define extra xml in its reset function*/
if (clixon_plugin_reset(h, "tmp") < 0)
goto done;
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = 0;
/* Get application extra xml from file */
- if (load_extraxml(h, extraxml_file, "tmp") < 0)
- goto done;
+ if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
+ goto fail;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
+
/* Commit startup */
- if (candidate_commit(h, "startup") < 0){ /* diff */
- /* We cannot differentiate between fatal errors and validation
- * failures
- * In both cases we copy back the original running and quit
- */
- clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting.", __FUNCTION__);
- if (xmldb_copy(h, "backup", "running") < 0)
- goto done;
- goto done;
- }
+ if (candidate_commit(h, "startup", cbret) < 1) /* diff */
+ goto fail;
/* Merge user reset state and extra xml file (no commit) */
- if (db_merge(h, "tmp", "running") < 0)
- goto done;
+ if (db_merge(h, "tmp", "running", cbret) < 1)
+ goto fail;
retval = 0;
done:
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
+ if (cbret)
+ cbuf_free(cbret);
if (xmldb_delete(h, "backup") < 0)
goto done;
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval;
+ fail:
+ /* We cannot differentiate between fatal errors and validation
+ * failures
+ * In both cases we copy back the original running and quit
+ */
+ if (strlen(cbuf_get(cbret)))
+ clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
+ __FUNCTION__, cbuf_get(cbret));
+ else
+ clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
+ __FUNCTION__, clicon_err_reason);
+ if (xmldb_copy(h, "backup", "running") < 0)
+ goto done;
+ goto done;
}
int
@@ -523,7 +564,6 @@ main(int argc,
int sockfamily;
char *xmldb_plugin;
int xml_cache;
- int xml_pretty;
char *xml_format;
char *nacm_mode;
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
@@ -662,6 +702,15 @@ main(int argc,
clicon_option_str_set(h, "CLICON_XMLDB_PLUGIN", optarg);
break;
}
+ case 'o':{ /* Configuration option */
+ char *val;
+ if ((val = index(optarg, '=')) == NULL)
+ usage(h, argv0);
+ *val++ = '\0';
+ if (clicon_option_add(h, optarg, val) < 0)
+ goto done;
+ break;
+ }
default:
usage(h, argv[0]);
break;
@@ -766,6 +815,9 @@ main(int argc,
if ((str = clicon_yang_main_dir(h)) != NULL)
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
+ /* Load clixon lib yang module */
+ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
+ goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@@ -778,7 +830,7 @@ main(int argc,
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
+ yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Set options: database dir and yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
@@ -791,9 +843,8 @@ main(int argc,
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
goto done;
- if ((xml_pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) >= 0)
- if (xmldb_setopt(h, "pretty", (void*)(intptr_t)xml_pretty) < 0)
- goto done;
+ if (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
+ goto done;
/* Startup mode needs to be defined, */
startup_mode = clicon_startup_mode(h);
if (startup_mode == -1){
diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c
index e8ce2972..6218c0cd 100644
--- a/apps/backend/backend_plugin.c
+++ b/apps/backend/backend_plugin.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -117,7 +117,7 @@ clixon_plugin_reset(clicon_handle h,
* @param[in,out] xtop State XML tree is merged with existing tree.
* @retval -1 Error
* @retval 0 OK
- * @retval 1 Statedata callback failed
+ * @retval 1 Statedata callback failed (xret set with netconf-error)
* @note xtop can be replaced
*/
int
diff --git a/apps/backend/backend_plugin.h b/apps/backend/backend_plugin.h
index a2736b19..eb038319 100644
--- a/apps/backend/backend_plugin.h
+++ b/apps/backend/backend_plugin.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c
index ec32fb53..0d823ec8 100644
--- a/apps/backend/backend_socket.c
+++ b/apps/backend/backend_socket.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/backend_socket.h b/apps/backend/backend_socket.h
index df6ac149..8778be50 100644
--- a/apps/backend/backend_socket.h
+++ b/apps/backend/backend_socket.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/clixon_backend.h b/apps/backend/clixon_backend.h
index b3a75763..37e046b1 100644
--- a/apps/backend/clixon_backend.h
+++ b/apps/backend/clixon_backend.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index f4707de4..3ab58e9c 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/clixon_backend_handle.h b/apps/backend/clixon_backend_handle.h
index bdfbb425..491778aa 100644
--- a/apps/backend/clixon_backend_handle.h
+++ b/apps/backend/clixon_backend_handle.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c
index 173ba01c..727a7332 100644
--- a/apps/backend/clixon_backend_transaction.c
+++ b/apps/backend/clixon_backend_transaction.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/backend/clixon_backend_transaction.h b/apps/backend/clixon_backend_transaction.h
index a290c5f1..447cd15f 100644
--- a/apps/backend/clixon_backend_transaction.h
+++ b/apps/backend/clixon_backend_transaction.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/Makefile.in b/apps/cli/Makefile.in
index a55918d9..2b75b479 100644
--- a/apps/cli/Makefile.in
+++ b/apps/cli/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 7e6f1307..8a52207e 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -236,8 +236,8 @@ cli_dbxml(clicon_handle h,
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
goto done;
xbot = xtop;
- if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
- goto done;
+ if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
+ goto done;
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
@@ -293,7 +293,7 @@ cli_set(clicon_handle h,
cvec *cvv,
cvec *argv)
{
- int retval = 1;
+ int retval = -1;
if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0)
goto done;
@@ -501,53 +501,54 @@ cli_start_shell(clicon_handle h,
cvec *vars,
cvec *argv)
{
- char *cmd;
+ char *cmd;
struct passwd *pw;
- int retval;
- char bcmd[128];
- cg_var *cv1 = cvec_i(vars, 1);
+ int retval = -1;
+ char bcmd[128];
+ cg_var *cv1 = cvec_i(vars, 1);
cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL);
if ((pw = getpwuid(getuid())) == NULL){
fprintf(stderr, "%s: getpwuid: %s\n",
__FUNCTION__, strerror(errno));
- return -1;
+ goto done;
}
if (chdir(pw->pw_dir) < 0){
fprintf(stderr, "%s: chdir(%s): %s\n",
__FUNCTION__, pw->pw_dir, strerror(errno));
endpwent();
- return -1;
+ goto done;
}
endpwent();
cli_signal_flush(h);
cli_signal_unblock(h);
if (cmd){
snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd);
- if ((retval = system(bcmd)) < 0){
+ if (system(bcmd) < 0){
cli_signal_block(h);
fprintf(stderr, "%s: system(bash -c): %s\n",
__FUNCTION__, strerror(errno));
- return -1;
+ goto done;
}
}
else
- if ((retval = system("bash -l")) < 0){
+ if (system("bash -l") < 0){
cli_signal_block(h);
fprintf(stderr, "%s: system(bash): %s\n",
__FUNCTION__, strerror(errno));
- return -1;
+ goto done;
}
cli_signal_block(h);
#if 0 /* Allow errcodes from bash */
if (retval != 0){
fprintf(stderr, "%s: system(%s) code=%d\n", __FUNCTION__, cmd, retval);
- return -1;
+ goto done;
}
#endif
-
- return 0;
+ retval = 0;
+ done:
+ return retval;
}
/*! Generic quit callback
@@ -958,7 +959,7 @@ cli_notification_cb(int s,
event_unreg_fd(s, cli_notification_cb);
goto done;
}
- if (clicon_msg_decode(reply, &xt) < 0)
+ if (clicon_msg_decode(reply, NULL, &xt) < 0) /* XXX pass yang_spec */
goto done;
if ((xe = xpath_first(xt, "//event")) != NULL){
x = NULL;
diff --git a/apps/cli/cli_common.h b/apps/cli/cli_common.h
index 8e177e5e..4e29838f 100644
--- a/apps/cli/cli_common.h
+++ b/apps/cli/cli_common.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 022e41b8..70c126bd 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -169,28 +169,6 @@ static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, cbuf *cb,
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
yang_stmt *ytype, cbuf *cb, char *helptext);
-/*! Patched maxstring to account for DEC64 types
- * @note kludge to fix overflow error -> Fix the original error in cvtype_max2str
- * by adding a fraction_digits argument.
- */
-static char *
-cvtype_max2str_dup2(enum cv_type type,
- int fraction_digits)
-{
- int len;
- char *str;
-
- if (type!=CGV_DEC64 || fraction_digits==0)
- return cvtype_max2str_dup(type);
- if ((len = cvtype_max2str(type, NULL, 0)) < 0)
- return NULL;
- if ((str = (char *)malloc(len+1)) == NULL)
- return NULL;
- memset(str, '\0', len+1);
- len = snprintf(str, len+1, "%" PRId64 ".0", (INT64_MAX/((int)pow(10,fraction_digits))));
- return str;
-}
-
/*! Generate CLI code for Yang leaf statement to CLIgen variable of specific type
* Check for completion (of already existent values), ranges (eg range[min:max]) and
* patterns, (eg regexp:"[0.9]*").
@@ -208,18 +186,17 @@ yang2cli_var_sub(clicon_handle h,
char *helptext,
enum cv_type cvtype,
int options,
- cg_var *mincv,
- cg_var *maxcv,
+ cvec *cvv,
char *pattern,
uint8_t fraction_digits
)
{
int retval = -1;
char *type;
- char *r;
yang_stmt *yi = NULL;
int i = 0;
char *cvtypestr;
+ cg_var *cv;
if (cvtype == CGV_VOID){
retval = 0;
@@ -276,47 +253,39 @@ yang2cli_var_sub(clicon_handle h,
if (options & YANG_OPTIONS_FRACTION_DIGITS)
cprintf(cb, " fraction-digits:%u", fraction_digits);
+
if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){
- assert(mincv || maxcv);
- cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length");
- if (mincv){
- if ((r = cv2str_dup(mincv)) == NULL){
- clicon_err(OE_UNIX, errno, "cv2str_dup");
- goto done;
- }
- cprintf(cb, "%s:", r);
- free(r);
- r = NULL;
- }
- if (maxcv != NULL){
- if ((r = cv2str_dup(maxcv)) == NULL){
- clicon_err(OE_UNIX, errno, "cv2str_dup");
- goto done;
- }
- }
- else{ /* Cligen does not have 'max' keyword in range so need to find actual
- max value of type if yang range expression is 0..max
- */
- if (cvtype==CGV_STRING){
- if ((r = malloc(512)) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- snprintf(r, 512, "%d", MAXPATHLEN);
- }
- else {
- if ((r = cvtype_max2str_dup2(cvtype, fraction_digits)) == NULL){
- clicon_err(OE_UNIX, errno, "cvtype_max2str");
- goto done;
+ /* Loop through range_min and range_min..rang_max */
+ i = 0;
+ while (iys_argument);
+ cv = cvec_i(cvv, i++);
+ if (strcmp(cv_name_get(cv),"range_min") == 0){
+ cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length");
+ cv2cbuf(cv, cb);
+ cprintf(cb,":");
+ /* probe next */
+ if (i");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
@@ -347,8 +316,7 @@ yang2cli_var_union_one(clicon_handle h,
{
int retval = -1;
int options = 0;
- cg_var *mincv = NULL;
- cg_var *maxcv = NULL;
+ cvec *cvv = NULL;
char *pattern = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
@@ -356,9 +324,9 @@ yang2cli_var_union_one(clicon_handle h,
char *restype;
/* Resolve the sub-union type to a resolved type */
- if (yang_type_resolve(ys, ytsub, /* in */
+ if (yang_type_resolve(ys, ys, ytsub, /* in */
&ytype, &options, /* resolved type */
- &mincv, &maxcv, &pattern, &fraction_digits) < 0)
+ &cvv, &pattern, &fraction_digits) < 0)
goto done;
restype = ytype?ytype->ys_argument:NULL;
@@ -367,10 +335,10 @@ yang2cli_var_union_one(clicon_handle h,
goto done;
}
else {
- if (clicon_type2cv(origtype, restype, &cvtype) < 0)
+ if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
if ((retval = yang2cli_var_sub(h, ys, ytype, cb, helptext, cvtype,
- options, mincv, maxcv, pattern, fraction_digits)) < 0)
+ options, cvv, pattern, fraction_digits)) < 0)
goto done;
}
retval = 0;
@@ -438,8 +406,7 @@ yang2cli_var(clicon_handle h,
char *origtype;
yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */
- cg_var *mincv = NULL;
- cg_var *maxcv = NULL;
+ cvec *cvv = NULL;
char *pattern = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
@@ -448,7 +415,7 @@ yang2cli_var(clicon_handle h,
char *type;
if (yang_type_get(ys, &origtype, &yrestype,
- &options, &mincv, &maxcv, &pattern, &fraction_digits) < 0)
+ &options, &cvv, &pattern, &fraction_digits) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
@@ -456,7 +423,7 @@ yang2cli_var(clicon_handle h,
retval = 0;
goto done;
}
- if (clicon_type2cv(origtype, restype, &cvtype) < 0)
+ if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
/* Note restype can be NULL here for example with unresolved hardcoded uuid */
if (restype && strcmp(restype, "union") == 0){
@@ -485,7 +452,7 @@ yang2cli_var(clicon_handle h,
if (completionp)
cprintf(cb, "(");
if ((retval = yang2cli_var_sub(h, ys, yrestype, cb, helptext, cvtype,
- options, mincv, maxcv, pattern, fraction_digits)) < 0)
+ options, cvv, pattern, fraction_digits)) < 0)
goto done;
if (completionp){
if (cli_expand_var_generate(h, ys, cvtype, cb,
@@ -751,11 +718,6 @@ yang2cli_stmt(clicon_handle h,
if (yang_config(ys)){
switch (ys->ys_keyword){
- case Y_GROUPING:
- case Y_RPC:
- case Y_AUGMENT:
- return 0;
- break;
case Y_CONTAINER:
if (yang2cli_container(h, ys, cbuf, gt, level) < 0)
goto done;
@@ -773,19 +735,21 @@ yang2cli_stmt(clicon_handle h,
if (yang2cli_leaf(h, ys, cbuf, gt, level, 1) < 0)
goto done;
break;
- default:
+ case Y_CASE:
+ case Y_SUBMODULE:
+ case Y_MODULE:
for (i=0; iys_len; i++)
if ((yc = ys->ys_stmt[i]) != NULL)
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
goto done;
break;
+ default: /* skip */
+ break;
}
}
-
retval = 0;
done:
return retval;
-
}
/*! Generate CLI code for Yang specification
@@ -814,13 +778,13 @@ yang2cli(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- /* Traverse YANG specification: loop through statements */
+ /* Traverse YANG, loop through all modules and generate CLI */
for (i=0; iyp_len; i++)
if ((ymod = yspec->yp_stmt[i]) != NULL){
if (yang2cli_stmt(h, ymod, cbuf, gt, 0) < 0)
goto done;
}
- clicon_debug(0, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cbuf));
+ clicon_debug(2, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cbuf));
/* Parse the buffer using cligen parser. XXX why this?*/
if ((globals = cvec_new(0)) == NULL)
goto done;
diff --git a/apps/cli/cli_generate.h b/apps/cli/cli_generate.h
index 371063ba..d049d600 100644
--- a/apps/cli/cli_generate.h
+++ b/apps/cli/cli_generate.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c
index 58e03948..34a92c4d 100644
--- a/apps/cli/cli_handle.c
+++ b/apps/cli/cli_handle.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_handle.h b/apps/cli/cli_handle.h
index bbf66742..c7fd728b 100644
--- a/apps/cli/cli_handle.h
+++ b/apps/cli/cli_handle.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index b22e1c9f..deeafd29 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -71,7 +71,7 @@
#include "cli_handle.h"
/* Command line options to be passed to getopt(3) */
-#define CLI_OPTS "hD:f:xl:F:1a:u:d:m:qpGLy:c:U:"
+#define CLI_OPTS "hD:f:xl:F:1a:u:d:m:qpGLy:c:U:o:"
#define CLI_LOGFILE "/tmp/clixon_cli.log"
@@ -227,7 +227,8 @@ usage(clicon_handle h,
"\t-l > \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
"\t-y \tOverride yang spec file (dont include .yang suffix)\n"
"\t-c \tSpecify cli spec file.\n"
- "\t-U \tOver-ride unix user with a pseudo user for NACM.\n",
+ "\t-U \tOver-ride unix user with a pseudo user for NACM.\n"
+ "\t-o \"=\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
plgdir ? plgdir : "none"
);
@@ -403,6 +404,15 @@ main(int argc, char **argv)
if (clicon_username_set(h, optarg) < 0)
goto done;
break;
+ case 'o':{ /* Configuration option */
+ char *val;
+ if ((val = index(optarg, '=')) == NULL)
+ usage(h, argv0);
+ *val++ = '\0';
+ if (clicon_option_add(h, optarg, val) < 0)
+ goto done;
+ break;
+ }
default:
usage(h, argv[0]);
break;
@@ -446,6 +456,9 @@ main(int argc, char **argv)
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
+ /* Load clixon lib yang module */
+ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
+ goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c
index 633c46ae..4f17f948 100644
--- a/apps/cli/cli_plugin.c
+++ b/apps/cli/cli_plugin.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_plugin.h b/apps/cli/cli_plugin.h
index 2df7fb47..75aa99c2 100644
--- a/apps/cli/cli_plugin.h
+++ b/apps/cli/cli_plugin.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c
index 2e56f329..0fa9fa56 100644
--- a/apps/cli/cli_show.c
+++ b/apps/cli/cli_show.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -171,7 +171,7 @@ expand_dbvar(void *h,
/* This is primarily to get "y",
* xpath2xml would have worked!!
*/
- if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
+ if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 1)
goto done;
if (y==NULL)
goto ok;
@@ -443,6 +443,7 @@ cli_show_config(clicon_handle h,
cxobj *xc;
cxobj *xerr;
enum genmodel_type gt;
+ yang_spec *yspec;
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv));
@@ -496,6 +497,13 @@ cli_show_config(clicon_handle h,
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
+ if ((yspec = clicon_dbspec_yang(h)) == NULL){
+ clicon_err(OE_FATAL, 0, "No DB_SPEC");
+ goto done;
+ }
+ /* Some formats (eg cli) require yang */
+ if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ goto done;
/* Print configuration according to format */
switch (format){
case FORMAT_XML:
@@ -516,7 +524,7 @@ cli_show_config(clicon_handle h,
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
xc = NULL; /* Dont print xt itself */
- while ((xc = xml_child_each(xt, xc, -1)) != NULL)
+ while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
xml2cli(stdout, xc, NULL, gt); /* cli syntax */
break;
case FORMAT_NETCONF:
diff --git a/apps/cli/clixon_cli.h b/apps/cli/clixon_cli.h
index c41d7185..aaebfb19 100644
--- a/apps/cli/clixon_cli.h
+++ b/apps/cli/clixon_cli.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h
index 3507e31f..d2926029 100644
--- a/apps/cli/clixon_cli_api.h
+++ b/apps/cli/clixon_cli_api.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/netconf/Makefile.in b/apps/netconf/Makefile.in
index 81d86577..ee2d0082 100644
--- a/apps/netconf/Makefile.in
+++ b/apps/netconf/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/apps/netconf/clixon_netconf.h b/apps/netconf/clixon_netconf.h
index e2fbb5bb..470474f1 100644
--- a/apps/netconf/clixon_netconf.h
+++ b/apps/netconf/clixon_netconf.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -44,6 +44,7 @@
* (Duplicated. Also in netconf_*.h)
*/
int netconf_output(int s, cbuf *xf, char *msg);
+int netconf_output_encap(int s, cbuf *xf, char *msg);
int netconf_xpath(cxobj *xsearch,
cxobj *xfilter,
diff --git a/apps/netconf/netconf_filter.c b/apps/netconf/netconf_filter.c
index 89b698e9..8451c33b 100644
--- a/apps/netconf/netconf_filter.c
+++ b/apps/netconf/netconf_filter.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/netconf/netconf_filter.h b/apps/netconf/netconf_filter.h
index ed5e3d98..ce7d76e6 100644
--- a/apps/netconf/netconf_filter.h
+++ b/apps/netconf/netconf_filter.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/netconf/netconf_hello.c b/apps/netconf/netconf_hello.c
index 1be1211c..9c4b07ba 100644
--- a/apps/netconf/netconf_hello.c
+++ b/apps/netconf/netconf_hello.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/netconf/netconf_hello.h b/apps/netconf/netconf_hello.h
index 4f877b32..2ccec1b8 100644
--- a/apps/netconf/netconf_hello.h
+++ b/apps/netconf/netconf_hello.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c
index fc24810b..0a6b869b 100644
--- a/apps/netconf/netconf_lib.c
+++ b/apps/netconf/netconf_lib.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -182,9 +182,9 @@ netconf_get_target(cxobj *xn,
* @param[in] s
* @param[in] cb Cligen buffer that contains the XML message
* @param[in] msg Only for debug
- * @note Assumes "cb" contains valid XML, ie encoding is correct. This is done
- * if it is output by a xml render routine (xml_print et al), but NOT
- * otherwise.
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see netconf_output_encap for function with encapsulation
*/
int
netconf_output(int s,
@@ -216,3 +216,34 @@ netconf_output(int s,
return retval;
}
+
+/*! Encapsulate and send outgoing netconf packet as cbuf on socket
+ * @param[in] s
+ * @param[in] cb Cligen buffer that contains the XML message
+ * @param[in] msg Only for debug
+ * @retval 0 OK
+ * @retval -1 Error
+ * @note Assumes "cb" contains valid XML
+ * @see netconf_output without encapsulation
+ */
+int
+netconf_output_encap(int s,
+ cbuf *cb,
+ char *msg)
+{
+ int retval = -1;
+ cbuf *cb1 = NULL;
+
+ if ((cb1 = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ add_preamble(cb1);
+ cprintf(cb1, "%s", cbuf_get(cb));
+ add_postamble(cb1);
+ retval = netconf_output(s, cb1, msg);
+ done:
+ if (cb1)
+ cbuf_free(cb1);
+ return retval;
+}
diff --git a/apps/netconf/netconf_lib.h b/apps/netconf/netconf_lib.h
index f2dc73e1..d243ac6a 100644
--- a/apps/netconf/netconf_lib.h
+++ b/apps/netconf/netconf_lib.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -75,5 +75,6 @@ int add_error_preamble(cbuf *xf, char *reason);
char *netconf_get_target(cxobj *xn, char *path);
int add_error_postamble(cbuf *xf);
int netconf_output(int s, cbuf *xf, char *msg);
+int netconf_output_encap(int s, cbuf *cb, char *msg);
#endif /* _NETCONF_LIB_H_ */
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index 55ee628b..b3fd3a22 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -71,7 +71,7 @@
#include "netconf_rpc.h"
/* Command line options to be passed to getopt(3) */
-#define NETCONF_OPTS "hD:f:l:qa:u:d:y:U:t:"
+#define NETCONF_OPTS "hD:f:l:qa:u:d:y:U:t:o:"
#define NETCONF_LOGFILE "/tmp/clixon_netconf.log"
@@ -80,21 +80,25 @@
* @param[in] cb Packet buffer
*/
static int
-process_incoming_packet(clicon_handle h,
+netconf_input_packet(clicon_handle h,
cbuf *cb)
{
- char *str;
- char *str0;
- cxobj *xreq = NULL; /* Request (in) */
- int isrpc = 0; /* either hello or rpc */
- cbuf *cbret = NULL;
- cxobj *xret = NULL; /* Return (out) */
- cxobj *xrpc;
- cxobj *xc;
+ int retval = -1;
+ char *str;
+ char *str0;
+ cxobj *xreq = NULL; /* Request (in) */
+ int isrpc = 0; /* either hello or rpc */
+ cbuf *cbret = NULL;
+ cxobj *xret = NULL; /* Return (out) */
+ cxobj *xrpc;
+ cxobj *xc;
yang_spec *yspec;
+ int ret;
+ cxobj *xa;
+ cxobj *xa2;
- clicon_debug(1, "RECV");
- clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
+ clicon_debug(1, "%s", __FUNCTION__);
+ clicon_debug(2, "%s: \"%s\"", __FUNCTION__, cbuf_get(cb));
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
@@ -108,22 +112,21 @@ process_incoming_packet(clicon_handle h,
/* Parse incoming XML message */
if (xml_parse_string(str, yspec, &xreq) < 0){
free(str0);
- if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
+ if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0)
goto done;
- netconf_output(1, cbret, "rpc-error");
+ netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
free(str0);
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
- int ret;
isrpc++;
- if ((ret = xml_yang_validate_rpc(xrpc)) < 0)
+ if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
goto done;
if (ret == 0){
- if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
- goto done;
- netconf_output(1, cbret, "rpc-error");
- goto done;
+ netconf_output_encap(1, cbret, "rpc-error");
+ goto ok;
}
}
else
@@ -142,8 +145,13 @@ process_incoming_packet(clicon_handle h,
goto done;
}
else{ /* there is a return message in xret */
- cxobj *xa, *xa2;
- assert(xret);
+
+ if (xret == NULL){
+ if (netconf_operation_failed(cbret, "rpc", "Internal error: no xml return")< 0)
+ goto done;
+ netconf_output_encap(1, cbret, "rpc-error");
+ goto done;
+ }
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
@@ -158,16 +166,15 @@ process_incoming_packet(clicon_handle h,
if (xml_addsub(xc, xa2) < 0)
goto done;
}
- add_preamble(cbret);
-
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
- add_postamble(cbret);
- if (netconf_output(1, cbret, "rpc-reply") < 0){
+ if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}
+ ok:
+ retval = 0;
done:
if (xreq)
xml_free(xreq);
@@ -175,7 +182,7 @@ process_incoming_packet(clicon_handle h,
xml_free(xret);
if (cbret)
cbuf_free(cbret);
- return 0;
+ return retval;
}
/*! Get netconf message: detect end-of-msg
@@ -228,8 +235,8 @@ netconf_input_cb(int s,
/* OK, we have an xml string from a client */
/* Remove trailer */
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
- if (process_incoming_packet(h, cb) < 0)
- goto done;
+ if (netconf_input_packet(h, cb) < 0)
+ ; //goto done; // ignore errors
if (cc_closed)
break;
cbuf_reset(cb);
@@ -326,7 +333,8 @@ usage(clicon_handle h,
"\t-y \tLoad yang spec file (override yang main module)\n"
"\t-U \tOver-ride unix user with a pseudo user for NACM.\n"
- "\t-t \tTimeout in seconds. Quit after this time.\n",
+ "\t-t \tTimeout in seconds. Quit after this time.\n"
+ "\t-o \"=\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
clicon_netconf_dir(h)
);
@@ -439,7 +447,15 @@ main(int argc,
case 't': /* timeout in seconds */
tv.tv_sec = atoi(optarg);
break;
-
+ case 'o':{ /* Configuration option */
+ char *val;
+ if ((val = index(optarg, '=')) == NULL)
+ usage(h, argv0);
+ *val++ = '\0';
+ if (clicon_option_add(h, optarg, val) < 0)
+ goto done;
+ break;
+ }
default:
usage(h, argv[0]);
break;
@@ -468,7 +484,9 @@ main(int argc,
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
-
+ /* Load clixon lib yang module */
+ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
+ goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index f53b8675..ba41648a 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -56,6 +56,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -134,20 +135,10 @@ netconf_get_config(clicon_handle h,
{
cxobj *xfilter; /* filter */
int retval = -1;
- char *source;
char *ftype = NULL;
cxobj *xfilterconf;
cxobj *xconf;
- if ((source = netconf_get_target(xn, "source")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "source "
- " ");
- goto ok;
- }
/* ie ... */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
ftype = xml_find_value(xfilter, "type");
@@ -186,7 +177,6 @@ netconf_get_config(clicon_handle h,
"type "
"");
}
- ok: /* netconf error is not fatal */
retval = 0;
done:
return retval;
@@ -222,11 +212,9 @@ get_edit_opts(cxobj *xn,
if ((optstr = xml_body(x)) != NULL){
if (strcmp(optstr, "test-then-set") == 0)
*testopt = TEST_THEN_SET;
- else
- if (strcmp(optstr, "set") == 0)
+ else if (strcmp(optstr, "set") == 0)
*testopt = SET;
- else
- if (strcmp(optstr, "test-only") == 0)
+ else if (strcmp(optstr, "test-only") == 0)
*testopt = TEST_ONLY;
else
goto parerr;
@@ -310,53 +298,18 @@ netconf_edit_config(clicon_handle h,
{
int retval = -1;
int optret;
- enum operation_type operation = OP_MERGE;
enum test_option testopt = TEST_THEN_SET;/* only supports this */
enum error_option erropt = STOP_ON_ERROR; /* only supports this */
- cxobj *xc; /* config */
- cxobj *x;
- cxobj *xfilter;
- char *ftype = NULL;
- char *target; /* db */
- /* must have target, and it should be candidate */
- if ((target = netconf_get_target(xn, "target")) == NULL ||
- strcmp(target, "candidate")){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "target "
- " ");
- goto ok;
- }
- /* CLICON addition, eg /> */
- if ((xfilter = xpath_first(xn, "filter")) != NULL) {
- if ((ftype = xml_find_value(xfilter, "type")) != NULL)
- if (strcmp(ftype,"restconf")){
- xml_parse_va(xret, NULL, ""
- "invalid-value "
- "protocol "
- "error "
- " ");
- goto ok;
- }
- }
- if ((x = xpath_first(xn, "default-operation")) != NULL){
- if (xml_operation(xml_body(x), &operation) < 0){
- xml_parse_va(xret, NULL, ""
- "invalid-value "
- "protocol "
- "error "
- " ");
- goto ok;
- }
- }
if ((optret = get_edit_opts(xn, &testopt, &erropt, xret)) < 0)
goto done;
if (optret == 0) /* error in opt parameters */
goto ok;
- /* not supported opts */
+ /* These constraints are clixon-specific since :validate should
+ * support all testopts, and erropts should be supported
+ * And therefore extends the validation
+ * (implement the features before removing these checks)
+ */
if (testopt!=TEST_THEN_SET || erropt!=STOP_ON_ERROR){
xml_parse_va(xret, NULL, ""
"operation-not-supported "
@@ -364,168 +317,15 @@ netconf_edit_config(clicon_handle h,
"error "
" ");
goto ok;
- }
-
- /* operation is OP_REPLACE, OP_MERGE, or OP_NONE pass all to backend */
- if ((xc = xpath_first(xn, "config")) != NULL){
-#if 0
- /* application-specific code registers 'config' */
- if ((ret = netconf_plugin_callbacks(h, xc, xret)) < 0){
- goto ok;
- }
-#endif
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- }
- ok:
- retval = 0;
- done:
- return retval;
-}
-
-/*! Netconf copy configuration
-
-
-
-
-
-
-
-
-
-
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_copy_config(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
- char *source;
- char *target; /* filenames */
-
- if ((source = netconf_get_target(xn, "source")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "source "
- " ");
- goto ok;
- }
- if ((target = netconf_get_target(xn, "target")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "target "
- " ");
- goto ok;
- }
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- ok:
- retval = 0;
- done:
- return retval;
-}
-
-/*! Delete configuration
-
-
-
-
-
- Delete a configuration datastore. The
- configuration datastore cannot be deleted.
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_delete_config(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- char *target; /* filenames */
- int retval = -1;
-
- if ((target = netconf_get_target(xn, "target")) == NULL ||
- strcmp(target, "running")==0){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "target "
- " ");
- goto ok;
- }
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- ok:
- retval = 0;
- done:
- return retval;
-}
-
-
-/*! Lock a database
-
-
-
-
-
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_lock(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
- char *target;
-
- if ((target = netconf_get_target(xn, "target")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "target "
- " ");
- goto ok;
}
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
ok:
retval = 0;
- done:
+ done:
return retval;
}
-/*! Unlock a database
-
-
-
-
-
- XXX
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_unlock(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- return netconf_lock(h, xn, xret);
-}
-
/*! Get running configuration and device state information
*
*
@@ -589,138 +389,11 @@ netconf_get(clicon_handle h,
"type "
"");
}
- // ok: /* netconf error is not fatal */
retval = 0;
done:
return retval;
}
-
-/*! Close a (user) session
-
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
-*/
-static int
-netconf_close_session(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
-
- cc_closed++;
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Kill other user sessions
-
- PID
-
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_kill_session(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval=-1;
- cxobj *xs;
-
- if ((xs = xpath_first(xn, "//session-id")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "session-id "
- " ");
- goto ok;
- }
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- ok:
- retval = 0;
- done:
- return retval;
-}
-/*! Check the semantic consistency of candidate
-
- :validate
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_validate(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
- char *target;
-
- if ((target = netconf_get_target(xn, "source")) == NULL){
- xml_parse_va(xret, NULL, ""
- "missing-element "
- "protocol "
- "error "
- "target "
- " ");
- goto ok;
- }
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- ok:
- retval = 0;
- done:
- return retval;
-}
-
-/*! Commit candidate -> running
-
- :candidate
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_commit(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
-
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Discard all changes in candidate / revert to running
-
- :candidate
- * @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at ... level.
- * @param[out] xret Return XML, error or OK
- */
-static int
-netconf_discard_changes(clicon_handle h,
- cxobj *xn,
- cxobj **xret)
-{
- int retval = -1;
-
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- retval = 0;
- done:
- return retval;
-}
-
/*! Called when a notification has happened on backend
* and this session has registered for that event.
* Filter it and forward it.
@@ -753,6 +426,8 @@ netconf_notification_cb(int s,
cbuf *cb;
cxobj *xn = NULL; /* event xml */
cxobj *xt = NULL; /* top xml */
+ clicon_handle h = (clicon_handle)arg;
+ yang_spec *yspec = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* get msg (this is the reason this function is called) */
@@ -766,7 +441,8 @@ netconf_notification_cb(int s,
event_unreg_fd(s, netconf_notification_cb);
goto done;
}
- if (clicon_msg_decode(reply, &xt) < 0)
+ yspec = clicon_dbspec_yang(h);
+ if (clicon_msg_decode(reply, yspec, &xt) < 0)
goto done;
if ((xn = xpath_first(xt, "notification")) == NULL)
goto ok;
@@ -775,12 +451,10 @@ netconf_notification_cb(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
- add_preamble(cb); /* Make it well-formed netconf xml */
if (clicon_xml2cbuf(cb, xn, 0, 0) < 0)
goto done;
- add_postamble(cb);
/* Send it to listening client on stdout */
- if (netconf_output(1, cb, "notification") < 0){
+ if (netconf_output_encap(1, cb, "notification") < 0){
cbuf_free(cb);
goto done;
}
@@ -840,7 +514,7 @@ netconf_create_subscription(clicon_handle h,
goto ok;
if (event_reg_fd(s,
netconf_notification_cb,
- NULL,
+ h,
"notification socket") < 0)
goto done;
ok:
@@ -876,12 +550,16 @@ netconf_application_rpc(clicon_handle h,
cbuf *cb = NULL;
cbuf *cbret = NULL;
int ret;
-
+
/* First check system / netconf RPC:s */
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
+ goto done;
+ }
/* Find yang rpc statement, return yang rpc statement if found
Check application RPC */
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@@ -902,13 +580,9 @@ netconf_application_rpc(clicon_handle h,
goto ok;
}
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
- if ((yrpc==NULL) && _CLICON_XML_NS_ITERATE){
- int i;
- for (i=0; iyp_len; i++){
- ymod = yspec->yp_stmt[i];
- if ((yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn))) != NULL)
- break;
- }
+ if ((yrpc==NULL) && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(xn, yspec, &yrpc) < 0) /* Y_RPC */
+ goto done;
}
/* Check if found */
if (yrpc != NULL){
@@ -917,15 +591,18 @@ 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 (xml_apply(xn, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
+ if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
goto done;
- if (xml_yang_validate_add(xn, NULL) < 0)
+ if (ret == 0){
+ netconf_output_encap(1, cbret, "rpc-error");
+ goto ok;
+ }
+ if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
goto done;
- }
- if ((cbret = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, 0, "cbuf_new");
- goto done;
+ if (ret == 0){
+ netconf_output_encap(1, cbret, "rpc-error");
+ goto ok;
+ }
}
/* Look for local (client-side) netconf plugins. */
if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0)
@@ -943,11 +620,18 @@ netconf_application_rpc(clicon_handle h,
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
- if (xml_apply(xoutput, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
+ if ((ret = xml_yang_validate_all_top(xoutput, cbret)) < 0)
goto done;
- if (xml_yang_validate_add(xoutput, NULL) < 0)
+ if (ret == 0){
+ clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
+ goto ok;
+ }
+ if ((ret = xml_yang_validate_add(xoutput, cbret)) < 0)
goto done;
+ if (ret == 0){
+ clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
+ goto ok;
+ }
}
retval = 1; /* handled by callback */
goto done;
@@ -992,9 +676,25 @@ netconf_rpc_dispatch(clicon_handle h,
if (xml_value_set(xa, username) < 0)
goto done;
}
+ /* Many of these calls are now calling generic clicon_rpc_netconf_xml
+ * directly, since the validation is generic and done before this place
+ * in the call. Some call however need extra validation, such as the
+ * filter parameter to get/get-config and tes- err-opts of edit-config.
+ */
xe = NULL;
while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) {
- if (strcmp(xml_name(xe), "get-config") == 0){
+ if (strcmp(xml_name(xe), "copy-config") == 0 ||
+ strcmp(xml_name(xe), "delete-config") == 0 ||
+ strcmp(xml_name(xe), "lock") == 0 ||
+ strcmp(xml_name(xe), "unlock") == 0 ||
+ strcmp(xml_name(xe), "kill-session") == 0 ||
+ strcmp(xml_name(xe), "validate") == 0 || /* :validate */
+ strcmp(xml_name(xe), "commit") == 0 || /* :candidate */
+ strcmp(xml_name(xe), "discard-changes") == 0){
+ if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
+ goto done;
+ }
+ else if (strcmp(xml_name(xe), "get-config") == 0){
if (netconf_get_config(h, xe, xret) < 0)
goto done;
}
@@ -1002,47 +702,14 @@ netconf_rpc_dispatch(clicon_handle h,
if (netconf_edit_config(h, xe, xret) < 0)
goto done;
}
- else if (strcmp(xml_name(xe), "copy-config") == 0){
- if (netconf_copy_config(h, xe, xret) < 0)
- goto done;
- }
- else if (strcmp(xml_name(xe), "delete-config") == 0){
- if (netconf_delete_config(h, xe, xret) < 0)
- goto done;
- }
- else if (strcmp(xml_name(xe), "lock") == 0) {
- if (netconf_lock(h, xe, xret) < 0)
- goto done;
- }
- else if (strcmp(xml_name(xe), "unlock") == 0){
- if (netconf_unlock(h, xe, xret) < 0)
- goto done;
- }
else if (strcmp(xml_name(xe), "get") == 0){
if (netconf_get(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "close-session") == 0){
- if (netconf_close_session(h, xe, xret) < 0)
- goto done;
- }
- else if (strcmp(xml_name(xe), "kill-session") == 0) {
- if (netconf_kill_session(h, xe, xret) < 0)
- goto done;
- }
- /* Validate capability :validate */
- else if (strcmp(xml_name(xe), "validate") == 0){
- if (netconf_validate(h, xe, xret) < 0)
- goto done;
- }
- /* Candidate configuration capability :candidate */
- else if (strcmp(xml_name(xe), "commit") == 0){
- if (netconf_commit(h, xe, xret) < 0)
- goto done;
- }
- else if (strcmp(xml_name(xe), "discard-changes") == 0){
- if (netconf_discard_changes(h, xe, xret) < 0)
- goto done;
+ cc_closed++;
+ if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
+ goto done;
}
/* RFC 5277 :notification */
else if (strcmp(xml_name(xe), "create-subscription") == 0){
diff --git a/apps/netconf/netconf_rpc.h b/apps/netconf/netconf_rpc.h
index 3ce6a615..4a20397f 100644
--- a/apps/netconf/netconf_rpc.h
+++ b/apps/netconf/netconf_rpc.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in
index 32366f89..768a261d 100644
--- a/apps/restconf/Makefile.in
+++ b/apps/restconf/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/apps/restconf/README.md b/apps/restconf/README.md
index 972ab99f..e951e18a 100644
--- a/apps/restconf/README.md
+++ b/apps/restconf/README.md
@@ -1,11 +1,11 @@
# Clixon Restconf
- * [Installation](#Installation)
- * [Streams](Streams)
- * [Nchan Streams](Nchan)
- * [Debugging](Debugging)
+ * [Installation](#installation)
+ * [Streams](#streams)
+ * [Nchan Streams](#nchan)
+ * [Debugging](#debugging)
-### 1. Installation
+## Installation
The examples are based on Nginx. Other reverse proxies should work but are not verified.
@@ -44,39 +44,39 @@ sudo systemctl start start.service
Start clixon restconf daemon
```
-olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
+> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
```
Make restconf calls with curl
```
-olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces
+> curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces
[
{
- "interfaces": {
+ "ietf-interfaces:interfaces": {
"interface":[
{
- "name": "eth0",
- "type": "eth",
- "enabled": "true",
"name": "eth9",
- "type": "eth",
- "enabled": "true"
+ "type": "ex:eth",
+ "enabled": true,
}
]
}
}
]
-olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
-[
- {
- "type": "eth"
- }
-]
-
-curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
+```
+Get the type of a specific interface:
+```
+> curl -G http://127.0.0.1/restconf/data/interfaces/interface=eth9/type
+{
+ "ietf-interfaces:type": "eth"
+}
+```
+Example of writing a new interfaces specification:
+```
+curl -sX PUT http://localhost/restconf/data -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth1","type":"ex:eth","enabled":true}}}'
```
-### 2. Streams
+## Streams
Clixon have two experimental restconf event stream implementations following
RFC8040 Section 6 using SSE. One native and one using Nginx
@@ -112,7 +112,7 @@ Add the following to extend the nginx configuration file with the following stat
AN example of a stream access is as follows:
```
-vandal> curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
+> curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
data: 2018-11-04T14:47:11.373124 fault Ethernet0 major
data: 2018-11-04T14:47:16.375265 fault Ethernet0 major
@@ -125,7 +125,7 @@ You can also specify start and stop time. Start-time enables replay of existing
See (stream tests)[../test/test_streams.sh] for more examples.
-### 3. Nchan
+## Nchan
As an alternative streams implementation, Nginx/Nchan can be used.
Nginx uses pub/sub channels and can be configured in a variety of
@@ -180,7 +180,7 @@ curl -H "Accept: text/event-stream" -H "Last-Event-ID: 1539961709:0" -s -X GET h
See (https://nchan.io/#eventsource) on more info on how to access an SSE sub endpoint.
-### 4. Debugging
+## Debugging
Start the restconf fastcgi program with debug flag:
```
diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h
index 2c79c3cc..a8625e9f 100644
--- a/apps/restconf/clixon_restconf.h
+++ b/apps/restconf/clixon_restconf.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index 75c06699..500a07c3 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -71,11 +71,12 @@ static const map_str2int netconf_restconf_map[] = {
{"missing-attribute", 400},
{"bad-attribute", 400},
{"unknown-attribute", 400},
+ {"missing-element", 400},
{"bad-element", 400},
{"unknown-element", 400},
{"unknown-namespace", 400},
- {"access-denied", 401},
- {"access-denied", 403},
+ {"access-denied", 401}, /* or 403 */
+ {"access-denied", 403},
{"lock-denied", 409},
{"resource-denied", 409},
{"rollback-failed", 500},
@@ -436,7 +437,8 @@ api_return_err(clicon_handle h,
goto ok;
}
tagstr = xml_body(xtag);
- code = restconf_err2code(tagstr);
+ if ((code = restconf_err2code(tagstr)) < 0)
+ code = 500; /* internal server error */
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
if (xml_name_set(xerr, "error") < 0)
@@ -448,9 +450,12 @@ api_return_err(clicon_handle h,
else
if (xml2json_cbuf(cb, xerr, pretty) < 0)
goto done;
+ clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
+ FCGX_SetExitStatus(code, r->out); /* Created */
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n\r\n",
use_xml?"xml":"json");
+
if (use_xml){
if (pretty){
FCGX_FPrintF(r->out, " \n", cbuf_get(cb));
diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h
index e05eb054..8c478370 100644
--- a/apps/restconf/restconf_lib.h
+++ b/apps/restconf/restconf_lib.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index d5067c4f..c92a2ff1 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -81,7 +81,7 @@
#include "restconf_stream.h"
/* Command line options to be passed to getopt(3) */
-#define RESTCONF_OPTS "hD:f:l:p:y:a:u:"
+#define RESTCONF_OPTS "hD:f:l:p:y:a:u:o:"
/* RESTCONF enables deployments to specify where the RESTCONF API is
located. The client discovers this by getting the "/.well-known/host-meta"
@@ -495,7 +495,8 @@ usage(clicon_handle h,
"\t-d \tSpecify restconf plugin directory dir (default: %s)\n"
"\t-y \tLoad yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n"
- "\t-u \tInternal socket domain path or IP addr (see -a)\n",
+ "\t-u \tInternal socket domain path or IP addr (see -a)\n"
+ "\t-o \"=\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
clicon_restconf_dir(h)
);
@@ -611,6 +612,15 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
+ case 'o':{ /* Configuration option */
+ char *val;
+ if ((val = index(optarg, '=')) == NULL)
+ usage(h, argv0);
+ *val++ = '\0';
+ if (clicon_option_add(h, optarg, val) < 0)
+ goto done;
+ break;
+ }
default:
usage(h, argv[0]);
break;
@@ -645,7 +655,9 @@ main(int argc,
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
-
+ /* Load clixon lib yang module */
+ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
+ goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@@ -654,7 +666,7 @@ main(int argc,
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
+ yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index 42d4d331..58980589 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -93,6 +93,7 @@ Mapping netconf error-tag -> status code
| malformed-message | 400 |
+-------------------------+-------------+
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
#ifdef HAVE_CONFIG_H
@@ -190,23 +191,30 @@ api_data_get2(clicon_handle h,
yang_spec *yspec;
cxobj *xret = NULL;
cxobj *xerr = NULL; /* malloced */
- cxobj *xe;
+ cxobj *xe = NULL;
cxobj **xvec = NULL;
size_t xlen;
int i;
cxobj *x;
-
+ int ret;
+
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
if ((cbpath = cbuf_new()) == NULL)
goto done;
cprintf(cbpath, "/");
- clicon_debug(1, "%s pi:%d", __FUNCTION__, pi);
/* We know "data" is element pi-1 */
- if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){
+ if ((ret = api_path2xpath(yspec, pcvec, pi, cbpath)) < 0)
+ goto done;
+ if (ret == 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ clicon_err_reset();
+ 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;
}
@@ -215,23 +223,29 @@ api_data_get2(clicon_handle h,
if (clicon_rpc_get(h, path, &xret) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
+ if (xml_apply(xret, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ goto done;
/* We get return via netconf which is complete tree from root
* We need to cut that tree to only the object.
*/
-#if 1 /* DEBUG */
- {
+#if 0 /* DEBUG */
+ if (debug){
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, xret, 0, 0);
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
cbuf_free(cb);
}
#endif
- /* Check if error return XXX this needs more work */
- if ((xe = xpath_first(xret, "/rpc-error")) != NULL){
+ /* Check if error return */
+ if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
@@ -239,11 +253,12 @@ api_data_get2(clicon_handle h,
/* Normal return, no error */
if ((cbx = cbuf_new()) == NULL)
goto done;
- FCGX_SetExitStatus(200, r->out); /* OK */
- FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
- FCGX_FPrintF(r->out, "\r\n");
- if (head)
+ if (head){
+ FCGX_SetExitStatus(200, r->out); /* OK */
+ FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
+ FCGX_FPrintF(r->out, "\r\n");
goto ok;
+ }
if (path==NULL || strcmp(path,"/")==0){ /* Special case: data root */
if (use_xml){
if (clicon_xml2cbuf(cbx, xret, 0, pretty) < 0) /* Dont print top object? */
@@ -259,16 +274,32 @@ api_data_get2(clicon_handle h,
goto done;
if (use_xml){
for (i=0; i0
+ * Out: {"example:x": {"0"}}
+ */
if (xml2json_cbuf_vec(cbx, xvec, xlen, pretty) < 0)
goto done;
+ }
}
- // clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
+ clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
+ FCGX_SetExitStatus(200, r->out); /* OK */
+ FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
+ FCGX_FPrintF(r->out, "\r\n");
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
FCGX_FPrintF(r->out, "\r\n\r\n");
ok:
@@ -412,7 +443,8 @@ api_data_post(clicon_handle h,
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe; /* dont free */
char *username;
-
+ int ret;
+
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
api_path, data);
@@ -427,14 +459,32 @@ api_data_post(clicon_handle h,
goto done;
/* Translate api_path to xtop/xbot */
xbot = xtop;
- if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
- goto done;
+ if (api_path){
+ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
+ goto done;
+ if (ret == 0){ /* validation failed */
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
+ goto done;
+ clicon_err_reset();
+ 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;
+ }
+ }
/* Parse input data as json or xml into xml */
if (parse_xml){
if (xml_parse_string(data, NULL, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
@@ -442,7 +492,11 @@ api_data_post(clicon_handle h,
else if (json_parse_str(data, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
@@ -452,7 +506,11 @@ api_data_post(clicon_handle h,
if (xml_child_nr(xdata) != 1){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
@@ -466,6 +524,19 @@ api_data_post(clicon_handle h,
/* Replace xbot with x, ie bottom of api-path with data */
if (xml_addsub(xbot, x) < 0)
goto done;
+ if (!parse_xml){ /* If JSON, translate namespace from module:name to xmlns=uri */
+ if (json2xml_ns(yspec, x, &xerr) < 0)
+ goto done;
+ if (xerr){
+ 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;
+ }
+ }
/* Create text buffer for transfer to backend */
if ((cbx = cbuf_new()) == NULL)
goto done;
@@ -504,7 +575,7 @@ api_data_post(clicon_handle h,
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0) /* Use original xe */
goto done;
goto ok;
}
@@ -639,6 +710,8 @@ api_data_put(clicon_handle h,
cxobj *xerr = NULL; /* malloced must be freed */
cxobj *xe;
char *username;
+ int ret;
+ char *namespace0;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__, api_path0, data);
@@ -654,43 +727,91 @@ api_data_put(clicon_handle h,
goto done;
/* Translate api_path to xtop/xbot */
xbot = xtop;
-
- if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
- goto done;
+ if (api_path){
+ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
+ goto done;
+ if (ret == 0){ /* validation failed */
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
+ goto done;
+ clicon_err_reset();
+ 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;
+ }
+ }
/* Parse input data as json or xml into xml */
if (parse_xml){
if (xml_parse_string(data, NULL, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
}
- else if (json_parse_str(data, &xdata) < 0){
- if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
+ else{
+ if (json_parse_str(data, &xdata) < 0){
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 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;
+ }
+ }
/* The message-body MUST contain exactly one instance of the
* expected data resource.
*/
if (xml_child_nr(xdata) != 1){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
x = xml_child_i(xdata,0);
+ if (!parse_xml){ /* If JSON, translate namespace from module:name to xmlns=uri */
+ if (json2xml_ns(yspec, x, &xerr) < 0)
+ goto done;
+ if (xerr){
+ 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;
+ }
+ }
/* Add operation (create/replace) as attribute */
if ((xa = xml_new("operation", x, 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)
@@ -705,7 +826,11 @@ api_data_put(clicon_handle h,
if (strcmp(xml_name(x), xml_name(xbot))){
if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
@@ -714,7 +839,11 @@ api_data_put(clicon_handle h,
if (match_list_keys((yang_stmt*)y, x, xbot) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
@@ -723,8 +852,15 @@ api_data_put(clicon_handle h,
xml_purge(xbot);
if (xml_addsub(xparent, x) < 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 (xml2ns(xparent, NULL, &namespace0) < 0)
+ goto done;
+ /* Set xmlns="" default namespace attribute (if diff from default) */
+ if (strcmp(namespace0, xml_value(xa))==0)
+ xml_purge(xa);
+ }
}
-
/* Create text buffer for transfer to backend */
if ((cbx = cbuf_new()) == NULL)
goto done;
@@ -849,6 +985,8 @@ api_data_delete(clicon_handle h,
cxobj *xretdis = NULL; /* return from discard */
cxobj *xerr = NULL;
char *username;
+ int ret;
+ cxobj *xe; /* xml error, no free */
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@@ -861,12 +999,26 @@ api_data_delete(clicon_handle h,
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
goto done;
xbot = xtop;
- if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
- goto done;
+ if (api_path){
+ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y)) < 0)
+ goto done;
+ if (ret == 0){ /* validation failed */
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
+ goto done;
+ clicon_err_reset();
+ 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;
+ }
+ }
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
- if (xml_value_set(xa, xml_operation2str(op)) < 0)
+ if (xml_value_set(xa, xml_operation2str(op)) < 0)
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
@@ -881,8 +1033,8 @@ api_data_delete(clicon_handle h,
cprintf(cbx, "");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
- if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
}
@@ -895,7 +1047,7 @@ api_data_delete(clicon_handle h,
cprintf(cbx, " ");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
- if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
+ if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
cbuf_reset(cbx);
cprintf(cbx, "", NACM_RECOVERY_USER);
cprintf(cbx, " ");
@@ -904,7 +1056,7 @@ api_data_delete(clicon_handle h,
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
}
@@ -971,36 +1123,37 @@ api_operations_get(clicon_handle h,
char *namespace;
cbuf *cbx = NULL;
cxobj *xt = NULL;
+ int i;
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
if ((cbx = cbuf_new()) == NULL)
goto done;
- cprintf(cbx, "");
+ if (use_xml)
+ cprintf(cbx, "");
+ else
+ cprintf(cbx, "{\"operations\": {");
ymod = NULL;
+ i = 0;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
namespace = yang_find_mynamespace(ymod);
- yc = NULL;
+ yc = NULL;
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
if (yc->ys_keyword != Y_RPC)
continue;
- cprintf(cbx, "<%s xmlns=\"%s\"/>", yc->ys_argument, namespace);
+ if (use_xml)
+ cprintf(cbx, "<%s xmlns=\"%s\"/>", yc->ys_argument, namespace);
+ else{
+ if (i++)
+ cprintf(cbx, ",");
+ cprintf(cbx, "\"%s:%s\": null", ymod->ys_argument, yc->ys_argument);
+ }
}
}
- cprintf(cbx, " ");
- if (xml_parse_string(cbuf_get(cbx), yspec, &xt) < 0)
- goto done;
- if (xml_rootchild(xt, 0, &xt) < 0)
- goto done;
- cbuf_reset(cbx); /* reuse same cbuf */
- if (use_xml){
- if (clicon_xml2cbuf(cbx, xt, 0, pretty) < 0) /* Dont print top object? */
- goto done;
- }
- else{
- if (xml2json_cbuf(cbx, xt, pretty) < 0)
- goto done;
- }
+ if (use_xml)
+ cprintf(cbx, " ");
+ else
+ cprintf(cbx, "}");
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
FCGX_FPrintF(r->out, "\r\n");
@@ -1017,6 +1170,298 @@ api_operations_get(clicon_handle h,
return retval;
}
+/*! Handle input data to api_operations_post
+ * @param[in] h CLIXON handle
+ * @param[in] r Fastcgi request handle
+ * @param[in] data Stream input data
+ * @param[in] yspec Yang top-level specification
+ * @param[in] yrpc Yang rpc spec
+ * @param[in] xrpc XML pointer to rpc method
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
+ * @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
+ * @retval 1 OK
+ * @retval 0 Fail, Error message sent
+ * @retval -1 Fatal error, clicon_err called
+ *
+ * RFC8040 3.6.1
+ * If the "rpc" or "action" statement has an "input" section, then
+ * instances of these input parameters are encoded in the module
+ * namespace where the "rpc" or "action" statement is defined, in an XML
+ * element or JSON object named "input", which is in the module
+ * namespace where the "rpc" or "action" statement is defined.
+ * (Any other input is assumed as error.)
+ */
+static int
+api_operations_post_input(clicon_handle h,
+ FCGX_Request *r,
+ char *data,
+ yang_spec *yspec,
+ yang_stmt *yrpc,
+ cxobj *xrpc,
+ int pretty,
+ int use_xml,
+ int parse_xml)
+{
+ int retval = -1;
+ cxobj *xdata = NULL;
+ cxobj *xerr = NULL; /* malloced must be freed */
+ cxobj *xe;
+ cxobj *xinput;
+ cxobj *x;
+ cbuf *cbret = NULL;
+
+ clicon_debug(1, "%s %s", __FUNCTION__, data);
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
+ goto done;
+ }
+ /* Parse input data as json or xml into xml */
+ if (parse_xml){
+ if (xml_parse_string(data, yspec, &xdata) < 0){
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
+ goto done;
+ if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
+ 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 fail;
+ }
+ }
+ else { /* JSON */
+ if (json_parse_str(data, &xdata) < 0){
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 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 fail;
+ }
+ /* Special case for JSON: It looks like:
+ * Need to translate to
+ */
+ if (json2xml_ns(yspec, xdata, &xerr) < 0)
+ goto done;
+ if (xerr){
+ 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 fail;
+ }
+ }
+ xml_name_set(xdata, "data");
+ /* Here xdata is:
+ * ...
+ * Validate that exactly only tag
+ */
+#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
+ if ((xinput = xml_child_i_type(xdata, 0, CX_ELMNT)) == NULL ||
+ strcmp(xml_name(xinput),"input") != 0 ||
+ xml_child_nr_type(xdata, CX_ELMNT) != 1){
+
+ if (xml_child_nr_type(xdata, CX_ELMNT) == 0){
+ if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have input statement") < 0)
+ goto done;
+ }
+ else
+ if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0)
+ goto done;
+ if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
+ 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 fail;
+ }
+ // clicon_debug(1, "%s input validation passed", __FUNCTION__);
+ /* Add all input under path */
+ x = NULL;
+ while ((x = xml_child_i_type(xinput, 0, CX_ELMNT)) != NULL)
+ if (xml_addsub(xrpc, x) < 0)
+ goto done;
+ /* Here xrpc is: 42
+ */
+ // ok:
+ retval = 1;
+ done:
+ clicon_debug(1, "%s retval: %d", __FUNCTION__, retval);
+ if (cbret)
+ cbuf_free(cbret);
+ if (xerr)
+ xml_free(xerr);
+ if (xdata)
+ xml_free(xdata);
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
+/*! Handle output data to api_operations_post
+ * @param[in] h CLIXON handle
+ * @param[in] r Fastcgi request handle
+ * @param[in] xret XML reply messages from backend/handler
+ * @param[in] yspec Yang top-level specification
+ * @param[in] youtput Yang rpc output specification
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] use_xml Set to 0 for JSON and 1 for XML for output data
+ * @param[out] xoutputp Restconf JSON/XML output
+ * @retval 1 OK
+ * @retval 0 Fail, Error message sent
+ * @retval -1 Fatal error, clicon_err called
+ * xret should like: 0
+ */
+static int
+api_operations_post_output(clicon_handle h,
+ FCGX_Request *r,
+ cxobj *xret,
+ yang_spec *yspec,
+ yang_stmt *youtput,
+ char *namespace,
+ int pretty,
+ int use_xml,
+ cxobj **xoutputp)
+
+{
+ int retval = -1;
+ cxobj *xoutput = NULL;
+ cxobj *xerr = NULL; /* assumed malloced, will be freed */
+ cxobj *xe; /* just pointer */
+ cxobj *xa; /* xml attribute (xmlns) */
+ cxobj *x;
+ cxobj *xok;
+ cbuf *cbret = NULL;
+ int ret;
+
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
+ goto done;
+ }
+ /* Validate that exactly only tag */
+ if ((xoutput = xml_child_i_type(xret, 0, CX_ELMNT)) == NULL ||
+ strcmp(xml_name(xoutput),"rpc-reply") != 0 ||
+ xml_child_nr_type(xret, CX_ELMNT) != 1){
+ if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0)
+ goto done;
+ if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
+ 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 fail;
+ }
+ /* xoutput should now look: 0 */
+ /* 9. Translate to restconf RPC data */
+ xml_name_set(xoutput, "output");
+ /* xoutput should now look: 0 */
+#if 0
+ if (debug){
+ cbuf *ccc=cbuf_new();
+ if (clicon_xml2cbuf(ccc, xoutput, 0, 0) < 0)
+ goto done;
+ clicon_debug(1, "%s XOUTPUT:%s", __FUNCTION__, cbuf_get(ccc));
+ }
+#endif
+ /* Validate output (in case handlers are wrong) */
+ if (youtput==NULL){
+ /* Special case, no yang output
+ * RFC 7950 7.14.4
+ * If the RPC operation invocation succeeded and no output parameters
+ * are returned, the contains a single element
+ * RFC 8040 3.6.2
+ * If the "rpc" statement has no "output" section, the response message
+ * MUST NOT include a message-body and MUST send a "204 No Content"
+ * status-line instead.
+ */
+ if ((xok = xml_child_i_type(xoutput, 0, CX_ELMNT)) == NULL ||
+ strcmp(xml_name(xok),"ok") != 0 ||
+ xml_child_nr_type(xoutput, CX_ELMNT) != 1){
+ /* Internal error - invalid output from rpc handler */
+ if (xok){
+ if (netconf_operation_failed_xml(&xerr, "application",
+ "Internal error: Empty RPC reply is not ok") < 0)
+ goto done;
+ }
+ else
+ if (netconf_operation_failed_xml(&xerr, "application",
+ "Internal error: Empty RPC reply should have OK") < 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 fail;
+ }
+ FCGX_SetExitStatus(204, r->out); /* OK */
+ FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
+ FCGX_FPrintF(r->out, "\r\n");
+ goto fail;
+ }
+ else{
+ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
+ if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_all(xoutput, cbret)) < 0)
+ goto done;
+ if (ret == 1 &&
+ (ret = xml_yang_validate_add(xoutput, cbret)) < 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;
+ }
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ goto done;
+ goto fail;
+ }
+
+ /* Clear namespace of parameters */
+ x = NULL;
+ while ((x = xml_child_each(xoutput, x, CX_ELMNT)) != NULL) {
+ if ((xa = xml_find_type(x, NULL, "xmlns", CX_ATTR)) != NULL)
+ if (xml_purge(xa) < 0)
+ goto done;
+ }
+ }
+ /* Set namespace on output */
+ if (xmlns_set(xoutput, NULL, namespace) < 0)
+ goto done;
+ *xoutputp = xoutput;
+ retval = 1;
+ done:
+ clicon_debug(1, "%s retval: %d", __FUNCTION__, retval);
+ if (cbret)
+ cbuf_free(cbret);
+ if (xerr)
+ xml_free(xerr);
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
/*! REST operation POST method
* @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle
@@ -1030,7 +1475,24 @@ api_operations_get(clicon_handle h,
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
* See RFC 8040 Sec 3.6 / 4.4.2
* @note We map post to edit-config create.
- POST {+restconf}/operations/
+ * POST {+restconf}/operations/
+ * 1. Initialize
+ * 2. Get rpc module and name from uri (oppath) and find yang spec
+ * 3. Build xml tree with user and rpc:
+ * 4. Parse input data (arguments):
+ * JSON: {"example:input":{"x":0}}
+ * XML: 0
+ * 5. Translate input args to Netconf RPC, add to xml tree:
+ * 42
+ * 6. Validate outgoing RPC and fill in default values
+ * 42 99
+ * 7. Send to RPC handler, either local or backend
+ * 8. Receive reply from local/backend handler as Netconf RPC
+ * 0
+ * 9. Translate to restconf RPC data:
+ * JSON: {"example:output":{"x":0}}
+ * XML: 0
+ * 10. Validate and send reply to originator
*/
int
api_operations_post(clicon_handle h,
@@ -1047,21 +1509,15 @@ api_operations_post(clicon_handle h,
int retval = -1;
int i;
char *oppath = path;
- yang_stmt *yrpc = NULL;
yang_spec *yspec;
- yang_stmt *yinput;
- yang_stmt *youtput;
- cxobj *xdata = NULL;
+ yang_stmt *youtput = NULL;
+ yang_stmt *yrpc = NULL;
cxobj *xret = NULL;
cxobj *xerr = NULL; /* malloced must be freed */
- cxobj *xer; /* non-malloced error */
- cbuf *cbx = NULL;
cxobj *xtop = NULL; /* xpath root */
cxobj *xbot = NULL;
yang_node *y = NULL;
- cxobj *xinput;
- cxobj *xoutput;
- cxobj *x;
+ cxobj *xoutput = NULL;
cxobj *xa;
cxobj *xe;
char *username;
@@ -1070,204 +1526,205 @@ api_operations_post(clicon_handle h,
char *prefix = NULL;
char *id = NULL;
yang_stmt *ys = NULL;
+ char *namespace = NULL;
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
+ /* 1. Initialize */
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
+ goto done;
+ }
for (i=0; i
*
* The field identifies the module name and rpc identifier
* string for the desired operation.
*/
- if (yang_nodeid_split(oppath+1, &prefix, &id) < 0) /* +1 skip / */
+ if (nodeid_split(oppath+1, &prefix, &id) < 0) /* +1 skip / */
goto done;
if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){
- if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
+ if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ 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;
}
- if (yrpc == NULL){
- if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- /* Create an xml message:
- * <"rpc">...
- * eg
+ /* 3. Build xml tree with user and rpc:
+ *
*/
- /* Create config top-of-tree */
if ((xtop = xml_new("rpc", NULL, NULL)) == NULL)
goto done;
xbot = xtop;
- /* For internal XML protocol: add username attribute for backend access control
- */
+ /* Here xtop is: */
if ((username = clicon_username_get(h)) != NULL){
if ((xa = xml_new("username", xtop, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, username) < 0)
goto done;
+ /* Here xtop is: */
}
- /* XXX: something strange for rpc user */
- if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
+ if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y)) < 0)
goto done;
- if (data && strlen(data)){
- /* Parse input data as json or xml into xml */
- if (parse_xml){
- if (xml_parse_string(data, NULL, &xdata) < 0){
- if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- }
- else if (json_parse_str(data, &xdata) < 0){
- if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL);
- /* xdata should have format */
- if ((xinput = xpath_first(xdata, "/input")) == NULL){
- xml_name_set(xdata, "input");
- xml_spec_set(xdata, yinput); /* needed for xml_spec_populate */
- if (yinput){
- if (xml_yang_validate_add(xdata, NULL) < 0){
- if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- }
- }
- else{
- /* Add all input under path */
- x = NULL;
- while (xml_child_nr(xinput)){
- x = xml_child_i(xinput, 0);
- if (xml_addsub(xbot, x) < 0)
- goto done;
- }
- if (yinput){
- xml_spec_set(xbot, yinput); /* needed for xml_spec_populate */
- /* XXX yinput <-> h ?*/
- if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
- if (xml_apply(xbot, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
- goto done;
- if (xml_yang_validate_add(xbot, NULL) < 0){
- if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
- goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- }
- }
- }
- if ((cbret = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, 0, "cbuf_new");
- goto done;
- }
- xe = NULL;
- while ((xe = xml_child_each(xtop, xe, CX_ELMNT)) != NULL) {
- /* Look for local (client-side) restconf plugins. */
- if ((ret = rpc_callback_call(h, xe, cbret, r)) < 0)
+ if (ret == 0){ /* validation failed */
+ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
- if (ret == 1){ /* Handled locally */
- if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
- goto done;
- /* Local error: return it and quit */
- if ((xer = xpath_first(xret, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xer, pretty, use_xml) < 0)
- goto done;
- goto ok;
- }
- }
- break; /* Just one if local */
- }
- if (ret == 0){ /* Send to backend */
- if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
+ clicon_err_reset();
+ if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
+ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
- if ((xer = xpath_first(xret, "//rpc-error")) != NULL){
- if (api_return_err(h, r, xer, pretty, use_xml) < 0)
- goto done;
- goto ok;
}
- }
- /* Check if RPC output section */
- if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) == NULL){
- /* If the RPC operation is invoked without errors and if the "rpc" or
- * "action" statement has no "output" section, the response message
- * MUST NOT include a message-body and MUST send a "204 No Content"
- * status-line instead.
- */
- FCGX_SetExitStatus(204, r->out); /* OK */
- FCGX_FPrintF(r->out, "\r\n");
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ goto done;
goto ok;
}
- if ((cbx = cbuf_new()) == NULL)
- goto done;
- if ((xoutput=xpath_first(xret, "/")) != NULL){
- xml_name_set(xoutput, "output");
-#if 0
- clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
-#endif
- cbuf_reset(cbx);
- xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
- if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
- if (xml_apply(xoutput, CX_ELMNT,
- (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
- goto done;
- if (xml_yang_validate_add(xoutput, NULL) < 0)
+ /* Here xtop is:
+ * xbot is
+ * 4. Parse input data (arguments):
+ * JSON: {"example:input":{"x":0}}
+ * XML: 0
+ */
+ namespace = xml_find_type_value(xbot, NULL, "xmlns", CX_ATTR);
+ clicon_debug(1, "%s : 4. Parse input data: %s", __FUNCTION__, data);
+ if (data && strlen(data)){
+ if ((ret = api_operations_post_input(h, r, data, yspec, yrpc, xbot,
+ pretty, use_xml, parse_xml)) < 0)
goto done;
+ if (ret == 0)
+ goto ok;
}
- /* Sanity check of outgoing XML */
+ /* Here xtop is:
+ 42 */
+#if 0
+ if (debug){
+ cbuf *ccc=cbuf_new();
+ if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
+ goto done;
+ clicon_debug(1, "%s 5. Translate input args: %s",
+ __FUNCTION__, cbuf_get(ccc));
+ }
+#endif
+ /* 6. Validate outgoing RPC and fill in defaults */
+ if (xml_spec_populate_rpc(h, xtop, yspec) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_rpc(xtop, cbret)) < 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){
+ 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;
+ }
+ /* Here xtop is (default values):
+ * 42 99
+ */
+#if 0
+ if (debug){
+ cbuf *ccc=cbuf_new();
+ if (clicon_xml2cbuf(ccc, xtop, 0, 0) < 0)
+ goto done;
+ clicon_debug(1, "%s 6. Validate and defaults:%s", __FUNCTION__, cbuf_get(ccc));
+ }
+#endif
+ /* 7. Send to RPC handler, either local or backend
+ * Note (1) xtop is xbot is
+ * (2) local handler wants and backend wants
+ */
+ /* Look for local (client-side) restconf plugins.
+ * -1:Error, 0:OK local, 1:OK backend
+ */
+ if ((ret = rpc_callback_call(h, xbot, cbret, r)) < 0)
+ goto done;
+ if (ret == 1){ /* Handled locally */
+ if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
+ goto done;
+ /* Local error: return it and quit */
+ if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
+ if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ goto done;
+ goto ok;
+ }
+ }
+ else { /* Send to backend */
+ if (clicon_rpc_netconf_xml(h, xtop, &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;
+ goto ok;
+ }
+ }
+ /* 8. Receive reply from local/backend handler as Netconf RPC
+ * 0
+ */
+#if 0
+ if (debug){
+ cbuf *ccc=cbuf_new();
+ if (clicon_xml2cbuf(ccc, xret, 0, 0) < 0)
+ goto done;
+ clicon_debug(1, "%s 8. Receive reply:%s", __FUNCTION__, cbuf_get(ccc));
+ }
+#endif
+ youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL);
+ if ((ret = api_operations_post_output(h, r, xret, yspec, youtput, namespace,
+ pretty, use_xml, &xoutput)) < 0)
+ goto done;
+ if (ret == 0)
+ goto ok;
+ /* xoutput should now look: 0 */
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
FCGX_FPrintF(r->out, "\r\n");
- if (xoutput){
- if (use_xml){
- if (clicon_xml2cbuf(cbx, xoutput, 0, pretty) < 0)
- goto done;
- }
- else
- if (xml2json_cbuf(cbx, xoutput, pretty) < 0)
- goto done;
- FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
- FCGX_FPrintF(r->out, "\r\n\r\n");
+ cbuf_reset(cbret);
+ if (use_xml){
+ if (clicon_xml2cbuf(cbret, xoutput, 0, pretty) < 0)
+ goto done;
+ /* xoutput should now look: 0 */
}
+ else{
+ if (xml2json_cbuf(cbret, xoutput, pretty) < 0)
+ goto done;
+ /* xoutput should now look: {"example:output": {"x":0,"y":42}} */
+ }
+ FCGX_FPrintF(r->out, "%s", cbuf_get(cbret));
+ FCGX_FPrintF(r->out, "\r\n\r\n");
ok:
retval = 0;
done:
@@ -1276,16 +1733,12 @@ api_operations_post(clicon_handle h,
free(prefix);
if (id)
free(id);
- if (xdata)
- xml_free(xdata);
if (xtop)
xml_free(xtop);
if (xret)
xml_free(xret);
if (xerr)
xml_free(xerr);
- if (cbx)
- cbuf_free(cbx);
if (cbret)
cbuf_free(cbret);
return retval;
diff --git a/apps/restconf/restconf_methods.h b/apps/restconf/restconf_methods.h
index 11daaa26..42525946 100644
--- a/apps/restconf/restconf_methods.h
+++ b/apps/restconf/restconf_methods.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c
index 08424521..3d34c798 100644
--- a/apps/restconf/restconf_stream.c
+++ b/apps/restconf/restconf_stream.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -180,7 +180,7 @@ restconf_stream_cb(int s,
clicon_exit_set();
goto done;
}
- if (clicon_msg_decode(reply, &xtop) < 0)
+ if (clicon_msg_decode(reply, NULL, &xtop) < 0) /* XXX pass yang_spec */
goto done;
/* create event */
if ((cb = cbuf_new()) == NULL){
@@ -249,7 +249,7 @@ restconf_stream(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- cprintf(cb, "%s ", name);
+ cprintf(cb, "%s ", name);
/* Print all fields */
for (i=0; i... */
@@ -530,12 +544,9 @@ text_get(xmldb_handle xh,
/* Add default values (if not set) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
- /* Order XML children according to YANG */
- if (!xml_child_sort && xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
- goto done;
-#if 0 /* debug */
- if (xml_child_sort && xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
- clicon_log(LOG_NOTICE, "%s: verify failed #2", __FUNCTION__);
+#if 1 /* debug */
+ if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
+ clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
#endif
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
@@ -555,17 +566,22 @@ text_get(xmldb_handle xh,
}
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
+ * @param[in] th text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
* @param[in] x0p Parent of x0
* @param[in] x1 xml tree which modifies base
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
- * @param[out] cbret Initialized cligen buffer. Contains return XML or "".
+ * @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
+ * @retval -1 Error
+ * @retval 0 Failed (cbret set)
+ * @retval 1 OK
* Assume x0 and x1 are same on entry and that y is the spec
- * @see put in clixon_keyvalue.c
+ * @see text_modify_top
*/
static int
-text_modify(cxobj *x0,
+text_modify(struct text_handle *th,
+ cxobj *x0,
yang_node *y0,
cxobj *x0p,
cxobj *x1,
@@ -576,6 +592,8 @@ text_modify(cxobj *x0,
char *opstr;
char *x1name;
char *x1cname; /* child name */
+ cxobj *x0a; /* attribute */
+ cxobj *x1a; /* attribute */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
@@ -583,6 +601,7 @@ text_modify(cxobj *x0,
yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
+ int ret;
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
@@ -598,7 +617,7 @@ text_modify(cxobj *x0,
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
- goto ok;
+ goto fail;
}
case OP_NONE: /* fall thru */
case OP_MERGE:
@@ -607,6 +626,13 @@ text_modify(cxobj *x0,
// int iamkey=0;
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
+ /* Copy xmlns attributes */
+ if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != 0){
+ if ((x0a = xml_dup(x1a)) == NULL)
+ goto done;
+ if (xml_addsub(x0, x0a) < 0)
+ goto done;
+ }
#if 0
/* If it is key I dont want to mark it */
if ((iamkey=yang_key_match(y0->yn_parent, x1name)) < 0)
@@ -636,7 +662,7 @@ text_modify(cxobj *x0,
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
- goto ok;
+ goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0){
@@ -653,7 +679,7 @@ text_modify(cxobj *x0,
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
- goto ok;
+ goto fail;
}
case OP_REPLACE: /* fall thru */
if (x0){
@@ -679,11 +705,19 @@ text_modify(cxobj *x0,
if (x0==NULL){
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
+ /* Copy xmlns attributes */
+ if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != 0){
+ if ((x0a = xml_dup(x1a)) == NULL)
+ goto done;
+ if (xml_addsub(x0, x0a) < 0)
+ goto done;
+ }
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
}
- /* First pass: mark existing children in base */
- /* Loop through children of the modification tree */
+ /* First pass: Loop through children of the x1 modification tree
+ * collect matching nodes from x0 in x0vec (no changes to x0 children)
+ */
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
@@ -699,28 +733,40 @@ text_modify(cxobj *x0,
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
- if (match_base_child(x0, x1c, &x0c, yc) < 0)
+ if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
- x0vec[i++] = x0c;
+#if 1
+ if (x0c && (yc != xml_spec(x0c))){
+ /* There is a match but is should be replaced (choice)*/
+ if (xml_purge(x0c) < 0)
+ goto done;
+ x0c = NULL;
+ }
+#endif
+ x0vec[i++] = x0c; /* != NULL if x0c is matching x1c */
}
- /* Second pass: modify tree */
+ /* Second pass: Loop through children of the x1 modification tree again
+ * Now potentially modify x0:s children
+ * Here x0vec contains one-to-one matching nodes of x1:s children.
+ */
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
+ x0c = x0vec[i++];
yc = yang_find_datanode(y0, x1cname);
- if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret) < 0)
+ if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
- if (cbuf_len(cbret))
- goto ok;
+ if (ret == 0)
+ goto fail;
}
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
- goto ok;
+ goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0)
@@ -731,20 +777,26 @@ text_modify(cxobj *x0,
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
xml_sort(x0p, NULL);
- ok:
- retval = 0;
+ retval = 1;
done:
if (x0vec)
free(x0vec);
return retval;
+ fail: /* cbret set */
+ retval = 0;
+ goto done;
} /* text_modify */
/*! Modify a top-level base tree x0 with modification tree x1
- * @param[in] x0 Base xml tree (can be NULL in add scenarios)
- * @param[in] x1 xml tree which modifies base
+ * @param[in] th text handle
+ * @param[in] x0 Base xml tree (can be NULL in add scenarios)
+ * @param[in] x1 xml tree which modifies base
* @param[in] yspec Top-level yang spec (if y is NULL)
- * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
- * @param[out] cbret Initialized cligen buffer. Contains return XML or "".
+ * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
+ * @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
+ * @retval -1 Error
+ * @retval 0 Failed (cbret set)
+ * @retval 1 OK
* @see text_modify
*/
static int
@@ -762,6 +814,7 @@ text_modify_top(struct text_handle *th,
yang_stmt *yc; /* yang child */
yang_stmt *ymod;/* yang module */
char *opstr;
+ int ret;
/* Assure top-levels are 'config' */
assert(x0 && strcmp(xml_name(x0),"config")==0);
@@ -790,7 +843,7 @@ text_modify_top(struct text_handle *th,
case OP_DELETE:
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
- goto ok;
+ goto fail;
break;
default:
break;
@@ -812,35 +865,42 @@ text_modify_top(struct text_handle *th,
goto done;
if (ymod != NULL)
yc = yang_find_datanode((yang_node*)ymod, x1cname);
- if (yc == NULL && _CLICON_XML_NS_ITERATE){
- int i;
- for (i=0; iyp_len; i++){
- ymod = yspec->yp_stmt[i];
- if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) != NULL)
- break;
- }
+ if (yc == NULL && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(x1c, yspec, &yc) < 0)
+ goto done;
}
if (yc == NULL){
- if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
+ if (netconf_unknown_element(cbret, "application", x1cname, "Unassigned yang spec") < 0)
goto done;
- goto ok;
+ goto fail;
}
/* See if there is a corresponding node in the base tree */
- if (match_base_child(x0, x1c, &x0c, yc) < 0)
+ if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
- if (text_modify(x0c, (yang_node*)yc, x0, x1c, op, cbret) < 0)
+#if 1
+ if (x0c && (yc != xml_spec(x0c))){
+ /* There is a match but is should be replaced (choice)*/
+ if (xml_purge(x0c) < 0)
+ goto done;
+ x0c = NULL;
+ }
+#endif
+ if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
- if (cbuf_len(cbret))
- goto ok;
+ if (ret == 0)
+ goto fail;
}
- ok:
- retval = 0;
+ // ok:
+ retval = 1;
done:
return retval;
+ fail: /* cbret set */
+ retval = 0;
+ goto done;
} /* text_modify_top */
-/*! For containers without presence and no children, remove
+/*! For containers without presence and no children(except attrs), remove
* @param[in] x XML tree node
* See section 7.5.1 in rfc6020bis-02.txt:
* No presence:
@@ -869,7 +929,7 @@ xml_container_presence(cxobj *x,
}
/* Mark node that is: container, have no children, dont have presence */
if (y->ys_keyword == Y_CONTAINER &&
- xml_child_nr(x)==0 &&
+ xml_child_nr_notype(x, CX_ATTR)==0 &&
yang_find((yang_node*)y, Y_PRESENCE, NULL) == NULL)
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
retval = 0;
@@ -897,14 +957,11 @@ text_put(xmldb_handle xh,
yang_spec *yspec;
cxobj *x0 = NULL;
struct db_element *de = NULL;
- int cbretlocal = 0; /* Set if cbret is NULL on entry */
+ int ret;
if (cbret == NULL){
- if ((cbret = cbuf_new()) == NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- cbretlocal++;
+ clicon_err(OE_XML, EINVAL, "cbret is NULL");
+ goto done;
}
if ((yspec = th->th_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@@ -957,25 +1014,19 @@ text_put(xmldb_handle xh,
xml_name(x0));
goto done;
}
-#if 0
- /* Add yang specification backpointer to all XML nodes
- * This is already done in from_client_edit_config() */
- if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
-#endif
#if 0 /* debug */
- if (xml_child_sort && xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
+ if (xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
#endif
/*
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
- if (text_modify_top(th, x0, x1, yspec, op, cbret) < 0)
+ if ((ret = text_modify_top(th, x0, x1, yspec, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
- if (cbuf_len(cbret))
- goto ok;
+ if (ret == 0)
+ goto fail;
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
@@ -990,7 +1041,7 @@ text_put(xmldb_handle xh,
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
goto done;
#if 0 /* debug */
- if (xml_child_sort && xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
+ if (xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
#endif
/* Write back to datastore cache if first time */
@@ -1025,11 +1076,8 @@ text_put(xmldb_handle xh,
}
else if (clicon_xml2file(f, x0, 0, th->th_pretty) < 0)
goto done;
- ok:
- retval = 0;
+ retval = 1;
done:
- if (cbretlocal && cbret)
- cbuf_free(cbret);
if (f != NULL)
fclose(f);
if (dbfile)
@@ -1041,6 +1089,9 @@ text_put(xmldb_handle xh,
if (!th->th_cache && x0)
xml_free(x0);
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! Copy database from db1 to db2
@@ -1386,6 +1437,8 @@ main(int argc,
char *yangmod; /* yang file */
yang_spec *yspec = NULL;
clicon_handle h;
+ cbuf *cbret = NULL;
+ int ret;
if ((h = clicon_handle_init()) == NULL)
goto done;
@@ -1400,7 +1453,7 @@ main(int argc,
db_init(db);
if ((yspec = yspec_new()) == NULL)
goto done
- if (yang_parse(h, NULL, yangmod, NULL, yspec) < 0)
+ if (yang_spec_parse_module(h, yangmod, NULL, yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){
if (argc < 5)
@@ -1434,13 +1487,21 @@ main(int argc,
op = OP_REMOVE;
else
usage(argv[0]);
- if (xmldb_put(h, db, op, NULL, xn, NULL) < 0)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
+ }
+ if ((ret = xmldb_put(h, db, op, NULL, xn, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ fprintf(stderr, "%s\n", cbuf_get(cbret));
}
else
usage(argv[0]);
printf("\n");
done:
+ if (cbret)
+ cbuf_free(cbret);
return 0;
}
diff --git a/datastore/text/clixon_xmldb_text.h b/datastore/text/clixon_xmldb_text.h
index 19e9351d..9bb46e42 100644
--- a/datastore/text/clixon_xmldb_text.h
+++ b/datastore/text/clixon_xmldb_text.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 9ecf46ff..ff49df1a 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -42,9 +42,9 @@ The example:
sudo make install
```
-## How do you run Clixon example commands?
+## How do I run Clixon example commands?
-- Start a backend server: `clixon_backend -Ff /usr/local/etc/example.xml`
+- Start a backend server: `clixon_backend -F -s init -f /usr/local/etc/example.xml`
- Start a cli session: `clixon_cli -f /usr/local/etc/example.xml`
- Start a netconf session: `clixon_netconf -f /usr/local/etc/example.xml`
- Start a restconf daemon: `sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data`
@@ -72,6 +72,82 @@ grep clicon /etc/group
clicon:x:1001:,www-data
```
+## How do I use the CLI?
+
+The easiest way to use Clixon is via the CLI. Once the backend is started
+Example:
+```
+clixon_cli -f /usr/local/etc/example.xml
+cli> set interfaces interface eth9 ?
+ description enabled ipv4
+ ipv6 link-up-down-trap-enable type
+cli> set interfaces interface eth9 type ex:eth
+cli> validate
+cli> commit
+cli> show configuration xml
+
+
+ eth9
+ ex:eth
+ true
+
+
+cli> delete interfaces interface eth9
+```
+
+## How do I use netconf?
+
+As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
+Example:
+```
+clixon_netconf -qf /usr/local/etc/example.xml
+ ]]>]]>
+eth9 ex:eth true ]]>]]>
+```
+
+However, more useful is to run clixon_netconf as an SSH
+subsystem. Register the subsystem in /etc/sshd_config:
+```
+ Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/example.xml
+```
+and then invoke it from a client using
+```
+ ssh -s netconf
+```
+
+## How do I use restconf?
+
+You can access clixon via REST API using restconf, such as using
+curl. GET, PUT, POST are supported.
+
+You need a web-server, such as nginx, and start a restconf fcgi
+daemon, clixon_restconf.
+
+For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default:
+```
+server {
+ ...
+ location /restconf {
+ fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
+ include fastcgi_params;
+ }
+}
+```
+Start nginx daemon
+```
+sudo /etc/init.d/nginx start
+```
+
+Example:
+```
+ curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type
+ [
+ {
+ "ietf-interfaces:type": "ex:eth"
+ }
+ ]
+```
+Read more in the (restconf)[../apps/restconf] docs.
## What about reference documentation?
Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
You need to install doxygen and graphviz on your system.
@@ -121,7 +197,7 @@ are included.
The following configuration file options control the loading of Yang files:
- `CLICON_YANG_DIR` - A list of directories (yang dir path) where Clixon searches for module and submodules.
-- `CLICON_YANG_MAIN_FILE` - Load a specific Yang module fiven by a file.
+- `CLICON_YANG_MAIN_FILE` - Load a specific Yang module given by a file.
- `CLICON_YANG_MODULE_MAIN` - Specifies a single module to load. The module is searched for in the yang dir path.
- `CLICON_YANG_MODULE_REVISION` : Specifies a revision to the main module.
- `CLICON_YANG_MAIN_DIR` - Load all yang modules in this directory.
@@ -161,59 +237,6 @@ sudo docker run -td olofhagsand/clixon_example
```
Look in the example documentation for more info.
-## How do I use netconf?
-
-As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
-Example:
-```
- echo " ]]>]]>" | clixon_netconf -f /usr/local/etc/example.xml
-```
-
-However, more useful is to run clixon_netconf as an SSH
-subsystem. Register the subsystem in /etc/sshd_config:
-```
- Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/example.xml
-```
-and then invoke it from a client using
-```
- ssh -s netconf
-```
-
-## How do I use restconf?
-
-You can access clixon via REST API using restconf, such as using
-curl. GET, PUT, POST are supported.
-
-You need a web-server, such as nginx, and start a restconf fcgi
-daemon, clixon_restconf.
-
-For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default:
-```
-server {
- ...
- location /restconf {
- fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
- include fastcgi_params;
- }
-}
-```
-Start nginx daemon
-```
-sudo /etc/init.d/nginx start
-```
-
-Example:
-```
- curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
- [
- {
- "type": "eth"
- }
- ]
-```
-Read more in the (restconf)[../apps/restconf] docs.
-
-
## Does Clixon support event streams?
Yes, Clixon supports event notification streams in the CLI, Netconf and Restconf API:s.
@@ -233,7 +256,7 @@ severity major;
or via NETCONF:
```
clixon_netconf -qf /usr/local/etc/example.xml
-EXAMPLE ]]>]]>
+EXAMPLE ]]>]]>
]]>]]>
2018-09-30T12:44:59.657276 fault Ethernet0 major ]]>]]>
...
@@ -242,11 +265,11 @@ or via restconf:
```
curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
```
-Consult (../apps/restconf/README.md) on more information on how to setup a reverse proxy for restconf streams. It is also possible to configure a pub/sub system such as (Nginx Nchan)[https://nchan.io].
+Consult [clixon restconf](../apps/restconf/README.md) on more information on how to setup a reverse proxy for restconf streams. It is also possible to configure a pub/sub system such as [Nginx Nchan](https://nchan.io).
## How should I start the backend daemon?
-There are four different backend startup modes. There is differences in running state treatment, ie what state the machine is when you startthe daemon and how loading the configuration affects it:
+There are four different backend startup modes. There is differences in running state treatment, ie what state the machine is when you start the daemon and how loading the configuration affects it:
- none - Do not touch running state. Typically after crash when running state and db are synched.
- init - Initialize running state. Start with a completely clean running state.
- running - Commit running db configuration into running state. Typically after reboot if a persistent running db exists.
@@ -277,7 +300,7 @@ There are two ways to add extra XML to running database after start. Note that
The first way is via a file. Assume you want to add this xml (the config tag is a necessary top-level tag):
```
- extra
+ extra
```
You add this via the -c option:
@@ -289,12 +312,13 @@ The second way is by programming the plugin_reset() in the backend
plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c).
## I want to program. How do I extend the example?
-See [../apps/example]
+See [../apps/example](../apps/example)
- example.xml - Change the configuration file
- The yang specifications - This is the central part. It changes the XML, database and the config cli.
- example_cli.cli - Change the fixed part of the CLI commands
- example_cli.c - Cli C-commands are placed here.
- example_backend.c - Commit and validate functions.
+- example_backend_nacm.c - Secondary example plugin (for authorization)
- example_netconf.c - Netconf plugin
- example_restconf.c - Add restconf authentication, etc.
diff --git a/doc/Makefile.in b/doc/Makefile.in
index ad0f4203..f67f183d 100644
--- a/doc/Makefile.in
+++ b/doc/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/docker/Dockerfile b/docker/Dockerfile
index b1a71ce3..a15c874b 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/docker/Makefile.in b/docker/Makefile.in
index c18b9fad..375fa22d 100644
--- a/docker/Makefile.in
+++ b/docker/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/etc/Makefile.in b/etc/Makefile.in
index 263840df..09c671af 100644
--- a/etc/Makefile.in
+++ b/etc/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/etc/clixonrc.in b/etc/clixonrc.in
index e06018c6..c2a5cde3 100644
--- a/etc/clixonrc.in
+++ b/etc/clixonrc.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/example/Dockerfile b/example/Dockerfile
index 08314bf8..27cfb737 100644
--- a/example/Dockerfile
+++ b/example/Dockerfile
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/example/Makefile.in b/example/Makefile.in
index bbbd9070..8e18d3ea 100644
--- a/example/Makefile.in
+++ b/example/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/example/README.md b/example/README.md
index 7621d9b5..a0a1d621 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,19 +1,33 @@
# Clixon example
+ * [Content](#content)
+ * [Compile and run](#compile)
+ * [Using the CLI](#using-the-cli)
+ * [Using netconf](#using-netconf)
+ * [Streams](#streams)
+ * [RPC Operations](#rpc-operations)
+ * [State data](#state-data)
+ * [Authentication and NACM](#authentication-and-nacm)
+ * [Systemd](#systemd)
+ * [Docker](#docker)
+ * [Plugins](#plugins)
+
+## Content
+
This directory contains a Clixon example which includes a simple example. It contains the following files:
-* example.xml The configuration file. See yang/clixon-config@.yang for all available fields.
-* example.yang The yang spec of the example. It mainly includes ietf routing and IP modules.
-* example_cli.cli CLIgen specification.
-* example_cli.c CLI callback plugin containing functions called in the cli file above: a generic callback (`mycallback`) and an RPC (`fib_route_rpc`).
-* example_backend.c Backend callback plugin including example of:
+* `example.xml` The configuration file. See (yang/clixon-config@.yang)[../yang/clixon-config@2018-10-21.yang] for the documentation of all available fields.
+* `example.yang` The yang spec of the example. It mainly includes ietf routing and IP modules.
+* `example_cli.cli` CLIgen specification.
+* `example_cli.c` CLI callback plugin containing functions called in the cli file above: a generic callback (`mycallback`) and an RPC (`fib_route_rpc`).
+* `example_backend.c` Backend callback plugin including example of:
* transaction callbacks (validate/commit),
* notification,
* rpc handler
* state-data handler, ie non-config data
-* example_backend_nacm.c Secondary backend plugin. Plugins are loaded alphabetically.
-* example_restconf.c Restconf callback plugin containing an HTTP basic authentication callback
-* example_netconf.c Netconf callback plugin
-* Makefile.in Example makefile where plugins are built and installed
+* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically.
+* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback
+* `example_netconf.c` Netconf callback plugin
+* `Makefile.in` Example makefile where plugins are built and installed
## Compile and run
@@ -47,10 +61,37 @@ Send restconf command
curl -G http://127.0.0.1/restconf/data
```
-## Setting data example using netconf
+## Using the CLI
+
+The example CLI allows you to modify and view the data model using `set`, `delete` and `show` via generated code.
+There are also many other commands available as examples. View the source file (example_cli.cli)[example_cli.cli] for more details.
+
+The following example shows how to add an interface in candidate, validate and commit it to running, then look at it (as xml) and finally delete it.
+```
+clixon_cli -f /usr/local/etc/example.xml
+cli> set interfaces interface eth9 ?
+ description enabled ipv4
+ ipv6 link-up-down-trap-enable type
+cli> set interfaces interface eth9 type ex:eth
+cli> validate
+cli> commit
+cli> show configuration xml
+
+
+ eth9
+ ex:eth
+ true
+
+
+cli> delete interfaces interface eth9
+```
+
+## Using Netconf
+
+The following example shows how to set data using netconf:
```
-
+
eth1
true
@@ -65,13 +106,13 @@ Send restconf command
]]>]]>
```
-## Getting data using netconf
+### Getting data using netconf
```
]]>]]>
]]>]]>
]]>]]>
- ]]>]]>
- ]]>]]>
+eth9 ex:eth ]]>]]>
+ ]]>]]>
]]>]]>
```
@@ -80,23 +121,134 @@ Send restconf command
The example has an EXAMPLE stream notification triggering every 5s. To start a notification
stream in the session using netconf, create a subscription:
```
-EXAMPLE ]]>]]>
+EXAMPLE ]]>]]>
]]>]]>
-Routing notification ]]>]]>
-Routing notification ]]>]]>
+2019-01-02T10:20:05.929272 fault Ethernet0 major ]]>]]>
...
```
This can also be triggered via the CLI:
```
-cli> notify
-cli> Routing notification
-Routing notification
+clixon_cli -f /usr/local/etc/example.xml
+cli> notify
+cli> event-class fault;
+reportingEntity {
+ card Ethernet0;
+}
+severity major;
...
+cli> no notify
+cli>
```
-Restconf support is also supported, see [../apps/restconf/README.md].
+Restconf support is also supported, see (restc)[../apps/restconf/README.md].
-## Initializing a plugin
+
+## RPC Operations
+
+Clixon implements Yang RPC operations by an extension mechanism. The
+extension mechanism enables you to add application-specific
+operations. It works by adding user-defined callbacks for added
+netconf operations. It is possible to use the extension mechanism
+independent of the yang rpc construct, but it is recommended. The example includes an example:
+
+Example using CLI:
+```
+cli> rpc ipv4
+ rpc-reply {
+ route {
+ address-family ipv4;
+ next-hop {
+ next-hop-list 2.3.4.5;
+ }
+ source-protocol static;
+ }
+ }
+```
+Netconf:
+```
+ipv4 ]]>]]>
+ipv4 2.3.4.5 static ]]>]]>
+```
+Restconf:
+```
+curl -X POST http://localhost/restconf/operations/ietf-routing:fib-route -d '{"ietf-routing:input":{"routing-instance-name":"ipv4"}}'
+```
+
+### Details
+
+The example works by creating a netconf rpc call and sending it to the backend: (see the fib_route_rpc() function in [example_cli.c](example_cli.c)).
+
+In the (example_backend.c)[example_backend.c], a callback is registered (fib_route()) which handles the RPC (this is just dummy data):
+```
+static int
+fib_route(clicon_handle h,
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* Client session */
+ void *regarg) /* Argument given at register */
+{
+ cprintf(cbret, ""
+ "ipv4 "
+ "2.3.4.5 "
+ "static "
+ " ");
+ return 0;
+}
+int
+clixon_plugin_init(clicon_handle h)
+{
+...
+ rpc_callback_register(h, fib_route, NULL, "fib-route");
+...
+}
+```
+
+## State data
+
+Netconf and restconf GET also returns state data(not only configuration data).
+
+In YANG state data is specified with `config false;`. In the example,
+`state` is state data, see (example.yang)[example.yang]
+
+To return state data, you need to write a backend state data callback
+with the name "plugin_statedata" where you return an XML tree with
+state. This is then merged with config data by the system.
+
+A static example of returning state data is in the example. Note that
+a real example would poll or get the interface counters via a system
+call, as well as use the "xpath" argument to identify the requested
+state data.
+
+## Authentication and NACM
+The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341):
+* A basic auth HTTP callback, see: example_restconf_credentials() containing three example users: andy, wilma, and guest, according to the examples in Appendix A in [RFC8341](https://tools.ietf.org/html/rfc8341).
+* A NACM backend plugin reporting the mandatory NACM state variables.
+
+## Systemd
+
+Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example.
+
+## Docker
+
+Run the example as a docker container and access it from a host CLI as follows:
+```
+ID=$(sudo docker run -td olofhagsand/clixon_example)
+IP=$(sudo docker inspect -f '{{.NetworkSettings.IPAddress }}' $ID)
+clixon_cli -a IPv4 -u $IP -f ./example.xml
+```
+
+Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push:
+```
+make docker
+make push
+sudo docker run -ti --rm you/clixon_example
+```
+
+Note that the configuration database is internal in the container, so
+it is deleted if the container is restarted. To make the configuration
+database persistent, you need to mount running_db using `-v`
+
+## Plugins
The example includes a restonf, netconf, CLI and two backend plugins.
Each plugin is initiated with an API struct followed by a plugin init function.
@@ -127,96 +279,3 @@ clixon_plugin_init(clicon_handle h)
return &api; /* Return NULL on error */
}
```
-
-## Operation data
-
-Clixon implements Yang RPC operations by an extension mechanism. The
-extension mechanism enables you to add application-specific
-operations. It works by adding user-defined callbacks for added
-netconf operations. It is possible to use the extension mechanism
-independent of the yang rpc construct, but it is recommended. The example includes an example:
-
-Example:
-```
-cli> rpc ipv4
-
-
-
-```
-
-The example works by creating a netconf rpc call and sending it to the backend: (see the fib_route_rpc() function).
-```
-
-
- ipv4
-
-
-```
-
-In the backend, a callback is registered (fib_route()) which handles the RPC.
-```
-static int
-fib_route(clicon_handle h,
- cxobj *xe, /* Request: */
- cbuf *cbret, /* Reply eg ... */
- void *arg, /* Client session */
- void *regarg) /* Argument given at register */
-{
- cprintf(cbret, " ");
- return 0;
-}
-int
-clixon_plugin_init(clicon_handle h)
-{
-...
- rpc_callback_register(h, fib_route, NULL, "fib-route");
-...
-}
-```
-## State data
-
-Netconf and restconf GET also returns state data, in contrast to
-config data.
-p
-In YANG state data is specified with "config false;". In the example, interface-state is state data.
-
-To return state data, you need to write a backend state data callback
-with the name "plugin_statedata" where you return an XML tree with
-state. This is then merged with config data by the system.
-
-A static example of returning state data is in the example. Note that
-a real example would poll or get the interface counters via a system
-call, as well as use the "xpath" argument to identify the requested
-state data.
-
-## Authentication and NACM
-The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341):
-* A basic auth HTTP callback, see: example_restconf_credentials() containing three example users: andy, wilma, and guest, according to the examples in Appendix A in [RFC8341](https://tools.ietf.org/html/rfc8341).
-* A NACM backend plugin reporting the mandatory NACM state variables.
-
-## Systemd files
-
-Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example.
-
-## Docker
-
-Run the example as a docker container and access it from a host CLI as follows:
-```
-ID=$(sudo docker run -td olofhagsand/clixon_example)
-IP=$(sudo docker inspect -f '{{.NetworkSettings.IPAddress }}' $ID)
-clixon_cli -a IPv4 -u $IP -f ./example.xml
-```
-
-Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push:
-```
-make docker
-make push
-sudo docker run -ti --rm you/clixon_example
-```
-
-Note that the configuration database is internal in the container, so
-it is deleted if the container is restarted. To make the configuration
-database persistent, you need to mount running_db using `-v`
-
-
-
diff --git a/example/example.yang b/example/example.yang
index 57f58fcb..f5e82063 100644
--- a/example/example.yang
+++ b/example/example.yang
@@ -26,6 +26,10 @@ module example {
}
/* Translation function example - See also example_cli */
list translate{
+ key k;
+ leaf k{
+ type string;
+ }
leaf value{
type string;
}
@@ -73,13 +77,55 @@ module example {
}
}
}
- rpc debug {
- description "Set debug level of backend. XXX should be in clixon-config";
+ rpc empty {
+ description "Smallest possible RPC with no input or output";
+ }
+ rpc example {
+ description "Some example input/output for testing RFC7950 7.14.
+ RPC simply echoes the input for debugging.";
input {
- leaf level {
- type uint32;
+ leaf x {
+ description
+ "If a leaf in the input tree has a 'mandatory' statement with
+ the value 'true', the leaf MUST be present in an RPC invocation.";
+ type string;
+ mandatory true;
+ }
+ leaf y {
+ description
+ "If a leaf in the input tree has a 'mandatory' statement with the
+ value 'true', the leaf MUST be present in an RPC invocation.";
+ type string;
+ default "42";
+ }
+ leaf-list z {
+ description
+ "If a leaf-list in the input tree has one or more default
+ values, the server MUST use these values (XXX not supported)";
+ type string;
+ }
+ leaf w {
+ description
+ "If any node has a 'when' statement that would evaluate to
+ 'false',then this node MUST NOT be present in the input tree.
+ (XXX not supported)";
+ type string;
+ when "/translate/k=5/value='w'";
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ leaf y {
+ type string;
+ }
+ leaf z {
+ type string;
+ }
+ leaf w {
+ type string;
}
}
}
-
}
diff --git a/example/example_backend.c b/example/example_backend.c
index 3a420a0e..a322ad1e 100644
--- a/example/example_backend.c
+++ b/example/example_backend.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -96,7 +96,7 @@ example_stream_timer(int fd,
int retval = -1;
clicon_handle h = (clicon_handle)arg;
- /* XXX Change to actual netconf notifications */
+ /* XXX Change to actual netconf notifications and namespace */
if (stream_notify(h, "EXAMPLE", "fault Ethernet0 major ") < 0)
goto done;
if (example_stream_timer_setup(h) < 0)
@@ -129,9 +129,10 @@ fib_route(clicon_handle h, /* Clicon handle */
void *arg, /* Client session */
void *regarg) /* Argument given at register */
{
- cprintf(cbret, ""
+ cprintf(cbret, ""
"ipv4 "
"2.3.4.5 "
+ "static "
" ");
return 0;
}
@@ -146,7 +147,7 @@ route_count(clicon_handle h,
void *arg,
void *regarg) /* Argument given at register */
{
- cprintf(cbret, "42 ");
+ cprintf(cbret, "42 ");
return 0;
}
@@ -157,16 +158,49 @@ route_count(clicon_handle h,
* in [RFC6241].
*/
static int
-empty(clicon_handle h, /* Clicon handle */
- cxobj *xe, /* Request: */
- cbuf *cbret, /* Reply eg ... */
- void *arg, /* client_entry */
- void *regarg) /* Argument given at register */
+empty_rpc(clicon_handle h, /* Clicon handle */
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* client_entry */
+ void *regarg) /* Argument given at register */
{
cprintf(cbret, " ");
return 0;
}
+/*! More elaborate example RPC for testing
+ * The RPC returns the incoming parameters
+ */
+static int
+example_rpc(clicon_handle h, /* Clicon handle */
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* client_entry */
+ void *regarg) /* Argument given at register */
+{
+ int retval = -1;
+ cxobj *x = NULL;
+ char *namespace;
+
+ /* get namespace from rpc name, return back in each output parameter */
+ if ((namespace = xml_find_type_value(xe, NULL, "xmlns", CX_ATTR)) == NULL){
+ clicon_err(OE_XML, ENOENT, "No namespace given in rpc %s", xml_name(xe));
+ goto done;
+ }
+ cprintf(cbret, "");
+ while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
+ if (xmlns_set(x, NULL, namespace) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
+ goto done;
+ }
+ cprintf(cbret, " ");
+ retval = 0;
+ done:
+ return retval;
+}
+
+
/*! Called to get state data from plugin
* @param[in] h Clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all
@@ -196,7 +230,7 @@ example_statedata(clicon_handle h,
* Note this state needs to be accomanied by yang snippet
* above
*/
- if (xml_parse_string(""
+ if (xml_parse_string(""
"42 "
" ", NULL, &xstate) < 0)
goto done;
@@ -225,19 +259,32 @@ example_reset(clicon_handle h,
{
int retval = -1;
cxobj *xt = NULL;
+ int ret;
+ cbuf *cbret = NULL;
- if (xml_parse_string(""
+ if (xml_parse_string(""
"lo ex:loopback "
" ", NULL, &xt) < 0)
goto done;
- /* Replace parent w fiorst child */
+ /* Replace parent w first child */
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
- /* Merge user reset state */
- if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
+ }
+ /* Merge user reset state */
+ if ((ret = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret)) < 0)
+ goto done;
+ if (ret == 0){
+ clicon_err(OE_XML, 0, "Error when writing to XML database: %s",
+ cbuf_get(cbret));
+ goto done;
+ }
retval = 0;
done:
+ if (cbret)
+ cbuf_free(cbret);
if (xt != NULL)
xml_free(xt);
return retval;
@@ -315,22 +362,31 @@ clixon_plugin_init(clicon_handle h)
if (example_stream_timer_setup(h) < 0)
goto done;
- /* Register callback for routing rpc calls */
+ /* Register callback for routing rpc calls
+ */
+
if (rpc_callback_register(h, fib_route,
NULL,
"fib-route"/* Xml tag when callback is made */
) < 0)
goto done;
+ /* From ietf-routing.yang */
if (rpc_callback_register(h, route_count,
NULL,
"route-count"/* Xml tag when callback is made */
) < 0)
goto done;
- if (rpc_callback_register(h, empty,
+ /* From example.yang (clicon) */
+ if (rpc_callback_register(h, empty_rpc,
NULL,
"empty"/* Xml tag when callback is made */
) < 0)
goto done;
+ if (rpc_callback_register(h, example_rpc,
+ NULL,
+ "example"/* Xml tag when callback is made */
+ ) < 0)
+ goto done;
/* Return plugin API */
return &api;
diff --git a/example/example_backend_nacm.c b/example/example_backend_nacm.c
index 3814a975..608916a8 100644
--- a/example/example_backend_nacm.c
+++ b/example/example_backend_nacm.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/example/example_cli.c b/example/example_cli.c
index 987aae30..ebd2305a 100644
--- a/example/example_cli.c
+++ b/example/example_cli.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -96,7 +96,7 @@ fib_route_rpc(clicon_handle h,
/* User supplied variable in CLI command */
instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
/* Create XML for fib-route netconf RPC */
- if (xml_parse_va(&xtop, NULL, "%s ",
+ if (xml_parse_va(&xtop, NULL, "%s ipv4 ",
clicon_username_get(h),
cv_string_get(instance)) < 0)
goto done;
@@ -110,7 +110,7 @@ fib_route_rpc(clicon_handle h,
goto done;
}
/* Print result */
- xml_print(stdout, xml_child_i(xret, 0));
+ xml2txt(stdout, xml_child_i(xret, 0), 0);
retval = 0;
done:
if (xret)
diff --git a/example/example_cli.cli b/example/example_cli.cli
index 302a6cf3..892c687a 100644
--- a/example/example_cli.cli
+++ b/example/example_cli.cli
@@ -24,7 +24,7 @@ debug("Debugging parts of the system"), cli_debug_cli((int32)1);{
}
copy("Copy and create a new object") {
interface("Copy interface"){
- ("name of interface to copy from") to("Copy to interface") ("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
+ ("name of interface to copy from") to("Copy to interface") ("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
}
}
discard("Discard edits (rollback 0)"), discard_changes();
diff --git a/example/example_netconf.c b/example/example_netconf.c
index 9993e851..742b1d98 100644
--- a/example/example_netconf.c
+++ b/example/example_netconf.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -71,7 +71,7 @@ int netconf_client_rpc(clicon_handle h,
void *regarg)
{
clicon_debug(1, "%s restconf", __FUNCTION__);
- cprintf(cbret, "ok ");
+ cprintf(cbret, "ok ");
return 0;
}
diff --git a/example/example_restconf.c b/example/example_restconf.c
index 43771662..e49a48f4 100644
--- a/example/example_restconf.c
+++ b/example/example_restconf.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -275,7 +275,7 @@ restconf_client_rpc(clicon_handle h,
{
// FCGX_Request *r = (FCGX_Request *)arg;
clicon_debug(1, "%s", __FUNCTION__);
- cprintf(cbret, "ok ");
+ cprintf(cbret, "ok ");
return 0;
}
diff --git a/include/Makefile.in b/include/Makefile.in
index 75aa04a1..d55e6e97 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index cbb3f1df..7a6c342a 100644
--- a/include/clixon_custom.h
+++ b/include/clixon_custom.h
@@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -51,11 +51,6 @@ int strverscmp (__const char *__s1, __const char *__s2);
*/
#define XMLNS_YANG_ONLY 1
-/* Set for full XML namespace code in XML, NETCONF and YANG
- * Experimental
- */
-#undef ENABLE_XMLNS
-
/* If set, patch all CLI spec calls to @datamodel:tree to @datamodel.
* This is a backward compatible fix for 3.9 for CLIgen specification files
* using model generation (CLIXON_CLI_GENMODEL).
diff --git a/lib/Makefile.in b/lib/Makefile.in
index d34e81ac..a0f09147 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/lib/clixon/Makefile.in b/lib/clixon/Makefile.in
index e455a860..eee5ce91 100644
--- a/lib/clixon/Makefile.in
+++ b/lib/clixon/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in
index 2fc6c50d..6615433c 100644
--- a/lib/clixon/clixon.h.in
+++ b/lib/clixon/clixon.h.in
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_err.h b/lib/clixon/clixon_err.h
index b5961453..63fff1fe 100644
--- a/lib/clixon/clixon_err.h
+++ b/lib/clixon/clixon_err.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_event.h b/lib/clixon/clixon_event.h
index 99b598e2..c2ef6edc 100644
--- a/lib/clixon/clixon_event.h
+++ b/lib/clixon/clixon_event.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_file.h b/lib/clixon/clixon_file.h
index 3e34b6ce..44915abd 100644
--- a/lib/clixon/clixon_file.h
+++ b/lib/clixon/clixon_file.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_handle.h b/lib/clixon/clixon_handle.h
index 711e6cb8..944f5911 100644
--- a/lib/clixon/clixon_handle.h
+++ b/lib/clixon/clixon_handle.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_hash.h b/lib/clixon/clixon_hash.h
index 0adbb047..0cbb8129 100644
--- a/lib/clixon/clixon_hash.h
+++ b/lib/clixon/clixon_hash.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_json.h b/lib/clixon/clixon_json.h
index 68b19559..0a4af828 100644
--- a/lib/clixon/clixon_json.h
+++ b/lib/clixon/clixon_json.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -43,6 +43,7 @@ int xml2json_cbuf(cbuf *cb, cxobj *x, int pretty);
int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty);
int xml2json(FILE *f, cxobj *x, int pretty);
int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty);
+int json2xml_ns(yang_spec *yspec, cxobj *x, cxobj **xerr);
int json_parse_str(char *str, cxobj **xt);
int json_parse_file(int fd, yang_spec *yspec, cxobj **xt);
diff --git a/lib/clixon/clixon_log.h b/lib/clixon/clixon_log.h
index 7db79142..a9835488 100644
--- a/lib/clixon/clixon_log.h
+++ b/lib/clixon/clixon_log.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_nacm.h b/lib/clixon/clixon_nacm.h
index 978b5486..cdae13bb 100644
--- a/lib/clixon/clixon_nacm.h
+++ b/lib/clixon/clixon_nacm.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h
index cdf570e9..2bb4e8f2 100644
--- a/lib/clixon/clixon_netconf_lib.h
+++ b/lib/clixon/clixon_netconf_lib.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -46,10 +46,14 @@ int netconf_too_big(cbuf *cb, char *type, char *message);
int netconf_missing_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_bad_attribute(cbuf *cb, char *type, char *info, char *message);
int netconf_unknown_attribute(cbuf *cb, char *type, char *info, char *message);
-int netconf_missing_element(cbuf *cb, char *type, char *info, char *message);
-int netconf_bad_element(cbuf *cb, char *type, char *info, char *message);
-int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message);
-int netconf_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
+int netconf_missing_element(cbuf *cb, char *type, char *element, char *message);
+int netconf_missing_element_xml(cxobj **xret, char *type, char *element, char *message);
+int netconf_bad_element(cbuf *cb, char *type, char *info, char *element);
+int netconf_bad_element_xml(cxobj **xret, char *type, char *info, char *element);
+int netconf_unknown_element(cbuf *cb, char *type, char *element, char *message);
+int netconf_unknown_element_xml(cxobj **xret, char *type, char *element, char *message);
+int netconf_unknown_namespace(cbuf *cb, char *type, char *namespace, char *message);
+int netconf_unknown_namespace_xml(cxobj **xret, char *type, char *namespace, char *message);
int netconf_access_denied(cbuf *cb, char *type, char *message);
int netconf_access_denied_xml(cxobj **xret, char *type, char *message);
int netconf_lock_denied(cbuf *cb, char *info, char *message);
diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index b5cb55c3..4168b9ed 100644
--- a/lib/clixon/clixon_options.h
+++ b/lib/clixon/clixon_options.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -80,6 +80,10 @@ enum startup_mode_t{
/* Print registry on file. For debugging. */
void clicon_option_dump(clicon_handle h, int dblevel);
+
+/* Add a clicon options overriding file setting */
+int clicon_option_add(clicon_handle h, char *name, char *value);
+
/* Initialize options: set defaults, read config-file, etc */
int clicon_options_main(clicon_handle h, yang_spec *yspec);
@@ -174,10 +178,8 @@ int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
-#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
yang_spec * clicon_config_yang(clicon_handle h);
int clicon_config_yang_set(clicon_handle h, struct yang_spec *ys);
-#endif
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);
diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h
index cd6038aa..09703d90 100644
--- a/lib/clixon/clixon_plugin.h
+++ b/lib/clixon/clixon_plugin.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h
index e3290c3f..e5457685 100644
--- a/lib/clixon/clixon_proto.h
+++ b/lib/clixon/clixon_proto.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -66,7 +66,7 @@ struct clicon_msg *clicon_msg_encode(char *format, ...) __attribute__ ((format (
#else
struct clicon_msg *clicon_msg_encode(char *format, ...);
#endif
-int clicon_msg_decode(struct clicon_msg *msg, cxobj **xml);
+int clicon_msg_decode(struct clicon_msg *msg, yang_spec *yspec, cxobj **xml);
int clicon_connect_unix(char *sockpath);
diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h
index ac917d77..07d808b5 100644
--- a/lib/clixon/clixon_proto_client.h
+++ b/lib/clixon/clixon_proto_client.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_queue.h b/lib/clixon/clixon_queue.h
index 70127756..ee277134 100644
--- a/lib/clixon/clixon_queue.h
+++ b/lib/clixon/clixon_queue.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_sha1.h b/lib/clixon/clixon_sha1.h
index 6f788e14..fbf9bfc5 100644
--- a/lib/clixon/clixon_sha1.h
+++ b/lib/clixon/clixon_sha1.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_sig.h b/lib/clixon/clixon_sig.h
index ac03a908..51c29341 100644
--- a/lib/clixon/clixon_sig.h
+++ b/lib/clixon/clixon_sig.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_stream.h b/lib/clixon/clixon_stream.h
index a1870395..ae09a8ab 100644
--- a/lib/clixon/clixon_stream.h
+++ b/lib/clixon/clixon_stream.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h
index cebb0871..f78ddbc9 100644
--- a/lib/clixon/clixon_string.h
+++ b/lib/clixon/clixon_string.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -86,7 +86,9 @@ int xml_chardata_encode(char **escp, char *fmt, ...);
int uri_percent_decode(char *enc, char **str);
const char *clicon_int2str(const map_str2int *mstab, int i);
int clicon_str2int(const map_str2int *mstab, char *str);
-
+int nodeid_split(char *nodeid, char **prefix, char **id);
+char *clixon_trim(char *str);
+int regexp_xsd2posix(char *xsd, char **posix);
#ifndef HAVE_STRNDUP
char *clicon_strndup (const char *, size_t);
#endif /* ! HAVE_STRNDUP */
diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h
index 803e5948..be3e2352 100644
--- a/lib/clixon/clixon_xml.h
+++ b/lib/clixon/clixon_xml.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -45,6 +45,8 @@
* namespace (rfc6241 3.1)
*/
#define DEFAULT_XML_RPC_NAMESPACE "urn:ietf:params:xml:ns:netconf:base:1.0"
+/* default namespace statement, such as in */
+#define DEFAULT_XMLNS "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\""
/*
* Types
@@ -84,14 +86,13 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg);
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
-
/* Iterate through modules to find the matching datanode
* or rpc if no xmlns attribute specifies namespace.
- * This is loose semantics of finding namespaces.
+ * This is lazy non-strict semantics of finding namespaces.
* And it is wrong, but is the way Clixon originally was written."
- * @see CLICON_XML_NS_ITERATE clixon configure option
+ * @see CLICON_XML_NS_STRICT clixon configure option
*/
-extern int _CLICON_XML_NS_ITERATE;
+extern int _CLICON_XML_NS_STRICT;
/*
* Prototypes
@@ -99,9 +100,10 @@ extern int _CLICON_XML_NS_ITERATE;
char *xml_type2str(enum cxobj_type type);
char *xml_name(cxobj *xn);
int xml_name_set(cxobj *xn, char *name);
-char *xml_namespace(cxobj *xn);
-int xml_namespace_set(cxobj *xn, char *name);
+char *xml_prefix(cxobj *xn);
+int xml_prefix_set(cxobj *xn, char *name);
int xml2ns(cxobj *x, char *localname, char **namespace);
+int xmlns_set(cxobj *x, char *prefix, char *namespace);
cxobj *xml_parent(cxobj *xn);
int xml_parent_set(cxobj *xn, cxobj *parent);
@@ -117,7 +119,9 @@ int xml_type_set(cxobj *xn, enum cxobj_type type);
int xml_child_nr(cxobj *xn);
int xml_child_nr_type(cxobj *xn, enum cxobj_type type);
+int xml_child_nr_notype(cxobj *xn, enum cxobj_type type);
cxobj *xml_child_i(cxobj *xn, int i);
+cxobj *xml_child_i_type(cxobj *xn, int i, enum cxobj_type type);
cxobj *xml_child_i_set(cxobj *xt, int i, cxobj *xc);
cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
@@ -139,6 +143,7 @@ char *xml_body(cxobj *xn);
cxobj *xml_body_get(cxobj *xn);
char *xml_find_type_value(cxobj *xn_parent, char *prefix,
char *name, enum cxobj_type type);
+cxobj *xml_find_type(cxobj *xn_parent, char *prefix, char *name, enum cxobj_type type);
char *xml_find_value(cxobj *xn_parent, char *name);
char *xml_find_body(cxobj *xn, char *name);
cxobj *xml_find_body_obj(cxobj *xt, char *name, char *val);
diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h
index 58607396..a79349d1 100644
--- a/lib/clixon/clixon_xml_db.h
+++ b/lib/clixon/clixon_xml_db.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index abd1fe50..2fa2afeb 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -43,9 +43,13 @@
*/
int xml2txt(FILE *f, cxobj *x, int level);
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
-int xml_yang_validate_rpc(cxobj *xrpc);
-int xml_yang_validate_add(cxobj *xt, void *arg);
-int xml_yang_validate_all(cxobj *xt, void *arg);
+int xml_yang_root(cxobj *x, cxobj **xr);
+int xmlns_assign(cxobj *x);
+int xml_yang_validate_rpc(cxobj *xrpc, cbuf *cbret);
+int xml_yang_validate_add(cxobj *xt, cbuf *cbret);
+int xml_yang_validate_all(cxobj *xt, cbuf *cbret);
+int xml_yang_validate_all_top(cxobj *xt, cbuf *cbret);
+int xml_yang_find_non_strict(cxobj *x, yang_spec *yspec, yang_stmt **y);
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
@@ -58,13 +62,11 @@ int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath);
int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark);
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
int xml_default(cxobj *x, void *arg);
-int xml_order(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg);
int xml_non_config_data(cxobj *xt, void *arg);
int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_spec *yspec);
int xml_spec_populate(cxobj *x, void *arg);
-int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
-int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
+int api_path2xpath(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec, char **reason);
diff --git a/lib/clixon/clixon_xml_sort.h b/lib/clixon/clixon_xml_sort.h
index 620456a0..8b1e6f82 100644
--- a/lib/clixon/clixon_xml_sort.h
+++ b/lib/clixon/clixon_xml_sort.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -36,15 +36,10 @@
#ifndef _CLIXON_XML_SORT_H
#define _CLIXON_XML_SORT_H
-/* Sort and binary search of XML children
- * Experimental
- */
-extern int xml_child_sort;
-
/*
* Prototypes
*/
-int xml_child_spec(char *name, cxobj *xp, yang_spec *yspec, yang_stmt **yp);
+int xml_child_spec(cxobj *x, cxobj *xp, yang_spec *yspec, yang_stmt **yp);
int xml_cmp(const void* arg1, const void* arg2);
int xml_sort(cxobj *x0, void *arg);
cxobj *xml_search(cxobj *x, char *name, int yangi, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
@@ -53,6 +48,6 @@ int xml_insert_pos(cxobj *x0, char *name, int yangi, enum rfc_6020 keyword,
int upper);
cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
int xml_sort_verify(cxobj *x, void *arg);
-int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
+int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
#endif /* _CLIXON_XML_SORT_H */
diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h
index ce62fc23..d0d6a360 100644
--- a/lib/clixon/clixon_xpath.h
+++ b/lib/clixon/clixon_xpath.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_xpath_ctx.h b/lib/clixon/clixon_xpath_ctx.h
index 03854495..0a1bb122 100644
--- a/lib/clixon/clixon_xpath_ctx.h
+++ b/lib/clixon/clixon_xpath_ctx.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index e9aee2e6..1035a429 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -176,8 +176,7 @@ typedef struct yang_stmt yang_stmt; /* forward */
*/
struct yang_type_cache{
int yc_options;
- cg_var *yc_mincv;
- cg_var *yc_maxcv;
+ cvec *yc_cvv; /* range and length restriction */
char *yc_pattern;
uint8_t yc_fraction;
yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */
@@ -213,7 +212,6 @@ struct yang_stmt{
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
};
-
/*! top-level yang parse-tree */
struct yang_spec{
int yp_len; /* Number of children */
@@ -252,27 +250,24 @@ yang_stmt *yn_each(yang_node *yn, yang_stmt *ys);
char *yang_key2str(int keyword);
char *yarg_prefix(yang_stmt *ys);
char *yarg_id(yang_stmt *ys);
-int yang_nodeid_split(char *nodeid, char **prefix, char **id);
int ys_module_by_xml(yang_spec *ysp, struct xml *xt, yang_stmt **ymodp);
yang_stmt *ys_module(yang_stmt *ys);
yang_spec *ys_spec(yang_stmt *ys);
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
yang_stmt *yang_find_module_by_namespace(yang_spec *yspec, char *namespace);
+yang_stmt *yang_find_module_by_name(yang_spec *yspec, char *name);
yang_stmt *yang_find(yang_node *yn, int keyword, const char *argument);
int yang_match(yang_node *yn, int keyword, char *argument);
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
-yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, yang_class class);
char *yang_find_myprefix(yang_stmt *ys);
char *yang_find_mynamespace(yang_stmt *ys);
+yang_node *yang_choice(yang_stmt *y);
int yang_order(yang_stmt *y);
int yang_print(FILE *f, yang_node *yn);
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
int ys_populate(yang_stmt *ys, void *arg);
yang_stmt *yang_parse_file(int fd, const char *name, yang_spec *ysp);
-int yang_parse(clicon_handle h, const char *filename,
- const char *module,
- const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg);
int yang_abs_schema_nodeid(yang_spec *yspec, yang_stmt *ys,
@@ -284,8 +279,9 @@ cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys, char *extra);
int yang_mandatory(yang_stmt *ys);
int yang_config(yang_stmt *ys);
-int yang_spec_parse_module(clicon_handle h, char *module, char *revision, yang_spec *yspec);
-int yang_spec_parse_file(clicon_handle h, char *filename, yang_spec *yspec);
+int yang_spec_parse_module(clicon_handle h, const char *module,
+ const char *revision, yang_spec *yspec);
+int yang_spec_parse_file(clicon_handle h, const char *filename, yang_spec *yspec);
int yang_spec_load_dir(clicon_handle h, char *dir, yang_spec *yspec);
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
int yang_key_match(yang_node *yn, char *name);
diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h
index 0ca4760c..2a26b7ba 100644
--- a/lib/clixon/clixon_yang_module.h
+++ b/lib/clixon/clixon_yang_module.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/clixon/clixon_yang_type.h b/lib/clixon/clixon_yang_type.h
index 61b581af..2e859e47 100644
--- a/lib/clixon/clixon_yang_type.h
+++ b/lib/clixon/clixon_yang_type.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -55,11 +55,11 @@
* Prototypes
*/
int yang_type_cache_set(yang_type_cache **ycache,
- yang_stmt *resolved, int options, cg_var *mincv,
- cg_var *maxcv, char *pattern, uint8_t fraction);
-int yang_type_cache_get(yang_type_cache *ycache,
- yang_stmt **resolved, int *options, cg_var **mincv,
- cg_var **maxcv, char **pattern, uint8_t *fraction);
+ yang_stmt *resolved, int options,
+ cvec *cvv, char *pattern, uint8_t fraction);
+int yang_type_cache_get(yang_type_cache *ycache, yang_stmt **resolved,
+ int *options, cvec **cvv, char **pattern,
+ uint8_t *fraction);
int yang_type_cache_cp(yang_type_cache **ycnew, yang_type_cache *ycold);
int yang_type_cache_free(yang_type_cache *ycache);
int ys_resolve_type(yang_stmt *ys, void *arg);
@@ -67,14 +67,13 @@ int yang2cv_type(char *ytype, enum cv_type *cv_type);
char *cv2yang_type(enum cv_type cv_type);
yang_stmt *yang_find_identity(yang_stmt *ys, char *identity);
int ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason);
-int clicon_type2cv(char *type, char *rtype, enum cv_type *cvtype);
+int clicon_type2cv(char *type, char *rtype, yang_stmt *ys, enum cv_type *cvtype);
int yang_type_get(yang_stmt *ys, char **otype, yang_stmt **restype,
- int *options, cg_var **mincv, cg_var **maxcv, char **pattern,
+ int *options, cvec **cvv, char **pattern,
uint8_t *fraction_digits);
-int yang_type_resolve(yang_stmt *ys, yang_stmt *ytype,
- yang_stmt **restype, int *options,
- cg_var **mincv, cg_var **maxcv,
- char **pattern, uint8_t *fraction);
+int yang_type_resolve(yang_stmt *yorig, yang_stmt *ys, yang_stmt *ytype,
+ yang_stmt **restype, int *options,
+ cvec **cvv, char **pattern, uint8_t *fraction);
#endif /* _CLIXON_YANG_TYPE_H_ */
diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in
index ce0c0587..fc8fa3e4 100644
--- a/lib/src/Makefile.in
+++ b/lib/src/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
diff --git a/lib/src/clixon_err.c b/lib/src/clixon_err.c
index aa4576df..b4f07fbf 100644
--- a/lib/src/clixon_err.c
+++ b/lib/src/clixon_err.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -140,6 +140,7 @@ clicon_err_reset(void)
* @param[in] err Error number, typically errno
* @param[in] suberr Sub-error number
* @param[in] reason Error string, format with argv
+ * @see clicon_err_reser Resetting the global error variables.
*/
int
clicon_err_fn(const char *fn,
diff --git a/lib/src/clixon_event.c b/lib/src/clixon_event.c
index d15e12c5..3085f89c 100644
--- a/lib/src/clixon_event.c
+++ b/lib/src/clixon_event.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_file.c b/lib/src/clixon_file.c
index 22998553..73a2b894 100644
--- a/lib/src/clixon_file.c
+++ b/lib/src/clixon_file.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_handle.c b/lib/src/clixon_handle.c
index dcb6da5c..a89d81fd 100644
--- a/lib/src/clixon_handle.c
+++ b/lib/src/clixon_handle.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_hash.c b/lib/src/clixon_hash.c
index aa397d4d..4769abfa 100644
--- a/lib/src/clixon_hash.c
+++ b/lib/src/clixon_hash.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c
index 00778c30..f6f8ff23 100644
--- a/lib/src/clixon_json.c
+++ b/lib/src/clixon_json.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -55,10 +55,12 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
+#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
+#include "clixon_netconf_lib.h"
#include "clixon_json.h"
#include "clixon_json_parse.h"
@@ -90,43 +92,31 @@ enum childtype{
ANY_CHILD, /* eg or */
};
-/*! Number of children EXCEPT attributes
- * @param[in] xn xml node
- * @retval number of children in XML tree (except children of type CX_ATTR)
- * @see xml_child_nr
- */
-static int
-xml_child_nr_noattr(cxobj *xn)
-{
- cxobj *x = NULL;
- int nr = 0;
-
- while ((x = xml_child_each(xn, x, -1)) != NULL) {
- if (xml_type(x) != CX_ATTR)
- nr++;
- }
- return nr;
-}
-
/*! x is element and has exactly one child which in turn has none
* remove attributes from x
* Clone from clixon_xml_map.c
*/
static enum childtype
-childtype(cxobj *x)
+child_type(cxobj *x)
{
- cxobj *xc1; /* the only child of x */
+ cxobj *xc; /* the only child of x */
int clen; /* nr of children */
- clen = xml_child_nr_noattr(x);
+ clen = xml_child_nr_notype(x, CX_ATTR);
if (xml_type(x) != CX_ELMNT)
return -1; /* n/a */
if (clen == 0)
return NULL_CHILD;
if (clen > 1)
return ANY_CHILD;
- xc1 = xml_child_i(x, 0); /* From here exactly one child */
- if (xml_child_nr_noattr(xc1) == 0 && xml_type(xc1)==CX_BODY)
+ /* From here exactly one noattr child, get it */
+ xc = NULL;
+ while ((xc = xml_child_each(x, xc, -1)) != NULL)
+ if (xml_type(xc) != CX_ATTR)
+ break;
+ if (xc == NULL)
+ return -2; /* n/a */
+ if (xml_child_nr_notype(xc, CX_ATTR) == 0 && xml_type(xc)==CX_BODY)
return BODY_CHILD;
else
return ANY_CHILD;
@@ -175,6 +165,9 @@ arraytype2str(enum array_element_type lt)
return "";
}
+/*! Check typeof x in array
+ * Some complexity when x is in different namespaces
+ */
static enum array_element_type
array_eval(cxobj *xprev,
cxobj *x,
@@ -184,7 +177,10 @@ array_eval(cxobj *xprev,
int eqprev=0;
int eqnext=0;
yang_stmt *ys;
+ char *nsx; /* namespace of x */
+ char *ns2;
+ nsx = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
if (xml_type(x)!=CX_ELMNT){
array=BODY_ARRAY;
goto done;
@@ -192,12 +188,18 @@ array_eval(cxobj *xprev,
ys = xml_spec(x);
if (xnext &&
xml_type(xnext)==CX_ELMNT &&
- strcmp(xml_name(x),xml_name(xnext))==0)
- eqnext++;
+ strcmp(xml_name(x),xml_name(xnext))==0){
+ ns2 = xml_find_type_value(xnext, NULL, "xmlns", CX_ATTR);
+ if (nsx && ns2 && strcmp(nsx,ns2)==0)
+ eqnext++;
+ }
if (xprev &&
xml_type(xprev)==CX_ELMNT &&
- strcmp(xml_name(x),xml_name(xprev))==0)
- eqprev++;
+ strcmp(xml_name(x),xml_name(xprev))==0){
+ ns2 = xml_find_type_value(xprev, NULL, "xmlns", CX_ATTR);
+ if (nsx && ns2 && strcmp(nsx,ns2)==0)
+ eqprev++;
+ }
if (eqprev && eqnext)
array = MIDDLE_ARRAY;
else if (eqprev)
@@ -315,9 +317,9 @@ xml2json1_cbuf(cbuf *cb,
* Harder if x has a prefix, then that should also be translated to associated
* module name
*/
- prefix = xml_namespace(x);
- if (xml2ns(x, prefix, &namespace) < 0)
- goto done;
+ prefix = xml_prefix(x);
+ namespace = xml_find_type_value(x, prefix, "xmlns", CX_ATTR);
+
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
yspec = ys_spec(ys);
/* Find module name associated with namspace URI */
@@ -325,7 +327,7 @@ xml2json1_cbuf(cbuf *cb,
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
modname = ymod->ys_argument;
}
- childt = childtype(x);
+ childt = child_type(x);
if (pretty==2)
cprintf(cb, "#%s_array, %s_child ",
arraytype2str(arraytype),
@@ -442,7 +444,7 @@ xml2json1_cbuf(cbuf *cb,
xc_arraytype,
level+1, pretty, 0, bodystr0) < 0)
goto done;
- if (i[ \t]
\n { _JY->jy_linenum++; }
-\r { }
+\r
<> { return J_EOF; }
\{ { return *yytext; }
\} { return *yytext; }
diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y
index 1cf6a583..07da89a4 100644
--- a/lib/src/clixon_json_parse.y
+++ b/lib/src/clixon_json_parse.y
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -103,7 +103,7 @@ object.
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
-/* add _yy to error paramaters */
+/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
diff --git a/lib/src/clixon_log.c b/lib/src/clixon_log.c
index 87e0c8de..4c8641c3 100644
--- a/lib/src/clixon_log.c
+++ b/lib/src/clixon_log.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c
index bf60b865..8efa4421 100644
--- a/lib/src/clixon_nacm.c
+++ b/lib/src/clixon_nacm.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index ccb88275..427e99fc 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -80,8 +80,8 @@ netconf_in_use(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "in-use "
"%s "
+ "in-use "
"error ",
type) <0)
goto err;
@@ -119,8 +119,8 @@ netconf_invalid_value(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "invalid-value "
"%s "
+ "invalid-value "
"error ",
type) <0)
goto err;
@@ -159,8 +159,8 @@ netconf_too_big(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "too-big "
"%s "
+ "too-big "
"error ",
type) <0)
goto err;
@@ -200,8 +200,8 @@ netconf_missing_attribute(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "missing-attribute "
"%s "
+ "missing-attribute "
"%s "
"error ",
type, info) <0)
@@ -241,8 +241,8 @@ netconf_bad_attribute(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "bad-attribute "
"%s "
+ "bad-attribute "
"%s "
"error ",
type, info) <0)
@@ -283,8 +283,8 @@ netconf_unknown_attribute(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "unknown-attribute "
"%s "
+ "unknown-attribute "
"%s "
"error ",
type, info) <0)
@@ -307,6 +307,46 @@ netconf_unknown_attribute(cbuf *cb,
goto done;
}
+/*! Common Netconf element XML tree according to RFC 6241 App A
+ * @param[out] xret Error XML tree. Free with xml_free after use
+ * @param[in] type Error type: "application" or "protocol"
+ * @param[in] tag Error tag
+ * @param[in] element bad-element xml
+ * @param[in] message Error message
+ */
+static int
+netconf_common_xml(cxobj **xret,
+ char *type,
+ char *tag,
+ char *infotag,
+ char *element,
+ char *message)
+{
+ int retval =-1;
+ cxobj *xerr;
+
+ 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, "%s "
+ "%s "
+ "<%s>%s%s> "
+ "error ",
+ type, tag, infotag, element, infotag) < 0)
+ goto done;
+ if (message && xml_parse_va(&xerr, NULL, "%s ",
+ message) < 0)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
*
* An expected element is missing.
@@ -318,35 +358,39 @@ netconf_unknown_attribute(cbuf *cb,
int
netconf_missing_element(cbuf *cb,
char *type,
- char *info,
+ char *element,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
+ int retval = -1;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "missing-element "
- "%s "
- "%s "
- "error ",
- type, info) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ if (netconf_common_xml(&xret, type, "missing-element",
+ "bad-element", element, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
+}
+
+
+/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
+ * @param[out] xret Error XML tree. Free with xml_free after use
+ * @param[in] type Error type: "application" or "protocol"
+ * @param[in] element bad-element xml
+ * @param[in] message Error message
+ */
+int
+netconf_missing_element_xml(cxobj **xret,
+ char *type,
+ char *element,
+ char *message)
+{
+ return netconf_common_xml(xret, type, "missing-element",
+ "bad-element", element, message);
}
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
@@ -355,41 +399,36 @@ netconf_missing_element(cbuf *cb,
* pattern mismatch.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
- * @param[in] info bad-element xml
+ * @param[in] elemnt Bad element name
* @param[in] message Error message
*/
int
netconf_bad_element(cbuf *cb,
char *type,
- char *info,
+ char *element,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
+ int retval = -1;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "bad-element "
- "%s "
- "%s "
- "error ",
- type, info) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ if (netconf_common_xml(&xret, type, "bad-element",
+ "bad-element",element, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
+}
+int
+netconf_bad_element_xml(cxobj **xret,
+ char *type,
+ char *element,
+ char *message)
+{
+ return netconf_common_xml(xret, type, "bad-element", "bad-element", element, message);
}
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
@@ -397,41 +436,46 @@ netconf_bad_element(cbuf *cb,
* An unexpected element is present.
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] type Error type: "application" or "protocol"
- * @param[in] info bad-element xml
+ * @param[in] element Bad element name
* @param[in] message Error message
*/
int
netconf_unknown_element(cbuf *cb,
char *type,
- char *info,
+ char *element,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
+ int retval = -1;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "unknown-element "
- "%s "
- "%s "
- "error ",
- type, info) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ if (netconf_common_xml(&xret, type, "unknown-element",
+ "bad-element", element, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
+}
+
+/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
+ *
+ * An unexpected element is present.
+ * @param[out] xret XML buffer
+ * @param[in] type Error type: "application" or "protocol"
+ * @param[in] element Bad element name
+ * @param[in] message Error message
+ */
+int
+netconf_unknown_element_xml(cxobj **xret,
+ char *type,
+ char *element,
+ char *message)
+{
+ return netconf_common_xml(xret, type, "unknown-element",
+ "bad-element", element, message);
}
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
@@ -445,82 +489,76 @@ netconf_unknown_element(cbuf *cb,
int
netconf_unknown_namespace(cbuf *cb,
char *type,
- char *info,
+ char *namespace,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
+ int retval = -1;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "unknown-namespace "
- "%s "
- "%s "
- "error ",
- type, info) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ if (netconf_common_xml(&xret, type, "unknown-namespace",
+ "bad-namespace", namespace, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
}
-/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
+int
+netconf_unknown_namespace_xml(cxobj **xret,
+ char *type,
+ char *namespace,
+ char *message)
+{
+ return netconf_common_xml(xret, type, "unknown-namespace",
+ "bad-namespace", namespace, message);
+}
+
+/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
*
- * An expected element is missing.
+ * Access to the requested protocol operation or data model is denied because
+ * authorization failed.
* @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
+ * @see netconf_access_denied_xml Same but returns XML tree
*/
int
netconf_access_denied(cbuf *cb,
char *type,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
-
- if (cprintf(cb, ""
- "access-denied "
- "%s "
- "error ",
- type) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ int retval = -1;
+ cxobj *xret = NULL;
+
+ if (netconf_access_denied_xml(&xret, type, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
}
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
*
- * An expected element is missing.
- * @param[out] xret Error XML tree
+ * Access to the requested protocol operation or data model is denied because
+ * 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
+ * @code
+ * cxobj *xret = NULL;
+ * if (netconf_access_denied_xml(&xret, "protocol", "Unauthorized") < 0)
+ * err;
+ * xml_free(xret);
+ * @endcode
+ * @see netconf_access_denied Same but returns cligen buffer
*/
int
netconf_access_denied_xml(cxobj **xret,
@@ -538,8 +576,8 @@ netconf_access_denied_xml(cxobj **xret,
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
- if (xml_parse_va(&xerr, NULL, "access-denied "
- "%s "
+ if (xml_parse_va(&xerr, NULL, "%s "
+ "access-denied "
"error ", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "%s ",
@@ -567,8 +605,8 @@ netconf_lock_denied(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "lock-denied "
"protocol "
+ "lock-denied "
"%s "
"error ",
info) <0)
@@ -593,7 +631,7 @@ netconf_lock_denied(cbuf *cb,
/*! Create Netconf resource-denied error XML tree according to RFC 6241 App A
*
- * An expected element is missing.
+ * Request could not be completed because of insufficient resources.
* @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
@@ -607,8 +645,8 @@ netconf_resource_denied(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "resource-denied "
"%s "
+ "resource-denied "
"error ",
type) <0)
goto err;
@@ -647,8 +685,8 @@ netconf_rollback_failed(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "rollback-failed "
"%s "
+ "rollback-failed "
"error ",
type) <0)
goto err;
@@ -686,8 +724,8 @@ netconf_data_exists(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "data-exists "
"application "
+ "data-exists "
"error ") <0)
goto err;
if (message){
@@ -724,8 +762,8 @@ netconf_data_missing(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "data-missing "
"application "
+ "data-missing "
"error ") <0)
goto err;
if (message){
@@ -763,8 +801,8 @@ netconf_operation_not_supported(cbuf *cb,
char *encstr = NULL;
if (cprintf(cb, ""
- "operation-not-supported "
"%s "
+ "operation-not-supported "
"error ",
type) <0)
goto err;
@@ -793,37 +831,25 @@ netconf_operation_not_supported(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] message Error message
+ * @see netconf_operation_failed_xml Same but returns XML tree
*/
int
netconf_operation_failed(cbuf *cb,
char *type,
char *message)
{
- int retval = -1;
- char *encstr = NULL;
+ int retval = -1;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "operation-failed "
- "%s "
- "error ",
- type) <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") < 0)
- goto err;
+ if (netconf_operation_failed_xml(&xret, type, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
}
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
@@ -833,6 +859,13 @@ netconf_operation_failed(cbuf *cb,
* @param[out] xret Error XML tree
* @param[in] type Error type: "rpc", "application" or "protocol"
* @param[in] message Error message
+ * @code
+ * cxobj *xret = NULL;
+ * if (netconf_operation_failed_xml(&xret, "protocol", "Unauthorized") < 0)
+ * err;
+ * xml_free(xret);
+ * @endcode
+ * @see netconf_operation_failed Same but returns cligen buffer
*/
int
netconf_operation_failed_xml(cxobj **xret,
@@ -850,8 +883,8 @@ netconf_operation_failed_xml(cxobj **xret,
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
- if (xml_parse_va(&xerr, NULL, "operation-failed "
- "%s "
+ if (xml_parse_va(&xerr, NULL, "%s "
+ "operation-failed "
"error ", type) < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "%s ",
@@ -870,35 +903,24 @@ netconf_operation_failed_xml(cxobj **xret,
* @param[out] cb CLIgen buf. Error XML is written in this buffer
* @param[in] message Error message
* @note New in :base:1.1
+ * @see netconf_malformed_message_xml Same but returns XML tree
*/
int
netconf_malformed_message(cbuf *cb,
char *message)
{
int retval = -1;
- char *encstr = NULL;
+ cxobj *xret = NULL;
- if (cprintf(cb, ""
- "malformed-message "
- "rpc "
- "error ") <0)
- goto err;
- if (message){
- if (xml_chardata_encode(&encstr, "%s", message) < 0)
- goto done;
- if (cprintf(cb, "%s ", encstr) < 0)
- goto err;
- }
- if (cprintf(cb, " ") <0)
- goto err;
+ if (netconf_malformed_message_xml(&xret, message) < 0)
+ goto done;
+ if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
+ goto done;
retval = 0;
done:
- if (encstr)
- free(encstr);
+ if (xret)
+ xml_free(xret);
return retval;
- err:
- clicon_err(OE_XML, errno, "cprintf");
- goto done;
}
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
@@ -909,10 +931,17 @@ netconf_malformed_message(cbuf *cb,
* @param[out] xret Error XML tree
* @param[in] message Error message
* @note New in :base:1.1
+ * @code
+ * cxobj *xret = NULL;
+ * if (netconf_malformed_message_xml(&xret, "Unauthorized") < 0)
+ * err;
+ * xml_free(xret);
+ * @endcode
+ * @see netconf_malformed_message Same but returns cligen buffer
*/
int
netconf_malformed_message_xml(cxobj **xret,
- char *message)
+ char *message)
{
int retval =-1;
cxobj *xerr;
@@ -925,8 +954,8 @@ netconf_malformed_message_xml(cxobj **xret,
goto done;
if ((xerr = xml_new("rpc-error", *xret, NULL)) == NULL)
goto done;
- if (xml_parse_va(&xerr, NULL, "malformed-message "
- "rpc "
+ if (xml_parse_va(&xerr, NULL, "rpc "
+ "malformed-message "
"error ") < 0)
goto done;
if (message && xml_parse_va(&xerr, NULL, "%s ",
@@ -972,7 +1001,7 @@ netconf_trymerge(cxobj *x,
}
/*! Load ietf netconf yang module and set enabled features
- * The features added are:
+ * The features added are (in order):
* candidate (8.3)
* validate (8.6)
* startup (8.7)
@@ -990,7 +1019,6 @@ netconf_module_load(clicon_handle h)
clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
goto done;
}
-
/* Enable features (hardcoded here) */
if (xml_parse_string("ietf-netconf:candidate ", yspec, &xc) < 0)
goto done;
@@ -1004,7 +1032,7 @@ netconf_module_load(clicon_handle h)
/* Load yang spec */
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
goto done;
- if (yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
+ if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
retval = 0;
done:
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 873d3aaf..2ef5b7e6 100644
--- a/lib/src/clixon_options.c
+++ b/lib/src/clixon_options.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -111,7 +111,6 @@ clicon_option_dump(clicon_handle h,
clicon_debug(dbglevel, "%s = NULL", keys[i]);
}
free(keys);
-
}
/*! Read filename and set values to global options registry. XML variant.
@@ -136,6 +135,8 @@ parse_configfile(clicon_handle h,
char *name;
char *body;
clicon_hash_t *copt = clicon_options(h);
+ cbuf *cbret = NULL;
+ int ret;
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
@@ -167,8 +168,16 @@ parse_configfile(clicon_handle h,
}
if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0)
goto done;
- if (xml_apply0(xc, CX_ELMNT, xml_yang_validate_add, NULL) < 0)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
+ }
+ if ((ret = xml_yang_validate_add(xc, cbret)) < 0)
+ goto done;
+ if (ret == 0){
+ clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
+ goto done;
+ }
while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
body = xml_body(x);
@@ -197,6 +206,8 @@ parse_configfile(clicon_handle h,
*xconfig = xt;
xt = NULL;
done:
+ if (cbret)
+ cbuf_free(cbret);
if (xt)
xml_free(xt);
if (f)
@@ -204,6 +215,42 @@ parse_configfile(clicon_handle h,
return retval;
}
+/*! Add configuration option overriding file setting
+ * Add to clicon_options hash, and to clicon_conf_xml tree
+ * @param[in] h Clicon handle
+ * @param[in] name Name of configuration option (see clixon-config.yang)
+ * @param[in] value String value
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see clicon_options_main For loading options from file
+ */
+int
+clicon_option_add(clicon_handle h,
+ char *name,
+ char *value)
+{
+ int retval = -1;
+ clicon_hash_t *copt = clicon_options(h);
+ cxobj *x;
+
+ if (strcmp(name, "CLICON_FEATURE")==0 ||
+ strcmp(name, "CLICON_YANG_DIR")==0){
+ if ((x = clicon_conf_xml(h)) == NULL)
+ goto done;
+ if (xml_parse_va(&x, NULL, "<%s>%s%s>",
+ name, value, name) < 0)
+ goto done;
+ }
+ if (hash_add(copt,
+ name,
+ value,
+ strlen(value)+1) == NULL)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! Parse clixon yang file. Parse XML config file. Initialize option values
*
* Set default options, Read config-file, Check that all values are set.
@@ -243,9 +290,8 @@ clicon_options_main(clicon_handle h,
clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix);
goto done;
}
-#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
- _CLICON_XML_NS_ITERATE = 1;
-#endif
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = 0;
/* Read configfile first without yangspec, for bootstrapping */
if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
goto done;
@@ -254,7 +300,7 @@ clicon_options_main(clicon_handle h,
/* Set clixon_conf pointer to handle */
clicon_conf_xml_set(h, xconfig);
/* Parse clixon yang spec */
- if (yang_parse(h, NULL, "clixon-config", NULL, yspec) < 0)
+ if (yang_spec_parse_module(h, "clixon-config", NULL, yspec) < 0)
goto done;
clicon_conf_xml_set(h, NULL);
if (xconfig)
@@ -266,14 +312,8 @@ clicon_options_main(clicon_handle h,
goto done;
/* Set clixon_conf pointer to handle */
clicon_conf_xml_set(h, xconfig);
- /* Specific option handling */
- if (clicon_option_bool(h, "CLICON_XML_SORT") == 1)
- xml_child_sort = 1;
- else
- xml_child_sort = 0;
-#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
- _CLICON_XML_NS_ITERATE = clicon_option_bool(h, "CLICON_XML_NS_ITERATE");
-#endif
+ /* XXX Kludge to low-level functions to search for xml in all yang modules */
+ _CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
retval = 0;
done:
return retval;
diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c
index 03ed2b29..f0a28a6f 100644
--- a/lib/src/clixon_plugin.c
+++ b/lib/src/clixon_plugin.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c
index 17b09614..76c7816e 100644
--- a/lib/src/clixon_proto.c
+++ b/lib/src/clixon_proto.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -160,10 +160,12 @@ clicon_msg_encode(char *format, ...)
/*! Decode a clicon netconf message
* @param[in] msg CLICON msg
+ * @param[in] yspec Yang specification, (can be NULL)
* @param[out] xml XML parse tree
*/
int
clicon_msg_decode(struct clicon_msg *msg,
+ yang_spec *yspec,
cxobj **xml)
{
int retval = -1;
@@ -172,7 +174,7 @@ clicon_msg_decode(struct clicon_msg *msg,
/* body */
xmlstr = msg->op_body;
clicon_debug(1, "%s %s", __FUNCTION__, xmlstr);
- if (xml_parse_string(xmlstr, NULL, xml) < 0)
+ if (xml_parse_string(xmlstr, yspec, xml) < 0)
goto done;
retval = 0;
done:
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index 0cf421d2..b454b608 100644
--- a/lib/src/clixon_proto_client.c
+++ b/lib/src/clixon_proto_client.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -351,7 +351,7 @@ clicon_rpc_edit_config(clicon_handle h,
if ((cb = cbuf_new()) == NULL)
goto done;
- cprintf(cb, "<%s/> ", db);
@@ -444,7 +444,7 @@ clicon_rpc_delete_config(clicon_handle h,
char *username;
username = clicon_username_get(h);
- if ((msg = clicon_msg_encode("<%s/> ",
+ if ((msg = clicon_msg_encode("<%s/> none ",
username?username:"", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
@@ -787,7 +787,7 @@ clicon_rpc_create_subscription(clicon_handle h,
char *username;
username = clicon_username_get(h);
- if ((msg = clicon_msg_encode(""
+ if ((msg = clicon_msg_encode(""
"%s "
" "
" ",
@@ -827,7 +827,7 @@ clicon_rpc_debug(clicon_handle h,
username = clicon_username_get(h);
/* XXX: hardcoded example yang, should be clixon-config!!! */
- if ((msg = clicon_msg_encode("%d ", username?username:"", level)) == NULL)
+ if ((msg = clicon_msg_encode("%d ", username?username:"", level)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
diff --git a/lib/src/clixon_sig.c b/lib/src/clixon_sig.c
index c1374876..d7c27335 100644
--- a/lib/src/clixon_sig.c
+++ b/lib/src/clixon_sig.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c
index ae454256..dbc5467a 100644
--- a/lib/src/clixon_stream.c
+++ b/lib/src/clixon_stream.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -548,7 +548,7 @@ stream_notify(clicon_handle h,
yang_spec *yspec = NULL;
char *str = NULL;
cbuf *cb = NULL;
- char timestr[27];
+ char timestr[28];
struct timeval tv;
event_stream_t *es;
@@ -622,7 +622,7 @@ stream_notify_xml(clicon_handle h,
yang_spec *yspec = NULL;
char *str = NULL;
cbuf *cb = NULL;
- char timestr[27];
+ char timestr[28];
struct timeval tv;
event_stream_t *es;
diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c
index 480805fb..f0dc1c24 100644
--- a/lib/src/clixon_string.c
+++ b/lib/src/clixon_string.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -57,14 +57,15 @@
/*! Split string into a vector based on character delimiters. Using malloc
*
* The given string is split into a vector where the delimiter can be
- * any of the characters in the specified delimiter string.
+ * _any_ of the characters in the specified delimiter string.
*
* The vector returned is one single memory block that must be freed
* by the caller
*
* @code
- * char **vec = NULL;
- * int nvec;
+ * char **vec = NULL;
+ * char *v;
+ * int nvec;
* if ((vec = clicon_strsep("/home/user/src/clixon", "/", &nvec)) == NULL)
* err;
* for (i=0; i [[a,"b"][c="d"]
* kalle&c=d -> [[c="d"]] # Discard elements with no delim2
* XXX differentiate between error and null cvec.
@@ -562,6 +566,119 @@ clicon_str2int(const map_str2int *mstab,
return -1;
}
+/*! Split colon-separated node identifier into prefix and name
+ * @param[in] node-id
+ * @param[out] prefix Malloced string. May be NULL.
+ * @param[out] id Malloced identifier.
+ * @retval 0 OK
+ * @retval -1 Error
+ * @code
+ * char *prefix = NULL;
+ * char *id = NULL;
+ * if (nodeid_split(nodeid, &prefix, &id) < 0)
+ * goto done;
+ * if (prefix)
+ * free(prefix);
+ * if (id)
+ * free(id);
+ * @note caller need to free id and prefix after use
+ */
+int
+nodeid_split(char *nodeid,
+ char **prefix,
+ char **id)
+{
+ int retval = -1;
+ char *str;
+
+ if ((str = strchr(nodeid, ':')) == NULL){
+ if ((*id = strdup(nodeid)) == NULL){
+ clicon_err(OE_YANG, errno, "strdup");
+ goto done;
+ }
+ }
+ else{
+ if ((*prefix = strdup(nodeid)) == NULL){
+ clicon_err(OE_YANG, errno, "strdup");
+ goto done;
+ }
+ (*prefix)[str-nodeid] = '\0';
+ str++;
+ if ((*id = strdup(str)) == NULL){
+ clicon_err(OE_YANG, errno, "strdup");
+ goto done;
+ }
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Trim blanks from front and end of a string, return new string
+ * @param[in] str
+ * @retval s Pointer into existing str after trimming blanks
+ */
+char *
+clixon_trim(char *str)
+{
+ char *s = str;
+ int i;
+
+ while (strlen(s) && isblank(s[0]))
+ s++;
+ for (i=0; i [0-9]
+ * POSIX ERE regexps according to man regex(3).
+ * @param[in] xsd Input regex string according XSD
+ * @param[out] posix Output (malloced) string according to POSIX ERE
+ * @see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028
+ * @note that the translation is ad-hoc, may need more translations
+ */
+int
+regexp_xsd2posix(char *xsd,
+ char **posix)
+{
+ int retval = -1;
+ char *x;
+ char *p = NULL;
+ int i;
+ int len;
+
+ len = strlen(xsd);
+ x = xsd;
+ while ((x = strstr(x, "\\d")) != NULL){
+ len += 3; /* \d --> [0-9] */
+ x += 2;
+ }
+ if ((p = malloc(len+1)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(p, 0, len+1);
+ *posix = p;
+ for (i=0; i string */
static const map_str2int xsmap[] = {
@@ -196,10 +198,9 @@ xml_name_set(cxobj *xn,
/*! Get namespace of xnode
* @param[in] xn xml node
* @retval namespace of xml node
- * XXX change to xml_localname
*/
char*
-xml_namespace(cxobj *xn)
+xml_prefix(cxobj *xn)
{
return xn->x_prefix;
}
@@ -209,11 +210,10 @@ xml_namespace(cxobj *xn)
* @param[in] localname new namespace, null-terminated string, copied by function
* @retval -1 on error with clicon-err set
* @retval 0 OK
- * XXX change to xml_localname_set
*/
int
-xml_namespace_set(cxobj *xn,
- char *localname)
+xml_prefix_set(cxobj *xn,
+ char *localname)
{
if (xn->x_prefix){
free(xn->x_prefix);
@@ -228,8 +228,7 @@ xml_namespace_set(cxobj *xn,
return 0;
}
-
-/*! Given an xml tree return URI namespace: default or localname given
+/*! Given an xml tree return URI namespace recursively : default or localname given
*
* Given an XML tree and a prefix (or NULL) return URI namespace.
* @param[in] x XML tree
@@ -237,7 +236,7 @@ xml_namespace_set(cxobj *xn,
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
* @retval 0 OK
* @retval -1 Error
- * @see xmlns_check XXX coordinate
+ * @see xmlns_check XXX can these be merged?
*/
int
xml2ns(cxobj *x,
@@ -248,9 +247,9 @@ xml2ns(cxobj *x,
char *ns;
cxobj *xp;
- if (prefix != NULL) /* xmlns: */
+ if (prefix != NULL) /* xmlns:="" */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
- else /* default ns */
+ else /* xmlns="" */
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
/* namespace not found, try parent */
@@ -272,6 +271,40 @@ xml2ns(cxobj *x,
return retval;
}
+/*! Add a namespace attribute to an XML node, either default or specific prefix
+ * @param[in] x XML tree
+ * @param[in] prefix prefix/ns localname. If NULL then set default xmlns
+ * @param[out] namespace URI namespace (or NULL). Will be copied
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see xml2ns
+ */
+int
+xmlns_set(cxobj *x,
+ char *prefix,
+ char *namespace)
+{
+ int retval = -1;
+ cxobj *xa;
+
+ if (prefix != NULL){ /* xmlns:="" */
+ if ((xa = xml_new(prefix, x, NULL)) == NULL)
+ goto done;
+ if (xml_prefix_set(xa, "xmlns") < 0)
+ goto done;
+ }
+ else{ /* xmlns="" */
+ if ((xa = xml_new("xmlns", x, NULL)) == NULL)
+ goto done;
+ xml_type_set(xa, CX_ATTR);
+ }
+ if (xml_value_set(xa, namespace) < 0)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
+}
+
/*! See if xmlns:[=] exists, if so return
*
* @param[in] xn XML node
@@ -288,7 +321,7 @@ xmlns_check(cxobj *xn,
char *xns;
while ((x = xml_child_each(xn, x, CX_ATTR)) != NULL)
- if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 &&
+ if ((xns = xml_prefix(x)) && strcmp(xns, "xmlns")==0 &&
strcmp(xml_name(x), nsn) == 0)
return xml_value(x);
return NULL;
@@ -311,7 +344,7 @@ xml_localname_check(cxobj *xn,
yang_stmt *ys = xml_spec(xn);
/* No namespace name - comply */
- if ((nsn = xml_namespace(xn)) == NULL)
+ if ((nsn = xml_prefix(xn)) == NULL)
return 0;
/* Check if NSN defined in same node */
if (xmlns_check(xn, nsn) != NULL)
@@ -485,6 +518,8 @@ xml_type_set(cxobj *xn,
/*! Get number of children
* @param[in] xn xml node
* @retval number of children in XML tree
+ * @see xml_child_nr_type
+ * @see xml_child_nr_notype
*/
int
xml_child_nr(cxobj *xn)
@@ -492,10 +527,33 @@ xml_child_nr(cxobj *xn)
return xn->x_childvec_len;
}
+/*! Get number of children of EXCEPT specific type
+ * @param[in] xn xml node
+ * @param[in] type XML type or -1 for all
+ * @retval number of typed children in XML tree (except type)
+ * @see xml_child_nr
+ * @see xml_child_nr_type
+ */
+int
+xml_child_nr_notype(cxobj *xn,
+ enum cxobj_type type)
+{
+ cxobj *x = NULL;
+ int nr = 0;
+
+ while ((x = xml_child_each(xn, x, -1)) != NULL) {
+ if (xml_type(x) != type)
+ nr++;
+ }
+ return nr;
+}
+
/*! Get number of children of specific type
* @param[in] xn xml node
* @param[in] type XML type or -1 for all
* @retval number of typed children in XML tree
+ * @see xml_child_nr
+ * @see xml_child_nr_notype
*/
int
xml_child_nr_type(cxobj *xn,
@@ -523,6 +581,28 @@ xml_child_i(cxobj *xn,
return NULL;
}
+/*! Get a specific child of a specific type
+ * @param[in] xn xml node
+ * @param[in] i the number of the child of specific type
+ * @param[in] type Child type
+ * @retval child in XML tree, or NULL if no such child, or empty child
+ * @see xml_child_i
+ */
+cxobj *
+xml_child_i_type(cxobj *xn,
+ int i,
+ enum cxobj_type type)
+{
+ cxobj *x = NULL;
+ int it = 0;
+
+ while ((x = xml_child_each(xn, x, type)) != NULL) {
+ if (x->x_type == type && (i == it++))
+ return x;
+ }
+ return NULL;
+}
+
/*! Set specific child
* @param[in] xn xml node
* @param[in] i the number of the child, eg order in children vector
@@ -940,7 +1020,7 @@ xml_body_get(cxobj *xt)
return NULL;
}
-/*! Find and return the value of an xml child of specific type
+/*! Find and return the value of an xml child of specific type given prefix and name
*
* The value can be of an attribute only
* @param[in] xt xml tree node
@@ -952,6 +1032,7 @@ xml_body_get(cxobj *xt)
* char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
* @endcode
* @note, make a copy of the return value to use it properly
+ * @see xml_find_type return the xml object
* @see xml_find_value where a body can be found as well
*/
char *
@@ -959,19 +1040,45 @@ xml_find_type_value(cxobj *xt,
char *prefix,
char *name,
enum cxobj_type type)
+{
+ cxobj *x;
+
+ if ((x = xml_find_type(xt, prefix, name, type)) != NULL)
+ return xml_value(x);
+ return NULL;
+}
+
+/*! Find and return the xml child of specific type given prefix and name
+ *
+ * The value can be of an attribute only
+ * @param[in] xt xml tree node
+ * @param[in] prefix Prefix (namespace local name) or NULL
+ * @param[in] name name of xml tree node (eg attr name or "body")
+ * @retval val Pointer to the name string
+ * @retval NULL No such node or no value in node
+ * @code
+ * cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
+ * @endcode
+ * @see xml_find_value where a body can be found as well
+ */
+cxobj *
+xml_find_type(cxobj *xt,
+ char *prefix,
+ char *name,
+ enum cxobj_type type)
{
cxobj *x = NULL;
int pmatch; /* prefix match */
char *xprefix; /* xprefix */
while ((x = xml_child_each(xt, x, type)) != NULL) {
- xprefix = xml_namespace(x);
+ xprefix = xml_prefix(x);
if (prefix)
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
else
pmatch = 1;
if (pmatch && strcmp(name, xml_name(x)) == 0)
- return xml_value(x);
+ return x;
}
return NULL;
}
@@ -1121,7 +1228,7 @@ clicon_xml2file(FILE *f,
if (x == NULL)
goto ok;
name = xml_name(x);
- namespace = xml_namespace(x);
+ namespace = xml_prefix(x);
switch(xml_type(x)){
case CX_BODY:
if ((val = xml_value(x)) == NULL) /* incomplete tree */
@@ -1246,7 +1353,7 @@ clicon_xml2cbuf(cbuf *cb,
char *val;
name = xml_name(x);
- namespace = xml_namespace(x);
+ namespace = xml_prefix(x);
switch(xml_type(x)){
case CX_BODY:
if ((val = xml_value(x)) == NULL) /* incomplete tree */
@@ -1333,10 +1440,10 @@ xmltree2cbuf(cbuf *cb,
cprintf(cb, " ");
if (xml_type(x) != CX_BODY)
cprintf(cb, "%s", xml_type2str(xml_type(x)));
- if (xml_namespace(x)==NULL)
+ if (xml_prefix(x)==NULL)
cprintf(cb, " %s", xml_name(x));
else
- cprintf(cb, " %s:%s", xml_namespace(x), xml_name(x));
+ cprintf(cb, " %s:%s", xml_prefix(x), xml_name(x));
if (xml_value(x))
cprintf(cb, " value:\"%s\"", xml_value(x));
if (x->x_flags)
@@ -1399,6 +1506,8 @@ _xml_parse(const char *str,
goto done;
/* Sort the complete tree after parsing */
if (yspec){
+ if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ goto done;
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
@@ -1594,7 +1703,7 @@ xml_parse_va(cxobj **xtop,
return retval;
}
-/*! Copy single xml node without copying children
+/*! Copy single xml node frm x0 to x1 without copying children
*/
int
xml_copy_one(cxobj *x0,
@@ -1612,8 +1721,8 @@ xml_copy_one(cxobj *x0,
if ((s = xml_name(x0))) /* malloced string */
if ((xml_name_set(x1, s)) < 0)
return -1;
- if ((s = xml_namespace(x0))) /* malloced string */
- if ((xml_namespace_set(x1, s)) < 0)
+ if ((s = xml_prefix(x0))) /* malloced string */
+ if ((xml_prefix_set(x1, s)) < 0)
return -1;
return 0;
}
diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c
index c3a4126a..73959bf7 100644
--- a/lib/src/clixon_xml_db.c
+++ b/lib/src/clixon_xml_db.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -379,8 +379,9 @@ xmldb_get(clicon_handle h,
* @param[in] db running or candidate
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
- * @param[out] cbret Initialized cligen buffer or NULL. On exit contains XML or "".
- * @retval 0 OK
+ * @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
+ * @retval 1 OK
+ * @retval 0 Failed, cbret contains error xml message
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
@@ -388,8 +389,10 @@ xmldb_get(clicon_handle h,
* cxobj *xret = NULL;
* if (xml_parse_string("17 ", yspec, &xt) < 0)
* err;
- * if (xmldb_put(xh, "running", OP_MERGE, xt, cbret) < 0)
+ * if ((ret = xmldb_put(xh, "running", OP_MERGE, xt, cbret)) < 0)
* err;
+ * if (ret==0)
+ * cbret contains netconf error message
* @endcode
* @note that you can add both config data and state data. In comparison,
* xmldb_get has a parameter to get config data only.
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 6794b503..3a62a5f7 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -50,6 +50,7 @@
* +---------+
* | file |
* +---------+
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@@ -87,6 +88,7 @@
#include "clixon_xpath.h"
#include "clixon_log.h"
#include "clixon_err.h"
+#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_map.h"
@@ -94,14 +96,20 @@
static int
tleaf(cxobj *x)
{
- cxobj *c;
+ cxobj *xc;
if (xml_type(x) != CX_ELMNT)
return 0;
- if (xml_child_nr(x) != 1)
+ if (xml_child_nr_notype(x, CX_ATTR) != 1)
return 0;
- c = xml_child_i(x, 0);
- return (xml_child_nr(c) == 0);
+ /* From here exactly one noattr child, get it */
+ xc = NULL;
+ while ((xc = xml_child_each(x, xc, -1)) != NULL)
+ if (xml_type(xc) != CX_ATTR)
+ break;
+ if (xc == NULL)
+ return -1; /* n/a */
+ return (xml_child_nr_notype(xc, CX_ATTR) == 0);
}
/*! Translate XML -> TEXT
@@ -113,39 +121,42 @@ xml2txt(FILE *f,
cxobj *x,
int level)
{
- cxobj *xe = NULL;
+ cxobj *xc = NULL;
int children=0;
- char *term = NULL;
int retval = -1;
- xe = NULL; /* count children */
- while ((xe = xml_child_each(x, xe, -1)) != NULL)
- children++;
- if (!children){
- if (xml_type(x) == CX_BODY){
- term = xml_value(x);
+ xc = NULL; /* count children (elements and bodies, not attributes) */
+ while ((xc = xml_child_each(x, xc, -1)) != NULL)
+ if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
+ children++;
+ if (!children){ /* If no children print line */
+ switch (xml_type(x)){
+ case CX_BODY:
+ fprintf(f, "%s;\n", xml_value(x));
+ break;
+ case CX_ELMNT:
+ fprintf(f, "%*s;\n", 4*level, xml_name(x));
+ break;
+ default:
+ break;
}
- else{
- fprintf(f, "%*s", 4*level, "");
- term = xml_name(x);
- }
- fprintf(f, "%s;\n", term);
- retval = 0;
- goto done;
+ goto ok;
}
fprintf(f, "%*s", 4*level, "");
fprintf(f, "%s ", xml_name(x));
if (!tleaf(x))
fprintf(f, "{\n");
- xe = NULL;
- while ((xe = xml_child_each(x, xe, -1)) != NULL){
- if (xml2txt(f, xe, level+1) < 0)
- break;
+ xc = NULL;
+ while ((xc = xml_child_each(x, xc, -1)) != NULL){
+ if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
+ if (xml2txt(f, xc, level+1) < 0)
+ break;
}
if (!tleaf(x))
fprintf(f, "%*s}\n", 4*level, "");
+ ok:
retval = 0;
- done:
+ // done:
return retval;
}
@@ -171,17 +182,21 @@ xml2cli(FILE *f,
int match;
char *body;
- ys = xml_spec(x);
+ if (xml_type(x)==CX_ATTR)
+ goto ok;
+ if ((ys = xml_spec(x)) == NULL)
+ goto ok;
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
if (prepend0)
fprintf(f, "%s", prepend0);
- body = xml_body(x);
if (gt == GT_ALL || gt == GT_VARS)
fprintf(f, "%s ", xml_name(x));
- if (index(body, ' '))
- fprintf(f, "\"%s\"", body);
- else
- fprintf(f, "%s", body);
+ if ((body = xml_body(x)) != NULL){
+ if (index(body, ' '))
+ fprintf(f, "\"%s\"", body);
+ else
+ fprintf(f, "%s", body);
+ }
fprintf(f, "\n");
goto ok;
}
@@ -230,10 +245,15 @@ 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
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed
+ * @retval -1 Error
*/
static int
validate_leafref(cxobj *xt,
- yang_stmt *ytype)
+ yang_stmt *ytype,
+ cbuf *cbret)
{
int retval = -1;
yang_stmt *ypath;
@@ -247,8 +267,9 @@ validate_leafref(cxobj *xt,
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){
- clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument);
- goto done;
+ if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0)
+ goto done;
+ goto fail;
}
if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0)
goto done;
@@ -260,9 +281,9 @@ validate_leafref(cxobj *xt,
break;
}
if (i==xlen){
- clicon_err(OE_DB, 0, "Leafref validation failed, no such leaf: %s",
- leafrefbody);
- goto done;
+ if (netconf_bad_element(cbret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
+ goto done;
+ goto fail;
}
ok:
retval = 0;
@@ -270,6 +291,9 @@ validate_leafref(cxobj *xt,
if (xvec)
free(xvec);
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! Validate xml node of type identityref, ensure value is a defined identity
@@ -285,14 +309,18 @@ 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
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed
+ * @retval -1 Error
* @see ys_populate_identity where the derived types are set
* @see RFC7950 Sec 9.10.2:
-
*/
static int
validate_identityref(cxobj *xt,
yang_stmt *ys,
- yang_stmt *ytype)
+ yang_stmt *ytype,
+ cbuf *cbret)
{
int retval = -1;
char *node;
@@ -305,37 +333,77 @@ validate_identityref(cxobj *xt,
* Always add default prefix because derived identifiers are stored with
* prefixes in the base identifiers derived-list.
*/
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
if ((node = xml_body(xt)) == NULL)
return 0;
if (strchr(node, ':') == NULL){
prefix = yang_find_myprefix(ys);
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
cprintf(cb, "%s:%s", prefix, node);
node = cbuf_get(cb);
}
/* This is the type's base reference */
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){
- clicon_err(OE_DB, 0, "Identityref validation failed, no base");
- goto done;
+ if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0)
+ goto done;
+ goto fail;
}
/* This is the actual base identity */
if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
- clicon_err(OE_DB, 0, "Identityref validation failed, no base identity");
- goto done;
+ if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0)
+ goto done;
+ goto fail;
}
/* Here check if node is in the derived node list of the base identity */
if (cvec_find(ybaseid->ys_cvec, node) == NULL){
- clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument);
- goto done;
+ cbuf_reset(cb);
+ cprintf(cb, "Identityref validation failed, %s not derived from %s",
+ node, ybaseid->ys_argument);
+ if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0)
+ goto done;
+ goto fail;
}
- retval = 0;
+ retval = 1;
done:
if (cb)
cbuf_free(cb);
return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
+/*! Given an XML node, return root node
+ * A root node is an ancestor xr of x with one or both of the following properties
+ * - its XML parent is NULL parent,
+ * - its associated yang specification's parent is a yang module.
+ * @param[in] x XML node
+ * @param[out] xr XML root
+ */
+int
+xml_yang_root(cxobj *x,
+ cxobj **xr)
+{
+ int retval = -1;
+ cxobj *xp;
+ yang_stmt *y;
+ yang_stmt *yp;
+
+ while ((xp = xml_parent(x)) != NULL){
+ if ((y = xml_spec(x)) != NULL &&
+ (yp = (yang_stmt*)y->ys_parent) != NULL)
+ /* Actually, maybe only the Y_MODULE clause is relevant */
+ if (yp==NULL ||
+ yp->ys_keyword == Y_MODULE ||
+ yp->ys_keyword == Y_SUBMODULE)
+ break; /* x is the root */
+ x = xp;
+ }
+ *xr = x;
+ retval = 0;
+ return retval;
}
/*! Validate an RPC node
@@ -375,26 +443,35 @@ validate_identityref(cxobj *xt,
* in [RFC6241]. If output parameters are returned, they are encoded as
* child elements to the element defined in [RFC6241], in
* the same order as they are defined within the "output" statement.
+ * @see xml_yang_validate_all
+ * @note Should need a variant accepting cxobj **xret
*/
int
-xml_yang_validate_rpc(cxobj *xrpc)
+xml_yang_validate_rpc(cxobj *xrpc,
+ cbuf *cbret)
{
int retval = -1;
yang_stmt *yn=NULL; /* rpc name */
cxobj *xn; /* rpc name */
- yang_stmt *yi=NULL; /* input name */
- cxobj *xi; /* input name */
- assert(strcmp(xml_name(xrpc), "rpc")==0);
+ if (strcmp(xml_name(xrpc), "rpc")){
+ clicon_err(OE_XML, EINVAL, "Expected RPC");
+ goto done;
+ }
xn = NULL;
+ /* xn is name of rpc, ie */
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
- if ((yn = xml_spec(xn)) == NULL)
+ if ((yn = xml_spec(xn)) == NULL){
+ if (netconf_unknown_element(cbret, "application", xml_name(xn), NULL) < 0)
+ goto done;
goto fail;
- xi = NULL;
- while ((xi = xml_child_each(xn, xi, CX_ELMNT)) != NULL) {
- if ((yi = xml_spec(xi)) == NULL)
- goto fail;
}
+ if ((retval = xml_yang_validate_all(xn, cbret)) < 1)
+ goto done; /* error or validation fail */
+ if ((retval = xml_yang_validate_add(xn, cbret)) < 1)
+ goto done; /* error or validation fail */
+ if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
+ goto done;
}
// ok: /* pass validation */
retval = 1;
@@ -405,52 +482,135 @@ xml_yang_validate_rpc(cxobj *xrpc)
goto done;
}
+/*! 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)
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed (cbret set)
+ * @retval -1 Error
+ */
+static int
+check_mandatory(cxobj *xt,
+ yang_stmt *yt,
+ cbuf *cbret)
+{
+ int retval = -1;
+ int i;
+ cxobj *x;
+ yang_stmt *y;
+ yang_stmt *yc;
+ yang_node *yp;
+
+ for (i=0; iys_len; i++){
+ yc = yt->ys_stmt[i];
+ if (!yang_mandatory(yc))
+ continue;
+ switch (yc->ys_keyword){
+ case Y_CONTAINER:
+ case Y_ANYDATA:
+ case Y_ANYXML:
+ case Y_LEAF:
+ if (yang_config(yc)==0)
+ break;
+ /* Find a child with the mandatory yang */
+ x = NULL;
+ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
+ if ((y = xml_spec(x)) != NULL
+ && y==yc)
+ break; /* got it */
+ }
+ if (x == NULL){
+ if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
+ goto done;
+ goto fail;
+ }
+ break;
+ case Y_CHOICE: /* More complex because of choice/case structure */
+ x = NULL;
+ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
+ if ((y = xml_spec(x)) != NULL &&
+ (yp = yang_choice(y)) != NULL &&
+ yp == (yang_node*)yc){
+ break; /* leave loop with x set */
+ }
+ }
+ if (x == NULL){
+ /* @see RFC7950: 15.6 Error Message for Data That Violates
+ * a Mandatory "choice" Statement */
+ if (cprintf(cbret, ""
+ "application "
+ "data-missing "
+ "missing-choice "
+#ifdef NYI
+ // " "
+#endif
+ "%s "
+ "error "
+ " ",
+ yc->ys_argument) <0)
+ goto done;
+ goto fail;
+ }
+ break;
+ default:
+ break;
+ } /* switch */
+ }
+ retval = 1;
+ done:
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
/*! Validate a single XML node with yang specification for added entry
* 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
- * @retval 0 Valid OK
- * @retval -1 Validation failed
+ * @param[in] xt XML node to be validated
+ * @param[out] cbret Error buffer (set w netconf error if retval == 0)
+ * @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(x, cbret)) < 0)
+ * err;
+ * if (ret == 0)
+ * fail;
+ * @endcode
* @see xml_yang_validate_all
+ * @see xml_yang_validate_rpc
+ * @note Should need a variant accepting cxobj **xret
*/
int
xml_yang_validate_add(cxobj *xt,
- void *arg)
+ cbuf *cbret)
{
int retval = -1;
cg_var *cv = NULL;
char *reason = NULL;
- yang_stmt *yc;
- int i;
- yang_stmt *ys;
+ yang_stmt *yt; /* yang spec of xt going in */
char *body;
+ int ret;
+ cxobj *x;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
- if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){
- switch (ys->ys_keyword){
- case Y_INPUT:
- case Y_LIST:
- /* fall thru */
- case Y_CONTAINER:
- for (i=0; iys_len; i++){
- yc = ys->ys_stmt[i];
- if (yc->ys_keyword != Y_LEAF)
- continue;
- if (yang_config(yc)==0)
- continue;
- if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
- clicon_err(OE_CFG, 0,"Missing mandatory variable: %s",
- yc->ys_argument);
- goto done;
- }
- }
- break;
+ if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
+ if ((ret = check_mandatory(xt, yt, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ /* Check leaf values */
+ switch (yt->ys_keyword){
case Y_LEAF:
/* fall thru */
case Y_LEAF_LIST:
/* validate value against ranges, etc */
- if ((cv = cv_dup(ys->ys_cv)) == NULL){
+ if ((cv = cv_dup(yt->ys_cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv_dup");
goto done;
}
@@ -458,17 +618,15 @@ xml_yang_validate_add(cxobj *xt,
* needs to be reparsed when concrete type is selected
*/
if ((body = xml_body(xt)) != NULL){
- if (cv_parse(body, cv) <0){
- clicon_err(OE_UNIX, errno, "cv_parse");
- goto done;
+ if (cv_parse1(body, cv, &reason) != 1){
+ if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
+ goto done;
+ goto fail;
}
- if ((ys_cv_validate(cv, ys, &reason)) != 1){
- clicon_err(OE_DB, 0,
- "validation of %s failed %s",
- ys->ys_argument, reason?reason:"");
- if (reason)
- free(reason);
- goto done;
+ if ((ys_cv_validate(cv, yt, &reason)) != 1){
+ if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
+ goto done;
+ goto fail;
}
}
break;
@@ -476,28 +634,47 @@ xml_yang_validate_add(cxobj *xt,
break;
}
}
- retval = 0;
+ x = NULL;
+ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
+ if ((ret = xml_yang_validate_add(x, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ }
+ retval = 1;
done:
if (cv)
cv_free(cv);
+ if (reason)
+ free(reason);
return retval;
+ fail:
+ retval = 0;
+ goto done;
}
/*! 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[in] arg Not used
- * @retval -1 Validation failed
- * @retval 0 Validation OK
- * @see xml_yang_validate_add
+ * @param[out] cbret Error buffer (set w netconf error if retval == 0)
+ * @retval 1 Validation OK
+ * @retval 0 Validation failed (cbret set)
+ * @retval -1 Error
* @code
- * if (xml_apply(x, CX_ELMNT, (xml_applyfn_t*)xml_yang_validate_all, 0) < 0)
+ * cxobj *x;
+ * cbuf *cbret = cbuf_new();
+ * if ((ret = xml_yang_validate_all(x, cbret)) < 0)
* err;
+ * if (ret == 0)
+ * fail;
* @endcode
+ * @see xml_yang_validate_add
+ * @see xml_yang_validate_rpc
+ * @note Should need a variant accepting cxobj **xret
*/
int
xml_yang_validate_all(cxobj *xt,
- void *arg)
+ cbuf *cbret)
{
int retval = -1;
yang_stmt *ys; /* yang node */
@@ -505,13 +682,24 @@ xml_yang_validate_all(cxobj *xt,
yang_stmt *ye; /* yang must error-message */
char *xpath;
int nr;
-
+ int ret;
+ cxobj *x;
+
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
- if ((ys = xml_spec(xt)) != NULL &&
- yang_config(ys) != 0){
+ ys=xml_spec(xt);
+ if (ys==NULL){
+ if (netconf_unknown_element(cbret, "application", xml_name(xt), NULL) < 0)
+ goto done;
+ goto fail;
+ }
+ if (ys != NULL && yang_config(ys) != 0){
/* Node-specific validation */
switch (ys->ys_keyword){
+ case Y_ANYXML:
+ case Y_ANYDATA:
+ goto ok;
+ break;
case Y_LEAF:
/* fall thru */
case Y_LEAF_LIST:
@@ -520,11 +708,11 @@ xml_yang_validate_all(cxobj *xt,
*/
if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){
- if (validate_leafref(xt, yc) < 0)
+ if (validate_leafref(xt, yc, cbret) < 0)
goto done;
}
else if (strcmp(yc->ys_argument, "identityref") == 0){
- if (validate_identityref(xt, ys, yc) < 0)
+ if (validate_identityref(xt, ys, yc, cbret) < 0)
goto done;
}
}
@@ -568,11 +756,11 @@ xml_yang_validate_all(cxobj *xt,
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done;
if (!nr){
- if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL)
- clicon_err(OE_DB, 0, "%s", ye->ys_argument);
- else
- clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
- goto done;
+ ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL);
+ if (netconf_operation_failed(cbret, "application",
+ ye?ye->ys_argument:"must xpath validation failed") < 0)
+ goto done;
+ goto fail;
}
}
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
@@ -581,11 +769,82 @@ xml_yang_validate_all(cxobj *xt,
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done;
if (!nr){
- clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
- goto done;
+ if (netconf_operation_failed(cbret, "application",
+ "when xpath validation failed") < 0)
+ goto done;
+ goto fail;
}
}
}
+ x = NULL;
+ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
+ if ((ret = xml_yang_validate_all(x, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ }
+ ok:
+ retval = 1;
+ done:
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
+/*! Translate a single xml node to a cligen variable vector. Note not recursive
+ */
+int
+xml_yang_validate_all_top(cxobj *xt,
+ cbuf *cbret)
+{
+ int ret;
+ cxobj *x;
+
+ x = NULL;
+ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
+ if ((ret = xml_yang_validate_all(x, cbret)) < 1)
+ return ret;
+ }
+ return 1;
+}
+
+/*! Given XML node x, find yang spec in _any_ module matching name
+ * This is non-struct namespace semantics (not correct) but necessary
+ * in historic Clixon code.
+ * Also, add a proper default namespaces statement (xmlns="uri") in x
+ * @param[in] x XML node (find yang statement on this one)
+ * @param[in] yspec Top-level yang spec
+ * @param[out] y Yang stmt associated to x. NULL i not found
+ * @retval 0 OK
+ * @see CLICON_XML_NS_STRICT clixon config option
+ */
+int
+xml_yang_find_non_strict(cxobj *x,
+ yang_spec *yspec,
+ yang_stmt **yp)
+{
+ int retval = -1;
+ char *name;
+ yang_stmt *ymod;
+ int i;
+ yang_stmt *y=NULL;
+ char *ns;
+
+ name = xml_name(x);
+ for (i=0; iyp_len; i++){
+ ymod = yspec->yp_stmt[i];
+ if ((y = yang_find_schemanode((yang_node*)ymod, name)) != NULL)
+ break;
+ }
+ if (y){
+ *yp = y;
+ if ((ns = yang_find_mynamespace(ymod)) != NULL){
+ if (xml_find_type_value(x, NULL, "xmlns", CX_ATTR) == NULL)
+ if (xmlns_set(x, NULL, ns) < 0)
+ goto done;
+ }
+ }
retval = 0;
done:
return retval;
@@ -757,35 +1016,13 @@ cvec2xml_1(cvec *cvv,
return retval;
}
-
-/*! Find next yang node, either start from yang_spec or some yang-node
- * @param[in] y Node spec or sny yang-node
- * @param[in] name Name of childnode to find
- * @retval ys yang statement
- * @retval NULL Error: no node found
- */
-static yang_stmt *
-yang_next(yang_node *y,
- char *name)
-{
- yang_stmt *ys;
-
- if (y->yn_keyword == Y_SPEC)
- ys = yang_find_topnode((yang_spec*)y, name, YC_DATANODE);
- else
- ys = yang_find_datanode(y, name);
- if (ys == NULL)
- clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
- return ys;
-}
-
/*! Recursive help function to compute differences between two xml trees
- * @param[in] x1 First XML tree
- * @param[in] x2 Second XML tree
+ * @param[in] x1 First XML tree
+ * @param[in] x2 Second XML tree
* @param[out] x1vec Pointervector to XML nodes existing in only first tree
* @param[out] x1veclen Length of first vector
- * @param[out] x2vec Pointervector to XML nodes existing in only second tree
- * @param[out] x2veclen Length of x2vec vector
+ * @param[out] x2vec Pointervector to XML nodes existing in only second tree
+ * @param[out] x2veclen Length of x2vec vector
* @param[out] changed_x1 Pointervector to XML nodes changed orig value
* @param[out] changed_x2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
@@ -816,15 +1053,25 @@ xml_diff1(yang_stmt *ys,
*/
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
- if ((yc = yang_next((yang_node*)ys, xml_name(x1c))) == NULL)
+ if ((yc = xml_spec(x1c)) == NULL){
+ clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
goto done;
- if (match_base_child(x2, x1c, &x2c, yc) < 0)
+ }
+ if (match_base_child(x2, x1c, yc, &x2c) < 0)
goto done;
if (x2c == NULL){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
}
- else{
+ else if (yang_choice(yc)){
+ /* if x1c and x2c are choice/case, then they are changed */
+ if (cxvec_append(x1c, changed_x1, changedlen) < 0)
+ goto done;
+ (*changedlen)--; /* append two vectors */
+ if (cxvec_append(x2c, changed_x2, changedlen) < 0)
+ goto done;
+ }
+ else{ /* if x1c and x2c are leafs w bodies, then they are changed */
if (yc->ys_keyword == Y_LEAF){
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
break;
@@ -850,9 +1097,11 @@ xml_diff1(yang_stmt *ys,
*/
x2c = NULL;
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
- if ((yc = yang_next((yang_node*)ys, xml_name(x2c))) == NULL)
+ if ((yc = xml_spec(x2c)) == NULL){
+ clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x2c));
goto done;
- if (match_base_child(x1, x2c, &x1c, yc) < 0)
+ }
+ if (match_base_child(x1, x2c, yc, &x1c) < 0)
goto done;
if (x1c == NULL)
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
@@ -921,10 +1170,11 @@ xml_diff(yang_spec *yspec,
* Recursively construct it to the top.
* Example:
* yang: container a -> list b -> key c -> leaf d
- * xpath: /a/b/%s/d
+ * xpath: /modname:a/b/%s/d
* @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] cb api_path_fmt,
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
static int
yang2api_path_fmt_1(yang_stmt *ys,
@@ -936,28 +1186,34 @@ yang2api_path_fmt_1(yang_stmt *ys,
cvec *cvk = NULL; /* vector of index keys */
int retval = -1;
- yp = ys->ys_parent;
- if (yp != NULL &&
+ if ((yp = ys->ys_parent) == NULL){
+ clicon_err(OE_YANG, EINVAL, "yang expected parent %s", ys->ys_argument);
+ goto done;
+ }
+ if (yp != NULL && /* XXX rm */
yp->yn_keyword != Y_MODULE &&
yp->yn_keyword != Y_SUBMODULE){
- if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0)
+ if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
goto done;
+ if (yp->yn_keyword != Y_CHOICE && yp->yn_keyword != Y_CASE)
+ cprintf(cb, "/");
}
+ else /* top symbol - mark with name prefix */
+ cprintf(cb, "/%s:", yp->yn_argument);
+
if (inclkey){
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
- cprintf(cb, "/%s", ys->ys_argument);
+ cprintf(cb, "%s", ys->ys_argument);
}
else{
-#if 1
if (ys->ys_keyword == Y_LEAF && yp &&
yp->yn_keyword == Y_LIST){
if (yang_key_match(yp, ys->ys_argument) == 0)
- cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
+ cprintf(cb, "%s", ys->ys_argument); /* Not if leaf and key */
}
else
-#endif
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
- cprintf(cb, "/%s", ys->ys_argument);
+ cprintf(cb, "%s", ys->ys_argument);
}
switch (ys->ys_keyword){
@@ -991,6 +1247,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
* @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] api_path_fmt XML api path. Needs to be freed after use.
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
int
yang2api_path_fmt(yang_stmt *ys,
@@ -1044,6 +1301,8 @@ yang2api_path_fmt(yang_stmt *ys,
* api_path_fmt: /subif-entry=%s,%s/subid
* cvv: foo
* api_path: /subif-entry=foo/subid
+ *
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
int
api_path_fmt2api_path(char *api_path_fmt,
@@ -1107,7 +1366,6 @@ api_path_fmt2api_path(char *api_path_fmt,
return retval;
}
-
/*! Transform an xml key format and a vector of values to an XML path
* Used to input xmldb_get() or xmldb_get_vec
* @param[in] api_path_fmt XML key format
@@ -1126,6 +1384,8 @@ api_path_fmt2api_path(char *api_path_fmt,
* api_path_fmt: /subif-entry=%s,%s/subid
* cvv: foo
* xpath: /subif-entry[if-name=foo]/subid"
+ *
+ * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
int
api_path_fmt2xpath(char *api_path_fmt,
@@ -1310,6 +1570,11 @@ xml_tree_prune_flagged(cxobj *xt,
/*! Add default values (if not set)
* @param[in] xt XML tree with some node marked
+ * @param[in] arg Ignored
+ * Typically called in a recursive apply function:
+ * @code
+ * xml_apply(xt, CX_ELMNT, xml_default, NULL);
+ * @endcode
*/
int
xml_default(cxobj *xt,
@@ -1328,7 +1593,8 @@ xml_default(cxobj *xt,
goto done;
}
/* Check leaf defaults */
- if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){
+ if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST ||
+ ys->ys_keyword == Y_INPUT){
for (i=0; iys_len; i++){
y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF)
@@ -1358,67 +1624,6 @@ xml_default(cxobj *xt,
return retval;
}
-/*! Order XML children according to YANG
- * @param[in] xt XML top of tree
- */
-int
-xml_order(cxobj *xt,
- void *arg)
-{
- int retval = -1;
- yang_stmt *y;
- yang_stmt *yc;
- int i;
- int j0;
- int j;
- cxobj *xc;
- cxobj *xj;
- char *yname; /* yang child name */
- char *xname; /* xml child name */
-
- if ((y = (yang_stmt*)xml_spec(xt)) == NULL){
- retval = 0;
- goto done;
- }
- j0 = 0;
- /* Go through yang node's children and ensure that the
- * xml children follow this order.
- * Do not order the list or leaf-list children (have same name).
- */
- for (i=0; iys_len; i++){
- yc = y->ys_stmt[i];
- if (!yang_datanode(yc))
- continue;
- yname = yc->ys_argument;
- /* First go thru xml children with same name in rest of list */
- for (; j0yp_len; i++){
- ymod = yspec->yp_stmt[i];
- if ((y = yang_find((yang_node*)ymod, Y_RPC, xml_name(x))) != NULL)
- break;
- }
+ !clicon_option_bool(h, "CLICON_XML_NS_STRICT")){
+ if (xml_yang_find_non_strict(x, yspec, &y) < 0) /* find rpc */
+ goto done;
}
if (y){
xml_spec_set(x, y);
if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){
- xi = NULL;
- while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) {
- if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL)
- xml_spec_set(xi, ya);
- }
+ /* kludge rpc -> input */
+ xml_spec_set(x, yi);
+ if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
+ goto done;
}
}
}
@@ -1534,13 +1733,14 @@ xml_spec_populate_rpc(clicon_handle h,
return retval;
}
+
/*! Add yang specification backpointer to XML node
* @param[in] xt XML tree node
* @param[in] arg Yang spec
* @note This may be unnecessary if yspec is set on creation
* @note For subs to anyxml nodes will not have spec set
* @note No validation is done,... XXX
- * @note relies on kludge _CLICON_XML_NS_ITERATE
+ * @note relies on kludge _CLICON_XML_NS_STRICT
* @code
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
* @endcode
@@ -1557,7 +1757,6 @@ xml_spec_populate(cxobj *x,
yang_stmt *ymod; /* yang module */
cxobj *xp; /* xml parent */
char *name;
- int i;
yspec = (yang_spec*)arg;
if (xml_spec(x))
@@ -1570,16 +1769,12 @@ xml_spec_populate(cxobj *x,
if (ys_module_by_xml(yspec, x, &ymod) < 0)
goto done;
if (ymod != NULL)
- y = yang_find_datanode((yang_node*)ymod, name);
- /* Loose semantics: loop through all modules to find the node
- * XXX clicon_option_bool(h, "CLICON_XML_NS_ITERATE")
+ y = yang_find_schemanode((yang_node*)ymod, name);
+ /* Non-strict semantics: loop through all modules to find the node
*/
- if (y == NULL && _CLICON_XML_NS_ITERATE){
- for (i=0; iyp_len; i++){
- ymod = yspec->yp_stmt[i];
- if ((y = yang_find_datanode((yang_node*)ymod, name)) != NULL)
- break;
- }
+ if (y == NULL && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(x, yspec, &y) < 0)
+ goto done;
}
}
if (y)
@@ -1596,15 +1791,19 @@ xml_spec_populate(cxobj *x,
return retval;
}
-
/*! Translate from restconf api-path in cvv form to xml xpath
* eg a/b=c -> a/[b=c]
- * @param[in] yspec Yang spec
- * @param[in] pcvec api-path as cvec
- * @param[in] pi Offset of cvec, where api-path starts
- * @param[out] path The xpath as cbuf variable string, must be initializeed
- * The api-path has some wierd encoding, use api_path2xpath() if you are
- * confused.
+ * eg example:a/b -> ex:a/b
+ * @param[in] yspec Yang spec
+ * @param[in] cvv api-path as cvec
+ * @param[in] offset Offset of cvec, where api-path starts
+ * @param[out] xpath The xpath as cbuf variable string, must be initializeed
+ * @retval 1 OK
+ * @retval 0 Invalid api_path or associated XML, clicon_err called
+ * @retval -1 Fatal error, clicon_err called
+ *
+ * @note both retval 0 and -1 set clicon_err, but the later is fatal
+ * @note Not proper namespace translation from api-path 2 xpath
* It works like this:
* Assume origin incoming path is
* "www.foo.com/restconf/a/b=c", pi is 2 and pcvec is:
@@ -1612,46 +1811,63 @@ xml_spec_populate(cxobj *x,
* which means the api-path is ["a" "b=c"] corresponding to "a/b=c"
* @code
* cbuf *xpath = cbuf_new();
- * if (api_path2xpath_cvv(yspec, cvv, i, xpath)
+ * cvec *cvv = NULL;
+ * if (str2cvec("www.foo.com/restconf/a/b=c", '/', '=', &cvv) < 0)
* err;
+ * if ((ret = api_path2xpath(yspec, cvv, 0, cxpath)) < 0)
+ * err;
+ * if (ret == 0){
+ * ... access error string in clicon_err_reason
+ * clicon_err_reset();
+ * return;
+ * }
* ... access xpath as cbuf_get(xpath)
* cbuf_free(xpath)
* @endcode
- * @see api_path2xpath for string api-path argument
+ * @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
+ * @see api_path2xml For api-path to xml tree
*/
int
-api_path2xpath_cvv(yang_spec *yspec,
- cvec *cvv,
- int offset,
- cbuf *xpath)
+api_path2xpath(yang_spec *yspec,
+ cvec *cvv,
+ int offset,
+ cbuf *xpath)
{
int retval = -1;
int i;
cg_var *cv;
- char *name;
+ char *nodeid;
+ char *prefix = NULL;
+ char *name = NULL;
cvec *cvk = NULL; /* vector of index keys */
yang_stmt *y = NULL;
+ yang_stmt *ymod;
char *val;
char *v;
cg_var *cvi;
for (i=offset; i 0){
@@ -1666,6 +1882,7 @@ api_path2xpath_cvv(yang_spec *yspec,
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
/* Iterate over individual yang keys */
+
cprintf(xpath, "/%s", name);
v = val;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
@@ -1675,46 +1892,27 @@ api_path2xpath_cvv(yang_spec *yspec,
if (val)
free(val);
}
- else{
+ else
cprintf(xpath, "%s%s", (i==offset?"":"/"), name);
- }
- }
- retval = 0;
+ if (prefix){
+ free(prefix);
+ prefix = NULL;
+ }
+ if (name){
+ free(name);
+ name = NULL;
+ }
+ } /* for */
+ retval = 1; /* OK */
done:
+ if (prefix)
+ free(prefix);
+ if (name)
+ free(name);
return retval;
-}
-
-/*! Translate from restconf api-path to xml xpath
- * eg a/b=c -> a/[b=c]
- * @param[in] yspec Yang spec
- * @param[in] str API-path as string
- * @param[out] path The xpath as cbuf variable string, must be initializeed
- * @code
- * cbuf *xpath = cbuf_new();
- * if (api_path2xpath(yspec, "a/b=c", xpath)
- * err;
- * ... access xpath as cbuf_get(xpath)
- * cbuf_free(xpath)
- * @endcode
- */
-int
-api_path2xpath(yang_spec *yspec,
- char *api_path,
- cbuf *xpath)
-{
- int retval = -1;
- cvec *api_path_cvv = NULL;
-
- /* rest url eg /album=ricky/foo */
- if (str2cvec(api_path, '/', '=', &api_path_cvv) < 0)
- goto done;
- if (api_path2xpath_cvv(yspec, api_path_cvv, 0, xpath) < 0)
- goto done;
- retval = 0;
- done:
- if (api_path_cvv)
- cvec_free(api_path_cvv);
- return retval;
+ fail:
+ retval = 0; /* Validation failed */
+ goto done;
}
/*! Create xml tree from api-path as vector
@@ -1725,6 +1923,12 @@ api_path2xpath(yang_spec *yspec,
* @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[out] xpathp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp
+ * @retval 1 OK
+ * @retval 0 Invalid api_path or associated XML, clicon_err called
+ * @retval -1 Fatal error, clicon_err called
+ *
+ * @note both retval 0 and -1 set clicon_err, but the later is fatal
+ * @see api_path2xpath For api-path to xml xpath translation
* @see api_path2xml
*/
static int
@@ -1738,7 +1942,9 @@ api_path2xml_vec(char **vec,
{
int retval = -1;
int j;
- char *name;
+ char *nodeid;
+ char *name = NULL;
+ char *prefix = NULL;
char *restval = NULL;
char *restval_enc;
cxobj *xn = NULL; /* new */
@@ -1751,43 +1957,50 @@ api_path2xml_vec(char **vec,
int nvalvec;
cxobj *x = NULL;
yang_stmt *y = NULL;
- char *local;
+ yang_stmt *ymod;
+ char *namespace = NULL;
- if ((name = vec[0]) == NULL){
+ if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
if (xpathp)
*xpathp = x0;
if (ypathp)
*ypathp = y0;
- return 0;
- } /* E.g "x=1,2" -> name:x restval=1,2 */
+ goto ok;
+ } /* E.g "x=1,2" -> nodeid:x restval=1,2 */
/* restval is RFC 3896 encoded */
- if ((restval_enc = index(name, '=')) != NULL){
+ if ((restval_enc = index(nodeid, '=')) != NULL){
*restval_enc = '\0';
restval_enc++;
if (uri_percent_decode(restval_enc, &restval) < 0)
goto done;
}
- /* Split into prefix and localname, ignore prefix for now */
- if ((local = index(name, ':')) != NULL){
- *local = '\0';
- local++;
- name = local;
- }
- if (y0->yn_keyword == Y_SPEC){ /* top-node */
- y = yang_find_topnode((yang_spec*)y0, name, nodeclass);
- }
- else {
- y = (nodeclass==YC_SCHEMANODE)?yang_find_schemanode((yang_node*)y0, name):
- yang_find_datanode((yang_node*)y0, name);
- }
- if (y == NULL){
- clicon_err(OE_YANG, errno, "No yang node found: %s", name);
+ /* Split into prefix and localname */
+ if (nodeid_split(nodeid, &prefix, &name) < 0)
goto done;
+ if (y0->yn_keyword == Y_SPEC){ /* top-node */
+ if (prefix == NULL){
+ clicon_err(OE_XML, EINVAL, "api-path element '%s', expected prefix:name", nodeid);
+ goto fail;
+ }
+ if ((ymod = yang_find_module_by_name((yang_spec*)y0, prefix)) == NULL){
+ clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix);
+ goto fail;
+ }
+ namespace = yang_find_mynamespace(ymod);
+ y0 = (yang_node*)ymod;
+
+ }
+ y = (nodeclass==YC_SCHEMANODE)?
+ yang_find_schemanode((yang_node*)y0, name):
+ yang_find_datanode((yang_node*)y0, name);
+ if (y == NULL){
+ clicon_err(OE_YANG, EINVAL, "api-path name: '%s', no such yang element", name);
+ goto fail;
}
switch (y->ys_keyword){
case Y_LEAF_LIST:
if (0 && restval==NULL){
- clicon_err(OE_XML, 0, "malformed key, expected '='");
+ clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
goto done;
}
if ((x = xml_new(y->ys_argument, x0, y)) == NULL)
@@ -1807,21 +2020,21 @@ api_path2xml_vec(char **vec,
}
if (restval==NULL){
// XXX patch to allow for lists without restval to be backward compat
- // clicon_err(OE_XML, 0, "malformed key, expected '='");
+ // clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
// goto done;
}
else{
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
goto done;
if (nvalvec > cvec_len(cvk)){
- clicon_err(OE_XML, errno, "List %s key length mismatch", name);
- goto done;
+ clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
+ goto fail;
}
}
cvi = NULL;
/* create list object */
if ((x = xml_new(name, x0, y)) == NULL)
- goto done;
+ goto done;
xml_type_set(x, CX_ELMNT);
j = 0;
/* Create keys */
@@ -1840,31 +2053,47 @@ api_path2xml_vec(char **vec,
break;
default: /* eg Y_CONTAINER, Y_LEAF */
if ((x = xml_new(name, x0, y)) == NULL)
- goto done;
+ goto done;
xml_type_set(x, CX_ELMNT);
break;
}
- if (api_path2xml_vec(vec+1, nvec-1,
- x, (yang_node*)y,
- nodeclass,
- xpathp, ypathp) < 0)
+ if (x && namespace){
+ if (xmlns_set(x, NULL, namespace) < 0)
+ goto done;
+ }
+ if ((retval = api_path2xml_vec(vec+1, nvec-1,
+ x, (yang_node*)y,
+ nodeclass,
+ xpathp, ypathp)) < 1)
goto done;
- retval = 0;
+ ok:
+ retval = 1; /* OK */
done:
+ if (prefix)
+ free(prefix);
+ if (name)
+ free(name);
if (restval)
free(restval);
if (valvec)
free(valvec);
return retval;
+ fail:
+ retval = 0; /* invalid api-path or XML */
+ goto done;
}
/*! Create xml tree from api-path
- * @param[in] api_path API-path as defined in RFC 8040
+ * @param[in] api_path (Absolute) API-path as defined in RFC 8040
* @param[in] yspec Yang spec
* @param[in,out] xtop Incoming XML tree
* @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[out] xbotp Resulting xml tree (end of xpath)
* @param[out] ybotp Yang spec matching xbotp
+ * @retval 1 OK
+ * @retval 0 Invalid api_path or associated XML, clicon_err called
+ * @retval -1 Fatal error, clicon_err called
+ * @note both retval 0 and -1 set clicon_err, but the later is fatal
* @example
* api_path: /subif-entry=foo/subid
* xtop[in]
@@ -1873,7 +2102,8 @@ api_path2xml_vec(char **vec,
*
* xbotp:
* ybotp: Y_LEAF subid
- * @see api_path2xml_vec
+ * @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
+ * @see api_path2xpath For api-path to xml xpath translation
*/
int
api_path2xml(char *api_path,
@@ -1886,10 +2116,13 @@ api_path2xml(char *api_path,
int retval = -1;
char **vec = NULL;
int nvec;
+ cxobj *xroot;
+ clicon_debug(1, "%s", __FUNCTION__);
if (*api_path!='/'){
- clicon_log(LOG_WARNING, "Invalid key: %s (must start with '/')", api_path);
- goto ok;
+ clicon_err(OE_XML, EINVAL, "Invalid api-path: %s (must start with '/')",
+ api_path);
+ goto fail;
}
if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL)
goto done;
@@ -1897,19 +2130,70 @@ api_path2xml(char *api_path,
if (nvec > 1 && !strlen(vec[nvec-1]))
nvec--;
if (nvec < 1){
- clicon_err(OE_XML, 0, "Malformed key: %s", api_path);
- goto done;
+ clicon_err(OE_XML, EINVAL, "Malformed api-path: %s", api_path);
+ goto fail;
}
nvec--; /* NULL-terminated */
- if (api_path2xml_vec(vec+1, nvec,
- xtop, (yang_node*)yspec, nodeclass,
- xbotp, ybotp) < 0)
+ if ((retval = api_path2xml_vec(vec+1, nvec,
+ xtop, (yang_node*)yspec, nodeclass,
+ xbotp, ybotp)) < 1)
+ goto done;
+ xml_yang_root(*xbotp, &xroot);
+ if (xmlns_assign(xroot) < 0)
+ goto done;
+ // ok:
+ retval = 1;
+ done:
+ if (vec)
+ free(vec);
+ return retval;
+ fail:
+ retval = 0;
+ goto done;
+}
+
+/*! Check if the module tree x is in is assigned right XML namespace, assign if not
+ * @param[in] x XML node
+ *(0. You should probably find the XML root and apply this function to that.)
+ * 1. Check which namespace x should have (via yang). This is correct namespace.
+ * 2. Check which namespace x has via its XML tree
+ * 3. If equal, OK
+ * 4. Assign the correct namespace to the XML node
+ * Assign default namespace to x. Create an "xmlns"
+ * @code
+ * xml_yang_root(x, &xroot);
+ * xmlns_assign(xroot);
+ * @endcode
+ */
+int
+xmlns_assign(cxobj *x)
+{
+ int retval = -1;
+ yang_stmt *y;
+ char *ns_correct; /* correct uri */
+ char *ns_xml; /* may be null or incorrect */
+
+ if ((y = xml_spec(x)) == NULL){
+ clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec", xml_name(x));
+ goto done;
+ }
+ /* 1. Check which namespace x should have (via yang). This is correct namespace. */
+ if ((ns_correct = yang_find_mynamespace(y)) == NULL){
+ clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", y->ys_argument);
+ goto done;
+ }
+ /* 2. Check which namespace x has via its XML tree */
+ if (xml2ns(x, NULL, &ns_xml) < 0)
+ goto done;
+ /* 3. If equal, OK, 4. Else, find root of XML tree */
+ if (ns_xml && strcmp(ns_xml, ns_correct)==0)
+ goto ok;
+ /* 4. Assign the correct namespace */
+ if (xmlns_set(x, NULL, ns_correct) < 0)
goto done;
ok:
retval = 0;
done:
- if (vec)
- free(vec);
return retval;
}
@@ -1936,6 +2220,8 @@ xml_merge1(cxobj *x0,
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
+ cxobj *x0a; /* x0 xmlns attribute */
+ cxobj *x1a; /* x1 xmlns attribute */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cbuf *cbr = NULL; /* Reason buffer */
@@ -1992,7 +2278,7 @@ xml_merge1(cxobj *x0,
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
- if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
+ if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
goto done;
@@ -2000,6 +2286,17 @@ xml_merge1(cxobj *x0,
goto ok;
}
} /* else Y_CONTAINER */
+ assert(x0);
+ /* Copy xmlns attributes (if it does not already exist) */
+ if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != NULL)
+ if (xml_find_type(x0, NULL, "xmlns", CX_ATTR)==NULL){
+ if ((x0a = xml_dup(x1a)) == NULL)
+ goto done;
+ if (xml_addsub(x0, x0a) < 0)
+ goto done;
+ }
+
+
ok:
retval = 0;
done:
@@ -2030,20 +2327,31 @@ xml_merge(cxobj *x0,
cxobj *x0c; /* base child */
cxobj *x1c; /* mod child */
yang_stmt *yc;
+ yang_stmt *ymod;
cbuf *cbr = NULL; /* Reason buffer */
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
+ if ((ys_module_by_xml(yspec, x1c, &ymod)) < 0)
+ goto done;
+ if (ymod == NULL){
+ if (reason &&
+ (*reason = strdup("No namespace in XML tree found")) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ goto done;
+ }
+ goto ok;
+ }
/* Get yang spec of the child */
- if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
+ if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) == NULL){
if (reason){
if ((cbr = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
+ cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?)", xml_name(x1), x1cname);
if ((*reason = strdup(cbuf_get(cbr))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
@@ -2052,13 +2360,18 @@ xml_merge(cxobj *x0,
break;
}
/* See if there is a corresponding node in the base tree */
- if (match_base_child(x0, x1c, &x0c, yc) < 0)
+ if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
+ /* There is a case where x0c and x1c are choice nodes, if so,
+ * it is treated as a match, and x0c will remain
+ * If it is overwritten, then x0c should be removed here.
+ */
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
goto done;
if (*reason != NULL)
break;
}
+ ok:
retval = 0; /* OK */
done:
if (cbr)
@@ -2100,8 +2413,8 @@ yang_enum_int_value(cxobj *node,
goto done;
if ((ytype = yang_find((yang_node *)ys, Y_TYPE, NULL)) == NULL)
goto done;
- if (yang_type_resolve(ys, ytype, &yrestype,
- NULL, NULL, NULL, NULL, NULL) < 0)
+ if (yang_type_resolve(ys, ys, ytype, &yrestype,
+ NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
goto done;
diff --git a/lib/src/clixon_xml_parse.h b/lib/src/clixon_xml_parse.h
index 3122e8f0..35ee2dee 100644
--- a/lib/src/clixon_xml_parse.h
+++ b/lib/src/clixon_xml_parse.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l
index 39173634..c0f0a104 100644
--- a/lib/src/clixon_xml_parse.l
+++ b/lib/src/clixon_xml_parse.l
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -106,6 +106,7 @@ ncname {namestart}{namechar}*
[ \t] ;
\n { _YA->ya_linenum++; }
+\r
{ncname} { clixon_xml_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y
index 7072ed01..9e2eafaa 100644
--- a/lib/src/clixon_xml_parse.y
+++ b/lib/src/clixon_xml_parse.y
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -130,6 +130,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
* @param[in] ya XML parser yacc handler struct
* @param[in] prefix Prefix, namespace, or NULL
* @param[in] localpart Name
+ * @note the call to xml_child_spec() may not have xmlns attribute read yet XXX
*/
static int
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
@@ -138,12 +139,14 @@ xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
int retval = -1;
cxobj *x;
yang_stmt *y = NULL; /* yang node */
- cxobj *xp; /* xml parent */
+ cxobj *xp; /* xml parent */
xp = ya->ya_xparent;
- if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
+ if ((x = xml_new(name, xp, NULL)) == NULL)
goto done;
- if ((x = xml_new(name, xp, y)) == NULL)
+ if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
+ goto done;
+ if (y && xml_spec_set(x, y) < 0)
goto done;
ya->ya_xelement = x;
retval = 0;
@@ -168,11 +171,13 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
cxobj *xp; /* xml parent */
xp = ya->ya_xparent;
- if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
+ if ((x = xml_new(name, xp, NULL)) == NULL)
goto done;
- if ((x = xml_new(name, xp, y)) == NULL)
+ if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
goto done;
- if (xml_namespace_set(x, prefix) < 0)
+ if (y && xml_spec_set(x, y) < 0)
+ goto done;
+ if (xml_prefix_set(x, prefix) < 0)
goto done;
ya->ya_xelement = x;
retval = 0;
@@ -223,9 +228,9 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
xml_name(x), name);
goto done;
}
- if (xml_namespace(x)!=NULL){
+ if (xml_prefix(x)!=NULL){
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s",
- xml_namespace(x), xml_name(x), name);
+ xml_prefix(x), xml_name(x), name);
goto done;
}
/* Strip pretty-print. Ad-hoc algorithm
@@ -263,16 +268,16 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
if (strcmp(xml_name(x), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
- xml_namespace(x),
+ xml_prefix(x),
xml_name(x),
namespace,
name);
goto done;
}
- if (xml_namespace(x)==NULL ||
- strcmp(xml_namespace(x), namespace)){
+ if (xml_prefix(x)==NULL ||
+ strcmp(xml_prefix(x), namespace)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
- xml_namespace(x),
+ xml_prefix(x),
xml_name(x),
namespace,
name);
@@ -313,19 +318,15 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
char *attval)
{
int retval = -1;
- cxobj *xa;
+ cxobj *xa = NULL;
-#ifdef ENABLE_XMLNS
- if (prefix && strcmp(prefix,"xmlns")==0)
- fprintf(stderr, "PrefixedAttName NCNAME:%s = %s\n", name, attval);
- if (prefix==NULL && strcmp(name,"xmlns")==0)
- fprintf(stderr, "DefaultAttName = %s\n", attval);
-#endif /* notyet */
- if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
- goto done;
- xml_type_set(xa, CX_ATTR);
- if (prefix && xml_namespace_set(xa, prefix) < 0)
- goto done;
+ if ((xa = xml_find_type(ya->ya_xelement, prefix, name, CX_ATTR)) == NULL){
+ if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
+ goto done;
+ xml_type_set(xa, CX_ATTR);
+ if (prefix && xml_prefix_set(xa, prefix) < 0)
+ goto done;
+ }
if (xml_value_set(xa, attval) < 0)
goto done;
retval = 0;
diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c
index 7e68d2b8..fb5e412b 100644
--- a/lib/src/clixon_xml_sort.c
+++ b/lib/src/clixon_xml_sort.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -61,29 +61,28 @@
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
+#include "clixon_options.h"
+#include "clixon_xml_map.h"
#include "clixon_xml_sort.h"
/*
* Variables
*/
-/* Sort and binary search of XML children
- * Experimental
- */
-int xml_child_sort = 1;
-
/*! Given a child name and an XML object, return yang stmt of child
* If no xml parent, find root yang stmt matching name
* @param[in] x Child
* @param[in] xp XML parent, can be NULL.
* @param[in] yspec Yang specification (top level)
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
+ * @retval 0 OK
+ * @retval -1 Error
* @note special rule for rpc, ie ,look for top "foo" node.
* @note works for import prefix, but not work for generic XML parsing where
* xmlns and xmlns:ns are used.
*/
int
-xml_child_spec(char *name,
+xml_child_spec(cxobj *x,
cxobj *xp,
yang_spec *yspec,
yang_stmt **yresult)
@@ -93,8 +92,9 @@ xml_child_spec(char *name,
yang_stmt *yparent; /* parent yang */
yang_stmt *ymod = NULL;
yang_stmt *yi;
- int i;
+ char *name;
+ name = xml_name(x);
if (xp && (yparent = xml_spec(xp)) != NULL){
if (yparent->ys_keyword == Y_RPC){
if ((yi = yang_find((yang_node*)yparent, Y_INPUT, NULL)) != NULL)
@@ -108,16 +108,16 @@ xml_child_spec(char *name,
goto done;
if (ymod != NULL)
y = yang_find_schemanode((yang_node*)ymod, name);
- if (y == NULL && _CLICON_XML_NS_ITERATE){
- for (i=0; iyp_len; i++){
- ymod = yspec->yp_stmt[i];
- if ((y = yang_find_schemanode((yang_node*)ymod, name)) != NULL)
- break;
- }
+ if (y == NULL && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(x, yspec, &y) < 0) /* schemanode */
+ goto done;
}
}
else
y = NULL;
+ /* kludge rpc -> input */
+ if (y && y->ys_keyword == Y_RPC && yang_find((yang_node*)y, Y_INPUT, NULL))
+ y = yang_find((yang_node*)y, Y_INPUT, NULL);
*yresult = y;
retval = 0;
done:
@@ -557,22 +557,18 @@ xml_sort_verify(cxobj *x0,
}
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
- * param[in] x0 Base tree node
- * param[in] x1c Modification tree child
- * param[in] yc Yang spec of tree child
- * param[out] x0cp Matching base tree child (if any)
- * @note XXX: room for optimization? on 1K calls we have 1M body calls and
- 500K xml_child_each/cvec_each calls.
- The outer loop is large for large lists
- The inner loop is small
- Major time in xml_find_body()
- Can one do a binary search in the x0 list?
-*/
+ * @param[in] x0 Base tree node
+ * @param[in] x1c Modification tree child
+ * @param[in] yc Yang spec of tree child
+ * @param[out] x0cp Matching base tree child (if any)
+ * @retval 0 OK
+ * @retval -1 Error
+ */
int
-match_base_child(cxobj *x0,
- cxobj *x1c,
- cxobj **x0cp,
- yang_stmt *yc)
+match_base_child(cxobj *x0,
+ cxobj *x1c,
+ yang_stmt *yc,
+ cxobj **x0cp)
{
int retval = -1;
cvec *cvk = NULL; /* vector of index keys */
@@ -584,8 +580,29 @@ match_base_child(cxobj *x0,
char **keyvec = NULL;
int i;
int yorder;
+ cxobj *x0c = NULL;
+ yang_stmt *y0c;
+ yang_node *y0p;
+ yang_node *yp; /* yang parent */
- *x0cp = NULL; /* return value */
+ *x0cp = NULL; /* init return value */
+#if 1
+ /* Special case is if yc parent (yp) is choice/case
+ * then find x0 child with same yc even though it does not match lexically
+ * However this will give another y0c != yc
+ */
+ if ((yp = yang_choice(yc)) != NULL){
+ x0c = NULL;
+ while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
+ if ((y0c = xml_spec(x0c)) != NULL &&
+ (y0p = yang_choice(y0c)) != NULL &&
+ y0p == yp)
+ break; /* x0c will have a value */
+ }
+ *x0cp = x0c;
+ goto ok; /* What to do if not found? */
+ }
+#endif
switch (yc->ys_keyword){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
@@ -629,23 +646,14 @@ match_base_child(cxobj *x0,
break;
}
/* Get match. Sorting mode(optimized) or not?*/
- if (xml_child_sort==0)
- *x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
- else{
- if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
- yorder = yang_order(yc);
- *x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
- }
- else{
-#if 1 /* This is just a warning, but a catcher for when xml tree is not
- populated with yang spec. If you see this, a previous invacation of,
- for example xml_spec_populate() may be missing
- */
- clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
-#endif
- *x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
- }
+ if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
+ yorder = yang_order(yc);
+ x0c = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
}
+ else{
+ x0c = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
+ }
+ *x0cp = x0c;
ok:
retval = 0;
done:
diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c
index 2fec113a..b814ffc4 100644
--- a/lib/src/clixon_xpath.c
+++ b/lib/src/clixon_xpath.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_xpath_ctx.c b/lib/src/clixon_xpath_ctx.c
index 721df45c..a50ff5c2 100644
--- a/lib/src/clixon_xpath_ctx.c
+++ b/lib/src/clixon_xpath_ctx.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_xpath_parse.h b/lib/src/clixon_xpath_parse.h
index 1affce3c..6ba57672 100644
--- a/lib/src/clixon_xpath_parse.h
+++ b/lib/src/clixon_xpath_parse.h
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l
index 5ac54644..a886dd70 100644
--- a/lib/src/clixon_xpath_parse.l
+++ b/lib/src/clixon_xpath_parse.l
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y
index 4e055ca7..db672129 100644
--- a/lib/src/clixon_xpath_parse.y
+++ b/lib/src/clixon_xpath_parse.y
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -95,7 +95,7 @@
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;}
-/* add _yy to error paramaters */
+/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 8b9af6d0..dc55bfee 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -34,6 +34,16 @@
* Yang functions
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
+ *
+ * yang_spec_parse_module
+ * \
+ * yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module
+ * \ / v
+ * yang_spec_load_dir ------------------------------------> yang_parse_filename
+ * v
+ * yang_parse_file
+ * v
+ * yang_parse_str
*/
#ifdef HAVE_CONFIG_H
@@ -469,94 +479,6 @@ yang_match(yang_node *yn,
}
return match;
}
-#ifdef NOTYET
-/*! Prototype more generic than yang_find_datanode and yang_find_schemanode
- */
-yang_stmt *
-yang_find_class(yang_node *yn,
- char *argument,
- yang_class class)
-{
- yang_stmt *ys = NULL;
- yang_stmt *yc = NULL;
- yang_stmt *ysmatch = NULL;
- int i, j;
- int ok;
-
- for (i=0; iyn_len; i++){
- ys = yn->yn_stmt[i];
- switch(class){
- case YC_NONE:
- ok = 1;
- break;
- case YC_DATANODE:
- ok = yang_datanode(ys);
- break;
- case YC_DATADEFINITION:
- ok = yang_datadefinition(ys);
- break;
- case YC_SCHEMANODE:
- ok = yang_schemanode(ys);
- break;
- }
- if (!ok)
- continue;
- switch(class){
- case YC_NONE:
- if (argument == NULL)
- ysmatch = ys;
- else
- if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
- ysmatch = ys;
- if (ysmatch)
- goto match;
- break;
- case YC_DATANODE:
- case YC_DATADEFINITION:
- if (argument == NULL)
- ysmatch = ys;
- else
- if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
- ysmatch = ys;
- if (ysmatch)
- goto match;
- break;
- case YC_SCHEMANODE:
- if (ys->ys_keyword == Y_CHOICE){ /* Look for its children */
- for (j=0; jys_len; j++){
- yc = ys->ys_stmt[j];
- if (yc->ys_keyword == Y_CASE) /* Look for its children */
- ysmatch = yang_find_class((yang_node*)yc, argument, class);
- else{
- if (yang_schemanode(yc)){
- if (argument == NULL)
- ysmatch = yc;
- else
- if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
- ysmatch = yc;
- }
- }
- if (ysmatch)
- goto match;
- }
- } /* Y_CHOICE */
- else{
- if (argument == NULL)
- ysmatch = ys;
- else
- if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
- ysmatch = ys;
- if (ysmatch)
- goto match;
-
- }
- break;
- } /* switch */
- } /* for */
- match:
- return ysmatch;
-}
-#endif /* NOTYET */
/*! Find child data node with matching argument (container, leaf, etc)
*
@@ -658,69 +580,6 @@ yang_find_schemanode(yang_node *yn,
return ysmatch;
}
-/*! Find first matching data node in all modules in a yang spec (prefixes)
- *
- * @param[in] ysp Yang specification
- * @param[in] nodeid Name of node. If NULL match first
- * @param[in] class See yang_class for class of yang nodes
- * A yang specification has modules as children which in turn can have
- * syntax-nodes as children. This function goes through all the modules to
- * look for nodes. Note that if a child to a module is a choice,
- * the search is made recursively made to the choice's children.
- * @note works for import prefix, but not work for generic XML parsing where
- * xmlns and xmlns:ns are used.
- * @see yang_find_top_ns
- */
-yang_stmt *
-yang_find_topnode(yang_spec *ysp,
- char *nodeid,
- yang_class class)
-{
- yang_stmt *ymod = NULL; /* module */
- yang_stmt *yres = NULL; /* result */
- char *prefix = NULL;
- char *id = NULL;
- int i;
-
- if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
- goto done;
- if (prefix){
- if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL ||
- (ymod = yang_find((yang_node*)ysp, Y_SUBMODULE, prefix)) != NULL){
- if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
- goto ok;
- goto done;
- }
- }
- else /* No prefix given - loop through and find first */
- for (i=0; iyp_len; i++){
- ymod = ysp->yp_stmt[i];
- switch (class){
- case YC_NONE:
- if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
- goto ok;
- break;
- case YC_DATANODE:
- if ((yres = yang_find_datanode((yang_node*)ymod, id)) != NULL)
- goto ok;
- break;
- case YC_SCHEMANODE:
- if ((yres = yang_find_schemanode((yang_node*)ymod, id)) != NULL)
- goto ok;
- break;
- case YC_DATADEFINITION:
- break; /* nyi */
- }
- }
- ok:
- done:
- if (prefix)
- free(prefix);
- if (id)
- free(id);
- return yres;
-}
-
/*! Given a yang statement, find the prefix associated to this module
* @param[in] ys Yang statement in module tree (or module itself)
* @retval NULL Not found
@@ -775,6 +634,27 @@ yang_find_mynamespace(yang_stmt *ys)
return namespace;
}
+/*! If a given yang stmt has a choice/case as parent, return the choice statement
+ */
+yang_node *
+yang_choice(yang_stmt *y)
+{
+ yang_node *yp;
+
+ if ((yp = y->ys_parent) != NULL){
+ switch (yp->yn_keyword){
+ case Y_CHOICE:
+ return yp;
+ break;
+ case Y_CASE:
+ return yp->yn_parent;
+ break;
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
* @retval 0 not found
@@ -862,7 +742,7 @@ ys_module_by_xml(yang_spec *ysp,
if (ymodp)
*ymodp = NULL;
- prefix = xml_namespace(xt);
+ prefix = xml_prefix(xt);
if (prefix){
/* Get namespace for prefix */
if (xml2ns(xt, prefix, &namespace) < 0)
@@ -972,60 +852,15 @@ yarg_prefix(yang_stmt *ys)
return prefix;
}
-/*! Split yang node identifier into prefix and identifer.
- * @param[in] node-id
- * @param[out] prefix Malloced string. May be NULL.
- * @param[out] id Malloced identifier.
- * @retval 0 OK
- * @retval -1 Error
- * @code
- * char *prefix = NULL;
- * char *id = NULL;
- * if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
- * goto done;
- * if (prefix)
- * free(prefix);
- * if (id)
- * free(id);
- * @note caller need to free id and prefix after use
- */
-int
-yang_nodeid_split(char *nodeid,
- char **prefix,
- char **id)
-{
- int retval = -1;
- char *str;
-
- if ((str = strchr(nodeid, ':')) == NULL){
- if ((*id = strdup(nodeid)) == NULL){
- clicon_err(OE_YANG, errno, "strdup");
- goto done;
- }
- }
- else{
- if ((*prefix = strdup(nodeid)) == NULL){
- clicon_err(OE_YANG, errno, "strdup");
- goto done;
- }
- (*prefix)[str-nodeid] = '\0';
- str++;
- if ((*id = strdup(str)) == NULL){
- clicon_err(OE_YANG, errno, "strdup");
- goto done;
- }
- }
- retval = 0;
- done:
- return retval;
-}
-
-/*! Given a yang statement and a prefix, return yang module to that prefix
+/*! Given a yang statement and a prefix, return yang module to that relative prefix
* Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement
* @param[in] prefix prefix
* @retval ymod Yang module statement if found
* @retval NULL not found
+ * @node Prefixes are relative to the module they are defined
+ * @see yang_find_module_by_name
+ * @see yang_find_module_by_namespace
*/
yang_stmt *
yang_find_module_by_prefix(yang_stmt *ys,
@@ -1081,12 +916,14 @@ yang_find_module_by_prefix(yang_stmt *ys,
return ymod;
}
-/*! Given a yang statement and a namespace, return yang module
+/*! Given a yang spec and a namespace, return yang module
*
* @param[in] yspec A yang specification
* @param[in] namespace namespace
* @retval ymod Yang module statement if found
* @retval NULL not found
+ * @see yang_find_module_by_name
+ * @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_namespace(yang_spec *yspec,
@@ -1104,6 +941,28 @@ yang_find_module_by_namespace(yang_spec *yspec,
return ymod;
}
+/*! Given a yang spec and a module name, return yang module
+ *
+ * @param[in] yspec A yang specification
+ * @param[in] name Name of module
+ * @retval ymod Yang module statement if found
+ * @retval NULL not found
+ * @see yang_find_module_by_namespace
+ * @see yang_find_module_by_prefix module-specific prefix
+ */
+yang_stmt *
+yang_find_module_by_name(yang_spec *yspec,
+ char *name)
+{
+ yang_stmt *ymod = NULL;
+
+ while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL)
+ if ((ymod->ys_keyword == Y_MODULE || ymod->ys_keyword == Y_SUBMODULE) &&
+ strcmp(ymod->ys_argument, name)==0)
+ return ymod;
+ return NULL;
+}
+
/*! string is quoted if it contains space or tab, needs double '' */
static int inline
quotedstring(char *s)
@@ -1219,10 +1078,11 @@ ys_populate_leaf(yang_stmt *ys,
yparent = ys->ys_parent; /* Find parent: list/container */
/* 1. Find type specification and set cv type accordingly */
- if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, NULL, &fraction_digits) < 0)
+ if (yang_type_get(ys, &type, &yrestype, &options, NULL, NULL, &fraction_digits)
+ < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
- if (clicon_type2cv(type, restype, &cvtype) < 0) /* This handles non-resolved also */
+ if (clicon_type2cv(type, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done;
/* 2. Create the CV using cvtype and name it */
if ((cv = cv_new(cvtype)) == NULL){
@@ -1284,12 +1144,62 @@ ys_populate_list(yang_stmt *ys,
return 0;
}
-/*! Populate range and length statements
+/*! Set range or length boundary for built-in yang types
+ * Help functions to range and length statements
+ */
+static int
+bound_add(yang_stmt *ys,
+ enum cv_type cvtype,
+ char *name,
+ char *val,
+ int options,
+ uint8_t fraction_digits
+ )
+{
+ int retval = -1;
+ cg_var *cv;
+ char *reason = NULL;
+ int ret = 1;
+
+ if ((cv = cvec_add(ys->ys_cvec, cvtype)) == NULL){
+ clicon_err(OE_YANG, errno, "cvec_add");
+ goto done;
+ }
+ if (cv_name_set(cv, name) == NULL){
+ clicon_err(OE_YANG, errno, "cv_name_set(%s)", name);
+ goto done;
+ }
+ if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64)
+ cv_dec64_n_set(cv, fraction_digits);
+ if (strcmp(val, "min") == 0)
+ cv_min_set(cv);
+ else if (strcmp(val, "max") == 0)
+ cv_max_set(cv);
+ else if ((ret = cv_parse1(val, cv, &reason)) < 0){
+ clicon_err(OE_YANG, errno, "cv_parse1");
+ goto done;
+ }
+ if (ret == 0){ /* parsing failed */
+ clicon_err(OE_YANG, errno, "range statement %s: %s", val, reason);
+ free(reason);
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Populate string built-in range statement
*
* Create cvec variables "range_min" and "range_max". Assume parent is type.
- * Actually: min..max [| min..max]*
- * where min,max is integer or keywords 'min' or 'max.
- * We only allow one range, ie not 1..2|4..5
+ * Actually: bound[..bound] (| bound[..bound])*
+ * where bound is integer, decimal or keywords 'min' or 'max.
+ * RFC 7950 9.2.4:
+ * A range consists of an explicit value, or a lower-inclusive bound,
+ * two consecutive dots "..", and an upper-inclusive bound. Multiple
+ * values or ranges can be given, separated by "|". If multiple values
+ * or ranges are given, they all MUST be disjoint and MUST be in
+ * ascending order
*/
static int
ys_populate_range(yang_stmt *ys,
@@ -1303,93 +1213,102 @@ ys_populate_range(yang_stmt *ys,
int options = 0x0;
uint8_t fraction_digits;
enum cv_type cvtype = CGV_ERR;
- char *minstr = NULL;
- char *maxstr;
- cg_var *cv;
- char *reason = NULL;
- int cvret;
+ char **vec = NULL;
+ char *v;
+ char *v2;
+ int nvec;
+ int i;
yparent = ys->ys_parent; /* Find parent: type */
if (yparent->yn_keyword != Y_TYPE){
clicon_err(OE_YANG, 0, "parent should be type");
goto done;
}
- if (yang_type_resolve(ys, (yang_stmt*)yparent, &yrestype,
- &options, NULL, NULL, NULL, &fraction_digits) < 0)
+ if (yang_type_resolve(ys, ys, (yang_stmt*)yparent, &yrestype,
+ &options, NULL, NULL, &fraction_digits) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
origtype = yarg_id((yang_stmt*)yparent);
/* This handles non-resolved also */
- if (clicon_type2cv(origtype, restype, &cvtype) < 0)
+ if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
- /* special case for strings, where limit is length, not a string */
- if (cvtype == CGV_STRING)
- cvtype = CGV_UINT64;
- if ((minstr = strdup(ys->ys_argument)) == NULL){
- clicon_err(OE_YANG, errno, "strdup");
+ if ((vec = clicon_strsep(ys->ys_argument, "|", &nvec)) == NULL)
goto done;
- }
- if ((maxstr = strstr(minstr, "..")) != NULL){
- if (strlen(maxstr) < 2){
- clicon_err(OE_YANG, 0, "range statement: %s not on the form: ..",
- ys->ys_argument);
- goto done;
- }
- minstr[maxstr-minstr] = '\0';
- maxstr += 2;
- /* minstr and maxstr need trimming */
- if (isblank(minstr[strlen(minstr)-1]))
- minstr[strlen(minstr)-1] = '\0';
- if (isblank(maxstr[0]))
- maxstr++;
- if ((cv = cvec_add(ys->ys_cvec, cvtype)) == NULL){
- clicon_err(OE_YANG, errno, "cvec_add");
- goto done;
+ for (i=0; iys_cvec, cvtype)) == NULL){
- clicon_err(OE_YANG, errno, "cvec_add");
- goto done;
- }
- if (cv_name_set(cv, "range_max") == NULL){
- clicon_err(OE_YANG, errno, "cv_name_set");
- goto done;
- }
- if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64)
- cv_dec64_n_set(cv, fraction_digits);
- if ((cvret = cv_parse1(maxstr, cv, &reason)) < 0){
- clicon_err(OE_YANG, errno, "cv_parse1");
- goto done;
- }
- if (cvret == 0){ /* parsing failed */
- clicon_err(OE_YANG, errno, "range statement, max: %s", reason);
- free(reason);
- goto done;
- }
+ if (v2)
+ if (bound_add(ys, cvtype, "range_max",v2,
+ options, fraction_digits) < 0)
+ goto done;
}
retval = 0;
done:
- if (minstr)
- free(minstr);
+ if (vec)
+ free(vec);
+ return retval;
+}
+
+/*! Populate integer built-in length statement
+ *
+ * Create cvec variables "range_min" and "range_max". Assume parent is type.
+ * Actually: len[..len] (| len[..len])*
+ * len is unsigned integer or keywords 'min' or 'max.
+ * RFC 7950 9.4.4
+ * A length range consists of an explicit value, or a lower bound, two
+ * consecutive dots "..", and an upper bound. Multiple values or ranges
+ * can be given, separated by "|". Length-restricting values MUST NOT
+ * be negative. If multiple values or ranges are given, they all MUST
+ * be disjoint and MUST be in ascending order.
+ */
+static int
+ys_populate_length(yang_stmt *ys,
+ void *arg)
+{
+ int retval = -1;
+ yang_node *yparent; /* type */
+ enum cv_type cvtype = CGV_ERR;
+ char **vec = NULL;
+ char *v;
+ int nvec;
+ int i;
+ char *v2;
+
+ yparent = ys->ys_parent; /* Find parent: type */
+ if (yparent->yn_keyword != Y_TYPE){
+ clicon_err(OE_YANG, 0, "parent should be type");
+ goto done;
+ }
+ cvtype = CGV_UINT64;
+ if ((vec = clicon_strsep(ys->ys_argument, "|", &nvec)) == NULL)
+ goto done;
+ for (i=0; iys_cv = cv;
ok:
retval = 0;
@@ -1635,10 +1555,13 @@ ys_populate(yang_stmt *ys,
goto done;
break;
case Y_RANGE:
- case Y_LENGTH:
if (ys_populate_range(ys, NULL) < 0)
goto done;
break;
+ case Y_LENGTH:
+ if (ys_populate_length(ys, NULL) < 0)
+ goto done;
+ break;
case Y_MANDATORY: /* call yang_mandatory() to check if set */
case Y_CONFIG:
if (ys_parse(ys, CGV_BOOL) == NULL)
@@ -1936,7 +1859,7 @@ yang_parse_str(char *str,
}
/*! Parse yang spec from an open file descriptor
- * @param[in] fd File descriptor containing the YANG file as ASCII characters
+ * @param[in] fd File descriptor containing the YANG file as ASCII characters
* @param[in] name For debug, eg filename
* @param[in] ysp Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
@@ -2083,7 +2006,7 @@ yang_parse_filename(const char *filename,
int fd = -1;
struct stat st;
- // clicon_debug(1, "%s %s", __FUNCTION__, filename);
+ clicon_debug(1, "%s %s", __FUNCTION__, filename);
if (stat(filename, &st) < 0){
clicon_err(OE_YANG, errno, "%s not found", filename);
goto done;
@@ -2173,7 +2096,7 @@ yang_parse_recurse(clicon_handle h,
subrevision = NULL;
/* if already loaded, ignore, else parse the file */
if (yang_find((yang_node*)ysp,
- keyw=Y_IMPORT?Y_MODULE:Y_SUBMODULE,
+ keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
submodule) == NULL){
/* recursive call */
if ((subymod = yang_parse_module(h, submodule, subrevision, ysp)) == NULL)
@@ -2258,7 +2181,7 @@ yang_features(clicon_handle h,
while (iys_len){ /* Note, children may be removed */
ys = yt->ys_stmt[i];
if (ys->ys_keyword == Y_IF_FEATURE){
- if (yang_nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
+ if (nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
goto done;
/* Specifically need to handle? strcmp(prefix, myprefix)) */
if (prefix == NULL)
@@ -2337,7 +2260,9 @@ yang_merge_submodules(clicon_handle h,
}
modname = yb->ys_argument;
if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, modname)) == NULL){
- clicon_err(OE_YANG, ENOENT, "Module %s which submodule %s belongs to is not found", modname, ysubm->ys_argument);
+ clicon_err(OE_YANG, ENOENT, "Submodule %s is loaded before/without its main module %s (you need to load the submodule together with or after the main module)",
+ ysubm->ys_argument,
+ modname);
goto done;
}
/* Move sub-module statements to modules
@@ -2392,64 +2317,29 @@ yang_merge_submodules(clicon_handle h,
* yang_parse_str # Set up yacc parser and call it given a string
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
-int
-yang_parse(clicon_handle h,
- const char *filename,
- const char *module,
- const char *revision,
- yang_spec *yspec)
+static int
+yang_parse_post(clicon_handle h,
+ yang_spec *yspec,
+ int modnr)
{
- int retval = -1;
- yang_stmt *ymod = NULL; /* Top-level yang (sub)module */
- int i;
- int modnr; /* Existing number of modules */
- char *base = NULL;;
-
- /* Apply steps 2.. on new modules, ie ones after modnr. */
- modnr = yspec->yp_len;
- if (filename){
- /* Find module, and do not load file if module already exists */
- if (basename(filename) == NULL){
- clicon_err(OE_YANG, errno, "No basename");
- goto done;
- }
- if ((base = strdup(basename(filename))) == NULL){
- clicon_err(OE_YANG, errno, "strdup");
- goto done;
- }
- if (index(base, '@') != NULL)
- *index(base, '@') = '\0';
- if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL)
- goto ok;
- if ((ymod = yang_parse_filename(filename, yspec)) == NULL)
- goto done;
- }
- else {
- /* Do not load module if it already exists */
- if (yang_find((yang_node*)yspec, Y_MODULE, module) != NULL)
- goto ok;
- if ((ymod = yang_parse_module(h, module, revision, yspec)) == NULL)
- goto done;
- }
-
+ int retval = -1;
+ int i;
+
/* 1: Parse from text to yang parse-tree. */
/* Iterate through modules */
- if (yang_parse_recurse(h, ymod, yspec) < 0)
- goto done;
+ for (i=modnr; iyp_len; i++)
+ if (yang_parse_recurse(h, yspec->yp_stmt[i], yspec) < 0)
+ goto done;
/* 2. Check cardinality maybe this should be done after grouping/augment */
- for (i=modnr; iyp_len; i++) /* XXX */
+ for (i=modnr; iyp_len; i++)
if (yang_cardinality(h, yspec->yp_stmt[i], yspec->yp_stmt[i]->ys_argument) < 0)
goto done;
/* 3: Merge sub-modules with modules - after this step, no submodules exist
* In the merge, remove submodule headers
*/
- for (i=modnr; iyp_len; i++){
- if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE)
- continue;
- }
- i = 0;
+ i = modnr;
while (iyp_len){
int j;
if (yspec->yp_stmt[i]->ys_keyword != Y_SUBMODULE){
@@ -2504,7 +2394,48 @@ yang_parse(clicon_handle h,
for (i=modnr; iyp_len; i++)
if (yang_apply((yang_node*)yspec->yp_stmt[i], -1, ys_schemanode_check, NULL) < 0)
goto done;
- /* Return main module parsed in step 1 */
+ retval = 0;
+ done:
+ return retval;
+}
+
+/*! Parse yang specification and its dependencies recursively given module
+ * @param[in] h clicon handle
+ * @param[in] module Module name, or absolute filename (including dir)
+ * @param[in] dir Directory where to look for modules and sub-modules
+ * @param[in] revision Revision, or NULL
+ * @param[in,out] yspec Modules parse are added to this yangspec
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see yang_spec_parse_file
+ */
+int
+yang_spec_parse_module(clicon_handle h,
+ const char *module,
+ const char *revision,
+ yang_spec *yspec)
+{
+ int retval = -1;
+ int modnr; /* Existing number of modules */
+ char *base = NULL;;
+
+ if (yspec == NULL){
+ clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
+ goto done;
+ }
+ if (module == NULL){
+ clicon_err(OE_YANG, EINVAL, "yang module not set");
+ goto done;
+ }
+ /* Apply steps 2.. on new modules, ie ones after modnr. */
+ modnr = yspec->yp_len;
+ /* Do not load module if it already exists */
+ if (yang_find((yang_node*)yspec, Y_MODULE, module) != NULL)
+ goto ok;
+ if (yang_parse_module(h, module, revision, yspec) == NULL)
+ goto done;
+ if (yang_parse_post(h, yspec, modnr) < 0)
+ goto done;
ok:
retval = 0;
done:
@@ -2513,6 +2444,132 @@ yang_parse(clicon_handle h,
return retval;
}
+/*! Parse yang specification and its dependencies recursively given filename
+ * @param[in] h clicon handle
+ * @param[in] filename Actual filename (including dir and revision)
+ * @param[in] dir Directory for sub-modules
+ * @param[in,out] yspec Modules parse are added to this yangspec
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see yang_spec_parse_module for yang dir,module,revision instead of
+ * actual filename
+ * @see yang_spec_load_dir For loading all files in a directory
+ */
+int
+yang_spec_parse_file(clicon_handle h,
+ const char *filename,
+ yang_spec *yspec)
+{
+ int retval = -1;
+ int modnr; /* Existing number of modules */
+ char *base = NULL;;
+
+ /* Apply steps 2.. on new modules, ie ones after modnr. */
+ modnr = yspec->yp_len;
+ /* Find module, and do not load file if module already exists */
+ if (basename(filename) == NULL){
+ clicon_err(OE_YANG, errno, "No basename");
+ goto done;
+ }
+ if ((base = strdup(basename(filename))) == NULL){
+ clicon_err(OE_YANG, errno, "strdup");
+ goto done;
+ }
+ if (index(base, '@') != NULL)
+ *index(base, '@') = '\0';
+ if (yang_find((yang_node*)yspec, Y_MODULE, base) != NULL)
+ goto ok;
+ if (yang_parse_filename(filename, yspec) == NULL)
+ goto done;
+ if (yang_parse_post(h, yspec, modnr) < 0)
+ goto done;
+ ok:
+ retval = 0;
+ done:
+ if (base)
+ free(base);
+ return retval;
+}
+
+/*! Load all yang modules in directory
+ * @param[in] h Clicon handle
+ * @param[in] dir Load all yang modules in this directory
+ * @param[in,out] yspec Modules parse are added to this yangspec
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see yang_spec_parse_file
+ */
+int
+yang_spec_load_dir(clicon_handle h,
+ char *dir,
+ yang_spec *yspec)
+{
+ int retval = -1;
+ int ndp;
+ struct dirent *dp = NULL;
+ int i;
+ char filename[MAXPATHLEN];
+ char *base = NULL; /* filename without dir */
+ char *b;
+ int j;
+ int modnr;
+
+ /* Get plugin objects names from plugin directory */
+ if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
+ goto done;
+ if (ndp == 0)
+ clicon_log(LOG_WARNING, "%s: No yang files found in %s",
+ __FUNCTION__, dir);
+ /* Apply post steps on new modules, ie ones after modnr. */
+ modnr = yspec->yp_len;
+ /* Load all yang files in dir */
+ for (i = 0; i < ndp; i++) {
+ /* base = module name [+ @rev ] + .yang */
+ if (base)
+ free(base);
+ if ((base = strdup(dp[i].d_name)) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ goto done;
+ }
+ *rindex(base, '.') = '\0'; /* strip postfix .yang */
+ /* base = module name [+ @rev]
+ * if it hasnt @rev then prefer it (dont check other files w @rev)
+ * Otherwise see if there is a newer
+ */
+ if ((b = index(base, '@')) != NULL){
+ *b = '\0';
+ /* base = module name */
+
+ /* Entries are sorted, see if later entry exists (include @), if so
+ * skip this one and take last.
+ * Assume file without @ is last
+ */
+ for (j = (i+1); j < ndp; j++)
+ if (strncmp(base, dp[j].d_name, strlen(base)) == 0)
+ break;
+ if (jys_argument, prefix) != 0){
- clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id);
- goto done;
- }
- }
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0)
goto done;
ok: /* yres may not be set */
@@ -2867,26 +2908,62 @@ ys_parse_sub(yang_stmt *ys,
* Note: one can cache this value in ys_cvec instead of functionally evaluating it.
* @retval 1 yang statement is leaf and it has a mandatory sub-stmt with value true
* @retval 0 The negation of conditions for return value 1.
+ * @see RFC7950 Sec 3:
+ * o mandatory node: A mandatory node is one of:
+ * 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
+ * statement with the value "true".
+ * 2) A list or leaf-list node with a "min-elements" statement with a
+ * value greater than zero.
+ * 3) A container node without a "presence" statement and that has at
+ * least one mandatory node as a child.
*/
int
yang_mandatory(yang_stmt *ys)
{
yang_stmt *ym;
+ char *reason = NULL;
+ uint8_t min_elements; /* XXX change to 32 (need new cligen version) */
- if (ys->ys_keyword != Y_LEAF)
- return 0;
- if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
- if (ym->ys_cv == NULL) /* shouldnt happen */
- return 0;
- return cv_bool_get(ym->ys_cv);
+ /* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
+ * statement with the value "true". */
+ if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_CHOICE ||
+ ys->ys_keyword == Y_ANYDATA || ys->ys_keyword == Y_ANYXML){
+ if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
+ if (ym->ys_cv != NULL) /* shouldnt happen */
+ return cv_bool_get(ym->ys_cv);
+ }
+ }
+ /* 2) A list or leaf-list node with a "min-elements" statement with a
+ * value greater than zero. */
+ else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){
+ if ((ym = yang_find((yang_node*)ys, Y_MIN_ELEMENTS, NULL)) != NULL){
+ /* XXX change to 32 (need new cligen version) */
+ if (parse_uint8(ym->ys_argument, &min_elements, &reason) != 1){
+ clicon_err(OE_YANG, EINVAL, "%s", reason?reason:"parse_uint8");
+ return 0; /* XXX ignore error */
+ }
+ return min_elements > 0;
+ }
+ }
+ /* 3) A container node without a "presence" statement and that has at
+ * least one mandatory node as a child. */
+ else if (ys->ys_keyword == Y_CONTAINER &&
+ yang_find((yang_node*)ys, Y_PRESENCE, NULL) == NULL){
+ yang_stmt *yc;
+ int i;
+ for (i=0; iys_len; i++){
+ yc = ys->ys_stmt[i];
+ if (yang_mandatory(yc))
+ return 1;
+ }
}
return 0;
}
/*! Return config state of this node
* config statement is default true.
- * Note that a node with config=false may not have a sub
- * statement where config=true. And this function does not check the sttaus of a parent.
+ * @note a node with config=false may not have a sub statement where
+ * config=true. And this function does not check the status of a parent.
* @retval 0 if node has a config sub-statement and it is false
* @retval 1 node has not config sub-statement or it is true
*/
@@ -2903,121 +2980,6 @@ yang_config(yang_stmt *ys)
return 1;
}
-/*! Parse yang specification and its dependencies recursively given module
- * @param[in] h clicon handle
- * @param[in] module Module name, or absolute filename (including dir)
- * @param[in] dir Directory where to look for modules and sub-modules
- * @param[in] revision Revision, or NULL
- * @param[in,out] yspec Modules parse are added to this yangspec
- * @retval 0 OK
- * @retval -1 Error
- * @see yang_spec_parse_file
- */
-int
-yang_spec_parse_module(clicon_handle h,
- char *module,
- char *revision,
- yang_spec *yspec)
-{
- int retval = -1;
-
- if (yspec == NULL){
- clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
- goto done;
- }
- if (module == NULL){
- clicon_err(OE_YANG, EINVAL, "yang module not set");
- goto done;
- }
- /* Sanity check - use yang_spec_parse_file instead */
- if (strlen(module) && module[0] == '/'){
- clicon_err(OE_YANG, EINVAL, "yang module illegal format");
- goto done;
- }
- if (yang_parse(h, NULL, module, revision, yspec) < 0)
- goto done;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Parse yang specification and its dependencies recursively given filename
- * @param[in] h clicon handle
- * @param[in] filename Actual filename (including dir and revision)
- * @param[in] dir Directory for sub-modules
- * @param[in,out] yspec Modules parse are added to this yangspec
- * @retval 0 OK
- * @retval -1 Error
- * @see yang_spec_parse_module for yang dir,module,revision instead of actual filename
- */
-int
-yang_spec_parse_file(clicon_handle h,
- char *filename,
- yang_spec *yspec)
-{
- int retval = -1;
-
- if (yspec == NULL){
- clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
- goto done;
- }
- if (yang_parse(h, filename, NULL, NULL, yspec) < 0)
- goto done;
- retval = 0;
- done:
- return retval;
-}
-
-/*! Load all yang modules in directory
- * @param[in] h Clicon handle
- * @param[in] dir Load all yang modules in this directory
- * @param[in,out] yspec Modules parse are added to this yangspec
- * @retval 0 OK
- * @retval -1 Error
- */
-int
-yang_spec_load_dir(clicon_handle h,
- char *dir,
- yang_spec *yspec)
-{
- int retval = -1;
- int ndp;
- struct dirent *dp = NULL;
- int i;
- char filename[MAXPATHLEN];
- char *base;
- char *b;
- int j;
- int len;
-
- /* Get plugin objects names from plugin directory */
- if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
- goto done;
- /* Load all yang files */
- for (i = 0; i < ndp; i++) {
- base = dp[i].d_name;
- /* Entries are sorted, see if later entry exists (include @), if so skip
- * this one and take last.
- */
- if ((b = index(base, '@')) != NULL)
- len = b-base;
- else
- len = strlen(base);
- /* remove duplicates: there may be cornercases that dont work, eg
- * mix of revisions and not? */
- for (j = (i+1); j < ndp; j++)
- if (strncmp(base, dp[j].d_name, len) == 0)
- break;
- if (jys_argument,
+ yang_key2str(pk),
+ yt->ys_argument);
goto done;
}
}
diff --git a/lib/src/clixon_yang_cardinality.h b/lib/src/clixon_yang_cardinality.h
index eb71e8ea..815d8d56 100644
--- a/lib/src/clixon_yang_cardinality.h
+++ b/lib/src/clixon_yang_cardinality.h
@@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c
index 6e08c96e..500aaf89 100644
--- a/lib/src/clixon_yang_module.c
+++ b/lib/src/clixon_yang_module.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -81,7 +81,7 @@
int
yang_modules_init(clicon_handle h)
{
- int retval = -1;
+ int retval = -1;
yang_spec *yspec;
yspec = clicon_dbspec_yang(h);
@@ -203,7 +203,7 @@ yang_modules_state_get(clicon_handle h,
cprintf(cb,"%s ", ys->ys_argument);
else
cprintf(cb," ");
- cprintf(cb, "implement ");
+ /* This follows order in rfc 7895: feature, conformance-type, submodules */
yc = NULL;
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
@@ -211,6 +211,14 @@ yang_modules_state_get(clicon_handle h,
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"%s ", yc->ys_argument);
break;
+ default:
+ break;
+ }
+ }
+ cprintf(cb, "implement ");
+ yc = NULL;
+ while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
+ switch(yc->ys_keyword){
case Y_SUBMODULE:
cprintf(cb,"");
cprintf(cb,"%s ", yc->ys_argument);
diff --git a/lib/src/clixon_yang_parse.h b/lib/src/clixon_yang_parse.h
index baa136df..d85e8f6b 100644
--- a/lib/src/clixon_yang_parse.h
+++ b/lib/src/clixon_yang_parse.h
@@ -5,7 +5,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/lib/src/clixon_yang_parse.l b/lib/src/clixon_yang_parse.l
index 58715c0c..a8e994da 100644
--- a/lib/src/clixon_yang_parse.l
+++ b/lib/src/clixon_yang_parse.l
@@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -103,9 +103,10 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
%%
/* Common tokens */
-[ \t]
-<> { return MY_EOF; }
+[ \t]
+<> { return MY_EOF; }
\n { _YY->yy_linenum++; }
+\r
"/*" { _YY->yy_lex_state = YYSTATE; BEGIN(COMMENT1); }
"//" { _YY->yy_lex_state = YYSTATE; BEGIN(COMMENT2); }
@@ -194,7 +195,8 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
: { return *yytext; }
; { BEGIN(KEYWORD); return *yytext; }
-[ \t] { return ' '; }
+\" { _YY->yy_lex_string_state =UNKNOWN; BEGIN(STRINGDQ); return *yytext; }
+\{ { BEGIN(KEYWORD); return *yytext; }
. { clixon_yang_parselval.string = strdup(yytext);
return CHAR; }
@@ -206,7 +208,7 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
\{ { BEGIN(KEYWORD); return *yytext; }
. { return *yytext; }
-[0-9][0-9]* { clixon_yang_parselval.string = strdup(yytext);
+\-?[0-9][0-9]* { clixon_yang_parselval.string = strdup(yytext);
return INT; }
; { BEGIN(KEYWORD); return *yytext; }
\{ { BEGIN(KEYWORD); return *yytext; }
@@ -220,14 +222,14 @@ identifier [A-Za-z_][A-Za-z0-9_\-\.]*
\{ { BEGIN(KEYWORD); return *yytext; }
; { BEGIN(KEYWORD); return *yytext; }
-\" { _YY->yy_lex_string_state =STRING; BEGIN(STRINGDQ); return DQ; }
+\" { _YY->yy_lex_string_state =STRING; BEGIN(STRINGDQ); return *yytext; }
\' { _YY->yy_lex_string_state =STRING; BEGIN(STRINGSQ); return SQ; }
\+ { return *yytext; }
. { clixon_yang_parselval.string = strdup(yytext);
return CHAR;}
\\ { _YY->yy_lex_state = STRINGDQ; BEGIN(ESCAPE); }
-\" { BEGIN(_YY->yy_lex_string_state); return DQ; }
+\" { BEGIN(_YY->yy_lex_string_state); return *yytext; }
\n { _YY->yy_linenum++; clixon_yang_parselval.string = strdup(yytext); return CHAR;}
. { clixon_yang_parselval.string = strdup(yytext);
return CHAR;}
diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y
index 7bd42cc1..6a9cce92 100644
--- a/lib/src/clixon_yang_parse.y
+++ b/lib/src/clixon_yang_parse.y
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -56,7 +56,6 @@
}
%token MY_EOF
-%token DQ /* Double quote: " */
%token SQ /* Single quote: ' */
%token CHAR
%token IDENTIFIER
@@ -70,7 +69,8 @@
%type integer_value_str
%type identifier_ref
%type abs_schema_nodeid
-%type desc_schema_node_str
+%type desc_schema_nodeid_strs
+%type desc_schema_nodeid_str
%type desc_schema_nodeid
%type node_identifier
%type identifier_str
@@ -165,7 +165,7 @@
#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_yang_parsetext, _YY->yy_linenum); YYERROR;}
-/* add _yy to error paramaters */
+/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
@@ -365,19 +365,6 @@ file : module_stmt MY_EOF
{ clicon_debug(2,"file->submodule-stmt"); YYACCEPT; }
;
- /* For extensions */
-unknown_stmt : ustring ':' ustring ';'
- { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
- if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
- clicon_debug(2,"unknown-stmt -> ustring : ustring");
- }
- | ustring ':' ustring ' ' string ';'
- { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknwon_stmt");
- if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("unknwon_stmt"); }
- clicon_debug(2,"unknown-stmt -> ustring : ustring string");
- }
- ;
-
/* module identifier-arg-str */
module_stmt : K_MODULE identifier_str
{ if ((_YY->yy_module = ysp_add_push(_yy, Y_MODULE, $2)) == NULL) _YYERROR("module_stmt");
@@ -591,15 +578,14 @@ revision_date_stmt : K_REVISION_DATE string stmtend /* XXX date-arg-str */
extension_stmt : K_EXTENSION identifier_str ';'
{ if (ysp_add(_yy, Y_EXTENSION, $2, NULL) == NULL) _YYERROR("extension_stmt");
- clicon_debug(2,"extenstion-stmt -> EXTENSION id-arg-str ;"); }
+ clicon_debug(2,"extenstion-stmt -> EXTENSION id-str ;"); }
| K_EXTENSION identifier_str
{ if (ysp_add_push(_yy, Y_EXTENSION, $2) == NULL) _YYERROR("extension_stmt"); }
'{' extension_substmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("extension_stmt");
- clicon_debug(2,"extension-stmt -> FEATURE id-arg-str { extension-substmts }"); }
+ clicon_debug(2,"extension-stmt -> EXTENSION id-str { extension-substmts }"); }
;
-
/* extension substmts */
extension_substmts : extension_substmts extension_substmt
{ clicon_debug(2,"extension-substmts -> extension-substmts extension-substmt"); }
@@ -615,10 +601,28 @@ extension_substmt : argument_stmt { clicon_debug(2,"extension-substmt -> argu
| { clicon_debug(2,"extension-substmt -> "); }
;
-argument_stmt : K_ARGUMENT identifier_str ';' { free($2); }
- | K_ARGUMENT identifier_str '{' yin_element_stmt1 '}' { free($2); }
+argument_stmt : K_ARGUMENT identifier_str ';'
+ { if (ysp_add(_yy, Y_ARGUMENT, $2, NULL) == NULL) _YYERROR("argument_stmt");
+ clicon_debug(2,"argument-stmt -> ARGUMENT identifier ;"); }
+ | K_ARGUMENT identifier_str
+ { if (ysp_add_push(_yy, Y_ARGUMENT, $2) == NULL) _YYERROR("argument_stmt"); }
+ '{' argument_substmts '}'
+ { if (ystack_pop(_yy) < 0) _YYERROR("argument_stmt");
+ clicon_debug(2,"argument-stmt -> ARGUMENT { argument-substmts }"); }
;
+/* argument substmts */
+argument_substmts : argument_substmts argument_substmt
+ { clicon_debug(2,"argument-substmts -> argument-substmts argument-substmt"); }
+ | argument_substmt
+ { clicon_debug(2,"argument-substmts -> argument-substmt"); }
+ ;
+
+argument_substmt : yin_element_stmt1 { clicon_debug(2,"argument-substmt -> yin-element-stmt1");}
+ | unknown_stmt { clicon_debug(2,"argument-substmt -> unknown-stmt");}
+ ;
+
+
/* Example of optional rule, eg [yin-element-stmt] */
yin_element_stmt1 : K_YIN_ELEMENT bool_str stmtend {free($2);}
;
@@ -641,7 +645,8 @@ identity_substmts : identity_substmts identity_substmt
{ clicon_debug(2,"identity-substmts -> identity-substmt"); }
;
-identity_substmt : base_stmt { clicon_debug(2,"identity-substmt -> base-stmt"); }
+identity_substmt : if_feature_stmt { clicon_debug(2,"identity-substmt -> if-feature-stmt"); }
+ | base_stmt { clicon_debug(2,"identity-substmt -> base-stmt"); }
| status_stmt { clicon_debug(2,"identity-substmt -> status-stmt"); }
| description_stmt { clicon_debug(2,"identity-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"identity-substmt -> reference-stmt"); }
@@ -751,6 +756,8 @@ type_body_stmt/* numerical-restrictions */
| bit_stmt { clicon_debug(2,"type-body-stmt -> bit-stmt"); }
/* union-specification */
| type_stmt { clicon_debug(2,"type-body-stmt -> type-stmt"); }
+/* Cisco uses this (eg Cisco-IOS-XR-sysadmin-nto-misc-set-hostname.yang) but I dont see this is in the RFC */
+ | unknown_stmt { clicon_debug(2,"type-body-stmt -> unknown-stmt");}
;
/* range-stmt */
@@ -876,7 +883,8 @@ enum_substmts : enum_substmts enum_substmt
{ clicon_debug(2,"enum-substmts -> enum-substmt"); }
;
-enum_substmt : value_stmt { clicon_debug(2,"enum-substmt -> value-stmt"); }
+enum_substmt : if_feature_stmt { clicon_debug(2,"enum-substmt -> if-feature-stmt"); }
+ | value_stmt { clicon_debug(2,"enum-substmt -> value-stmt"); }
| status_stmt { clicon_debug(2,"enum-substmt -> status-stmt"); }
| description_stmt { clicon_debug(2,"enum-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"enum-substmt -> reference-stmt"); }
@@ -911,7 +919,8 @@ bit_substmts : bit_substmts bit_substmt
{ clicon_debug(2,"bit-substmts -> bit-substmt"); }
;
-bit_substmt : position_stmt { clicon_debug(2,"bit-substmt -> positition-stmt"); }
+bit_substmt : if_feature_stmt { clicon_debug(2,"bit-substmt -> if-feature-stmt"); }
+ | position_stmt { clicon_debug(2,"bit-substmt -> positition-stmt"); }
| status_stmt { clicon_debug(2,"bit-substmt -> status-stmt"); }
| description_stmt { clicon_debug(2,"bit-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"bit-substmt -> reference-stmt"); }
@@ -1302,10 +1311,10 @@ uses_substmt : when_stmt { clicon_debug(2,"uses-substmt -> when-stmt
;
/* refine-stmt = refine-keyword sep refine-arg-str */
-refine_stmt : K_REFINE desc_schema_node_str ';'
+refine_stmt : K_REFINE desc_schema_nodeid_strs ';'
{ if (ysp_add(_yy, Y_REFINE, $2, NULL) == NULL) _YYERROR("refine_stmt");
clicon_debug(2,"refine-stmt -> REFINE id-arg-str ;"); }
- | K_REFINE desc_schema_node_str
+ | K_REFINE desc_schema_nodeid_strs
{ if (ysp_add_push(_yy, Y_REFINE, $2) == NULL) _YYERROR("refine_stmt"); }
'{' refine_substmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("refine_stmt");
@@ -1318,15 +1327,24 @@ refine_substmts : refine_substmts refine_substmt
{ clicon_debug(2,"refine-substmts -> refine-substmt"); }
;
-refine_substmt : must_stmt { clicon_debug(2,"refine-substmt -> must-stmt"); }
- | mandatory_stmt { clicon_debug(2,"refine-substmt -> mandatory-stmt"); }
+refine_substmt : if_feature_stmt { clicon_debug(2,"refine-substmt -> if-feature-stmt"); }
+ | must_stmt { clicon_debug(2,"refine-substmt -> must-stmt"); }
+ | presence_stmt { clicon_debug(2,"refine-substmt -> presence-stmt"); }
| default_stmt { clicon_debug(2,"refine-substmt -> default-stmt"); }
+ | config_stmt { clicon_debug(2,"refine-substmt -> config-stmt"); }
+ | mandatory_stmt { clicon_debug(2,"refine-substmt -> mandatory-stmt"); }
+ | min_elements_stmt { clicon_debug(2,"refine-substmt -> min-elements-stmt"); }
+ | max_elements_stmt { clicon_debug(2,"refine-substmt -> max-elements-stmt"); }
+ | description_stmt { clicon_debug(2,"refine-substmt -> description-stmt"); }
+ | reference_stmt { clicon_debug(2,"refine-substmt -> reference-stmt"); }
| unknown_stmt { clicon_debug(2,"refine-substmt -> unknown-stmt");}
| { clicon_debug(2,"refine-substmt -> "); }
;
-/* uses-augment-stmt = augment-keyword augment-arg-str */
-uses_augment_stmt : K_AUGMENT desc_schema_node_str
+/* uses-augment-stmt = augment-keyword augment-arg-str
+uses_augment_stmt : K_AUGMENT desc_schema_nodeid_strs
+*/
+uses_augment_stmt : K_AUGMENT string
{ if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("uses_augment_stmt"); }
'{' augment_substmts '}'
{ if (ystack_pop(_yy) < 0) _YYERROR("uses_augment_stmt");
@@ -1335,6 +1353,7 @@ uses_augment_stmt : K_AUGMENT desc_schema_node_str
/* augment-stmt = augment-keyword sep augment-arg-str
* XXX abs-schema-nodeid-str is too difficult, it needs the + semantics
+augment_stmt : K_AUGMENT abs_schema_nodeid_strs
*/
augment_stmt : K_AUGMENT string
{ if (ysp_add_push(_yy, Y_AUGMENT, $2) == NULL) _YYERROR("augment_stmt"); }
@@ -1408,6 +1427,7 @@ rpc_substmt : if_feature_stmt { clicon_debug(2,"rpc-substmt -> if-feature-stm
| grouping_stmt { clicon_debug(2,"rpc-substmt -> grouping-stmt"); }
| input_stmt { clicon_debug(2,"rpc-substmt -> input-stmt"); }
| output_stmt { clicon_debug(2,"rpc-substmt -> output-stmt"); }
+ | unknown_stmt { clicon_debug(2,"rpc-substmt -> unknown-stmt");}
| { clicon_debug(2,"rpc-substmt -> "); }
;
@@ -1471,13 +1491,147 @@ deviation_substmts : deviation_substmts deviation_substmt
deviation_substmt : description_stmt { clicon_debug(2,"deviation-substmt -> description-stmt"); }
| reference_stmt { clicon_debug(2,"deviation-substmt -> reference-stmt"); }
- | deviate_not_supported_stmt { clicon_debug(2,"deviation-substmt -> deviate-not-supported-stmt"); }
+ | deviate_stmt { clicon_debug(2,"deviation-substmt -> deviate-stmt"); }
;
-deviate_not_supported_stmt : K_DEVIATE string stmtend
+/* RFC7950 differentiates between deviate-not-supported, deviate-add,
+ * deviate-replave, and deviate-delete. Here all are bundled into a single
+ * deviate rule. For now, until "deviate" gets supported.
+ */
+deviate_stmt : K_DEVIATE string ';'
{ if (ysp_add(_yy, Y_DEVIATE, $2, NULL) == NULL) _YYERROR("notification_stmt");
clicon_debug(2,"deviate-not-supported-stmt -> DEVIATE string ;"); }
- ;
+ | K_DEVIATE string
+ { if (ysp_add_push(_yy, Y_DEVIATE, $2) == NULL) _YYERROR("deviate_stmt"); }
+ '{' deviate_substmts '}'
+ { if (ystack_pop(_yy) < 0) _YYERROR("deviate_stmt");
+ clicon_debug(2,"deviate-stmt -> DEVIATE string { deviate-substmts }"); }
+ ;
+
+/* RFC7950 differentiates between deviate-not-supported, deviate-add,
+ * deviate-replave, and deviate-delete. Here all are bundled into a single
+ * deviate-substmt rule. For now, until "deviate" gets supported.
+ */
+deviate_substmts : deviate_substmts deviate_substmt
+ { clicon_debug(2,"deviate-substmts -> deviate-substmts deviate-substmt"); }
+ | deviate_substmt
+ { clicon_debug(2,"deviate-substmts -> deviate-substmt"); }
+ ;
+/* Bundled */
+deviate_substmt : type_stmt { clicon_debug(2,"deviate-substmt -> type-stmt"); }
+ | units_stmt { clicon_debug(2,"deviate-substmt -> units-stmt"); }
+ | must_stmt { clicon_debug(2,"deviate-substmt -> must-stmt"); }
+ | unique_stmt { clicon_debug(2,"deviate-substmt -> unique-stmt"); }
+ | default_stmt { clicon_debug(2,"deviate-substmt -> default-stmt"); }
+ | config_stmt { clicon_debug(2,"deviate-substmt -> config-stmt"); }
+ | mandatory_stmt { clicon_debug(2,"deviate-substmt -> mandatory-stmt"); }
+ | min_elements_stmt { clicon_debug(2,"deviate-substmt -> min-elements-stmt"); }
+ | max_elements_stmt { clicon_debug(2,"deviate-substmt -> max-elements-stmt"); }
+ | { clicon_debug(2,"deviate-substmt -> "); }
+ ;
+
+
+ /* For extensions */
+unknown_stmt : ustring ':' ustring ';'
+ { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
+ if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
+ clicon_debug(2,"unknown-stmt -> ustring : ustring");
+ }
+ | ustring ':' ustring string ';'
+ { char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
+ if (ysp_add(_yy, Y_UNKNOWN, id, $4) == NULL){ _YYERROR("unknwon_stmt"); }
+ clicon_debug(2,"unknown-stmt -> ustring : ustring string");
+ }
+ | ustring ':' ustring
+ { if (ysp_add_push(_yy, Y_UNKNOWN, NULL) == NULL) _YYERROR("unknown_stmt"); }
+ '{' yang_stmts '}'
+ { if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
+ clicon_debug(2,"unknown-stmt -> ustring : ustring { yang-stmts }"); }
+ | ustring ':' ustring string
+ { if (ysp_add_push(_yy, Y_UNKNOWN, NULL) == NULL) _YYERROR("unknown_stmt"); }
+ '{' yang_stmts '}'
+ { if (ystack_pop(_yy) < 0) _YYERROR("unknown_stmt");
+ clicon_debug(2,"unknown-stmt -> ustring : ustring string { yang-stmts }"); }
+ ;
+
+yang_stmts : yang_stmts yang_stmt { clicon_debug(2,"yang-stmts -> yang-stmts yang-stmt"); }
+ | yang_stmt { clicon_debug(2,"yang-stmts -> yang-stmt");}
+ ;
+
+yang_stmt : action_stmt { clicon_debug(2,"yang-stmt -> action-stmt");}
+ | anydata_stmt { clicon_debug(2,"yang-stmt -> anydata-stmt");}
+ | anyxml_stmt { clicon_debug(2,"yang-stmt -> anyxml-stmt");}
+ | argument_stmt { clicon_debug(2,"yang-stmt -> argument-stmt");}
+ | augment_stmt { clicon_debug(2,"yang-stmt -> augment-stmt");}
+ | base_stmt { clicon_debug(2,"yang-stmt -> base-stmt");}
+ | bit_stmt { clicon_debug(2,"yang-stmt -> bit-stmt");}
+ | case_stmt { clicon_debug(2,"yang-stmt -> case-stmt");}
+ | choice_stmt { clicon_debug(2,"yang-stmt -> choice-stmt");}
+ | config_stmt { clicon_debug(2,"yang-stmt -> config-stmt");}
+ | contact_stmt { clicon_debug(2,"yang-stmt -> contact-stmt");}
+ | container_stmt { clicon_debug(2,"yang-stmt -> container-stmt");}
+ | default_stmt { clicon_debug(2,"yang-stmt -> default-stmt");}
+ | description_stmt { clicon_debug(2,"yang-stmt -> description-stmt");}
+ | deviate_stmt { clicon_debug(2,"yang-stmt -> deviate-stmt");}
+/* deviate is not yet implemented, the above may be replaced by the following lines
+ | deviate_add_stmt { clicon_debug(2,"yang-stmt -> deviate-add-stmt");}
+ | deviate_delete_stmt { clicon_debug(2,"yang-stmt -> deviate-add-stmt");}
+ | deviate_replace_stmt { clicon_debug(2,"yang-stmt -> deviate-add-stmt");}
+*/
+ | deviation_stmt { clicon_debug(2,"yang-stmt -> deviation-stmt");}
+ | enum_stmt { clicon_debug(2,"yang-stmt -> enum-stmt");}
+ | error_app_tag_stmt { clicon_debug(2,"yang-stmt -> error-app-tag-stmt");}
+ | error_message_stmt { clicon_debug(2,"yang-stmt -> error-message-stmt");}
+ | extension_stmt { clicon_debug(2,"yang-stmt -> extension-stmt");}
+ | feature_stmt { clicon_debug(2,"yang-stmt -> feature-stmt");}
+ | fraction_digits_stmt { clicon_debug(2,"yang-stmt -> fraction-digits-stmt");}
+ | grouping_stmt { clicon_debug(2,"yang-stmt -> grouping-stmt");}
+ | identity_stmt { clicon_debug(2,"yang-stmt -> identity-stmt");}
+ | if_feature_stmt { clicon_debug(2,"yang-stmt -> if-feature-stmt");}
+ | import_stmt { clicon_debug(2,"yang-stmt -> import-stmt");}
+ | include_stmt { clicon_debug(2,"yang-stmt -> include-stmt");}
+ | input_stmt { clicon_debug(2,"yang-stmt -> input-stmt");}
+ | key_stmt { clicon_debug(2,"yang-stmt -> key-stmt");}
+ | leaf_list_stmt { clicon_debug(2,"yang-stmt -> leaf-list-stmt");}
+ | leaf_stmt { clicon_debug(2,"yang-stmt -> leaf-stmt");}
+ | length_stmt { clicon_debug(2,"yang-stmt -> length-stmt");}
+ | list_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | mandatory_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | max_elements_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | min_elements_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | modifier_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | module_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | must_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | namespace_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | notification_stmt { clicon_debug(2,"yang-stmt -> notification-stmt");}
+ | ordered_by_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | organization_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | output_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | path_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | pattern_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | position_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | prefix_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | presence_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | range_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | reference_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | refine_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | require_instance_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | revision_date_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | revision_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | rpc_stmt { clicon_debug(2,"yang-stmt -> rpc-stmt");}
+ | status_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | submodule_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | typedef_stmt { clicon_debug(2,"yang-stmt -> typedef-stmt");}
+ | type_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | unique_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | units_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | uses_augment_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | uses_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | value_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | when_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+ | yang_version_stmt { clicon_debug(2,"yang-stmt -> list-stmt");}
+/* | yin_element_stmt { clicon_debug(2,"yang-stmt -> list-stmt");} */
+ ;
/* body */
body_stmts : body_stmts body_stmt { clicon_debug(2,"body-stmts -> body-stmts body-stmt"); }
@@ -1561,7 +1715,8 @@ qstrings : qstrings '+' qstring
{ $$=$1; clicon_debug(2,"qstrings-> qstring"); }
;
-qstring : DQ ustring DQ { $$=$2; clicon_debug(2,"string-> \" ustring \"");}
+qstring : '"' ustring '"' { $$=$2; clicon_debug(2,"string-> \" ustring \"");}
+ | '"' '"' { $$=strdup(""); clicon_debug(2,"string-> \" \"");}
| SQ ustring SQ { $$=$2; clicon_debug(2,"string-> ' ustring '"); }
;
@@ -1585,12 +1740,25 @@ abs_schema_nodeid : abs_schema_nodeid '/' node_identifier
clicon_debug(2,"absolute-schema-nodeid -> / node-identifier"); }
;
-desc_schema_node_str : desc_schema_nodeid
- { $$=$1; clicon_debug(2,"descendant-schema-node-str -> descendant-node"); }
+desc_schema_nodeid_strs : desc_schema_nodeid_strs '+' desc_schema_nodeid_str
+ {
+ int len = strlen($1);
+ $$ = realloc($1, len + strlen($3) + 1);
+ sprintf($$+len, "%s", $3);
+ free($3);
+ clicon_debug(2,"desc-schema-nodeid-strs-> desc-schema-nodeid-strs + desc-schema-nodeid-str");
+ }
+ | desc_schema_nodeid_str
+ { $$=$1; clicon_debug(2,"desc-schema-nodeid-strs-> desc-schema-nodeid-str"); }
+ ;
+
+desc_schema_nodeid_str : desc_schema_nodeid
+ { $$=$1; clicon_debug(2,"descendant-schema-nodeid-str -> descendant-schema-nodeid"); }
| '"' desc_schema_nodeid '"'
- { $$=$2; clicon_debug(2,"descendant-schema-node-str -> descendant-node"); }
+ { $$=$2; clicon_debug(2,"descendant-schema-nodeid-str -> descendant-schema-nodeid"); }
;
+/* descendant-schema-nodeid */
desc_schema_nodeid : node_identifier
{ $$= $1; clicon_debug(2,"descendant-schema-nodeid -> node_identifier"); }
| node_identifier abs_schema_nodeid
diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c
index 788af4f7..ce43aec3 100644
--- a/lib/src/clixon_yang_type.c
+++ b/lib/src/clixon_yang_type.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -115,8 +115,7 @@ int
yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
int options,
- cg_var *mincv,
- cg_var *maxcv,
+ cvec *cvv,
char *pattern,
uint8_t fraction)
{
@@ -132,13 +131,11 @@ yang_type_cache_set(yang_type_cache **ycache0,
*ycache0 = ycache;
ycache->yc_resolved = resolved;
ycache->yc_options = options;
- if (mincv && (ycache->yc_mincv = cv_dup(mincv)) == NULL){
- clicon_err(OE_UNIX, errno, "cv_dup");
- goto done;
- }
- if (maxcv && (ycache->yc_maxcv = cv_dup(maxcv)) == NULL){
- clicon_err(OE_UNIX, errno, "cv_dup");
- goto done;
+ if (cvv){
+ if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
+ clicon_err(OE_UNIX, errno, "cvec_dup");
+ goto done;
+ }
}
if (pattern && (ycache->yc_pattern = strdup(pattern)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@@ -155,8 +152,7 @@ int
yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved,
int *options,
- cg_var **mincv,
- cg_var **maxcv,
+ cvec **cvv,
char **pattern,
uint8_t *fraction)
{
@@ -164,10 +160,8 @@ yang_type_cache_get(yang_type_cache *ycache,
*resolved = ycache->yc_resolved;
if (options)
*options = ycache->yc_options;
- if (mincv)
- *mincv = ycache->yc_mincv;
- if (maxcv)
- *maxcv = ycache->yc_maxcv;
+ if (cvv)
+ *cvv = ycache->yc_cvv;
if (pattern)
*pattern = ycache->yc_pattern;
if (fraction)
@@ -181,14 +175,13 @@ yang_type_cache_cp(yang_type_cache **ycnew,
{
int retval = -1;
int options;
- cg_var *mincv;
- cg_var *maxcv;
+ cvec *cvv;
char *pattern;
uint8_t fraction;
yang_stmt *resolved;
- yang_type_cache_get(ycold, &resolved, &options, &mincv, &maxcv, &pattern, &fraction);
- if (yang_type_cache_set(ycnew, resolved, options, mincv, maxcv, pattern, fraction) < 0)
+ yang_type_cache_get(ycold, &resolved, &options, &cvv, &pattern, &fraction);
+ if (yang_type_cache_set(ycnew, resolved, options, cvv, pattern, fraction) < 0)
goto done;
retval = 0;
done:
@@ -198,10 +191,8 @@ yang_type_cache_cp(yang_type_cache **ycnew,
int
yang_type_cache_free(yang_type_cache *ycache)
{
- if (ycache->yc_mincv)
- cv_free(ycache->yc_mincv);
- if (ycache->yc_maxcv)
- cv_free(ycache->yc_maxcv);
+ if (ycache->yc_cvv)
+ cvec_free(ycache->yc_cvv);
if (ycache->yc_pattern)
free(ycache->yc_pattern);
free(ycache);
@@ -220,8 +211,7 @@ ys_resolve_type(yang_stmt *ys,
{
int retval = -1;
int options = 0x0;
- cg_var *mincv = NULL;
- cg_var *maxcv = NULL;
+ cvec *cvv = NULL;
char *pattern = NULL;
uint8_t fraction = 0;
yang_stmt *resolved = NULL;
@@ -230,13 +220,14 @@ ys_resolve_type(yang_stmt *ys,
/* Recursively resolve ys -> resolve with restrictions(options, etc)
* Note that the resolved type could be ys itself.
*/
- if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
- &options, &mincv, &maxcv, &pattern, &fraction) < 0)
+ if (yang_type_resolve((yang_stmt*)ys->ys_parent, (yang_stmt*)ys->ys_parent,
+ ys, &resolved,
+ &options, &cvv, &pattern, &fraction) < 0)
goto done;
/* Cache the resolve locally */
if (yang_type_cache_set(&ys->ys_typecache,
- resolved, options, mincv, maxcv, pattern, fraction) < 0)
+ resolved, options, cvv, pattern, fraction) < 0)
goto done;
retval = 0;
done:
@@ -263,35 +254,6 @@ yang2cv_type(char *ytype,
*cv_type = ret;
return 0;
}
- /* special derived types */
- if (strcmp("ipv4-address", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_IPV4ADDR;
- return 0;
- }
- if (strcmp("ipv6-address", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_IPV6ADDR;
- return 0;
- }
- if (strcmp("ipv4-prefix", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_IPV4PFX;
- return 0;
- }
- if (strcmp("ipv6-prefix", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_IPV6PFX;
- return 0;
- }
- if (strcmp("date-and-time", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_TIME;
- return 0;
- }
- if (strcmp("mac-address", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_MACADDR;
- return 0;
- }
- if (strcmp("uuid", ytype) == 0){ /* RFC6991 */
- *cv_type = CGV_UUID;
- return 0;
- }
return 0;
}
@@ -338,13 +300,15 @@ cv2yang_type(enum cv_type cv_type)
* to find special cligen types such as ipv4addr.
* not true yang types
* @param[in] origtype Name of original type
- * @param[in] restype Resolved type. may be null, in that case origtype is used
+ * @param[in] restype Resolved type. May be null, in that case origtype is used
+ * @param[in] ys Yang stmt of original resolving node
* @param[out] cvtype Translation from resolved type
* @note Thereis a kludge for handling direct translations of native cligen types
*/
int
clicon_type2cv(char *origtype,
char *restype,
+ yang_stmt *ys,
enum cv_type *cvtype)
{
int retval = -1;
@@ -353,7 +317,8 @@ clicon_type2cv(char *origtype,
if (restype != NULL){
yang2cv_type(restype, cvtype);
if (*cvtype == CGV_ERR){
- clicon_err(OE_DB, 0, "\"%s\" type not translated", restype);
+ clicon_err(OE_YANG, 0, "%s: \"%s\" type not translated",
+ ys_module(ys)->ys_argument, restype);
goto done;
}
}
@@ -364,7 +329,8 @@ clicon_type2cv(char *origtype,
*/
yang2cv_type(origtype, cvtype);
if (*cvtype == CGV_ERR){
- clicon_err(OE_DB, 0, "\"%s\": type not resolved", origtype);
+ clicon_err(OE_YANG, 0, "%s:\"%s\": type not resolved",
+ ys_module(ys)->ys_argument, origtype);
goto done;
}
}
@@ -388,132 +354,121 @@ static int
cv_validate1(cg_var *cv,
enum cv_type cvtype,
int options,
- cg_var *range_min,
- cg_var *range_max,
+ cvec *cvv,
char *pattern,
yang_stmt *yrestype,
char *restype,
char **reason)
{
int retval = 1; /* OK */
+ cg_var *cv1;
+ cg_var *cv2;
int retval2;
yang_stmt *yi = NULL;
- unsigned int u = 0;
- int i = 0;
char *str;
int found;
char **vec = NULL;
int nvec;
char *v;
+ uint64_t uu = 0;
+ int64_t ii = 0;
+ int i;
+ int ret;
if (reason && *reason){
free(*reason);
*reason = NULL;
}
+ /* check options first for length and range */
+ if ((options & YANG_OPTIONS_RANGE) != 0 ||
+ (options & YANG_OPTIONS_LENGTH) != 0){
+ i = 0;
+ while (iys_argument:NULL;
@@ -651,7 +591,7 @@ ys_cv_validate_union_one(yang_stmt *ys,
goto done;
}
else {
- if (clicon_type2cv(type, restype, &cvtype) < 0)
+ if (clicon_type2cv(type, restype, ys, &cvtype) < 0)
goto done;
/* reparse value with the new type */
if ((cvt = cv_new(cvtype)) == NULL){
@@ -664,7 +604,7 @@ ys_cv_validate_union_one(yang_stmt *ys,
}
if (retval == 0)
goto done;
- if ((retval = cv_validate1(cvt, cvtype, options, range_min, range_max,
+ if ((retval = cv_validate1(cvt, cvtype, options, cvv,
pattern, yrt, restype, reason)) < 0)
goto done;
}
@@ -736,8 +676,7 @@ ys_cv_validate(cg_var *cv,
int retval = -1;
cg_var *ycv; /* cv of yang-statement */
int options = 0;
- cg_var *range_min = NULL;
- cg_var *range_max = NULL;
+ cvec *cvv = NULL;
char *pattern = NULL;
enum cv_type cvtype;
char *type; /* orig type */
@@ -756,11 +695,10 @@ ys_cv_validate(cg_var *cv,
}
ycv = ys->ys_cv;
if (yang_type_get(ys, &type, &yrestype,
- &options, &range_min, &range_max, &pattern,
- &fraction) < 0)
+ &options, &cvv, &pattern, &fraction) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
- if (clicon_type2cv(type, restype, &cvtype) < 0)
+ if (clicon_type2cv(type, restype, ys, &cvtype) < 0)
goto done;
if (cv_type_get(ycv) != cvtype){
@@ -782,7 +720,7 @@ ys_cv_validate(cg_var *cv,
retval = retval2; /* invalid (0) with latest reason or valid 1 */
}
else
- if ((retval = cv_validate1(cv, cvtype, options, range_min, range_max, pattern,
+ if ((retval = cv_validate1(cv, cvtype, options, cvv, pattern,
yrestype, restype, reason)) < 0)
goto done;
done:
@@ -889,7 +827,17 @@ yang_find_identity(yang_stmt *ys,
return yid;
}
-/*
+/*! Resolve type restrictions, return contraining parameters
+ * @param[in] yrange Yang type range restriction if any
+ * @param[in] ylength Yang type length restriction if any
+ * @param[in] ypattern Yang type pattern restriction if any
+ * @param[in] yfraction Yang type fraction restriction if any
+ * @param[out] options Pointer to flags field of optional values. optional
+ * @param[out] cvv Pointer to cvec with min range or length.
+ * If options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH
+ * @param[out] pattern Pointer to static string of yang string pattern. optional
+ * @param[out] fraction For decimal64, how many digits after period
+ * @retval 0 OK.
*/
static int
resolve_restrictions(yang_stmt *yrange,
@@ -897,19 +845,16 @@ resolve_restrictions(yang_stmt *yrange,
yang_stmt *ypattern,
yang_stmt *yfraction,
int *options,
- cg_var **mincv,
- cg_var **maxcv,
+ cvec **cvv,
char **pattern,
uint8_t *fraction)
{
- if (options && mincv && maxcv && yrange != NULL){
- *mincv = cvec_find(yrange->ys_cvec, "range_min");
- *maxcv = cvec_find(yrange->ys_cvec, "range_max");
+ if (options && cvv && yrange != NULL){
+ *cvv = yrange->ys_cvec;
*options |= YANG_OPTIONS_RANGE;
}
- if (options && mincv && maxcv && ylength != NULL){
- *mincv = cvec_find(ylength->ys_cvec, "range_min"); /* XXX fel typ */
- *maxcv = cvec_find(ylength->ys_cvec, "range_max");
+ if (options && cvv && ylength != NULL){
+ *cvv = ylength->ys_cvec;
*options |= YANG_OPTIONS_LENGTH;
}
if (options && pattern && ypattern != NULL){
@@ -924,12 +869,13 @@ resolve_restrictions(yang_stmt *yrange,
}
/*! Recursively resolve a yang type to built-in type with optional restrictions
- * @param[in] ys (original) type yang-stmt where the current search is based
+ * @param[in] yorig (original) type yang-stmt where original search is based
+ * @param[in] ys (transitive) yang-stmt where current search is based
* @param[in] ytype yang-stmt object containing currently resolving type
* @param[out] yrestype resolved type. return built-in type or NULL. mandatory
* @param[out] options pointer to flags field of optional values. optional
- * @param[out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE
- * @param[out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE
+ * @param[out] cvv pointer to cvec with min range or length.
+ * If options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH
* @param[out] pattern pointer to static string of yang string pattern. optional
* @param[out] fraction for decimal64, how many digits after period
* @retval 0 OK. Note yrestype may still be NULL.
@@ -942,12 +888,12 @@ resolve_restrictions(yang_stmt *yrange,
* Note also that for all pointer arguments, if NULL is given, no value is assigned.
*/
int
-yang_type_resolve(yang_stmt *ys,
+yang_type_resolve(yang_stmt *yorig,
+ yang_stmt *ys,
yang_stmt *ytype,
yang_stmt **yrestype,
int *options,
- cg_var **mincv,
- cg_var **maxcv,
+ cvec **cvv,
char **pattern,
uint8_t *fraction)
{
@@ -970,11 +916,12 @@ yang_type_resolve(yang_stmt *ys,
prefix = yarg_prefix(ytype); /* And this its prefix */
/* Cache does not work for eg string length 32? */
if (!yang_builtin(type) && ytype->ys_typecache != NULL){
- if (yang_type_cache_get(ytype->ys_typecache,
- yrestype, options, mincv, maxcv, pattern, fraction) < 0)
+ if (yang_type_cache_get(ytype->ys_typecache, yrestype,
+ options, cvv, pattern, fraction) < 0)
goto done;
goto ok;
}
+ /* Resolving type restrictions */
yrange = yang_find((yang_node*)ytype, Y_RANGE, NULL);
ylength = yang_find((yang_node*)ytype, Y_LENGTH, NULL);
ypattern = yang_find((yang_node*)ytype, Y_PATTERN, NULL);
@@ -984,7 +931,7 @@ yang_type_resolve(yang_stmt *ys,
if (prefix == NULL && yang_builtin(type)){
*yrestype = ytype;
resolve_restrictions(yrange, ylength, ypattern, yfraction, options,
- mincv, maxcv, pattern, fraction);
+ cvv, pattern, fraction);
goto ok;
}
@@ -992,11 +939,12 @@ yang_type_resolve(yang_stmt *ys,
if (prefix){ /* Go to top and find import that matches */
if ((yrmod = yang_find_module_by_prefix(ytype, prefix)) == NULL){
clicon_err(OE_DB, 0, "Type not resolved: \"%s:%s\" in module %s",
- prefix, type, ys_module(ys)->ys_argument);
+ prefix, type, ys_module(yorig)->ys_argument);
goto done;
}
if ((rytypedef = yang_find((yang_node*)yrmod, Y_TYPEDEF, type)) == NULL)
goto ok; /* unresolved */
+ ys = rytypedef;
}
else
while (1){
@@ -1010,7 +958,7 @@ yang_type_resolve(yang_stmt *ys,
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
- if (yn && yn->yn_keyword == Y_SPEC)
+ if (yn && (yn->yn_keyword == Y_SPEC))
yn = NULL;
ys = (yang_stmt*)yn;
}
@@ -1021,14 +969,13 @@ yang_type_resolve(yang_stmt *ys,
goto done;
}
/* recursively resolve this new type */
- if (yang_type_resolve(ys, rytype, yrestype,
- options, mincv, maxcv, pattern, fraction) < 0)
+ if (yang_type_resolve(yorig, ys, rytype, yrestype,
+ options, cvv, pattern, fraction) < 0)
goto done;
/* overwrites the resolved if any */
- resolve_restrictions(yrange, ylength, ypattern, yfraction,
- options, mincv, maxcv, pattern, fraction);
+ resolve_restrictions(yrange, ylength, ypattern, yfraction, options,
+ cvv, pattern, fraction);
}
-
ok:
retval = 0;
done:
@@ -1042,11 +989,11 @@ yang_type_resolve(yang_stmt *ys,
* @code
* yang_stmt *yrestype;
* int options;
- * int64_t min, max;
+ * cvec *cvv = NULL;
* char *pattern;
* uint8_t fraction;
*
- * if (yang_type_get(ys, &type, &yrestype, &options, &min, &max, &pattern, &fraction) < 0)
+ * if (yang_type_get(ys, &type, &yrestype, &options, &cvv, &pattern, &fraction) < 0)
* goto err;
* if (yrestype == NULL) # unresolved
* goto err;
@@ -1078,8 +1025,7 @@ yang_type_get(yang_stmt *ys,
char **origtype,
yang_stmt **yrestype,
int *options,
- cg_var **mincv,
- cg_var **maxcv,
+ cvec **cvv,
char **pattern,
uint8_t *fraction
)
@@ -1099,8 +1045,8 @@ yang_type_get(yang_stmt *ys,
type = yarg_id(ytype);
if (origtype)
*origtype = type;
- if (yang_type_resolve(ys, ytype, yrestype,
- options, mincv, maxcv, pattern, fraction) < 0)
+ if (yang_type_resolve(ys, ys, ytype, yrestype,
+ options, cvv, pattern, fraction) < 0)
goto done;
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
*yrestype?(*yrestype)->ys_argument:"null");
diff --git a/test/README.md b/test/README.md
index 7ae292bb..1326365e 100644
--- a/test/README.md
+++ b/test/README.md
@@ -1,9 +1,10 @@
# Clixon tests
This directory contains testing code for clixon and the example
-routing application. Assumes setup of http daemon as describe under apps/restonf
-- clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script
-- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically.
+application. Assumes setup of http daemon as describe under apps/restonf
+- Jenkinsfile Makefile for Jenkins tests. Build clixon and run tests.
+- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically. By default the script will exit on first error. Run as `all.sh summary` to continue and print a summary on all tests.
+- site.sh Add your site-specific modifications here
- test_nacm.sh Auth tests using internal NACM
- test_nacm_ext.sh Auth tests using external NACM (separate file)
- test_cli.sh CLI tests
@@ -12,4 +13,16 @@ routing application. Assumes setup of http daemon as describe under apps/restonf
- test_yang.sh Yang tests for constructs not in the example.
- test_leafref.sh Yang leafref tests
- test_datastore.sh Datastore tests
+- and many more...
+
+Example runs:
+```
+> run.sh
+# Runs through all tests matching 'test_*.sh' in the directory. Prints test output
+# and stops on first error
+
+> run.sh summary
+# Same as above but continues after errors and does not print test output.
+```
+
diff --git a/test/all.sh b/test/all.sh
index d0e92410..1e036ab1 100755
--- a/test/all.sh
+++ b/test/all.sh
@@ -1,17 +1,44 @@
#!/bin/bash
# Run, eg as:
-# ./run.sh 2>&1 | tee test.log
+# ./all.sh 2>&1 | tee test.log # break on first test
+# ./all.sh summary # to run all tests and print
+
+summary=0
+if [ $# -gt 0 ]; then
+ summary=1
+fi
+if [ $# -gt 1 ]; then
+ echo "usage: $0 [summary] # pipe to dev/null and continue on error"
+ exit -1
+fi
# include err() and new() functions
. ./lib.sh
-
+err=0
for test in test*.sh; do
echo "Running $test"
- ./$test
- errcode=$?
+ if [ $summary -ne 0 ]; then
+ ./$test > /dev/null 2>&1
+ errcode=$?
+ else
+ ./$test
+ errcode=$?
+ fi
if [ $errcode -ne 0 ]; then
- echo "Error in $test errcode=$errcode"
- exit $errcode
+ err=1
+ echo -e "\e[31mError in $test errcode=$errcode"
+ echo -ne "\e[0m"
+ if [ $summary -eq 0 ]; then
+ exit $errcode
+ fi
fi
done
-echo OK
+if [ $err -eq 0 ]; then
+ echo OK
+else
+ echo -e "\e[31mError"
+ echo -ne "\e[0m"
+ exit -1
+fi
+
+
diff --git a/test/lib.sh b/test/lib.sh
index 2a66d2d5..ce980ffa 100755
--- a/test/lib.sh
+++ b/test/lib.sh
@@ -4,6 +4,10 @@
#set -e
+if [ -x ./site.sh ]; then
+ . ./site.sh
+fi
+
testnr=0
testname=
@@ -13,6 +17,14 @@ testname=
# If set, enable debugging (of backend)
: ${DBG:=0}
+# Parse yangmodels from https://github.com/YangModels/yang
+# Recommended: checkout yangmodels elsewhere in the tree and set the env
+# to that
+: ${YANGMODELS=$(pwd)/yang}
+
+# Parse yang openconfig models from https://github.com/openconfig/public
+: ${OPENCONFIG=$(pwd)/public}
+
# For memcheck
#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli"
clixon_cli=clixon_cli
@@ -38,7 +50,9 @@ if [ ! -d $dir ]; then
fi
rm -rf $dir/*
-# error and exit, arg is optional extra errmsg
+# error and exit,
+# arg1: expected
+# arg2: errmsg[optional]
err(){
echo -e "\e[31m\nError in Test$testnr [$testname]:"
if [ $# -gt 0 ]; then
@@ -53,7 +67,7 @@ err(){
echo "$expect"| od -t c > $dir/clixon-expect
diff $dir/clixon-expect $dir/clixon-ret
- exit $testnr
+ exit -1 #$testnr
}
# Increment test number and print a nice string
@@ -221,8 +235,9 @@ expectwait(){
done
# cat /tmp/flag
if [ $(cat /tmp/flag) != "ok" ]; then
- cat /tmp/flag
- exit
+# err "ok" $(cat /tmp/flag)
+# cat /tmp/flag
+ exit -1
fi
}
@@ -250,5 +265,4 @@ expectmatch(){
fi
fi
fi
-
}
diff --git a/test/test_choice.sh b/test/test_choice.sh
new file mode 100755
index 00000000..1289a0f5
--- /dev/null
+++ b/test/test_choice.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+# Choice type and mandatory
+# Example from RFC7950 Sec 7.9.6
+# Also test mandatory behaviour as in 7.6.5
+# (XXX Would need default test in 7.6.4)
+# Use-case: The ietf-netconf edit-config has a shorthand version of choice w mandatory:
+# container { choice target { mandatory; leaf candidate; leaf running; }}
+
+APPNAME=example
+# include err() and new() functions and creates $dir
+. ./lib.sh
+
+cfg=$dir/choice.xml
+fyang=$dir/type.yang
+
+cat < $cfg
+
+ $cfg
+ $dir
+ /usr/local/share/$APPNAME/yang
+ /usr/local/share/clixon
+ system
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ false
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ /usr/local/lib/xmldb/text.so
+
+EOF
+
+cat < $fyang
+module system{
+ yang-version 1.1;
+ namespace "urn:example:config";
+ prefix ex;
+ container system{
+ /* From RFC 7950 7.9.6 */
+ container protocol {
+ presence true;
+ choice name {
+ case a {
+ leaf udp {
+ type empty;
+ }
+ }
+ case b {
+ leaf tcp {
+ type empty;
+ }
+ }
+ }
+ }
+ /* Same but shorthand */
+ container shorthand {
+ presence true;
+ choice name {
+ leaf udp {
+ type empty;
+ }
+ leaf tcp {
+ type empty;
+ }
+ }
+ }
+ /* Same with mandatory true */
+ container mandatory {
+ presence true;
+ choice name {
+ mandatory true;
+ case a {
+ leaf udp {
+ type empty;
+ }
+ }
+ case b {
+ leaf tcp {
+ type empty;
+ }
+ }
+ }
+ }
+ }
+}
+EOF
+
+new "test params: -f $cfg -y $fyang"
+
+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"
+ sudo $clixon_backend -s init -f $cfg -y $fyang
+ if [ $? -ne 0 ]; then
+ err
+ fi
+fi
+
+new "kill old restconf daemon"
+sudo pkill -u www-data clixon_restconf
+
+new "start restconf daemon"
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
+
+# First vanilla (protocol) case
+new "netconf validate empty"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "netconf set empty protocol"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf validate protocol"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "netconf set protocol tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf get protocol tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
+
+new "netconf commit protocol tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^ ]]>]]>$'
+
+new "netconf get protocol udp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
+
+new "netconf commit protocol udp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "restconf set protocol tcp"
+expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d {\"system:protocol\":{\"tcp\":null}})" ""
+
+new2 "restconf get protocol tcp"
+expecteq "$(curl -s -X GET http://localhost/restconf/data/system:system)" '{"system:system": {"protocol": {"tcp": null}}}
+
'
+
+new "cli set protocol udp"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol udp" 0 "^$"
+
+new "cli get protocol udp"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol udp$"
+
+new "cli delete all"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o delete all" 0 "^$"
+
+new "commit"
+expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o commit" 0 "^$"
+
+# Second shorthand (no case)
+new "netconf set shorthand tcp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^ ]]>]]>$'
+
+new "netconf get shorthand udp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
+
+new "netconf validate shorthand"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+# Third check mandatory
+new "netconf set empty mandatory"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf get mandatory empty"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
+
+new "netconf validate mandatory"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^application data-missing missing-choice name error ]]>]]>$'
+
+new "netconf set mandatory udp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf get mandatory udp"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
+
+new "netconf validate mandatory"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "Kill restconf daemon"
+sudo pkill -u www-data -f "/www-data/clixon_restconf"
+
+if [ $BE -eq 0 ]; then
+ exit # BE
+fi
+
+new "Kill backend"
+# Check if premature kill
+pid=`pgrep -u root -f clixon_backend`
+if [ -z "$pid" ]; then
+ err "backend already dead"
+fi
+# kill backend
+sudo clixon_backend -z -f $cfg
+if [ $? -ne 0 ]; then
+ err "kill backend"
+fi
+
+rm -rf $dir
diff --git a/test/test_cli.sh b/test/test_cli.sh
index 3bf4af77..23fee546 100755
--- a/test/test_cli.sh
+++ b/test/test_cli.sh
@@ -13,8 +13,10 @@ APPNAME=example
. ./lib.sh
cfg=$dir/conf_yang.xml
+# Use yang in example
+
cat < $cfg
-
+
$cfg
/usr/local/share/$APPNAME/yang
/usr/local/share/clixon
@@ -57,7 +59,7 @@ expectfn "$clixon_cli -1 -f $cfg delete interfaces" 0 "^$"
new "cli show configuration delete top"
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$"
-new "cli configure"
+new "cli configure set interfaces"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" 0 "^$"
new "cli show configuration"
@@ -113,9 +115,15 @@ expectfn "$clixon_cli -1 -f $cfg -l o debug level 1" 0 "^$"
expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" 0 "^$"
new "cli rpc"
-expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 "ipv4 " "2.3.4.5 "
+expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 'rpc-reply {
+ route {
+ address-family ipv4;
+ next-hop {
+ next-hop-list 2.3.4.5;
+ }
+ source-protocol static;'
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_compat.sh b/test/test_compat.sh
new file mode 100755
index 00000000..77875410
--- /dev/null
+++ b/test/test_compat.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+# Test of backward compatibility
+# 1) Load <3.9 startup/running/extra files without namespaces - ensure it returns namespaces
+#
+
+APPNAME=example
+# include err() and new() functions and creates $dir
+. ./lib.sh
+cfg=$dir/conf_startup.xml
+
+cat < $cfg
+
+ $cfg
+ /usr/local/share/$APPNAME/yang
+ /usr/local/share/clixon
+ example
+ $APPNAME
+ /usr/local/lib/$APPNAME/backend
+ /usr/local/lib/$APPNAME/netconf
+ /usr/local/lib/$APPNAME/restconf
+ /usr/local/lib/$APPNAME/cli
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ /usr/local/lib/xmldb/text.so
+ 0
+ init
+ true
+
+
+EOF
+
+
+run(){
+ mode=$1
+ expect=$2
+
+ dbdir=$dir/db
+ cat < $dbdir
+
+
+
+ run
+ ex:eth
+
+
+
+EOF
+ sudo mv $dbdir /usr/local/var/$APPNAME/running_db
+
+ cat < $dbdir
+
+
+
+ startup
+ ex:eth
+
+
+
+EOF
+ sudo mv $dbdir /usr/local/var/$APPNAME/startup_db
+
+ cat < $dir/config
+
+
+
+ extra
+ ex:eth
+
+
+
+EOF
+
+ new "test params: -f $cfg -s $mode -c $dir/config"
+
+ if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+
+ new "start backend -f $cfg -s $mode -c $dir/config"
+ sudo $clixon_backend -f $cfg -s $mode -c $dir/config
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ fi
+
+ new "Check $mode"
+ expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' "^$expect ]]>]]>$"
+
+ if [ $BE -eq 0 ]; then
+ exit # BE
+ fi
+
+ new "Kill backend"
+ # Check if premature kill
+ pid=`pgrep -u root -f clixon_backend`
+ if [ -z "$pid" ]; then
+ err "backend already dead"
+ fi
+ # kill backend
+ sudo clixon_backend -z -f $cfg
+ if [ $? -ne 0 ]; then
+ err "kill backend"
+ fi
+
+} # run
+
+run running 'extra ex:eth true lo ex:loopback true run ex:eth true '
+run startup 'extra ex:eth true lo ex:loopback true startup ex:eth true '
+
+rm -rf $dir
diff --git a/test/test_datastore.sh b/test/test_datastore.sh
index bcc7b27b..9744413d 100755
--- a/test/test_datastore.sh
+++ b/test/test_datastore.sh
@@ -50,9 +50,6 @@ EOF
xml='1 2 first-entry 1 3 second-entry 2 3 third-entry a b c astring '
-# Without xmlns
-xmlxxx='1 2 first-entry 1 3 second-entry 2 3 third-entry a b c astring '
-
run(){
name=$1
mydir=$dir/$name
@@ -72,7 +69,7 @@ run(){
expectmatch "$ret" $? "0" ""
new "datastore $name get"
- expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
+ expectfn "$datastore $conf get /" 0 "^$xml$"
new "datastore $name put all remove"
expectfn "$datastore $conf put remove " 0 ""
@@ -87,7 +84,7 @@ run(){
# expectfn "$datastore $conf put merge $xml" 0 ""
new "datastore $name get"
- expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
+ expectfn "$datastore $conf get /" 0 "^$xml$"
new "datastore $name put all delete"
expectfn "$datastore $conf put remove " 0 ""
@@ -100,7 +97,7 @@ run(){
expectmatch "$ret" $? "0" ""
new "datastore $name get"
- expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
+ expectfn "$datastore $conf get /" 0 "^$xml$"
new "datastore $name put top create"
expectfn "$datastore $conf put create " 0 "" # error
diff --git a/test/test_feature.sh b/test/test_feature.sh
index 4bbb334c..20adfa3f 100755
--- a/test/test_feature.sh
+++ b/test/test_feature.sh
@@ -90,14 +90,13 @@ new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf enabled feature"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'foo ]]>]]>' "^ ]]>]]>$"
new "netconf validate enabled feature"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf disabled feature"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo ]]>]]>" '^operation-failed application error Validation failed ]]>]]>$'
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo ]]>]]>" '^operation-failed protocol error XML node config/A has no corresponding yang specification (Invalid XML or wrong Yang spec?'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'foo ]]>]]>' '^application unknown-element y error Unassigned yang spec ]]>]]>$'
# This test has been broken up into all different modules instead of one large
# reply since the modules change so often
@@ -106,6 +105,13 @@ ret=$($clixon_netconf -qf $cfg -y $fyang< ]]>]]>
EOF
)
+new "netconf modules-state header"
+expect='^'
+match=`echo "$ret" | grep -GZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
+
new "netconf module A"
expect="example urn:example:clixon A implement "
match=`echo "$ret" | grep -GZo "$expect"`
@@ -136,8 +142,9 @@ if [ -z "$match" ]; then
err "$expect" "$ret"
fi
+# Note order of features in ietf-netconf yang is alphabetically: candidate, startup, validate, xpath
new "netconf module ietf-netconf"
-expect="module>ietf-netconf 2011-06-01 urn:ietf:params:xml:ns:netconf:base:1.0 candidate startup validate xpath implement "
+expect="ietf-netconf 2011-06-01 urn:ietf:params:xml:ns:netconf:base:1.0 candidate startup validate xpath implement "
match=`echo "$ret" | grep -GZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -162,7 +169,7 @@ if [ -z "$match" ]; then
err "$expect" "$ret"
fi
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_identity.sh b/test/test_identity.sh
index 1acbc764..eeaa6198 100755
--- a/test/test_identity.sh
+++ b/test/test_identity.sh
@@ -122,47 +122,47 @@ if [ $BE -ne 0 ]; then
fi
new "Set crypto to aes"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'aes ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'aes ]]>]]>' '^ ]]>]]>$'
new "netconf validate "
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "Set crypto to mc:aes"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "mc:aes ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'mc:aes ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "Set crypto to des:des3"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "des:des3 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'des:des3 ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "Set crypto to mc:foo"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "mc:foo ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'mc:foo ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "Set crypto to des:des3 using xmlns"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "des:des3 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'des:des3 ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
# XXX this is not supported
#new "Set crypto to x:des3 using xmlns"
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "x:des3 ]]>]]>" "^ ]]>]]>$"
-
-#new "netconf validate"
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
-
-new "Set crypto to foo:bar"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo:bar ]]>]]>" "^ ]]>]]>$"
+#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'x:des3 ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error Identityref validation failed, foo:bar not derived from crypto-alg ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "Set crypto to foo:bar"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'foo:bar ]]>]]>' "^ ]]>]]>$"
+
+new "netconf validate"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^application operation-failed error Identityref validation failed, foo:bar not derived from crypto-alg ]]>]]>$"
new "cli set crypto to mc:aes"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto mc:aes" 0 "^$"
@@ -182,7 +182,7 @@ expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set crypto des:des3" 0 "^$"
new "cli validate"
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o validate" 0 "^$"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_install.sh b/test/test_install.sh
index 032bba4a..09867c42 100755
--- a/test/test_install.sh
+++ b/test/test_install.sh
@@ -12,19 +12,23 @@ if [ $? -ne 0 ]; then
err
fi
-new "Check installed files"
+new "Check installed files /usr"
if [ ! -d $dir/usr ]; then
err $dir/usr
fi
+new "Check installed files /www-data"
if [ ! -d $dir/www-data ]; then
err $dir/www-data
fi
+new "Check installed files clixon-config"
if [ ! -f $dir/usr/local/share/clixon/clixon-config* ]; then
err $dir/usr/local/share/clixon/clixon-config*
fi
+new "Check installed files libclixon.so"
if [ ! -h $dir/usr/local/lib/libclixon.so ]; then
err $dir/usr/local/lib/libclixon.so
fi
+new "Check installed files libclixon_backend.so"
if [ ! -h $dir/usr/local/lib/libclixon_backend.so ]; then
err $dir/usr/local/lib/libclixon_backend.so
fi
diff --git a/test/test_leafref.sh b/test/test_leafref.sh
index 72faaa4e..4bb83b5c 100755
--- a/test/test_leafref.sh
+++ b/test/test_leafref.sh
@@ -88,34 +88,31 @@ if [ $BE -ne 0 ]; then
fi
new "leafref base config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "
-eth0 ex:eth 192.0.2.1 192.0.2.2
-lo ex:lo 127.0.0.1
- ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth0 ex:eth 192.0.2.1 24 192.0.2.2 24 lo ex:lo 127.0.0.1 32 ]]>]]>' '^ ]]>]]>$'
new "leafref get config"
-expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^eth0 '
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^eth0 '
new "leafref base commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "leafref get config"
-expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^eth0 '
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^eth0 '
new "leafref add wrong ref"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth3 10.0.4.6 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth3 10.0.4.6 ]]>]]>' '^ ]]>]]>$'
-new "leafref validate XXX shouldnt really be operation-failed, more work in validate code"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed "
+new "leafref validate"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^application bad-element eth3 error Leafref validation failed: No such leaf ]]>]]>$'
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 " ]]>]]>" "^ ]]>]]>$"
new "leafref add correct absref"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth0 ]]>]]>' '^ ]]>]]>$'
new "leafref add correct relref"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth0 ]]>]]>' '^ ]]>]]>$'
# XXX add address
@@ -123,10 +120,10 @@ new "leafref validate (ok)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ "
new "leafref delete leaf"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth0 ]]>]]>" "^ "
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth0 ]]>]]>' '^ '
new "leafref validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed "
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^application bad-element eth0 error Leafref validation failed: No such leaf ]]>]]>$'
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -143,7 +140,7 @@ expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender a" 0 "^$"
new "cli sender template"
expectfn "$clixon_cli -1f $cfg -y $fyang -l o set sender b template a" 0 "^$"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_list.sh b/test/test_list.sh
index 7e42a23b..1b9bdc2e 100755
--- a/test/test_list.sh
+++ b/test/test_list.sh
@@ -81,19 +81,19 @@ if [ $BE -ne 0 ]; then
fi
new "minmax: minimal"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 0 ]]>]]>' "^ ]]>]]>$"
new "minmax: minimal validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: maximal"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 1 unbounded 0 1 0 1 unbounded 0 1 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 1 unbounded 0 1 0 1 unbounded 0 1 ]]>]]>' '^ ]]>]]>$'
new "minmax: validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: empty"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace ]]>]]>' '^ ]]>]]>$'
# NYI
if false; then
@@ -101,31 +101,31 @@ new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: no list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 ]]>]]>' '^ ]]>]]>$'
new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: no leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 ]]>]]>' '^ ]]>]]>$'
new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: Too large list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 1 2 0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 1 2 0 ]]>]]>' '^ ]]>]]>$'
new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "minmax: Too large leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "replace 0 0 1 2 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'replace 0 0 1 2 ]]>]]>' '^ ]]>]]>$'
new "minmax: validate should fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
fi # NYI
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_nacm.sh b/test/test_nacm.sh
index c22159c2..aac4be87 100755
--- a/test/test_nacm.sh
+++ b/test/test_nacm.sh
@@ -49,7 +49,7 @@ EOF
# The groups are slightly modified from RFC8341 A.1
# The rule-list is from A.2
RULES=$(cat <
+
false
deny
deny
@@ -99,7 +99,7 @@ RULES=$(cat <
- 0
+ 0
EOF
)
@@ -123,7 +123,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
sleep 1
new "start restconf daemon (-a is enable basic authentication)"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
sleep $RCWAIT
@@ -131,7 +131,7 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" 'null
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null
'
new "auth set authentication config"
@@ -141,44 +141,44 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new2 "auth get (no user: access denied)"
-expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
+expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
new2 "auth get (wrong passwd: access denied)"
-expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
+expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
new2 "auth get (access)"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
#----------------Enable NACM
new "enable nacm"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
+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)" ""
new2 "admin get nacm"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
new2 "limited get nacm"
-expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
new2 "guest get nacm"
-expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
+expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
new "admin edit nacm"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
+expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" ""
new2 "limited edit nacm"
-expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}}
'
+expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
new2 "guest edit nacm"
-expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
+expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh
index 1db3949f..edbef77f 100755
--- a/test/test_nacm_ext.sh
+++ b/test/test_nacm_ext.sh
@@ -137,8 +137,8 @@ EOF
new "test params: -f $cfg -y $fyang"
if [ $BE -ne 0 ]; then
- new "kill old backend -zf $cfg -y $fyang"
- sudo clixon_backend -zf $cfg -y $fyang
+ new "kill old backend -zf $cfg "
+ sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
@@ -155,7 +155,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon (-a is enable http basic auth)"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang -- -a" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
sleep $RCWAIT
@@ -163,41 +163,41 @@ new "restconf DELETE whole datastore"
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
new2 "auth get"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
'
new "Set x to 0"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 0}' http://localhost/restconf/data/x)" ""
+expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 0}' http://localhost/restconf/data/example:x)" ""
new2 "auth get (no user: access denied)"
-expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
+expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
new2 "auth get (wrong passwd: access denied)"
-expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
+expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
new2 "auth get (access)"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
new2 "admin get nacm"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
new2 "limited get nacm"
-expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
new2 "guest get nacm"
-expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
+expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
new "admin edit nacm"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
+expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" ""
new2 "limited edit nacm"
-expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}}
'
+expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
new2 "guest edit nacm"
-expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
+expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","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 -y $fyang show conf" 0 "^x 1;$"
@@ -209,7 +209,7 @@ new "cli show conf as guest"
expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang show conf" 255 "protocol access-denied"
new "cli rpc as admin"
-expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang rpc ipv4" 0 "2.3.4.5 "
+expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang rpc ipv4" 0 "next-hop-list 2.3.4.5;"
new "cli rpc as limited"
expectfn "$clixon_cli -1 -U wilma -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol access-denied default deny"
@@ -220,7 +220,7 @@ expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh
index 2ab70b4b..8a3c298e 100755
--- a/test/test_nacm_protocol.sh
+++ b/test/test_nacm_protocol.sh
@@ -21,7 +21,7 @@
# automatically committed to running immediately after each successful
# edit.
# Which means that restconf -X DELETE /data translates to edit-config + commit
-# WHICH IS allowed.
+# which is allowed.
APPNAME=example
# include err() and new() functions and creates $dir
@@ -65,7 +65,6 @@ module $APPNAME{
}
}
EOF
-
# The groups are slightly modified from RFC8341 A.1
# The rule-list is from A.2
RULES=$(cat <
- 0
+ 0
EOF
)
@@ -147,7 +146,7 @@ sudo pkill -u www-data -f "/www-data/clixon_restconf"
sleep 1
new "start restconf daemon (-a is enable basic authentication)"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang -D 1 -- -a" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data &
sleep $RCWAIT
@@ -158,35 +157,35 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "enable nacm"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
+expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" ""
new2 "admin get nacm"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
'
# Rule 1: deny-kill-session
new "deny-kill-session: limited fail (netconf)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "44 ]]>]]>" "^access-denied protocol error access denied ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "44 ]]>]]>" "^protocol access-denied error access denied ]]>]]>$"
new "deny-kill-session: guest fail (netconf)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "44 ]]>]]>" "^access-denied protocol error access denied ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "44 ]]>]]>" "^protocol access-denied error access denied ]]>]]>$"
new "deny-kill-session: admin ok (netconf)"
expecteof "$clixon_netconf -qf $cfg -y $fyang -U andy" 0 "44 ]]>]]>" "^ ]]>]]>$"
# Rule 2: deny-delete-config
new "deny-delete-config: limited fail (netconf)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 " ]]>]]>" "^access-denied protocol error access denied ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 " ]]>]]>" "^protocol access-denied error access denied ]]>]]>$"
new2 "deny-delete-config: guest fail (restconf)"
-expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}}
'
+expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
# In restconf delete-config is translated to edit-config which is permitted
new "deny-delete-config: limited fail (restconf) ok"
expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data)" ''
new2 "admin get nacm (should be null)"
-expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" 'null
+expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null
'
new "deny-delete-config: admin ok (restconf)"
@@ -200,19 +199,19 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "enable nacm"
-expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
+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)" ""
# Rule 3: permit-edit-config
new "permit-edit-config: limited ok restconf"
-expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" ''
+expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" ''
new2 "permit-edit-config: guest fail restconf"
-expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}}
'
+expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
@@ -228,4 +227,4 @@ if [ $? -ne 0 ]; then
err "kill backend"
fi
-#rm -rf $dir # XXX
+rm -rf $dir
diff --git a/test/test_netconf.sh b/test/test_netconf.sh
index a0eae7d8..ced3047d 100755
--- a/test/test_netconf.sh
+++ b/test/test_netconf.sh
@@ -114,14 +114,13 @@ new "netconf get-config single quotes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^ ]]>]]>$'
new "Add subtree eth/0/0 using none which should not change anything"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "none eth/0/0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'none eth/0/0 ]]>]]>' "^ ]]>]]>$"
new "Check nothing added"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^ ]]>]]>$'
new "Add subtree eth/0/0 using none and create which should add eth/0/0"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' "^ ]]>]]>$"
-
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' "^ ]]>]]>$"
# Too many quotes, (single inside double inside single) need to fool bash
cat < $tmp # new
@@ -129,22 +128,22 @@ cat < $tmp # new
EOF
new "Check eth/0/0 added using xpath"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth/0/0 ex:eth true ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^eth/0/0 ex:eth true ]]>]]>$'
new "Re-create same eth/0/0 which should generate error"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' "^"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' '^'
new "Delete eth/0/0 using none config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' '^ ]]>]]>$'
new "Check deleted eth/0/0 (non-presence container)"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^ ]]>]]>$'
new "Re-Delete eth/0/0 using none should generate error"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' "^"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth none ]]>]]>' '^'
new "netconf edit config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0 eth1 true 9.2.3.4 24 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 eth1 true 9.2.3.4 24 ]]>]]>' "^ ]]>]]>$"
# Too many quotes
cat < $tmp # new
@@ -152,7 +151,7 @@ cat < $tmp # new
EOF
new "netconf get config xpath"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth1 true ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^eth1 true ]]>]]>$'
# Too many quotes
cat < $tmp # new
@@ -160,7 +159,7 @@ cat < $tmp # new
EOF
new "netconf get config xpath parent"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^eth/0/0 true eth1 true true false 9.2.3.4 24 ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^eth/0/0 true eth1 true true false 9.2.3.4 24 ]]>]]>$'
new "netconf validate missing type"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^"
@@ -172,13 +171,13 @@ new "netconf get empty config2"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf edit extra xml"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf edit config eth1"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth1 ex:eth ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth1 ex:eth ]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -187,32 +186,32 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf edit config merge"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth2 ex:eth merge ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth2 ex:eth merge ]]>]]>' "^ ]]>]]>$"
new "netconf edit ampersand encoding(<&): name:'eth&' type:'t<>'"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth& t<> ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth& t<> ]]>]]>' '^ ]]>]]>$'
new "netconf get replaced config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^eth& t<> true eth1 ex:eth true eth2 ex:eth true ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^eth& t<> true eth1 ex:eth true eth2 ex:eth true ]]>]]>$'
new "cli show configuration eth& - encoding tests"
expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" 0 "interfaces interface eth& type t<>
interfaces interface eth& enabled true"
new "netconf edit CDATA"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "eth/0/0 ex:eth ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'eth/0/0 ex:eth ]]>]]>' "^ ]]>]]>$"
#new "netconf get CDATA"
-#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "eth/0/0 true ]]>]]>"
+#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' 'eth/0/0 true ]]>]]>'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf edit state operation should fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42 ]]>]]>" "^invalid-value "
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '42 ]]>]]>' "^protocol invalid-value "
new "netconf get state operation"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^42 ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^42 ]]>]]>$'
new "netconf lock/unlock"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]> ]]>]]>" "^ ]]>]]> ]]>]]>$"
@@ -234,7 +233,7 @@ new "copy startup"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf get startup"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^eth1 ex:eth true ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^eth1 ex:eth true ]]>]]>$'
new "netconf delete startup"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -243,18 +242,18 @@ new "netconf check empty startup"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf rpc"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'ipv4 ipv4 ]]>]]>' "^ipv4 "
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'ipv4 ipv4 ]]>]]>' '^ipv4 '
-new "netconf rpc without namespace (iterate kludge should work)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4 ipv4 ]]>]]>" "^ipv4 "
+#new "netconf rpc without namespace (iterate kludge should work)"
+#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ipv4 ipv4 ]]>]]>" "^ipv4 "
new "netconf empty rpc"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
new "netconf client-side rpc"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "example ]]>]]>" "^ok ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'example ]]>]]>' '^ok ]]>]]>$'
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_openconfig.sh b/test/test_openconfig.sh
index 26d81976..b9a4f680 100755
--- a/test/test_openconfig.sh
+++ b/test/test_openconfig.sh
@@ -1,36 +1,27 @@
#!/bin/bash
-# Parse yang openconfig tests
-# Note that the openconfig test suites are patched to counter CLixon issues as follows:
-# - release/models/mpls/openconfig-mpls-te.yang
+# Parse yang openconfig yangs from https://github.com/openconfig/public
+# Notes:
+# - openconfig test suites are patched to counter Clixon issues as follows:
+# - release/models/mpls/openconfig-mpls-te.yang
# issue: https://github.com/clicon/clixon/issues/60
-# - release/models/wifi/types/openconfig-wifi-types.yang
-# issue: https://github.com/clicon/clixon/issues/59
-#
-OPENCONFIG=public
-OCDIR=$OPENCONFIG/release/models
+# - Env variable YANGMODELS should point to checkout place. (define it in site.sh for example)
-# Clone openconfig dir if not there
-if false; then
-if [ ! -d public ]; then
- git clone https://github.com/openconfig/public
-else
- (cd public;
- #git pull
- )
-fi
-fi
-
-# include err() and new() functions and creates $dir
-. ./lib.sh
-
-# Yang specifics: multi-keys and empty type
APPNAME=example
+
# include err() and new() functions and creates $dir
. ./lib.sh
cfg=$dir/conf_yang.xml
fyang=$dir/test.yang
+new "openconfig"
+if [ ! -d "$OPENCONFIG" ]; then
+ err "Hmm Openconfig dir does not seem to exist, try git clone https://github.com/openconfig/public?"
+fi
+
+OCDIR=$OPENCONFIG/release/models
+
+
cat < $cfg
$cfg
@@ -86,17 +77,33 @@ cat < $cfg
EOF
files=$(find $OPENCONFIG -name "*.yang")
-# Just cound nr of modules (exclude submodule)
-let m=0; # Nr of modules
+# Count nr of modules (exclude submodule) Assume "module" or "submodule"
+# first word on first line
+let ms=0; # Nr of modules
+let ss=0; # Nr of smodules
for f in $files; do
- if [ -n "$(head -1 $f|grep '^module')" ]; then
+ let m=0; # Nr of modules
+ let s=0; # Nr of modules
+ if [ -n "$(head -15 $f|grep '^[ ]*module')" ]; then
let m++;
+ let ms++;
+ elif [ -n "$(head -15 $f|grep '^[ ]*submodule')" ]; then
+ let s++;
+ let ss++;
+ else
+ echo "No module or submodule found $f"
+ exit
+ fi
+ if [ $m -eq 1 -a $s -eq 1 ]; then
+ echo "Double match $f"
+ exit
fi
done
+echo "m:$ms s:$ss"
new "Openconfig test: $clixon_cli -1f $cfg -y $f show version ($m modules)"
for f in $files; do
if [ -n "$(head -1 $f|grep '^module')" ]; then
- new "cli $f"
+ new "$clixon_cli -1f $cfg -y $f show version"
expectfn "$clixon_cli -1f $cfg -y $f show version" 0 "3."
fi
diff --git a/test/test_order.sh b/test/test_order.sh
index a06a036b..4db73e9c 100755
--- a/test/test_order.sh
+++ b/test/test_order.sh
@@ -16,7 +16,6 @@ fyang=$dir/order.yang
dbdir=$dir/order
-new "Set up $dbdir"
rm -rf $dbdir
if [ ! -d $dbdir ]; then
mkdir $dbdir
@@ -84,30 +83,32 @@ module example{
}
EOF
+rm -f $dbdir/candidate_db
+# alt
cat < $dbdir/running_db
- d
- d
- d bar
- d bar
- b
- b
- hej
- c
- c
- a bar
- a bar
- hopp
- a
- a
- c bar
- c bar
- b bar
- b bar
+ d
+ d
+ d bar
+ d bar
+ b
+ b
+ hej
+ c
+ c
+ a bar
+ a bar
+ hopp
+ a
+ a
+ c bar
+ c bar
+ b bar
+ b bar
EOF
-new "test params: -f $cfg -y $fyang"
+new "test params: -s running -f $cfg -y $fyang"
if [ $BE -ne 0 ]; then
new "kill old backend"
@@ -123,56 +124,56 @@ if [ $BE -ne 0 ]; then
fi
# Check as file
-new "verify running from start, should be: l,c,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if XML_SORT set to false"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^hej hopp d b c a a b c d d bar a bar c bar b bar a bar b bar c bar d bar ]]>]]>$"
+new "verify running from start, should be: c,l,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if CLICON_XML_SORT set to false"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^hej hopp d b c a a b c d d bar a bar c bar b bar a bar b bar c bar d bar ]]>]]>$'
new "get each ordered-by user leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^a bar ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^a bar ]]>]]>$'
new "get each ordered-by user leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^a bar ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^a bar ]]>]]>$'
new "get each ordered-by user leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^b bar ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^b bar ]]>]]>$'
new "get each ordered-by user leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^b bar ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^b bar ]]>]]>$'
new "delete candidate"
-expecteof "$clixon_netconf -qf $cfg" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 'none ]]>]]>' "^ ]]>]]>$"
# LEAF_LISTS
new "add two entries to leaf-list user order"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "c b ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'c b ]]>]]>' "^ ]]>]]>$"
new "add one entry to leaf-list user order"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'a ]]>]]>' "^ ]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "add one entry to leaf-list user order after commit"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "0 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '0 ]]>]]>' "^ ]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "verify leaf-list user order in running (as entered)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^c b a 0 ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^c b a 0 ]]>]]>$'
# LISTS
new "add two entries to list user order"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "c bar b foo ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'c bar b foo ]]>]]>' "^ ]]>]]>$"
new "add one entry to list user order"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a fie ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'a fie ]]>]]>' "^ ]]>]]>$"
new "verify list user order (as entered)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^c bar b foo a fie ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^c bar b foo a fie ]]>]]>$'
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_perf.sh b/test/test_perf.sh
index d03fcbde..eb1fe687 100755
--- a/test/test_perf.sh
+++ b/test/test_perf.sh
@@ -78,12 +78,12 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
sleep $RCWAIT
new "generate 'large' config with $number list entries"
-echo -n "" > $fconfig
+echo -n "" > $fconfig
for (( i=0; i<$number; i++ )); do
echo -n "$i $i " >> $fconfig
done
@@ -111,7 +111,7 @@ new "netconf commit large config again"
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf add small (1 entry) config"
-expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "x y ]]>]]>" "^ ]]>]]>$"
+expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 'x y ]]>]]>' "^ ]]>]]>$"
new "netconf get $req small config"
time -p for (( i=0; i<$req; i++ )); do
@@ -128,7 +128,7 @@ done
new "netconf add $req small config"
time -p for (( i=0; i<$req; i++ )); do
rnd=$(( ( RANDOM % $number ) ))
- echo "$rnd $rnd ]]>]]>"
+ echo "$rnd $rnd ]]>]]>"
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
new "netconf add $req restconf small config"
@@ -138,10 +138,10 @@ time -p for (( i=0; i<$req; i++ )); do
done
new "netconf get large config"
-expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^0 0 1 1 "
+expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^0 0 1 1 '
new "generate large leaf-list config"
-echo -n "replace " > $fconfig
+echo -n "replace " > $fconfig
for (( i=0; i<$number; i++ )); do
echo -n "$i " >> $fconfig
done
@@ -158,22 +158,22 @@ expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "$rnd ]]>]]>"
+ echo "$rnd ]]>]]>"
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
new "netconf add small leaf-list config"
-expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "x ]]>]]>" "^ ]]>]]>$"
+expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 'x ]]>]]>' "^ ]]>]]>$"
new "netconf commit small leaf-list config"
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf get large leaf-list config"
-expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^0 1 "
+expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^0 1 '
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index f98b87e4..66710e91 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -52,14 +52,6 @@ module example{
}
rpc empty {
}
- rpc input {
- input {
- }
- }
- rpc output {
- output {
- }
- }
rpc client-rpc {
description "Example local client-side rpc";
input {
@@ -84,7 +76,7 @@ module example{
EOF
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
-state='{"state": {"op": "42"}}'
+state='{"example:state": {"op": "42"}}'
new "test params: -f $cfg -y $fyang"
if [ $BE -ne 0 ]; then
@@ -104,7 +96,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
new "start restconf daemon"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang -D 1" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
sleep $RCWAIT
@@ -124,13 +116,14 @@ new2 "restconf get restconf resource. RFC 8040 3.3 (xml)"
expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" '2016-06-21
'
+# Should be alphabetically ordered
new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)"
-expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:input": null,"example:output": null,"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null}}
+expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null,"clixon-lib:debug": null}
'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
-expect=' '
+expect=' '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -147,8 +140,8 @@ if [ -z "$match" ]; then
err "$expect" "$ret"
fi
-new2 "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895"
-expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"module": [{"name": "ietf-routing","revision": "2014-10-26","namespace": "urn:ietf:params:xml:ns:yang:ietf-routing","conformance-type": "implement"}]}
+new2 "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)"
+expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"ietf-yang-library:module": [{"name": "ietf-routing","revision": "2014-10-26","namespace": "urn:ietf:params:xml:ns:yang:ietf-routing","conformance-type": "implement"}]}
'
new "restconf options. RFC 8040 4.1"
@@ -159,169 +152,174 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json"
new "restconf empty rpc"
-expecteq "$(curl -s -X POST -d {\"input\":null} http://localhost/restconf/operations/example:empty)" ""
+expecteq "$(curl -s -X POST -d {\"example:input\":null} http://localhost/restconf/operations/example:empty)" ""
+
+new2 "restconf empty rpc with extra args (should fail)"
+expecteq "$(curl -s -X POST -d {\"example:input\":{\"extra\":null}} http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
new2 "restconf get empty config + state json"
-expecteq "$(curl -sSG http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
+expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
'
new2 "restconf get empty config + state json + module"
-expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"state": {"op": "42"}}
+expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
'
new2 "restconf get empty config + state json with wrong module name"
-expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "No yang node found: badmodule:state"}}}}
'
+expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No such yang module: badmodule"}}}
'
new "restconf get empty config + state xml"
-ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state)
-expect="42 "
+ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state)
+expect='42 '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
new2 "restconf get data/ json"
-expecteq "$(curl -s -G http://localhost/restconf/data/state/op=42)" '{"op": "42"}
+expecteq "$(curl -s -G http://localhost/restconf/data/example:state/op=42)" '{"example:op": "42"}
'
new "restconf get state operation eth0 xml"
# Cant get shell macros to work, inline matching from lib.sh
-ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state/op=42)
-expect="42 "
+ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state/op=42)
+expect='42 '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
new2 "restconf get state operation eth0 type json"
-expecteq "$(curl -s -G http://localhost/restconf/data/state/op=42)" '{"op": "42"}
+expecteq "$(curl -s -G http://localhost/restconf/data/example:state/op=42)" '{"example:op": "42"}
'
new "restconf get state operation eth0 type xml"
# Cant get shell macros to work, inline matching from lib.sh
-ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state/op=42)
-expect="42 "
+ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state/op=42)
+expect='42 '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
new2 "restconf GET datastore"
-expecteq "$(curl -s -X GET http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
+expecteq "$(curl -s -X GET http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
'
# Exact match
new "restconf Add subtree to datastore using POST"
-expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK'
+expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK'
new "restconf Re-add subtree which should give error"
-expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
+expectfn 'curl -s -X POST -d {"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} 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"}}}'
# XXX Cant get this to work
-#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
+#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"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 Check interfaces eth/0/0 added"
-expectfn "curl -s -G http://localhost/restconf/data" 0 '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]},"state": {"op": "42"}}
-
'
+expectfn "curl -s -G http://localhost/restconf/data" 0 '"ietf-interfaces:interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]}'
new "restconf delete interfaces"
-expecteq $(curl -s -X DELETE http://localhost/restconf/data/interfaces) ""
+expecteq $(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces) ""
new "restconf Check empty config"
-expectfn "curl -sG http://localhost/restconf/data/state" 0 "$state"
+expectfn "curl -sG http://localhost/restconf/data/example:state" 0 "$state"
+# XXX: gives
+#
new "restconf Add interfaces subtree eth/0/0 using POST"
-expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/interfaces' 0 ""
+expectfn 'curl -s -X POST -d {"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/ietf-interfaces:interfaces' 0 ""
# XXX cant get this to work
#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" ""
new2 "restconf Check eth/0/0 added config"
-expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
+expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
'
new2 "restconf Check eth/0/0 added state"
-expecteq "$(curl -s -G http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
+expecteq "$(curl -s -G http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
'
new2 "restconf Re-post eth/0/0 which should generate error"
-expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
+expecteq "$(curl -s -X POST -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
new "Add leaf description using POST"
-expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
+expecteq "$(curl -s -X POST -d '{"ietf-interfaces:description":"The-first-interface"}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
new "Add nothing using POST"
-expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "malformed-message","error-type": "rpc","error-severity": "error","error-message": " on line 1: syntax error at or before:'
+expectfn 'curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": " on line 1: syntax error at or before:'
new2 "restconf Check description added"
-expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}}
+expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}}
'
new "restconf delete eth/0/0"
-expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
+expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
new "Check deleted eth/0/0"
expectfn 'curl -s -G http://localhost/restconf/data' 0 $state
new2 "restconf Re-Delete eth/0/0 using none should generate error"
-expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-missing","error-type": "application","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}}
'
+expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}}
'
new "restconf Add subtree eth/0/0 using PUT"
-expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
+expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
new2 "restconf get subtree"
-expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
+expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
'
new2 "restconf rpc using POST json"
-expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"}}}}
+expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"routing-instance-name":"ipv4","destination-address":{"address-family":"ipv6"}}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-routing:output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"},"source-protocol": "static"}}}
'
# Cant get this to work due to quoting
#new2 "restconf rpc using POST wrong JSON"
-#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}}
'
+#expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}
'
-new2 "restconf rpc using POST json w/o mandatory element"
-expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}}
'
-new2 "restconf rpc non-existing rpc w/o namespace"
-expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}}
'
+new2 "restconf rpc using POST json without mandatory element"
+expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}}
'
+
+new2 "restconf rpc non-existing rpc without namespace"
+expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
new2 "restconf rpc non-existing rpc"
-expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang node not found"}}}}
'
+expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/example:kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
new2 "restconf rpc missing name"
-expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Operation name expected"}}}}
'
+expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}}
'
new2 "restconf rpc missing input"
-expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "Missing mandatory variable: routing-instance-name"}}}}
'
+expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "restconf RPC does not have input statement"}}}
'
new "restconf rpc using POST xml"
-ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)
-expect="ipv4 2.3.4.5 "
+ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"ietf-routing:input":{"routing-instance-name":"ipv4","destination-address":{"address-family":"ipv4"}}}' http://localhost/restconf/operations/ietf-routing:fib-route)
+expect='ipv4 2.3.4.5 static '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
new2 "restconf rpc using wrong prefix"
-expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "yang module not found"}}}}
'
+expecteq "$(curl -s -X POST -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
new "restconf local client rpc using POST xml"
-ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc)
-expect="ok "
+ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"example:input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc)
+expect='ok '
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
# XXX cant get -H to work
-#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' 'ipv4 2.3.4.5 '
+#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"ietf-routing:input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' 'ipv4 2.3.4.5 '
# Cant get shell macros to work, inline matching from lib.sh
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh
index dbddee09..68677250 100755
--- a/test/test_restconf2.sh
+++ b/test/test_restconf2.sh
@@ -65,85 +65,94 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
sleep $RCWAIT
new "restconf tests"
new "restconf POST initial tree"
-expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
+expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
new "restconf GET datastore intial"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
-new "restconf GET interface"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" 0 '{"interface": \[{"name": "local0","type": "regular"}\]}'
+new "restconf GET interface subtree"
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1/interface=local0" 0 '{"example:interface": \[{"name": "local0","type": "regular"}\]}'
+
+new "restconf GET interface subtree xml"
+ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:cont1/interface=local0)
+expect='local0 regular '
+match=`echo $ret | grep -EZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
new "restconf GET if-type"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" 0 '{"type": "regular"}'
+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 -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' 0 '"error-message": "Missing mandatory variable: type"'
+expectfn 'curl -s -X POST -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"}}}
'
new "restconf POST interface"
-expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 ""
+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?
new2 "restconf POST again"
-expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/cont1)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
+expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
new2 "restconf POST from top"
-expecteq "$(curl -s -X POST -d '{"cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "data-exists","error-type": "application","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
+expecteq "$(curl -s -X POST -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"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 DELETE"
-expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 ""
+expectfn 'curl -s -X DELETE http://localhost/restconf/data/example:cont1' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
new "restconf POST initial tree"
-expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
+expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
new "restconf GET initial tree"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
new "restconf DELETE whole datastore"
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
new "restconf GET null datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
new "restconf PUT initial datastore"
-expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
+expectfn 'curl -s -X PUT -d {"data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
new "restconf GET datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
new "restconf PUT replace datastore"
-expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
+expectfn 'curl -s -X PUT -d {"data":{"example:cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
new "restconf GET replaced datastore"
-expectfn "curl -s -X GET http://localhost/restconf/data/cont2" 0 '{"cont2": {"name": "foo"}}'
+expectfn "curl -s -X GET http://localhost/restconf/data/example:cont2" 0 '{"example:cont2": {"name": "foo"}}'
new "restconf PUT initial datastore again"
-expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
+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/cont1/interface=local0' 0 ""
+expectfn 'curl -s -X PUT -d {"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/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}'
+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/cont1/interface=TEST' 0 ""
+expectfn 'curl -s -X PUT -d {"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/cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"rpc-error": {"error-tag": "operation-failed","error-type": "protocol","error-severity": "error","error-message": "api-path keys do not match data keys"}}}}'
+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"}}}'
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_rpc.sh b/test/test_rpc.sh
new file mode 100755
index 00000000..27fb74c8
--- /dev/null
+++ b/test/test_rpc.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+# RPC tests
+# Validate parameters in restconf and netconf, check namespaces, etc
+# See rfc8040 3.6
+APPNAME=example
+
+# include err() and new() functions and creates $dir
+. ./lib.sh
+cfg=$dir/conf.xml
+
+# Use yang in example
+cat < $cfg
+
+ $cfg
+ /usr/local/share/$APPNAME/yang
+ /usr/local/share/clixon
+ $APPNAME
+ /usr/local/lib/$APPNAME/backend
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ false
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ /usr/local/lib/xmldb/text.so
+
+EOF
+
+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"
+ sudo $clixon_backend -s init -f $cfg -D $DBG
+ if [ $? -ne 0 ]; then
+ err
+ fi
+fi
+
+new "kill old restconf daemon"
+sudo pkill -u www-data clixon_restconf
+
+new "start restconf daemon"
+sudo su -c "$clixon_restconf -f $cfg -D $DBG" -s /bin/sh www-data &
+
+sleep $RCWAIT
+
+new "rpc tests"
+
+# 1.First some positive tests vary media types
+# extra complex because pattern matching on return haders
+new "restconf empty rpc"
+ret=$(curl -is -X POST http://localhost/restconf/operations/example:empty)
+expect="204 No Content"
+match=`echo $ret | grep -EZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
+
+new "netconf empty rpc"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^ ]]>]]>$'
+
+new2 "restconf example rpc json/json default - no http media headers"
+expecteq "$(curl -s -X POST -d '{"example:input":{"x":0}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
+
'
+
+new2 "restconf example rpc json/json change y default"
+expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "99"}}
+
'
+
+new2 "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 '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
+
'
+
+new2 "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 '0 ' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
+
'
+
+new2 "restconf example rpc json/xml"
+expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '0 42
+
'
+
+new2 "restconf example rpc xml/xml"
+expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '0 ' http://localhost/restconf/operations/example:example)" '0 42
+
'
+
+new "netconf example rpc"
+expecteof "$clixon_netconf -qf $cfg" 0 '0 ]]>]]>' '^0 42 ]]>]]>$'
+
+# 2. Then error cases
+#
+new "restconf empy rpc with null input"
+ret=$(curl -is -X POST -d '{"example:input":null}' http://localhost/restconf/operations/example:empty)
+expect="204 No Content"
+match=`echo $ret | grep -EZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
+
+new2 "restconf empy rpc with input x"
+expecteq "$(curl -s -X POST -d '{"example:input":{"x":0}}' http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "x"},"error-severity": "error"}}}
'
+
+new2 "restconf omit mandatory"
+expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
+
+new2 "restconf add extra"
+expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
+
+new2 "restconf wrong method"
+expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
+
+new2 "restconf example missing input"
+expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
+
+
+new "netconf kill-session missing session-id mandatory"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^application missing-element session-id error Mandatory variable ]]>]]>$'
+
+new "netconf edit-config ok"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' "^ ]]>]]>$"
+
+new "netconf edit-config extra arg should fail"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' "^application unknown-element extra error ]]>]]>$"
+
+new "netconf edit-config empty target should fail"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^application data-missing missing-choice config-target error ]]>]]>$'
+
+new "netconf edit-config missing target should fail"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^application missing-element target error Mandatory variable ]]>]]>$'
+
+new "netconf edit-config missing config should fail"
+expecteof "$clixon_netconf -qf $cfg" 0 ' ]]>]]>' '^application data-missing missing-choice edit-content error ]]>]]>$'
+
+new "Kill restconf daemon"
+sudo pkill -u www-data -f "/www-data/clixon_restconf"
+
+if [ $BE -eq 0 ]; then
+ exit # BE
+fi
+
+new "Kill backend"
+# Check if premature kill
+pid=`pgrep -u root -f clixon_backend`
+if [ -z "$pid" ]; then
+ err "backend already dead"
+fi
+# kill backend
+sudo clixon_backend -z -f $cfg
+if [ $? -ne 0 ]; then
+ err "kill backend"
+fi
+
+rm -rf $dir
diff --git a/test/test_startup.sh b/test/test_startup.sh
index d9ec5b07..ab0acb0f 100755
--- a/test/test_startup.sh
+++ b/test/test_startup.sh
@@ -41,7 +41,7 @@ run(){
dbdir=$dir/db
cat < $dbdir
-
+
run
ex:eth
@@ -53,7 +53,7 @@ EOF
cat < $dbdir
-
+
startup
ex:eth
@@ -65,7 +65,7 @@ EOF
cat < $dir/config
-
+
extra
ex:eth
@@ -104,8 +104,8 @@ EOF
}
run init ' '
-run none 'run ex:eth true '
-run running 'extra ex:eth true lo ex:loopback true run ex:eth true '
-run startup 'extra ex:eth true lo ex:loopback true startup ex:eth true '
+run none 'run ex:eth true '
+run running 'extra ex:eth true lo ex:loopback true run ex:eth true '
+run startup 'extra ex:eth true lo ex:loopback true startup ex:eth true '
rm -rf $dir
diff --git a/test/test_stream.sh b/test/test_stream.sh
index 910a29c8..75c7e613 100755
--- a/test/test_stream.sh
+++ b/test/test_stream.sh
@@ -65,7 +65,7 @@ EOF
# using reportingEntity (rfc5277) not reporting-entity (rfc8040)
cat < $fyang
module example {
- namespace "http://example.com/event/1.0";
+ namespace "urn:example:clixon";
prefix ex;
organization "Example, Inc.";
contact "support at example.com";
@@ -121,7 +121,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
-sudo su -c "$clixon_restconf -f $cfg -y $fyang" -s /bin/sh www-data &
+sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
sleep $RCWAIT
@@ -130,35 +130,35 @@ sleep $RCWAIT
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 ' ]]>]]>' 'EXAMPLE Example event stream true ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' 'EXAMPLE Example event stream true ]]>]]>'
new "netconf event stream discovery RFC8040 Sec 6.2"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' 'EXAMPLE Example event stream true xml https://localhost/streams/EXAMPLE ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' 'EXAMPLE Example event stream true xml https://localhost/streams/EXAMPLE ]]>]]>'
#
# 1.2 Netconf stream subscription
new "netconf EXAMPLE subscription"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE ]]>]]>' '^ ]]>]]>20' $NCWAIT
+expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE ]]>]]>' '^ ]]>]]>20' $NCWAIT
new "netconf subscription with empty startTime"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE ]]>]]>' '^ ]]>]]>20' $NCWAIT
+expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE ]]>]]>' '^ ]]>]]>20' $NCWAIT
new "netconf EXAMPLE subscription with simple filter"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE ]]>]]>" '^ ]]>]]>20' $NCWAIT
+expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE ]]>]]>' '^ ]]>]]>20' $NCWAIT
new "netconf EXAMPLE subscription with filter classifier"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE ]]>]]>" '^ ]]>]]>20' $NCWAIT
+expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE ]]>]]>" '^ ]]>]]>20' $NCWAIT
new "netconf NONEXIST subscription"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" 'NONEXIST ]]>]]>' '^invalid-value application error No such stream ]]>]]>$' $NCWAIT
+expectwait "$clixon_netconf -qf $cfg -y $fyang" 'NONEXIST ]]>]]>' '^application invalid-value error No such stream ]]>]]>$' $NCWAIT
new "netconf EXAMPLE subscription with wrong date"
-expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE kallekaka ]]>]]>' '^bad-element application startTime error Expected timestamp ]]>]]>$' 0
+expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLE kallekaka ]]>]]>' '^application bad-element startTime error regexp match fail: "kallekaka" does not match' 0
#new "netconf EXAMPLE subscription with replay"
#NOW=$(date +"%Y-%m-%dT%H:%M:%S")
#sleep 10
-#expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE $NOW ]]>]]>" '^ ]]>]]>20' 10
+#expectwait "$clixon_netconf -qf $cfg -y $fyang" "EXAMPLE $NOW ]]>]]>" '^ ]]>]]>20' 10
sleep 2
#
@@ -166,22 +166,22 @@ sleep 2
new "2. Restconf RFC8040 stream testing"
# 2.1 Stream discovery
new "restconf event stream discovery RFC8040 Sec 6.2"
-expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"streams": {"stream": \[{"name": "EXAMPLE","description": "Example event stream","replay-support": true,"access": \[{"encoding": "xml","location": "https://localhost/streams/EXAMPLE"}\]}\]}'
+expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"ietf-restconf-monitoring:streams": {"stream": \[{"name": "EXAMPLE","description": "Example event stream","replay-support": true,"access": \[{"encoding": "xml","location": "https://localhost/streams/EXAMPLE"}\]}\]}'
sleep 2
new "restconf subscribe RFC8040 Sec 6.3, get location"
-expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location" 0 '{"location": "https://localhost/streams/EXAMPLE"}'
+expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location" 0 '{"ietf-restconf-monitoring:location": "https://localhost/streams/EXAMPLE"}'
sleep 2
# Restconf stream subscription RFC8040 Sec 6.3
# Start Subscription w error
new "restconf monitor event nonexist stream"
-expectwait 'curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" http://localhost/streams/NOTEXIST' 0 'invalid-value application error No such stream ' 2
+expectwait 'curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" http://localhost/streams/NOTEXIST' 0 'application invalid-value error No such stream ' 2
# 2a) start subscription 8s - expect 1-2 notifications
new "2a) start subscriptions 8s - expect 1-2 notifications"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
@@ -193,10 +193,11 @@ if [ $nr -lt 1 -o $nr -gt 2 ]; then
fi
sleep 2
+
# 2b) start subscription 8s - stoptime after 5s - expect 1-2 notifications
new "2b) start subscriptions 8s - stoptime after 5s - expect 1-2 notifications"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8 -e +10)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -209,7 +210,7 @@ fi
# 2c
new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -8)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -222,7 +223,7 @@ fi
# 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications
new "2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -30 -e +4)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -235,7 +236,7 @@ fi
# 2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications
new "2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -90 -e +0)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -246,15 +247,14 @@ if [ $nr -lt 9 -o $nr -gt 14 ]; then
err 10 "$nr"
fi
-
# Try parallell
# start background job
curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null &
PID=$!
-new "Start subscription in parallell"
+new "Start subscriptions in parallell"
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8)
-expect="data: ${DATE}T[0-9:.]* fault Ethernet0 major "
+expect="data: ${DATE}T[0-9:.]*Z fault Ethernet0 major "
match=$(echo "$ret" | grep -Eo "$expect")
if [ -z "$match" ]; then
@@ -278,7 +278,7 @@ sleep 5
new "Kill restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_type.sh b/test/test_type.sh
index 4ff09bbc..188107a6 100755
--- a/test/test_type.sh
+++ b/test/test_type.sh
@@ -1,8 +1,10 @@
#!/bin/bash
# Advanced union types and generated code
# and enum w values
+# XXX NO SUPPORT FOR lists of ranges and lengths !!!
APPNAME=example
# include err() and new() functions and creates $dir
+
. ./lib.sh
cfg=$dir/conf_yang.xml
@@ -123,57 +125,47 @@ module example{
enum down;
}
}
- leaf length1 {
- type string {
- length "1";
- }
- }
-/* leaf length2 {
- type string {
- length "max";
- }
- }
- leaf length3 {
- type string {
- length "min";
- }
- }*/
- leaf length4 {
- type string {
- length "4..4000";
- }
- }
-/* leaf length5 {
- type string {
- length "min..max";
- }
- }*/
leaf num1 {
type int32 {
range "1";
}
}
-/* leaf num2 {
- type int32 {
- range "min";
- }
- }
- leaf num3 {
- type int32 {
- range "max";
- }
- }
-*/
- leaf num4 {
+ leaf num2 {
type int32 {
range "4..4000";
}
}
-/* leaf num5 {
- type int32 {
+ leaf num3 {
+ type uint8 {
range "min..max";
}
- }*/
+ }
+ leaf num4 { /* XXX multiple ranges not supported yet - only first*/
+ type uint8 {
+ range "1 .. 2 | 42";
+ }
+ }
+ leaf len1 {
+ type string {
+ length "2";
+ }
+ }
+ leaf len2 {
+ type string {
+ length "4..4000";
+ }
+ }
+ leaf len3 {
+ type string {
+ length "min..max";
+ }
+ }
+ leaf len4 {
+ type string {
+ range "1 .. 2 | 42";
+ }
+ }
+
typedef mybits {
description "Test adding several bits";
type bits {
@@ -214,10 +206,10 @@ new "cli set transitive string error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle 9xx" 255 "^$"
new "netconf set transitive string error"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "9xx ]]>]]>" "^ ]]>]]>"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '9xx ]]>]]>' "^ ]]>]]>"
new "netconf validate should fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error validation of talle failed regexp match fail"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" 'application bad-element talle error regexp match fail: "9xx" does not match \[a-z\]\[0-9\]\* ]]>]]>$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -241,10 +233,10 @@ new "cli set transitive union error int"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 55" 255 ""
new "netconf set transitive union error int"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "55 ]]>]]>" "^ ]]>]]>"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '55 ]]>]]>' "^ ]]>]]>"
new "netconf validate should fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error validation of ulle failed"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^application bad-element ulle error '55' does not match enumeration ]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -276,7 +268,7 @@ new "netconf validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf set ab wrong"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a.b& c.d
]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'a.b& c.d
]]>]]>' "^ ]]>]]>$"
new "netconf validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^"
@@ -298,12 +290,76 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits create" 0 "^$"
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits \"create read\"" 0 "^$"
new "netconf bits two values"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "create read ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'create read ]]>]]>' "^ ]]>]]>$"
new "cli bits validate"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$"
-if [ $BE -ne 0 ]; then
+#----------------int ranges---------------------
+
+new "cli range test num1 1 OK"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num1 1" 0 "^$"
+
+#new "cli range test num1 -100 ok" # XXX - cant be given on cmd line
+#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num1 \-100" 0 "^$"
+
+new "cli range test num1 2 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num1 2" 255 "^$"
+
+new "netconf range set num1 -1"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '-1 ]]>]]>' "^ ]]>]]>$"
+
+new "netconf validate num1 -1 wrong"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^application bad-element num1 error Number out of range: -1 ]]>]]>$'
+
+new "cli range test num2 1000 ok"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num2 1000" 0 "^$"
+
+new "cli range test num2 3 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num2 3" 255 "^$"
+
+new "cli range test num2 5000 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num2 5000" 255 "^$"
+
+new "cli range test num3 42 ok"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num3 42" 0 "^$"
+
+new "cli range test num3 260 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num3 260" 255 "^$"
+
+#----------------string ranges---------------------
+
+new "cli length test len1 1 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len1 x" 255 "^$"
+
+new "cli length test len1 2 OK"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len1 xy" 0 "^$"
+
+new "cli length test len1 3 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len1 hej" 255 "^$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "netconf length set len1 1"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'x ]]>]]>' "^ ]]>]]>$"
+
+new "netconf validate len1 1 wrong"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^application bad-element len1 error string length out of range: 1 ]]>]]>$'
+
+new "cli length test len2 42 ok"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len2 hejhophdsakjhkjsadhkjsahdkjsad" 0 "^$"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+
+new "cli length test len2 3 error"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len2 ab" 255 "^$"
+
+new "cli range test len3 42 ok"
+expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len3 hsakjdhkjsahdkjsahdksahdksajdhsakjhd" 0 "^$"
+
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_union.sh b/test/test_union.sh
index d0f7af99..c583593a 100755
--- a/test/test_union.sh
+++ b/test/test_union.sh
@@ -100,7 +100,7 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang 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 ""
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_when_must.sh b/test/test_when_must.sh
index 6e3b971f..82fc5647 100755
--- a/test/test_when_must.sh
+++ b/test/test_when_must.sh
@@ -106,42 +106,42 @@ if [ $BE -ne 0 ]; then
fi
new "when: add static route"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "static r1 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'static r1 ]]>]]>' "^ ]]>]]>$"
new "when: validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "when: add direct route"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "direct r2 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'direct r2 ]]>]]>' "^ ]]>]]>$"
new "when get config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^direct r2 static r1 ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^direct r2 static r1 ]]>]]>$'
new "when: validate fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error xpath static-routes validation failed ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^application operation-failed error when xpath validation failed ]]>]]>$"
new "when: discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "must: add interface"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet 1500 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'ethernet 1500 ]]>]]>' "^ ]]>]]>$"
new "must: validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "must: add atm interface"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "atm 32 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'atm 32 ]]>]]>' "^ ]]>]]>$"
new "must: atm validate fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error An ATM MTU must be 64 .. 17966 ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^application operation-failed error An ATM MTU must be 64 .. 17966 ]]>]]>$"
new "must: add eth interface"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "ethernet 989 ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'ethernet 989 ]]>]]>' "^ ]]>]]>$"
new "must: eth validate fail"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^operation-failed application error An Ethernet MTU must be 1500 ]]>]]>"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^application operation-failed error An Ethernet MTU must be 1500 ]]>]]>"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_yang.sh b/test/test_yang.sh
index 9a3f7cfe..46eb6234 100755
--- a/test/test_yang.sh
+++ b/test/test_yang.sh
@@ -160,13 +160,13 @@ new "cli defined extension"
expectfn "$clixon_cli -1f $cfg -y $fyang show version" 0 "3."
new "empty values in leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'a ]]>]]>' "^ ]]>]]>$"
new "empty values in leaf-list2"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
new "netconf get config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^a ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^a ]]>]]>$'
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -182,7 +182,7 @@ new "netconf schema resource, RFC 7895"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' 'ietf-yang-types 2013-07-15 urn:ietf:params:xml:ns:yang:ietf-yang-types implement '
new "netconf edit config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "1 2 5 one ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 2 5 one ]]>]]>' "^ ]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -192,33 +192,34 @@ new "netconf commit 2nd"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf get config xpath"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^1 2 5 one ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^1 2 5 one ]]>]]>$'
new "netconf edit leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "hej hopp ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'hej hopp ]]>]]>' "^ ]]>]]>$"
new "netconf get leaf-list"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^hej hopp ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^hej hopp ]]>]]>$'
new "netconf get leaf-list path"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^hej hopp ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^hej hopp ]]>]]>$"
new "netconf get (should be some)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^1 2 5 one "
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" '^1 2 5 one '
new "cli set leaf-list"
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
new "cli show leaf-list"
expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "foo "
+
new "netconf set state data (not allowed)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "42 ]]>]]>" "^invalid-value"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '42 ]]>]]>' '^protocol invalid-value error State data not allowed ]]>]]>$'
new "netconf set presence and not present"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
new "netconf get presence only"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^ ]]>]]>$'
new "netconf get presence only"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
@@ -227,51 +228,51 @@ new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf anyxml"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' "^ ]]>]]>$"
new "netconf validate anyxml"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconf delete candidate"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'none ]]>]]>' "^ ]]>]]>$"
# Check 3-keys
new "netconf add one 3-key entry"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "1 1 1 one ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 1 1 one ]]>]]>' "^ ]]>]]>$"
new "netconf check add one 3-key"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 one ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 one ]]>]]>'
new "netconf add another (with same 1st key)"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "1 2 1 two ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 2 1 two ]]>]]>' "^ ]]>]]>$"
new "netconf check add another"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 one 1 2 1 two ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 one 1 2 1 two ]]>]]>'
new "netconf replace first"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "1 1 1 replace ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 1 1 replace ]]>]]>' "^ ]]>]]>$"
new "netconf check replace"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 replace 1 2 1 two ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 1 1 replace 1 2 1 two ]]>]]>'
new "netconf delete first"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 1 1 ]]>]]>' "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '1 1 1 ]]>]]>' "^ ]]>]]>$"
new "netconf check delete"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 2 1 two ]]>]]>'
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '1 2 1 two ]]>]]>'
# clear db for next test
new "netconf delete candidate"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'none ]]>]]>' "^ ]]>]]>$"
new "netconf commit empty candidate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
new "netconfig config submodule"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "a foo ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 'a foo ]]>]]>' "^ ]]>]]>$"
new "netconf submodule get config"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^a foo ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ' ]]>]]>' '^a foo ]]>]]>$'
new "netconf submodule validate"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
@@ -279,7 +280,7 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
diff --git a/test/test_yang_load.sh b/test/test_yang_load.sh
index 9bcae5fa..4f168fca 100755
--- a/test/test_yang_load.sh
+++ b/test/test_yang_load.sh
@@ -20,7 +20,7 @@ cat < $fyang1
module $APPNAME{
prefix ex;
revision 2018-12-02;
- namespace "urn:example:example";
+ namespace "urn:example:clixon";
leaf newex{
type string;
}
@@ -32,7 +32,7 @@ cat < $fyang2
module $APPNAME{
prefix ex;
revision 2018-01-01;
- namespace "urn:example:example";
+ namespace "urn:example:clixon";
leaf oldex{
type string;
}
@@ -44,7 +44,7 @@ cat < $fyang3
module other{
prefix oth;
revision 2018-01-01;
- namespace "urn:example:example2";
+ namespace "urn:example:clixon2";
leaf other{
type string;
}
@@ -85,15 +85,15 @@ if [ $BE -ne 0 ]; then
fi
new "1. Set newex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set oldex should fail (since oldex is in old revision and only the new is loaded)"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed application error Validation failed ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element oldex error Unassigned yang spec ]]>]]>$'
new "Set other should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed application error Validation failed ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element other error Unassigned yang spec ]]>]]>$'
-if [ $BE -ne 0 ]; then
+if [ $BE -eq 0 ]; then
exit # BE
fi
@@ -137,13 +137,13 @@ if [ $? -ne 0 ]; then
fi
new "Set oldex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set newex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element newex error Unassigned yang spec ]]>]]>$'
new "Set other should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element other error Unassigned yang spec ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -181,13 +181,13 @@ if [ $? -ne 0 ]; then
fi
new "Set newex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set oldex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element oldex error Unassigned yang spec ]]>]]>$'
new "Set other should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element other error Unassigned yang spec ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -226,13 +226,13 @@ if [ $? -ne 0 ]; then
fi
new "Set oldex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set newex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element newex error Unassigned yang spec ]]>]]>$'
new "Set other should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element other error Unassigned yang spec ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -270,13 +270,13 @@ if [ $? -ne 0 ]; then
fi
new "Set newex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set oldex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element oldex error Unassigned yang spec ]]>]]>$'
new "Set other"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -315,13 +315,13 @@ if [ $? -ne 0 ]; then
fi
new "Set oldex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set newex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element newex error Unassigned yang spec ]]>]]>'
new "Set other"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -362,13 +362,13 @@ if [ $? -ne 0 ]; then
fi
new "Set oldex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set newex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element newex error Unassigned yang spec ]]>]]>$'
new "Set other"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Kill backend"
# Check if premature kill
@@ -408,13 +408,13 @@ if [ $? -ne 0 ]; then
fi
new "Set oldex"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^ ]]>]]>$'
new "Set newex should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element newex error Unassigned yang spec ]]>]]>$'
new "Set other should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^operation-failed protocol error XML'
+expecteof "$clixon_netconf -qf $cfg" 0 'str ]]>]]>' '^application unknown-element other error Unassigned yang spec ]]>]]>$'
new "Kill backend"
# Check if premature kill
diff --git a/test/test_yang_namespace.sh b/test/test_yang_namespace.sh
index 215e91f3..43fb2893 100755
--- a/test/test_yang_namespace.sh
+++ b/test/test_yang_namespace.sh
@@ -1,11 +1,15 @@
#!/bin/bash
-# Yang specifics: multi-keys and empty type
+
APPNAME=example
-# include err() and new() functions and creates $dir
+# test two modules example1 and example2 with overlapping statements x.
+# x is leaf in example1 and list on example2.
+# Test netconf and restconf
+# BTW, this is not supported in generated CLI
+
. ./lib.sh
cfg=$dir/conf_yang.xml
-fyang=$dir/example.yang
+fyang1=$dir/example1.yang
fyang2=$dir/example2.yang
# /usr/local/share/$APPNAME/yang
@@ -18,17 +22,35 @@ cat < $cfg
/usr/local/lib/$APPNAME/clispec
/usr/local/lib/$APPNAME/cli
$APPNAME
+ false
/usr/local/var/$APPNAME/$APPNAME.sock
/usr/local/var/$APPNAME/$APPNAME.pidfile
1
/usr/local/var/$APPNAME
/usr/local/lib/xmldb/text.so
+ true
true
EOF
+
+cat < $fyang1
+module example1{
+ yang-version 1.1;
+ prefix ex1;
+ namespace "urn:example:clixon1";
+ import ietf-routing {
+ description "defines fib-route";
+ prefix rt;
+ }
+ leaf x{
+ type int32;
+ }
+}
+EOF
+
# For testing namespaces -
-# x.y is different type. Here it is string whereas in fyang it is list.
+# x.y is different type. Here it is string whereas in fyang1 it is list.
#
cat < $fyang2
module example2{
@@ -43,37 +65,6 @@ module example2{
}
EOF
-cat < $fyang
-module example{
- yang-version 1.1;
- prefix ex;
- namespace "urn:example:clixon";
- import ietf-routing {
- description "defines fib-route";
- prefix rt;
- }
- leaf x{
- type int32;
- }
- rpc client-rpc {
- description "Example local client-side RPC that is processed by the
- the netconf/restconf and not sent to the backend.
- This is a clixon implementation detail: some rpc:s
- are better processed by the client for API or perf reasons";
- input {
- leaf request {
- type string;
- }
- }
- output {
- leaf result{
- type string;
- }
- }
- }
-}
-EOF
-
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
@@ -91,38 +82,59 @@ if [ $BE -ne 0 ]; then
fi
fi
+new "kill old restconf daemon"
+sudo pkill -u www-data clixon_restconf
+new "start restconf daemon"
+sudo su -c "$clixon_restconf -f $cfg -D $DBG" -s /bin/sh www-data &
-new "netconf xmlns module ex"
-expecteof "$clixon_netconf -qf $cfg" 0 '42 ]]>]]>' '^ ]]>]]>$'
+new "netconf set x in example1"
+expecteof "$clixon_netconf -qf $cfg" 0 '42 ]]>]]>' '^ ]]>]]>$'
-new "netconf get config XXX xmlfn in return"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^42 ]]>]]>$"
+new "netconf get config example1"
+expecteof "$clixon_netconf -qf $cfg" 0 " ]]>]]>" '^42 ]]>]]>$'
-new "netconf xmlns module ex2"
+new "netconf set x in example2"
expecteof "$clixon_netconf -qf $cfg" 0 '99 ]]>]]>' '^ ]]>]]>$'
-new "netconf get config XXX xmlns"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^42 99 ]]>]]>$"
+new "netconf get config example1 and example2"
+expecteof "$clixon_netconf -qf $cfg" 0 " ]]>]]>" '^42 99 ]]>]]>$'
new "netconf discard-changes"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^ ]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg -y $fyang1" 0 " ]]>]]>" "^ ]]>]]>$"
-new "netconf xmlns:ex"
-expecteof "$clixon_netconf -qf $cfg" 0 '4422 ]]>]]>' '^ ]]>]]>$'
+new "restconf set x in example1"
+expecteq "$(curl -s -X POST -d '{"example1:x":42}' http://localhost/restconf/data)" ''
-new "netconf get config XXX xmlns:ex"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^4422 ]]>]]>$"
+new2 "restconf get config example1"
+expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42}
+
'
-new "netconf xmlns:ex2"
-expecteof "$clixon_netconf -qf $cfg" 0 '9999 ]]>]]>' '^ ]]>]]>$'
+new "restconf set x in example2"
+expecteq "$(curl -s -X POST -d '{"example2:x":{"y":99}}' http://localhost/restconf/data)" ''
-new "netconf get config XXX xmlns:ex2"
-expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 " ]]>]]>" "^4422 9999 ]]>]]>$"
+# XXX GET ../example1:x is translated to select=/x which gets both example1&2
+#new "restconf get config example1"
+#expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42}
+#
'
-# rpc
+# XXX GET ../example2:x is translated to select=/x which gets both example1&2
+#new "restconf get config example2"
+#expecteq "$(curl -s -X GET http://localhost/restconf/data/example2:x)" '{"example2:x": {"y":42}}
+#
'
-if [ $BE -ne 0 ]; then
+new "restconf get config example1 and example2"
+ret=$(curl -s -X GET http://localhost/restconf/data)
+expect='"example1:x": 42,"example2:x": {"y": 99}'
+match=`echo $ret | grep -EZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
+
+new "Kill restconf daemon"
+sudo pkill -u www-data -f "/www-data/clixon_restconf"
+
+if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
diff --git a/test/test_yangmodels.sh b/test/test_yangmodels.sh
new file mode 100755
index 00000000..92cc45a0
--- /dev/null
+++ b/test/test_yangmodels.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# Parse yangmodels from https://github.com/YangModels/yang
+# Notes:
+# - Env variable YANGMODELS should point to checkout place. (define it in site.sh for example)
+# - Only cisco/nx/9.2-2 # Many other versions
+# - Only cisco/xe/1631 # Many other versions
+# - Only cisco/xr/530 # Many other versions
+# - Only juniper/18.2/18.2R/junos # Many other versions and platoforms
+
+# These are the test scripts:
+#./experimental/ieee/check.sh
+#./standard/ieee/check.sh
+#./standard/ietf/check.sh
+#./vendor/cisco/xr/check.sh
+#./vendor/cisco/check.sh
+#./vendor/cisco/xe/check.sh
+#./vendor/cisco/nx/check.sh
+
+# Yang specifics: multi-keys and empty type
+APPNAME=example
+
+# include err() and new() functions and creates $dir
+. ./lib.sh
+
+cfg=$dir/conf_yang.xml
+fyang=$dir/test.yang
+
+if [ ! -d "$YANGMODELS" ]; then
+ err "Hmm Yangmodels dir does not seem to exist, try git clone https://github.com/YangModels/yang?"
+fi
+
+# Experimental IEEE
+cat < $cfg
+
+ $cfg
+ $YANGMODELS/standard/ietf/RFC
+ $YANGMODELS/standard/ieee/draft
+ $YANGMODELS/standard/ieee/802.1/draft
+ /usr/local/share/clixon
+ /usr/local/lib/$APPNAME/clispec
+ /usr/local/lib/$APPNAME/cli
+ $APPNAME
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ 1
+ /usr/local/var/$APPNAME
+ /usr/local/lib/xmldb/text.so
+ true
+
+EOF
+
+new "yangmodels parse: -f $cfg"
+
+new "yangmodel Experimental IEEE 802.1: $YANGMODELS/experimental/ieee/802.1"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/802.1 -o CLICON_YANG_DIR=$YANGMODELS/experimental/ieee/1588 show version" 0 "3."
+
+# experimental 802.3 dir is empty
+#new "yangmodel Experimental IEEE 802.3: $YANGMODELS/experimental/ieee/802.3"
+#expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/802.3 show version" 0 "3."
+
+new "yangmodel Experimental IEEE 1588: $YANGMODELS/experimental/ieee/1588"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/1588 show version" 0 "3."
+
+# Standard IEEE
+new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/802.1/draft"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/802.1/draft show version" 0 "3."
+
+new "yangmodel Standard IEEE 802.3: $YANGMODELS/standard/ieee/802.3/draft"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/802.3/draft show version" 0 "3."
+
+# Standard IETF
+new "yangmodel Standard IETF: $YANGMODELS/standard/ietf/RFC"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ietf/RFC show version" 0 "3."
+
+if false; then
+# vendor/cisco/xr
+new "yangmodel vendor cisco xr 623: $YANGMODELS/vendor/cisco/xr/623"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/623 show version" 0 "3."
+
+new "yangmodel vendor cisco xr 632: $YANGMODELS/vendor/cisco/xr/632"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/632 show version" 0 "3."
+
+new "yangmodel vendor cisco xr 623: $YANGMODELS/vendor/cisco/xr/642"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/642 show version" 0 "3."
+
+new "yangmodel vendor cisco xr 651: $YANGMODELS/vendor/cisco/xr/651"
+expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/651 show version" 0 "3."
+fi
+
+rm -rf $dir
+
diff --git a/util/Makefile.in b/util/Makefile.in
index 5095b612..9d4cebc0 100644
--- a/util/Makefile.in
+++ b/util/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
@@ -100,6 +100,9 @@ distclean: clean
rm -f Makefile *~ .depend
install:
+# install -d -m 0755 $(DESTDIR)$(bindir)
+# install -m 0755 $(INSTALLFLAGS) $(APPS) $(DESTDIR)$(bindir)
+
install-include:
diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c
index dd16e0e0..b60e2299 100644
--- a/util/clixon_util_json.c
+++ b/util/clixon_util_json.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/util/clixon_util_stream.c b/util/clixon_util_stream.c
index 6f45de57..6b52fb3e 100644
--- a/util/clixon_util_stream.c
+++ b/util/clixon_util_stream.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -216,8 +216,8 @@ main(int argc, char **argv)
char *url = NULL;
char *getdata = NULL;
int timeout = 10;
- char start[27] = {0,}; /* strlen = 0 */
- char stop[27] = {0,};
+ char start[28] = {0,}; /* strlen = 0 */
+ char stop[28] = {0,};
int c;
char *argv0 = argv[0];
struct timeval now;
diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c
index 88d2ab53..8b3e3fa6 100644
--- a/util/clixon_util_xml.c
+++ b/util/clixon_util_xml.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c
index 81b33361..b711b072 100644
--- a/util/clixon_util_xpath.c
+++ b/util/clixon_util_xpath.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
diff --git a/util/clixon_util_yang.c b/util/clixon_util_yang.c
index 3615fd27..1be09377 100644
--- a/util/clixon_util_yang.c
+++ b/util/clixon_util_yang.c
@@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@@ -68,7 +68,7 @@
static int
usage(char *argv0)
{
- fprintf(stderr, "usage:%s [options]\n"
+ fprintf(stderr, "usage:%s [options] # input yang spec on stdin\n"
"where options are\n"
"\t-h \t\tHelp\n"
"\t-D \tDebug\n"
diff --git a/yang/Makefile.in b/yang/Makefile.in
index b595b5ea..3d8e38dd 100644
--- a/yang/Makefile.in
+++ b/yang/Makefile.in
@@ -1,7 +1,7 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
-# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
@@ -41,12 +41,14 @@ datarootdir = @datarootdir@
CLIXON_DATADIR = @CLIXON_DATADIR@
YANGSPECS = clixon-config@2018-10-21.yang
+YANGSPECS += clixon-lib@2019-01-02.yang
YANGSPECS += ietf-netconf@2011-06-01.yang
YANGSPECS += ietf-netconf-acm@2018-02-14.yang
YANGSPECS += ietf-inet-types@2013-07-15.yang
YANGSPECS += ietf-yang-types@2013-07-15.yang
+YANGSPECS += ietf-restconf@2017-01-26.yang
YANGSPECS += ietf-restconf-monitoring@2017-01-26.yang
-YANGSPECS += ietf-netconf-notification@2008-07-01.yang
+YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += ietf-yang-library@2016-06-21.yang
APPNAME = clixon # subdir ehere these files are installed
diff --git a/yang/clixon-config@2018-10-21.yang b/yang/clixon-config@2018-10-21.yang
index 162c3c0b..a9ad7414 100644
--- a/yang/clixon-config@2018-10-21.yang
+++ b/yang/clixon-config@2018-10-21.yang
@@ -1,6 +1,6 @@
module clixon-config {
yang-version 1.1;
- namespace "http://clicon.org";
+ namespace "http://clicon.org/config";
prefix cc;
organization
@@ -12,7 +12,7 @@ module clixon-config {
description
"Clixon configuration file
***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON
@@ -344,23 +344,19 @@ module clixon-config {
If set, insert spaces and line-feeds making the XML/JSON human
readable. If not set, make the XML/JSON more compact.";
}
- leaf CLICON_XML_SORT {
+ leaf CLICON_XML_NS_STRICT {
type boolean;
default true;
description
- "If set, sort XML lists and leaf-lists alphabetically and uses binary
- search. Unless ordered-by user is used.
- Only works for Yang specified XML.
- If not set, all lists accessed via linear search.";
- }
- leaf CLICON_XML_NS_ITERATE {
- type boolean;
- default true;
- description
- "If set, iterate through modules to find the matching datanode
+ "If not set, make non-strict laze namespace checks, by iterating
+ through modules to find the matching datanode
or rpc if no xmlns attribute specifies namespace.
- This is loose semantics of finding namespaces.
- And it is wrong, but is the way Clixon originally was written.";
+ This is lazy semantics of finding namespaces, which means you
+ do not need to explicitly give the namespace if the symbol exists
+ in some loaded module.
+ Example: is enough, instead of
+ But it is wrong, but is the way Clixon originally was written.
+ Strict semantics is the default.";
}
leaf CLICON_USE_STARTUP_CONFIG {
type int32;
@@ -459,12 +455,4 @@ module clixon-config {
}
}
- rpc debug {
- description "Set debug level of backend.";
- input {
- leaf level {
- type uint32;
- }
- }
- }
}
diff --git a/yang/clixon-lib@2019-01-02.yang b/yang/clixon-lib@2019-01-02.yang
new file mode 100644
index 00000000..1e26187b
--- /dev/null
+++ b/yang/clixon-lib@2019-01-02.yang
@@ -0,0 +1,55 @@
+module clixon-lib {
+ yang-version 1.1;
+ namespace "http://clicon.org/lib";
+ prefix cl;
+
+ organization
+ "Clicon / Clixon";
+
+ contact
+ "Olof Hagsand ";
+
+ description
+ "Clixon Netconf extensions for communication between clients and backend.
+
+ ***** BEGIN LICENSE BLOCK *****
+ Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
+
+ This file is part of CLIXON
+
+ Licensed under the Apache License, Version 2.0 (the \"License\");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an \"AS IS\" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Alternatively, the contents of this file may be used under the terms of
+ the GNU General Public License Version 3 or later (the \"GPL\"),
+ in which case the provisions of the GPL are applicable instead
+ of those above. If you wish to allow use of your version of this file only
+ under the terms of the GPL, and not to allow others to
+ use your version of this file under the terms of Apache License version 2,
+ indicate your decision by deleting the provisions above and replace them with
+ the notice and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this file under
+ the terms of any one of the Apache License version 2 or the GPL.
+
+ ***** END LICENSE BLOCK *****";
+
+ revision 2019-01-02 {
+ description
+ "Released in Clixon 3.9";
+ }
+ rpc debug {
+ description "Set debug level of backend.";
+ input {
+ leaf level {
+ type uint32;
+ }
+ }
+ }
+}
diff --git a/yang/ietf-netconf-notification@2008-07-01.yang b/yang/clixon-rfc5277@2008-07-01.yang
similarity index 92%
rename from yang/ietf-netconf-notification@2008-07-01.yang
rename to yang/clixon-rfc5277@2008-07-01.yang
index 978cdd4b..aaa45ed2 100644
--- a/yang/ietf-netconf-notification@2008-07-01.yang
+++ b/yang/clixon-rfc5277@2008-07-01.yang
@@ -1,6 +1,7 @@
-module ietf-netconf-notification {
- namespace "urn:ietf:params:xml:ns:netconf:notification:1:0";
- prefix "rcmon";
+module clixon-rfc5277 {
+ /* namespace "urn:ietf:params:xml:ns:netconf:notification:1.0";*/
+ namespace "urn:ietf:params:xml:ns:netmod:notification";
+ prefix "ncevent";
import ietf-yang-types { prefix yang; }
import ietf-inet-types { prefix inet; }
@@ -9,7 +10,8 @@ module ietf-netconf-notification {
"IETF NETCONF (Network Configuration) Working Group";
description
- "Note this is a translation from RFC 5277 schema in section 4 to Yang.
+ "Note this is a translation from RFC 5277 schema in section 4 to Yang
+ made by Olof Hagsand manually for the Clixon project.
RFC 5277 is Copyright (C) The IETF Trust (2008).";
revision 2008-07-01 {
@@ -119,14 +121,14 @@ module ietf-netconf-notification {
for more information on filters";
}
leaf startTime {
- type date-and-time;
+ type yang:date-and-time;
description "used to trigger the replay feature
and indicate that the replay should start at the time
specified. If is not present, this is not a replay
subscription.";
}
leaf stopTime {
- type date-and-time;
+ type yang:date-and-time;
description "used with the optional
replay feature to indicate the newest notifications of
interest. If is not present, the notifications will
diff --git a/yang/ietf-restconf@2017-01-26.yang b/yang/ietf-restconf@2017-01-26.yang
new file mode 100644
index 00000000..249006af
--- /dev/null
+++ b/yang/ietf-restconf@2017-01-26.yang
@@ -0,0 +1,279 @@
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ Author: Andy Bierman
+
+
+ Author: Martin Bjorklund
+
+
+ Author: Kent Watsen
+ ";
+
+ description
+ "This module contains conceptual YANG specifications
+ for basic RESTCONF media type definitions used in
+ RESTCONF protocol messages.
+
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The 'restconf-media-type' YANG extension statement
+ provides a normative syntax for XML and JSON
+ message-encoding purposes.
+
+ Copyright (c) 2017 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8040; see
+ the RFC itself for full legal notices.";
+
+ revision 2017-01-26 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8040: RESTCONF Protocol.";
+ }
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify a YANG data template that
+ represents conceptual data defined in YANG. It is
+ intended to describe hierarchical data independent of
+ protocol context or specific message-encoding format.
+ Data definition statements within a yang-data extension
+ specify the generic syntax for the specific YANG data
+ template, whose name is the argument of the 'yang-data'
+ extension statement.
+
+ Note that this extension does not define a media type.
+ A specification using this extension MUST specify the
+ message-encoding rules, including the content media type.
+
+ The mandatory 'name' parameter value identifies the YANG
+ data template that is being defined. It contains the
+ template name.
+
+ This extension is ignored unless it appears as a top-level
+ statement. It MUST contain data definition statements
+ that result in exactly one container data node definition.
+ An instance of a YANG data template can thus be translated
+ into an XML instance document, whose top-level element
+ corresponds to the top-level container.
+
+ The module name and namespace values for the YANG module using
+ the extension statement are assigned to instance document data
+ conforming to the data definition statements within
+ this extension.
+
+ The substatements of this extension MUST follow the
+ 'data-def-stmt' rule in the YANG ABNF.
+
+ The XPath document root is the extension statement itself,
+ such that the child nodes of the document root are
+ represented by the data-def-stmt substatements within
+ this extension. This conceptual document is the context
+ for the following YANG statements:
+
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+
+ The following data-def-stmt substatements are constrained
+ when used within a 'yang-data' extension statement.
+
+ - The list-stmt is not required to have a key-stmt defined.
+ - The if-feature-stmt is ignored if present.
+ - The config-stmt is ignored if present.
+ - The available identity values for any 'identityref'
+ leaf or leaf-list nodes are limited to the module
+ containing this extension statement and the modules
+ imported into that module.
+ ";
+ }
+
+ rc:yang-data yang-errors {
+ uses errors;
+ }
+
+ rc:yang-data yang-api {
+ uses restconf;
+ }
+
+ grouping errors {
+ description
+ "A grouping that contains a YANG container
+ representing the syntax and semantics of a
+ YANG Patch error report within a response message.";
+
+ container errors {
+ description
+ "Represents an error report returned by the server if
+ a request results in an error.";
+
+ list error {
+ description
+ "An entry containing information about one
+ specific error that occurred while processing
+ a RESTCONF request.";
+ reference
+ "RFC 6241, Section 4.3.";
+
+ leaf error-type {
+ type enumeration {
+ enum transport {
+ description
+ "The transport layer.";
+ }
+ enum rpc {
+ description
+ "The rpc or notification layer.";
+ }
+ enum protocol {
+ description
+ "The protocol operation layer.";
+ }
+ enum application {
+ description
+ "The server application layer.";
+ }
+ }
+ mandatory true;
+ description
+ "The protocol layer where the error occurred.";
+ }
+
+ leaf error-tag {
+ type string;
+ mandatory true;
+ description
+ "The enumerated error-tag.";
+ }
+
+ leaf error-app-tag {
+ type string;
+ description
+ "The application-specific error-tag.";
+ }
+
+ leaf error-path {
+ type instance-identifier;
+ description
+ "The YANG instance identifier associated
+ with the error node.";
+ }
+
+ leaf error-message {
+ type string;
+ description
+ "A message describing the error.";
+ }
+
+ anydata error-info {
+ description
+ "This anydata value MUST represent a container with
+ zero or more data nodes representing additional
+ error information.";
+ }
+ }
+ }
+ }
+
+ grouping restconf {
+ description
+ "Conceptual grouping representing the RESTCONF
+ root resource.";
+
+ container restconf {
+ description
+ "Conceptual container representing the RESTCONF
+ root resource.";
+ container data {
+ description
+ "Container representing the datastore resource.
+ Represents the conceptual root of all state data
+ and configuration data supported by the server.
+ The child nodes of this container can be any data
+ resources that are defined as top-level data nodes
+ from the YANG modules advertised by the server in
+ the 'ietf-yang-library' module.";
+ }
+
+ container operations {
+ description
+ "Container for all operation resources.
+
+ Each resource is represented as an empty leaf with the
+ name of the RPC operation from the YANG 'rpc' statement.
+
+ For example, the 'system-restart' RPC operation defined
+ in the 'ietf-system' module would be represented as
+ an empty leaf in the 'ietf-system' namespace. This is
+ a conceptual leaf and will not actually be found in
+ the module:
+
+ module ietf-system {
+ leaf system-reset {
+ type empty;
+ }
+ }
+
+ To invoke the 'system-restart' RPC operation:
+
+ POST /restconf/operations/ietf-system:system-restart
+
+ To discover the RPC operations supported by the server:
+
+ GET /restconf/operations
+
+ In XML, the YANG module namespace identifies the module:
+
+
+
+ In JSON, the YANG module name identifies the module:
+
+ { 'ietf-system:system-restart' : [null] }
+ ";
+ }
+
+ leaf yang-library-version {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}';
+ }
+ config false;
+ mandatory true;
+ description
+ "Identifies the revision date of the 'ietf-yang-library'
+ module that is implemented by this RESTCONF server.
+ Indicates the year, month, and day in YYYY-MM-DD
+ numeric format.";
+ }
+ }
+ }
+
+}