diff --git a/.gitignore b/.gitignore
index e9ed0e3e..9aaadb67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,12 +12,14 @@ etc/Makefile
example/Makefile
lib/Makefile
lib/*/Makefile
+test/Makefile
+yang/Makefile
+yang/*/Makefile
autom4te.cache/
-clixon.conf.cpp
-clixon.mk
config.log
config.status
+TAGS
apps/backend/clixon_backend
apps/backend/test
@@ -44,3 +46,6 @@ lib/clixon/clixon.h
build-root/*.tar.xz
build-root/*.rpm
build-root/rpmbuild
+
+test/site.sh
+doc/html
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..b8c1a54e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: c
+# safelist
+branches:
+ only:
+ - master
+ - develop
+before_script:
+ - sudo apt-get install -y libfcgi-dev
+ - ./test/travis/before_script.sh
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fdfe034..a3bab479 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,170 @@
# Clixon Changelog
+## 3.9.0 (Preliminary Target: February 2019)
+
+### Major New features
+1. Correct XML namespace handling
+ * According to [XML 1.0](https://www.w3.org/TR/2009/REC-xml-names-20091208) in restconf and Netconf.
+ * Remaining deviations from strict namespace handling:
+ * edit-config xpath select statement does not support namespaces
+ * notifications do not support namespaces.
+ * ietf-netconf base syntax is default `urn:ietf:params:xml:ns:netconf:base:1.0` and may not be explicitly given. However, in future versions this may be mandatory.
+ * CLI syntax (ie generated commands) do not have namespaces.
+
+ * The following example shows changes in netconf and restconf:
+ * Wrong Netconf RPC:
+ ```
+
+
+
+
+
+ ipv4
+
+
+ ```
+ * Correct Netconf RPC:
+ ```
+ # xmlns may be ommitted
+
+
+
+
+ ipv4
+
+
+ ```
+ * Example: Correct restconf request:
+ ```
+ POST http://localhost/restconf/operations/example:example)
+ Content-Type: application/yang-data+json
+ {
+ "example:input":{
+ "x":0
+ }
+ }
+ ```
+ * Example: correct Restconf 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]
+1. Yang 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.
+ * 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.
+1. NACM (RFC8341)
+ * Experimental support, no performance enhancements and need further testing
+ * Incoming RPC Message validation is supported (3.4.4)
+ * Data Node Access validation is supported (3.4.5), except:
+ * rule-type data-node path is not supported
+ * Outgoing noitification aithorization is _not_ supported (3.4.6)
+ * RPC:s are supported _except_:
+ * `copy-config`for other src/target combinations than running/startup (3.2.6)
+ * `commit` - NACM is applied to candidate and running operations only (3.2.8)
+ * Client-side RPC:s are _not_ supported.
+ * Recovery user "_nacm_recovery" added.
+
+### API changes on existing features (you may need to change your code)
+* Added `username` argument to `xmldb_put()` datastore function for NACM data-node write checks
+* Rearranged yang files
+ * Moved and updated all standard ietf and iana yang files from example and yang/ to `yang/standard`.
+ * Moved clixon yang files from yang to `yang/clixon`
+ * New configure option to disable standard yang files: `./configure --disable-stdyangs`
+ * This is to make it easier to use standard IETF/IANA yang files in separate directory
+ * Renamed example yang from example.yang -> clixon-example.yang
+* clixon_cli -p (printspec) changed semantics to add new yang path dir (see minor changes).
+* 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.yang` to `clixon-rfc5277.yang`.
+ * 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
+ * Example: In `choice c{ mandatory true; leaf x; }`, `x` was not previously enforced but is now.
+* 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.
+* XML namespace handling is corrected (see major changes)
+ * 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
+ * If you generate CLI code from the model (CLIXON_CLI_GENMODEL).
+ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
+
+### Minor changes
+* Change GIT branch handling to a single working master branch
+ * Develop branched abandoned
+ * Travis CI supported, see [https://travis-ci.org/clicon/clixon]
+* XML parser conformance to W3 spec
+ * Names lexically correct (NCName)
+ * Syntactically Correct handling of '' (processing instructions) and '=" command-line option to all programs: backend, cli, netconf, restconf.
+ * Any config option from file can be overrided by giving them on command-line.
+* Added -p command-line option to all programs: backend, cli, netconf, restconf.
+ * -p adds a new dir to the yang path dir. (same as -o CLICON_YAN_DIR=)
+* 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. This fixes the most acute problems, but 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()` and `xml_namespace_set()` to `xml_prefix()` and `xml_prefix_set()`, respectively.
+* Changed all make tags --> make TAGS
+* Keyvalue datastore removed (it has been disabled since 3.3.3)
+* New config option: CLICON_CLI_MODEL_TREENAME defining name of generated syntax tree if CLIXON_CLI_GENMODEL is set.
+
+### Corrected Bugs
+* Partially corrected: [yang type range statement does not support multiple values](https://github.com/clicon/clixon/issues/59).
+ * Should work for netconf and restconf, but not for CLI.
+* Fixed: [Range parsing is not RFC 7950 compliant](https://github.com/clicon/clixon/issues/71)
+* xml_cmp() compares numeric nodes based on string value [https://github.com/clicon/clixon/issues/64]
+* xml_cmp() respects 'ordered-by user' for state nodes, which violates RFC 7950 [https://github.com/clicon/clixon/issues/63]. (Thanks JDL)
+* XML<>JSON conversion problems [https://github.com/clicon/clixon/issues/66]
+ * CDATA sections stripped from XML when converted to JSON
+* Restconf returns error when RPC generates "ok" reply [https://github.com/clicon/clixon/issues/69]
+* xsd regular expression support for character classes [https://github.com/clicon/clixon/issues/68]
+ * added support for \c, \d, \w, \W, \s, \S.
+* Removing newlines from XML data [https://github.com/clicon/clixon/issues/65]
+* Fixed [ietf-netconf-notification@2008-07-01.yang validation problem #62](https://github.com/clicon/clixon/issues/62)
+* Ignore CR(\r) in yang files for DOS files
+* Keyword "min" (not only "max") can be used in built-in types "range" and "length" statements.
+* Support for empty yang string added, eg `default "";`
+* Removed CLI generation for yang notifications (and other non-data yang nodes)
+* Some restconf error messages contained `rpc-reply` or `rpc-error` which have now been removed.
+* getopt return value changed from char to int (https://github.com/clicon/clixon/issues/58)
+* Netconf/Restconf RPC extra input arguments are ignored (https://github.com/clicon/clixon/issues/47)
+
## 3.8.0 (6 Nov 2018)
### Major New features
@@ -200,7 +365,7 @@ translate {
### Known issues
* Namespace name relabeling is not supported.
- * Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlfns is not supported, such as:
+ * Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlns is not supported, such as:
```
x:des3
```
diff --git a/README_DEVELOP.md b/DEVELOP.md
similarity index 83%
rename from README_DEVELOP.md
rename to DEVELOP.md
index 350d3636..1755effa 100644
--- a/README_DEVELOP.md
+++ b/DEVELOP.md
@@ -4,6 +4,7 @@
* [How to work in git (branching)](#branching)
* [How the meta-configure stuff works](#meta-configure)
* [How to debug](#debug)
+ * [New release](#new-release)
## Documentation
How to document the code
@@ -30,10 +31,9 @@ How to document the code
## Branching
How to work in git (branching)
-Basically follows: http://nvie.com/posts/a-successful-git-branching-model/
-only somewhat simplified:
+Try to keep a single master branch always working. Currently testing is made using [Travis CI](https://travis-ci.org/clicon/clixon).
-Do commits in develop branch. When done, merge with master.
+However, releases are made periodically (ca every 3 months) which is more tested.
## How the meta-configure stuff works
```
@@ -92,4 +92,14 @@ EOF
valgrind --leak-check=full --show-leak-kinds=all clixon_netconf -qf /tmp/myconf.xml -y /tmp/my.yang
valgrind --tool=callgrind clixon_netconf -qf /tmp/myconf.xml -y /tmp/my.yang
sudo kcachegrind
- ```
\ No newline at end of file
+ ```
+
+## 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
diff --git a/LICENSE.md b/LICENSE.md
index 84997252..af683735 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,4 +1,4 @@
-Copyright 2009-2018 Olof Hagsand and Benny Holmgren
+Copyright 2009-2019 Olof Hagsand and Benny Holmgren
CLIXON is dual license.
diff --git a/Makefile.in b/Makefile.in
index 3d34933a..b5a833b1 100644
--- a/Makefile.in
+++ b/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
#
@@ -54,7 +54,7 @@ SHELL = /bin/sh
SUBDIRS = lib apps include etc datastore util yang
-.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker
+.PHONY: doc example all clean depend $(SUBDIRS) install loc TAGS .config.status docker test
all: $(SUBDIRS)
@@ -138,6 +138,12 @@ pkg-rpm: dist
pkg-srpm: dist
make -C extras/rpm srpm
+example:
+ (cd $@ && $(MAKE) $(MFLAGS) all)
+
+test: #example
+ (cd $@ && $(MAKE) $(MFLAGS) all)
+
docker:
for i in docker; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done
diff --git a/README.md b/README.md
index 9c52da1b..77ff632f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://travis-ci.org/clicon/clixon)
+
# Clixon
Clixon is a YANG-based configuration manager, with interactive CLI,
@@ -5,7 +7,7 @@ NETCONF and RESTCONF interfaces, an embedded database and transaction
support.
* [Background](#background)
- * [Frequently asked questions](doc/FAQ.md)
+ * [Frequently asked questions (FAQ)](doc/FAQ.md)
* [Installation](#installation)
* [Licenses](#licenses)
* [Support](#support)
@@ -24,15 +26,16 @@ support.
* [Clixon project page](http://www.clicon.org)
* [Tests](test/)
* [Docker](docker/)
- * [Reference manual](http://www.clicon.org/doxygen/index.html) (Note: the link may not be up-to-date. It is better to build your own: `cd doc; make doc`)
+ * [Roadmap](ROADMAP.md)
+ * [Reference manual](#reference)
Background
==========
Clixon was implemented to provide an open-source generic configuration
-tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while clixon is a system with configuration database, xml and rest interfaces. Most of the projects using clixon are for embedded network and measuring devices. But Clixon is more generic than that.
+tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while Clixon is a system with configuration database, xml and rest interfaces all defined by Yang. Most of the projects using Clixon are for embedded network and measuring devices. But Clixon can be used for other systems as well due to its modular and pluggable architecture.
-Users of clixon currently include:
+Users of Clixon currently include:
* [Netgate](https://www.netgate.com)
* [CloudMon360](http://cloudmon360.com)
* [Grideye](http://hagsand.se/grideye)
@@ -97,13 +100,33 @@ XML
Clixon has its own implementation of XML and XPATH implementation.
The standards covered include:
-- [XML](https://www.w3.org/TR/2008/REC-xml-20081126)
-- [Namespaces](https://www.w3.org/TR/2009/REC-xml-names-20091208)
-- [XPATH](https://www.w3.org/TR/xpath-10)
+- [XML 1.0](https://www.w3.org/TR/2008/REC-xml-20081126)
+- [Namespaces in XML 1.0](https://www.w3.org/TR/2009/REC-xml-names-20091208)
+- [XPATH 1.0](https://www.w3.org/TR/xpath-10)
+
+Not supported:
+- !DOCTYPE (ie DTD)
+
+Historically, Clixon has not until 3.9 made strict namespace
+enforcing. For example, the following non-strict netconf was
+previously accepted:
+```
+
+```
+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 at the heart of Clixon. Yang modules are used as a
+YANG and XML is the heart of Clixon. Yang modules are used as a
specification for handling XML configuration data. The YANG spec is
used to generate an interactive CLI, netconf and restconf clients. It
also manages an XML datastore.
@@ -120,6 +143,12 @@ However, the following YANG syntax modules are not implemented:
- action
- belongs-to
+Restrictions on Yang types are as follows:
+- 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
=======
Clixon implements the following NETCONF proposals or standards:
@@ -128,7 +157,14 @@ Clixon implements the following NETCONF proposals or standards:
- [RFC 5277: NETCONF Event Notifications](http://www.rfc-base.org/txt/rfc-5277.txt)
- [RFC 8341: Network Configuration Access Control Model](http://www.rfc-base.org/txt/rfc-8341.txt)
-Clixon does not yet support the following netconf features:
+The following RFC6241 capabilities/features are hardcoded in Clixon:
+- :candidate (RFC6241 8.3)
+- :validate (RFC6241 8.6)
+- :startup (RFC6241 8.7)
+- :xpath (RFC6241 8.9)
+- :notification: (RFC5277)
+
+Clixon does not support the following netconf features:
- :url capability
- copy-config source config
@@ -137,6 +173,9 @@ Clixon does not yet 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
@@ -158,7 +197,7 @@ Datastore
=========
The Clixon datastore is a stand-alone XML based datastore. The idea is
to be able to use different datastores backends with the same
-API.
+API. Currently only an XML plain text datastore is supported.
The datastore is primarily designed to be used by Clixon but can be used
separately.
@@ -175,7 +214,7 @@ subsystem can be used.
Restconf however needs credentials. This is done by writing a credentials callback in a restconf plugin. See:
* [FAQ](doc/FAQ.md#how-do-i-write-an-authentication-callback).
* [Example](example/README.md) has an example how to do this with HTTP basic auth.
- * I have done this for another project using Oauth2 or (https://github.com/CESNET/Netopeer2/tree/master/server/configuration)
+ * It has been done for other projects using Oauth2 or (https://github.com/CESNET/Netopeer2/tree/master/server/configuration)
The clients send the ID of the user using a "username" attribute with
the RPC calls to the backend. Note that the backend trusts the clients
@@ -183,36 +222,28 @@ so the clients can in principle fake a username.
NACM
====
-Clixon includes an experimental Network Configuration Access Control Model (NACM) according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341). It has limited functionality.
+Clixon includes an experimental Network Configuration Access Control Model (NACM) according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341).
-The support is as follows:
+To enable NACM:
-* There is a yang config variable `CLICON_NACM_MODE` to set whether NACM is disabled, uses internal(embedded) NACM configuration, or external configuration. (See yang/clixon-config.yang)
-* If the mode is internal, NACM configurations is expected to be in the regular configuration, managed by regular candidate/runing/commit procedures. This mode may have some problems with bootstrapping.
+* The `CLICON_NACM_MODE` config variable is by default `disabled`.
+* If the mode is internal`, NACM configurations are expected to be in the regular configuration, managed by regular candidate/runing/commit procedures. This mode may have some problems with bootstrapping.
* If the mode is `external`, the `CLICON_NACM_FILE` yang config variable contains the name of a separate configuration file containing the NACM configurations. After changes in this file, the backend needs to be restarted.
-* The [example](example/README.md) contains a http basic auth and a NACM backend callback for mandatory state variables.
-* There are two [tests](test/README.md) using internal and external NACM config
-* The backend provides a limited NACM support (when enabled) described below
-NACM is implemented in the backend and a single access check is made
-in `from_client_msg()` when an internal netconf RPC has
-just been received and decoded. The code is in `nacm_access()`.
+The [example](example/README.md) contains a http basic auth and a NACM backend callback for mandatory state variables.
-The functionality is as follows:
-* Notification is not supported
-* Groups are supported
-* Rule-lists are supported
-* Rules are supported as follows
- * module-name: Only '*' supported
- * access-operations: only '*' and 'exec' supported
- * rpc-name: fully supported (eg edit-config/get-config, etc)
- * action: fully supported (permit/deny)
-
-The tests outlines an example of three groups (taken from the RFC): admin, limited and guest:
-* admin: Full access
-* limited: Read access (get and get-config)
-* guest: No access
+NACM is implemented in the backend with incoming RPC and data node access control points.
+The functionality is as follows (references to sections in [RFC8341](https://tools.ietf.org/html/rfc8341)):
+* Access control point support:
+ * Incoming RPC Message validation is supported (3.4.4)
+ * Data Node Access validation is supported (3.4.5), except:
+ * rule-type data-node path is not supported
+ * Outgoing noitification aithorization is _not_ supported (3.4.6)
+* RPC:s are supported _except_:
+ * `copy-config`for other src/target combinations than running/startup (3.2.6)
+ * `commit` - NACM is applied to candidate and running operations only (3.2.8)
+* Client-side RPC:s are _not_ supported.
Runtime
=======
@@ -221,3 +252,13 @@ Runtime
The figure shows the SDK runtime of Clixon.
+Reference
+=========
+Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
+You need to install doxygen and graphviz on your system.
+Build it in the doc directory and point the browser to `.../clixon/doc/html/index.html` as follows:
+```
+> cd doc
+> make doc
+> make graphs # detailed callgraphs
+```
\ No newline at end of file
diff --git a/ROADMAP.md b/ROADMAP.md
index e0dd98f3..2b884845 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,27 +1,42 @@
# Clixon roadmap
-Not in prio order (yet)
+## High prio
+- 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
+- (DONE) NACM (RFC 8341)
+ - NACM support for create, read, update, delete operations
+ - ACM support for specifying a module name other than '*'
+- (DONE)XML [Namespace handling](https://github.com/clicon/clixon/issues/49) (DONE)
-- XML
- - [Namespace handling](https://github.com/clicon/clixon/issues/49)
+## Medium prio:
+- Support a plugin callback that is invoked when copy-config is called.
+- Preserve CLI command history across sessions. The up/down arrows
+- (DONE)Support for XML regex's.
+ - Currently Posix extended regular expressions
+- (DONE) Input validation on custom RPCs/
+ - [Sanity checks](https://github.com/clicon/clixon/issues/47)
+
+## 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 2f096de0..b6513757 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
#
@@ -44,6 +44,7 @@ SHELL = /bin/sh
SUBDIRS = backend
SUBDIRS += cli
SUBDIRS += netconf
+# See configure.ac
ifeq ($(with_restconf),yes)
SUBDIRS += restconf
endif
@@ -80,5 +81,5 @@ distclean: clean
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
-tags:
+TAGS:
find $(srcdir) -name '*.[chyl]' -print | etags -
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 fedad77a..c2c370fe 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.
@@ -177,14 +177,19 @@ netconf_db_find(cxobj *xn,
static int
from_client_get_config(clicon_handle h,
cxobj *xe,
+ char *username,
cbuf *cbret)
{
- int retval = -1;
- char *db;
- cxobj *xfilter;
- char *selector = "/";
- cxobj *xret = NULL;
- cbuf *cbx = NULL; /* Assist cbuf */
+ int retval = -1;
+ char *db;
+ cxobj *xfilter;
+ char *xpath = "/";
+ cxobj *xret = NULL;
+ cbuf *cbx = NULL; /* Assist cbuf */
+ cxobj *xnacm = NULL;
+ cxobj **xvec = NULL;
+ size_t xlen;
+ int ret;
if ((db = netconf_db_find(xe, "source")) == NULL){
clicon_err(OE_XML, 0, "db not found");
@@ -201,13 +206,23 @@ from_client_get_config(clicon_handle h,
goto ok;
}
if ((xfilter = xml_find(xe, "filter")) != NULL)
- if ((selector = xml_find_value(xfilter, "select"))==NULL)
- selector="/";
- if (xmldb_get(h, db, selector, 1, &xret) < 0){
+ if ((xpath = xml_find_value(xfilter, "select"))==NULL)
+ xpath="/";
+ if (xmldb_get(h, db, xpath, 1, &xret) < 0){
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
+ /* Pre-NACM access step */
+ if ((ret = nacm_access_pre(h, username, &xnacm)) < 0)
+ goto done;
+ if (ret == 0){ /* Do NACM validation */
+ if (xpath_vec(xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
+ goto done;
+ /* NACM datanode/module read validation */
+ if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
+ goto done;
+ }
cprintf(cbret, "");
if (xret==NULL)
cprintf(cbret, " ");
@@ -221,6 +236,10 @@ from_client_get_config(clicon_handle h,
ok:
retval = 0;
done:
+ if (xnacm)
+ xml_free(xnacm);
+ if (xvec)
+ free(xvec);
if (cbx)
cbuf_free(cbx);
if (xret)
@@ -292,7 +311,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 +328,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 +358,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);
@@ -356,6 +375,7 @@ client_statedata(clicon_handle h,
static int
from_client_get(clicon_handle h,
cxobj *xe,
+ char *username,
cbuf *cbret)
{
int retval = -1;
@@ -363,8 +383,10 @@ from_client_get(clicon_handle h,
char *xpath = "/";
cxobj *xret = NULL;
int ret;
- cbuf *cbx = NULL; /* Assist cbuf */
-
+ cxobj **xvec = NULL;
+ size_t xlen;
+ cxobj *xnacm = NULL;
+
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
xpath="/";
@@ -379,33 +401,38 @@ 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;
}
+ /* Pre-NACM access step */
+ if ((ret = nacm_access_pre(h, username, &xnacm)) < 0)
+ goto done;
+ if (ret == 0){ /* Do NACM validation */
+ if (xpath_vec(xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
+ goto done;
+ /* NACM datanode/module read validation */
+ if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
+ goto done;
+ }
+ 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 (xnacm)
+ xml_free(xnacm);
+ if (xvec)
+ free(xvec);
if (xret)
xml_free(xret);
return retval;
@@ -422,6 +449,7 @@ static int
from_client_edit_config(clicon_handle h,
cxobj *xn,
int mypid,
+ char *username,
cbuf *cbret)
{
int retval = -1;
@@ -433,14 +461,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");
@@ -467,12 +497,18 @@ from_client_edit_config(clicon_handle h,
goto ok;
}
}
- if ((xc = xpath_first(xn, "config")) == NULL){
- if (netconf_missing_element(cbret, "protocol", "config ", NULL) < 0)
+ if ((xc = xpath_first(xn, "config")) == NULL){
+ if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
goto done;
goto ok;
}
else{
+ /* 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?)
+ * Maybe validate xml here as in text_modify_top?
+ */
if (xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0)
@@ -485,24 +521,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, username, 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
@@ -524,7 +563,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;
}
@@ -583,7 +622,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;
}
@@ -645,7 +684,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;
}
@@ -689,6 +728,10 @@ from_client_kill_session(clicon_handle h,
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
+ * NACM: If source running and target startup --> only exec permission
+ * else:
+ * - omit data nodes to which the client does not have read access
+ * - access denied if user lacks create/delete/update
*/
static int
from_client_copy_config(clicon_handle h,
@@ -703,7 +746,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;
}
@@ -718,7 +761,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;
}
@@ -771,7 +814,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;
}
@@ -850,7 +893,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;
}
@@ -858,7 +901,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;
}
@@ -919,7 +962,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;
}
@@ -935,272 +978,6 @@ from_client_debug(clicon_handle h,
return retval;
}
-/*! Match nacm access operations according to RFC8341 3.4.4.
- * Incoming RPC Message Validation Step 7 (c)
- * The rule's "access-operations" leaf has the "exec" bit set or
- * has the special value "*".
- * @retval 0 No match
- * @retval 1 Match
- * XXX access_operations is bit-fields
- */
-static int
-nacm_match_access(char *access_operations,
- char *mode)
-{
- if (access_operations==NULL)
- return 0;
- if (strcmp(access_operations,"*")==0)
- return 1;
- if (strstr(access_operations, mode)!=NULL)
- return 1;
- return 0;
-}
-
-/*! Match nacm single rule. Either match with access or deny. Or not match.
- * @param[in] h Clicon handle
- * @param[in] name rpc name
- * @param[in] xrule NACM rule XML tree
- * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
- * @retval -1 Error
- * @retval 0 Matching rule AND Not access and cbret set
- * @retval 1 Matchung rule AND Access
- * @retval 2 No matching rule Goto step 10
- * From RFC8341 3.4.4. Incoming RPC Message Validation
- +---------+-----------------+---------------------+-----------------+
- | Method | Resource class | NETCONF operation | Access |
- | | | | operation |
- +---------+-----------------+---------------------+-----------------+
- | OPTIONS | all | none | none |
- | HEAD | all | , | read |
- | GET | all | , | read |
- | POST | datastore, data | | create |
- | POST | operation | specified operation | execute |
- | PUT | data | | create, update |
- | PUT | datastore | | update |
- | PATCH | data, datastore | | update |
- | DELETE | data | | delete |
-
- 7.(cont) A rule matches if all of the following criteria are met:
- * The rule's "module-name" leaf is "*" or equals the name of
- the YANG module where the protocol operation is defined.
-
- * Either (1) the rule does not have a "rule-type" defined or
- (2) the "rule-type" is "protocol-operation" and the
- "rpc-name" is "*" or equals the name of the requested
- protocol operation.
-
- * The rule's "access-operations" leaf has the "exec" bit set or
- has the special value "*".
- */
-static int
-nacm_match_rule(clicon_handle h,
- char *name,
- cxobj *xrule,
- cbuf *cbret)
-{
- int retval = -1;
- // cxobj *x;
- char *module_name;
- char *rpc_name;
- char *access_operations;
- char *action;
-
- module_name = xml_find_body(xrule, "module-name");
- rpc_name = xml_find_body(xrule, "rpc-name");
- /* XXX access_operations can be a set of bits */
- access_operations = xml_find_body(xrule, "access-operations");
- action = xml_find_body(xrule, "action");
- clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__,
- module_name, rpc_name, access_operations, action);
- if (module_name && strcmp(module_name,"*")==0){
- if (nacm_match_access(access_operations, "exec")){
- if (rpc_name==NULL ||
- strcmp(rpc_name, "*")==0 || strcmp(rpc_name, name)==0){
- /* Here is a matching rule */
- if (action && strcmp(action, "permit")==0){
- retval = 1;
- goto done;
- }
- else{
- if (netconf_access_denied(cbret, "protocol", "access denied") < 0)
- goto done;
- retval = 0;
- goto done;
- }
- }
- }
- }
- retval = 2; /* no matching rule */
- done:
- return retval;
-
-}
-
-/*! Make nacm access control
- * @param[in] h Clicon handle
- * @param[in] mode NACMmode, internal or external
- * @param[in] name rpc name
- * @param[in] username
- * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
- * @retval -1 Error
- * @retval 0 Not access and cbret set
- * @retval 1 Access
- * From RFC8341 3.4.4. Incoming RPC Message Validation
- */
-static int
-nacm_access(clicon_handle h,
- char *mode,
- char *name,
- char *username,
- cbuf *cbret)
-{
- int retval = -1;
- cxobj *xtop = NULL;
- cxobj *xacm;
- cxobj *x;
- cxobj *xrlist;
- cxobj *xrule;
- char *enabled = NULL;
- cxobj **gvec = NULL; /* groups */
- size_t glen;
- cxobj **rlistvec = NULL; /* rule-list */
- size_t rlistlen;
- cxobj **rvec = NULL; /* rules */
- size_t rlen;
- int i, j;
- char *exec_default = NULL;
- int ret;
-
- clicon_debug(1, "%s", __FUNCTION__);
- /* 0. If nacm-mode is external, get NACM defintion from separet tree,
- otherwise get it from internal configuration */
- if (strcmp(mode, "external")==0){
- if ((xtop = backend_nacm_list_get(h)) == NULL){
- clicon_err(OE_XML, 0, "No nacm external tree");
- goto done;
- }
- }
- else if (strcmp(mode, "internal")==0){
- if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0)
- goto done;
- }
- else{
- clicon_err(OE_UNIX, 0, "Invalid NACM mode: %s", mode);
- goto done;
- }
-
- /* 1. If the "enable-nacm" leaf is set to "false", then the protocol
- operation is permitted. (or config does not exist) */
-
- if ((xacm = xpath_first(xtop, "nacm")) == NULL)
- goto permit;
- exec_default = xml_find_body(xacm, "exec-default");
- if ((x = xpath_first(xacm, "enable-nacm")) == NULL)
- goto permit;
- enabled = xml_body(x);
- if (strcmp(enabled, "true") != 0)
- goto permit;
-
- /* 2. If the requesting session is identified as a recovery session,
- then the protocol operation is permitted. NYI */
-
- /* 3. If the requested operation is the NETCONF
- protocol operation, then the protocol operation is permitted.
- */
- if (strcmp(name, "close-session") == 0)
- goto permit;
- /* 4. Check all the "group" entries to see if any of them contain a
- "user-name" entry that equals the username for the session
- making the request. (If the "enable-external-groups" leaf is
- "true", add to these groups the set of groups provided by the
- transport layer.) */
- if (username == NULL)
- goto step10;
- /* User's group */
- if (xpath_vec(xacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
- goto done;
- /* 5. If no groups are found, continue with step 10. */
- if (glen == 0)
- goto step10;
- /* 6. Process all rule-list entries, in the order they appear in the
- configuration. If a rule-list's "group" leaf-list does not
- match any of the user's groups, proceed to the next rule-list
- entry. */
- if (xpath_vec(xacm, "rule-list", &rlistvec, &rlistlen) < 0)
- goto done;
- for (i=0; i or , then the protocol operation
- is denied. */
- if (strcmp(name, "kill-session")==0 || strcmp(name, "delete-config")==0){
- if (netconf_access_denied(cbret, "protocol", "default deny") < 0)
- goto done;
- goto deny;
- }
- /* 12. If the "exec-default" leaf is set to "permit", then permit the
- protocol operation; otherwise, deny the request. */
- if (exec_default ==NULL || strcmp(exec_default, "permit")==0)
- goto permit;
- if (netconf_access_denied(cbret, "protocol", "default deny") < 0)
- goto done;
- goto deny;
- permit:
- retval = 1;
- done:
- clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
- if (strcmp(mode, "internal")==0 && xtop)
- xml_free(xtop);
- if (gvec)
- free(gvec);
- if (rlistvec)
- free(rlistvec);
- if (rvec)
- free(rvec);
- return retval;
- deny: /* Here, cbret must contain a netconf error msg */
- assert(cbuf_len(cbret));
- retval = 0;
- goto done;
-}
-
/*! An internal clicon message has arrived from a client. Receive and dispatch.
* @param[in] h Clicon handle
* @param[in] s Socket where message arrived. read from this.
@@ -1218,16 +995,21 @@ from_client_msg(clicon_handle h,
cxobj *xt = NULL;
cxobj *x;
cxobj *xe;
- char *name = NULL;
+ char *rpc = NULL;
+ char *module = NULL;
char *db;
cbuf *cbret = NULL; /* return message */
int pid;
int ret;
char *username;
- char *nacm_mode;
-
+ yang_spec *yspec;
+ yang_stmt *ye;
+ yang_stmt *ymod;
+ cxobj *xnacm = NULL;
+
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.
*/
@@ -1235,88 +1017,115 @@ 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;
}
+
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 -
+ * 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 == 0)
+ goto reply;
xe = NULL;
username = xml_find_value(x, "username");
+ /* May be used by callbacks, etc */
+ clicon_username_set(h, username);
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
- name = xml_name(xe);
- clicon_debug(1, "%s name:%s", __FUNCTION__, name);
- /* Make NACM access control if enabled as "internal"*/
- nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
- if (nacm_mode && strcmp(nacm_mode, "disabled") != 0){
- if ((ret = nacm_access(h, nacm_mode, name, username, cbret)) < 0)
+ rpc = xml_name(xe);
+ if ((ye = xml_spec(xe)) == NULL){
+ if (netconf_operation_not_supported(cbret, "protocol", rpc) < 0)
goto done;
- if (!ret)
+ goto reply;
+ }
+ if ((ymod = ys_module(ye)) == NULL){
+ clicon_err(OE_XML, ENOENT, "rpc yang does not have module");
+ goto done;
+ }
+ module = ymod->ys_argument;
+ clicon_debug(1, "%s module:%s rpc:%s", __FUNCTION__, module, rpc);
+ /* Pre-NACM access step */
+ xnacm = NULL;
+ if ((ret = nacm_access_pre(h, username, &xnacm)) < 0)
+ goto done;
+ if (ret == 0){ /* Do NACM validation */
+ /* NACM rpc operation exec validation */
+ if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
+ goto done;
+ if (xnacm)
+ xml_free(xnacm);
+ if (ret == 0) /* Not permitted and cbret set */
goto reply;
}
- if (strcmp(name, "get-config") == 0){
- if (from_client_get_config(h, xe, cbret) <0)
+ if (strcmp(rpc, "get-config") == 0){
+ if (from_client_get_config(h, xe, username, cbret) <0)
goto done;
}
- else if (strcmp(name, "edit-config") == 0){
- if (from_client_edit_config(h, xe, pid, cbret) <0)
+ else if (strcmp(rpc, "edit-config") == 0){
+ if (from_client_edit_config(h, xe, pid, username, cbret) <0)
goto done;
}
- else if (strcmp(name, "copy-config") == 0){
+ else if (strcmp(rpc, "copy-config") == 0){
if (from_client_copy_config(h, xe, pid, cbret) <0)
goto done;
}
- else if (strcmp(name, "delete-config") == 0){
+ else if (strcmp(rpc, "delete-config") == 0){
if (from_client_delete_config(h, xe, pid, cbret) <0)
goto done;
}
- else if (strcmp(name, "lock") == 0){
+ else if (strcmp(rpc, "lock") == 0){
if (from_client_lock(h, xe, pid, cbret) < 0)
goto done;
}
- else if (strcmp(name, "unlock") == 0){
+ else if (strcmp(rpc, "unlock") == 0){
if (from_client_unlock(h, xe, pid, cbret) < 0)
goto done;
}
- else if (strcmp(name, "get") == 0){
- if (from_client_get(h, xe, cbret) < 0)
+ else if (strcmp(rpc, "get") == 0){
+ if (from_client_get(h, xe, username, cbret) < 0)
goto done;
}
- else if (strcmp(name, "close-session") == 0){
+ else if (strcmp(rpc, "close-session") == 0){
xmldb_unlock_all(h, pid);
stream_ss_delete_all(h, ce_event_cb, (void*)ce);
cprintf(cbret, " ");
}
- else if (strcmp(name, "kill-session") == 0){
+ else if (strcmp(rpc, "kill-session") == 0){
if (from_client_kill_session(h, xe, cbret) < 0)
goto done;
}
- else if (strcmp(name, "validate") == 0){
+ 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;
}
if (from_client_validate(h, db, cbret) < 0)
goto done;
}
- else if (strcmp(name, "commit") == 0){
+ else if (strcmp(rpc, "commit") == 0){
if (from_client_commit(h, pid, cbret) < 0)
goto done;
}
- else if (strcmp(name, "discard-changes") == 0){
+ else if (strcmp(rpc, "discard-changes") == 0){
if (from_client_discard_changes(h, pid, cbret) < 0)
goto done;
}
- else if (strcmp(name, "create-subscription") == 0){
+ else if (strcmp(rpc, "create-subscription") == 0){
if (from_client_create_subscription(h, xe, ce, cbret) < 0)
goto done;
}
- else if (strcmp(name, "debug") == 0){
+ else if (strcmp(rpc, "debug") == 0){
if (from_client_debug(h, xe, cbret) < 0)
goto done;
}
@@ -1370,7 +1179,7 @@ from_client_msg(clicon_handle h,
/* Sanity: log if clicon_err() is not called ! */
if (retval < 0 && clicon_errno < 0)
clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on error (message: %s)",
- __FUNCTION__, name?name:"");
+ __FUNCTION__, rpc?rpc:"");
// clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;// -1 here terminates backend
}
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..453e3e6c 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,70 @@ 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,
+ clicon_username_get(h), 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
@@ -274,6 +323,10 @@ candidate_commit(clicon_handle h,
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
+ * NACM: The server MUST determine the exact nodes in the running
+ * configuration datastore that are actually different and only check
+ * "create", "update", and "delete" access permissions for this set of
+ * nodes, which could be empty.
*/
int
from_client_commit(clicon_handle h,
@@ -283,6 +336,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 +350,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;
}
@@ -317,6 +372,7 @@ from_client_commit(clicon_handle h,
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
+ * NACM: No datastore permissions are needed.
*/
int
from_client_discard_changes(clicon_handle h,
@@ -367,6 +423,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 +436,21 @@ 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,
+ clicon_username_get(h), 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 676cc50c..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.
@@ -52,8 +52,4 @@ struct client_entry *backend_client_list(clicon_handle h);
int backend_client_delete(clicon_handle h, struct client_entry *ce);
-int backend_nacm_list_set(clicon_handle h, cxobj *xnacm);
-
-cxobj * backend_nacm_list_get(clicon_handle h);
-
#endif /* _BACKEND_HANDLE_H_ */
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 3856eb0e..93e25bed 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:p:b:Fza:u:P:1s:c:g:y:x:o:"
#define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log"
@@ -91,6 +91,8 @@ backend_terminate(clicon_handle h)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
yspec_free(yspec);
+ if ((x = clicon_nacm_ext(h)) != NULL)
+ xml_free(x);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
stream_publish_exit();
@@ -140,6 +142,7 @@ usage(clicon_handle h,
"\t-f \tCLICON config file\n"
"\t-l (s|e|o|f) Log on (s)yslog, std(e)rr or std(o)ut (stderr is default) Only valid if -F, if background syslog is on syslog.\n"
"\t-d \tSpecify backend plugin directory (default: %s)\n"
+ "\t-p \tYang directory path (see CLICON_YANG_DIR)\n"
"\t-b \tSpecify XMLDB database directory\n"
"\t-F\t\tRun in foreground, do not run as daemon\n"
"\t-z\t\tKill other config daemon and exit\n"
@@ -152,7 +155,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",
@@ -174,22 +178,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, clicon_username_get(h), cbret);
done:
if (xt)
xml_free(xt);
@@ -264,7 +270,7 @@ nacm_load_external(clicon_handle h)
}
if ((yspec = yspec_new()) == NULL)
goto done;
- if (yang_parse(h, NULL, "ietf-netconf-acm", CLIXON_DATADIR, NULL, yspec, NULL) < 0)
+ if (yang_spec_parse_module(h, "ietf-netconf-acm", NULL, yspec) < 0)
goto done;
fd = fileno(f);
/* Read configfile */
@@ -274,7 +280,7 @@ nacm_load_external(clicon_handle h)
clicon_err(OE_XML, 0, "No xml tree in %s", filename);
goto done;
}
- if (backend_nacm_list_set(h, xt) < 0)
+ if (clicon_nacm_ext_set(h, xt) < 0)
goto done;
retval = 0;
done:
@@ -286,18 +292,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;
@@ -308,9 +318,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, clicon_username_get(h), cbret);
done:
if (fd != -1)
close(fd);
@@ -383,8 +391,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;
@@ -397,34 +410,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
@@ -450,10 +475,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;
@@ -470,33 +501,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
@@ -504,7 +548,7 @@ main(int argc,
char **argv)
{
int retval = -1;
- char c;
+ int c;
int zap;
int foreground;
int once;
@@ -521,13 +565,12 @@ 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;
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
+ char *str;
/* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -612,12 +655,18 @@ main(int argc,
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg);
+ if (clicon_option_add(h, "CLICON_BACKEND_DIR", optarg) < 0)
+ goto done;
break;
case 'b': /* XMLDB database directory */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_XMLDB_DIR", optarg);
+ if (clicon_option_add(h, "CLICON_XMLDB_DIR", optarg) < 0)
+ goto done;
+ break;
+ case 'p' : /* yang dir path */
+ if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
+ goto done;
break;
case 'F' : /* foreground */
foreground = 1;
@@ -626,21 +675,25 @@ main(int argc,
zap++;
break;
case 'a': /* internal backend socket address family */
- clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
+ if (clicon_option_add(h, "CLICON_SOCK_FAMILY", optarg) < 0)
+ goto done;
break;
case 'u': /* config unix domain path / ip address */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_SOCK", optarg);
+ if (clicon_option_add(h, "CLICON_SOCK", optarg) < 0)
+ goto done;
break;
case 'P': /* pidfile */
- clicon_option_str_set(h, "CLICON_BACKEND_PIDFILE", optarg);
+ if (clicon_option_add(h, "CLICON_BACKEND_PIDFILE", optarg) < 0)
+ goto done;
break;
case '1' : /* Quit after reading database once - dont wait for events */
once = 1;
break;
case 's' : /* startup mode */
- clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg);
+ if (clicon_option_add(h, "CLICON_STARTUP_MODE", optarg) < 0)
+ goto done;
if (clicon_startup_mode(h) < 0){
fprintf(stderr, "Invalid startup mode: %s\n", optarg);
usage(h, argv[0]);
@@ -650,14 +703,24 @@ main(int argc,
extraxml_file = optarg;
break;
case 'g': /* config socket group */
- clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg);
+ if (clicon_option_add(h, "CLICON_SOCK_GROUP", optarg) < 0)
+ goto done;
break;
- case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ case 'y' : /* Load yang absolute filename */
+ if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
+ goto done;
break;
- }
- case 'x' :{ /* xmldb plugin */
- clicon_option_str_set(h, "CLICON_XMLDB_PLUGIN", optarg);
+ case 'x' : /* xmldb plugin */
+ if (clicon_option_add(h, "CLICON_XMLDB_PLUGIN", 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:
@@ -750,18 +813,23 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec, NULL) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL)
+ if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
- }
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_dir(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL)
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ /* 3. Load all modules in a directory */
+ 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;
@@ -770,11 +838,11 @@ main(int argc,
goto done;
/* Load yang Restconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
- yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec, NULL)< 0)
+ yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
- yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec, NULL)< 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)
@@ -787,9 +855,12 @@ main(int argc,
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
goto done;
- if ((xml_pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) >= 0)
- if (xmldb_setopt(h, "pretty", (void*)(intptr_t)xml_pretty) < 0)
- goto done;
+ if (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
+ goto done;
+ if (xmldb_setopt(h, "nacm_mode", (void*)nacm_mode) < 0)
+ goto done;
+ if (xmldb_setopt(h, "nacm_xtree", (void*)clicon_nacm_ext(h)) < 0)
+ goto done;
/* Startup mode needs to be defined, */
startup_mode = clicon_startup_mode(h);
if (startup_mode == -1){
@@ -839,7 +910,6 @@ main(int argc,
}
}
/* Write pid-file */
-
if ((pid = pidfile_write(pidfile)) < 0)
goto done;
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 8ed13d97..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.
@@ -91,7 +91,6 @@ struct backend_handle {
/* ------ end of common handle ------ */
struct client_entry *bh_ce_list; /* The client list */
int bh_ce_nr; /* Number of clients, just increment */
- cxobj *bh_nacm; /* NACM external struct */
};
/*! Creates and returns a clicon config handle for other CLICON API calls
@@ -109,7 +108,6 @@ backend_handle_init(void)
int
backend_handle_exit(clicon_handle h)
{
- struct backend_handle *bh = handle(h);
struct client_entry *ce;
/* only delete client structs, not close sockets, etc, see backend_client_rm WHY NOT? */
@@ -120,8 +118,6 @@ backend_handle_exit(clicon_handle h)
}
backend_client_delete(h, ce);
}
- if (bh->bh_nacm)
- xml_free(bh->bh_nacm);
clicon_handle_exit(h); /* frees h and options (and streams) */
return 0;
}
@@ -188,22 +184,3 @@ backend_client_delete(clicon_handle h,
return 0;
}
-int
-backend_nacm_list_set(clicon_handle h,
- cxobj *xnacm)
-{
- struct backend_handle *bh = handle(h);
-
- if (bh->bh_nacm)
- xml_free(bh->bh_nacm);
- bh->bh_nacm = xnacm;
- return 0;
-}
-
-cxobj *
-backend_nacm_list_get(clicon_handle h)
-{
- struct backend_handle *bh = handle(h);
-
- return bh->bh_nacm;
-}
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 22f76883..d2c5c39c 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.
@@ -50,8 +50,9 @@
#include
#include
#include
+#include
#include
-
+#include /* For pow() kludge in cvtype_max2str_dup2 */
/* cligen */
#include
@@ -162,6 +163,98 @@ cli_callback_generate(clicon_handle h,
return retval;
}
+
+/*! Generate identityref statements for CLI variables
+ * @param[in] ys Yang statement
+ * @param[in] ytype Yang union type being resolved
+ * @param[in] helptext CLI help text
+ * @param[out] cb Buffer where cligen code is written
+ * @see yang2cli_var_sub Its sub-function
+ */
+static int
+yang2cli_var_identityref(yang_stmt *ys,
+ yang_stmt *ytype,
+ char *cvtypestr,
+ char *helptext,
+ cbuf *cb)
+{
+ int retval = -1;
+ yang_stmt *ybaseref;
+ yang_stmt *ybaseid;
+ cg_var *cv = NULL;
+ char *name;
+ char *id;
+ int i;
+
+ /* Add a wildchar string first -let validate take it for default prefix */
+ cprintf(cb, ">");
+ if (helptext)
+ cprintf(cb, "(\"%s\")", helptext);
+ if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) != NULL &&
+ (ybaseid = yang_find_identity(ys, ybaseref->ys_argument)) != NULL){
+ if (cvec_len(ybaseid->ys_cvec) > 0){
+ cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr);
+ i = 0;
+ while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){
+ if (i++)
+ cprintf(cb, "|");
+ name = strdup(cv_name_get(cv));
+ if ((id=strchr(name, ':')) != NULL)
+ *id = '\0';
+ cprintf(cb, "%s:%s", name, id+1);
+ if (name)
+ free(name);
+ }
+ }
+ }
+ retval = 0;
+ // done:
+ return retval;
+}
+
+/*! Generate range check statements for CLI variables
+ * @param[out] cb Buffer where cligen code is written
+ * @see yang2cli_var_sub Its sub-function
+ */
+static int
+yang2cli_var_range(yang_stmt *ys,
+ cvec *cvv,
+ int options,
+ cbuf *cb)
+{
+ int retval = -1;
+ int i;
+ cg_var *cv;
+
+ /* Loop through range_min and range_min..range_max */
+ i = 0;
+ while (iys_argument);
+ break;
+ }
+ 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 (iys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT)
continue;
@@ -223,82 +315,30 @@ yang2cli_var_sub(clicon_handle h,
}
}
else if (strcmp(type, "identityref") == 0){
- yang_stmt *ybaseref;
- yang_stmt *ybaseid;
- cg_var *cv = NULL;
- char *name;
- char *id;
- /* Add a wildchar string first -let validate take it for default prefix */
- cprintf(cb, ">");
- if (helptext)
- cprintf(cb, "(\"%s\")", helptext);
- if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) != NULL &&
- (ybaseid = yang_find_identity(ys, ybaseref->ys_argument)) != NULL){
- if (cvec_len(ybaseid->ys_cvec) > 0){
- cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr);
- i = 0;
- while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){
- if (i++)
- cprintf(cb, "|");
- name = strdup(cv_name_get(cv));
- if ((id=strchr(name, ':')) != NULL)
- *id = '\0';
- cprintf(cb, "%s:%s", name, id+1);
- if (name)
- free(name);
- }
- }
- }
+ if (yang2cli_var_identityref(ys, ytype, cvtypestr, helptext, cb) < 0)
+ goto done;
}
}
-
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_dup(cvtype)) == NULL){
- clicon_err(OE_UNIX, errno, "cvtype_max2str");
- goto done;
- }
- }
- cprintf(cb, "%s]", r); /* range */
- free(r);
- r = NULL;
+ if (yang2cli_var_range(ys, cvv, options, cb) < 0)
+ goto done;
+ }
+ if (options & YANG_OPTIONS_PATTERN){
+ char *posix = NULL;
+ if (regexp_xsd2posix(pattern, &posix) < 0)
+ goto done;
+ cprintf(cb, " regexp:\"%s\"", posix);
+ if (posix)
+ free(posix);
}
- if (options & YANG_OPTIONS_PATTERN)
- cprintf(cb, " regexp:\"%s\"", pattern);
cprintf(cb, ">");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
if (type && strcmp(type, "identityref") == 0)
cprintf(cb, ")");
-
retval = 0;
done:
return retval;
@@ -323,8 +363,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;
@@ -332,9 +371,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;
@@ -343,10 +382,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;
@@ -410,12 +449,11 @@ yang2cli_var(clicon_handle h,
cbuf *cb,
char *helptext)
{
- int retval = -1;
+ int retval = -1;
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;
@@ -424,7 +462,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;
@@ -432,7 +470,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){
@@ -461,7 +499,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,
@@ -513,10 +551,12 @@ yang2cli_leaf(clicon_handle h,
if (helptext)
cprintf(cbuf, "(\"%s\")", helptext);
cprintf(cbuf, " ");
- yang2cli_var(h, ys, cbuf, helptext);
+ if (yang2cli_var(h, ys, cbuf, helptext) < 0)
+ goto done;
}
else
- yang2cli_var(h, ys, cbuf, helptext);
+ if (yang2cli_var(h, ys, cbuf, helptext) < 0)
+ goto done;
if (callback){
if (cli_callback_generate(h, ys, cbuf) < 0)
goto done;
@@ -725,11 +765,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;
@@ -747,19 +782,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
@@ -788,13 +825,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 c651127e..0cbafccd 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:qp:GLy:c:U:o:"
#define CLI_LOGFILE "/tmp/clixon_cli.log"
@@ -221,13 +221,14 @@ usage(clicon_handle h,
"\t-d \tSpecify plugin directory (default: %s)\n"
"\t-m \tSpecify plugin syntax mode\n"
"\t-q \t\tQuiet mode, dont print greetings or prompt, terminate on ctrl-C\n"
- "\t-p \t\tPrint database yang specification\n"
+ "\t-p \tYang directory path (see CLICON_YANG_DIR)\n"
"\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n"
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
"\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"
);
@@ -240,25 +241,23 @@ int
main(int argc, char **argv)
{
int retval = -1;
- char c;
+ int c;
int once;
char *tmp;
char *argv0 = argv[0];
clicon_handle h;
- int printspec = 0;
int printgen = 0;
int logclisyntax = 0;
int help = 0;
char *treename = NULL;
- int len;
+ // int len;
int logdst = CLICON_LOG_STDERR;
char *restarg = NULL; /* what remains after options */
int dump_configfile_xml = 0;
yang_spec *yspec;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
struct passwd *pw;
- char *yang_filename = NULL;
- yang_stmt *ymod = NULL; /* Main module */
+ char *str;
/* Defaults */
once = 0;
@@ -361,28 +360,33 @@ main(int argc, char **argv)
once = 1;
break;
case 'a': /* internal backend socket address family */
- clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
+ if (clicon_option_add(h, "CLICON_SOCK_FAMILY", optarg) < 0)
+ goto done;
break;
case 'u': /* internal backend socket unix domain path or ip host */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_SOCK", optarg);
+ if (clicon_option_add(h, "CLICON_SOCK", optarg) < 0)
+ goto done;
break;
case 'd': /* Plugin directory: overrides configfile */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_CLI_DIR", optarg);
+ if (clicon_option_add(h, "CLICON_CLI_DIR", optarg) < 0)
+ goto done;
break;
case 'm': /* CLI syntax mode */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_CLI_MODE", optarg);
+ if (clicon_option_add(h, "CLICON_CLI_MODE", optarg) < 0)
+ goto done;
break;
case 'q' : /* Quiet mode */
clicon_quiet_mode_set(h, 1);
break;
- case 'p' : /* Print spec */
- printspec++;
+ case 'p' : /* yang dir path */
+ if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
+ goto done;
break;
case 'G' : /* Print generated CLI syntax */
printgen++;
@@ -390,20 +394,29 @@ main(int argc, char **argv)
case 'L' : /* Debug print dynamic CLI syntax */
logclisyntax++;
break;
- case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ case 'y' : /* Load yang absolute filename */
+ if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
+ goto done;
break;
- }
- case 'c' :{ /* Overwrite clispec with absolute filename */
- clicon_option_str_set(h, "CLICON_CLISPEC_FILE", optarg);
+ case 'c' : /* Overwrite clispec with absolute filename */
+ if (clicon_option_add(h, "CLICON_CLISPEC_FILE", optarg) < 0)
+ goto done;
break;
- }
case 'U': /* Clixon 'pseudo' user */
if (!strlen(optarg))
usage(h, argv[0]);
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;
@@ -430,44 +443,49 @@ main(int argc, char **argv)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec, &ymod) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_dir(h),
- clicon_yang_module_revision(h),
- yspec, &ymod) < 0)
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ 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;
- if (printspec)
- yang_print(stdout, (yang_node*)yspec);
/* Create tree generated from dataspec. If no other trees exists, this is
* the only one.
+ * The following code creates the tree @datamodel
+ * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR)
+ * using the "tree reference"
+ * syntax, ie @datamodel
+ * But note that yang2cli generates syntax for ALL modules, not just for
+ * .
*/
if (clicon_cli_genmodel(h)){
parse_tree pt = {0,}; /* cli parse tree */
- char *name; /* main module name */
+ char *treeref;
+ treeref = clicon_cli_model_treename(h);
/* Create cli command tree from dbspec */
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
goto done;
-
- /* name of main module */
- name = ymod->ys_argument;
-
- len = strlen("datamodel:") + strlen(name) + 1;
- if ((treename = malloc(len)) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- snprintf(treename, len, "datamodel:%s", name);
- cligen_tree_add(cli_cligen(h), treename, pt);
+ cligen_tree_add(cli_cligen(h), treeref, pt);
if (printgen)
cligen_print(stdout, pt, 1);
diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c
index 0fb58736..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.
@@ -209,6 +209,30 @@ clixon_str2fn(char *name,
return NULL;
}
+#ifdef CLICON_CLI_MODEL_TREENAME_PATCH
+/*! 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).
+ * All new references should use @datamodel (or CLICON_CLI_MODEL_TREENAME).
+ * whereas older code used @datamodel:tree.
+ */
+static int
+mask_datamodel_fn(cg_obj *co,
+ void *arg)
+{
+ char *str = "datamodel:";
+ int len = strlen(str);
+ if (co->co_type == CO_REFERENCE){
+
+ if (strlen(co->co_command) > len &&
+ strncmp(co->co_command, "datamodel:", len)==0){
+ co->co_command[len-1] = '\0';
+ }
+ }
+ return 0;
+}
+#endif /* CLICON_CLI_MODEL_TREENAME_PATCH */
+
/*! Append to syntax mode from file
* @param[in] h Clixon handle
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
@@ -253,7 +277,10 @@ cli_load_syntax(clicon_handle h,
goto done;
}
fclose(f);
-
+#ifdef CLICON_CLI_MODEL_TREENAME_PATCH
+ if (pt_apply(pt, mask_datamodel_fn, h) < 0)
+ goto done;
+#endif
/* Get CLICON specific global variables */
prompt = cvec_find_str(cvv, "CLICON_PROMPT");
plgnam = cvec_find_str(cvv, "CLICON_PLUGIN");
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 6f3f30bc..b6c1e930 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:p:y:U:t:o:"
#define NETCONF_LOGFILE "/tmp/clixon_netconf.log"
@@ -80,21 +80,29 @@
* @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;
+ }
yspec = clicon_dbspec_yang(h);
if ((str0 = strdup(cbuf_get(cb))) == NULL){
clicon_log(LOG_ERR, "%s: strdup: %s", __FUNCTION__, strerror(errno));
@@ -103,19 +111,24 @@ process_incoming_packet(clicon_handle h,
str = str0;
/* Parse incoming XML message */
if (xml_parse_string(str, yspec, &xreq) < 0){
- if ((cbret = cbuf_new()) == NULL){
- if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
- goto done;
- netconf_output(1, cbret, "rpc-error");
- }
- else
- clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
free(str0);
+ if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0)
+ goto done;
+ netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
free(str0);
- if ((xrpc=xpath_first(xreq, "//rpc")) != NULL)
+ if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
isrpc++;
+ if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
+ goto done;
+ if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
+ goto done;
+ if (ret == 0){
+ netconf_output_encap(1, cbret, "rpc-error");
+ goto ok;
+ }
+ }
else
if (xpath_first(xreq, "//hello") != NULL)
;
@@ -132,35 +145,36 @@ process_incoming_packet(clicon_handle h,
goto done;
}
else{ /* there is a return message in xret */
- cxobj *xa, *xa2;
- assert(xret);
- if ((cbret = cbuf_new()) != NULL){
- if ((xc = xml_child_i(xret,0))!=NULL){
- xa=NULL;
- /* Copy message-id attribute from incoming to reply.
- * RFC 6241:
- * If additional attributes are present in an element, a NETCONF
- * peer MUST return them unmodified in the element. This
- * includes any "xmlns" attributes.
- */
- while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
- if ((xa2 = xml_dup(xa)) ==NULL)
- goto done;
- 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){
- cbuf_free(cbret);
+ 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.
+ * RFC 6241:
+ * If additional attributes are present in an element, a NETCONF
+ * peer MUST return them unmodified in the element. This
+ * includes any "xmlns" attributes.
+ */
+ while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
+ if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
- }
+ if (xml_addsub(xc, xa2) < 0)
+ goto done;
+ }
+ clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
+ if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
+ cbuf_free(cbret);
+ goto done;
}
}
}
+ ok:
+ retval = 0;
done:
if (xreq)
xml_free(xreq);
@@ -168,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
@@ -221,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);
@@ -310,16 +324,17 @@ usage(clicon_handle h,
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D \tDebug level\n"
- "\t-q\t\tQuiet: dont send hello prompt\n"
"\t-f \tConfiguration file (mandatory)\n"
"\t-l (e|o|s|f) \tLog on std(e)rr, std(o)ut, (s)yslog, (f)ile (syslog is default)\n"
+ "\t-q\t\tQuiet: dont send hello prompt\n"
"\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n"
"\t-u \tInternal socket domain path or IP addr (see -a)\n"
"\t-d \tSpecify netconf plugin directory dir (default: %s)\n"
-
+ "\t-p \tYang directory path (see CLICON_YANG_DIR)\n"
"\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)
);
@@ -330,7 +345,7 @@ int
main(int argc,
char **argv)
{
- char c;
+ int c;
char *tmp;
char *argv0 = argv[0];
int quiet = 0;
@@ -341,7 +356,7 @@ main(int argc,
struct timeval tv = {0,}; /* timeout */
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
+ char *str;
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
@@ -403,6 +418,9 @@ main(int argc,
case 'f': /* config file */
case 'l': /* log */
break; /* see above */
+ case 'q': /* quiet: dont write hello */
+ quiet++;
+ break;
case 'a': /* internal backend socket address family */
clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
break;
@@ -411,18 +429,20 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
- case 'q': /* quiet: dont write hello */
- quiet++;
- break;
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(h, argv[0]);
- clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
+ if (clicon_option_add(h, "CLICON_NETCONF_DIR", optarg) < 0)
+ goto done;
break;
- case 'y' :{ /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ case 'p' : /* yang dir path */
+ if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
+ goto done;
+ break;
+ case 'y' : /* Load yang spec file (override yang main module) */
+ if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
+ goto done;
break;
- }
case 'U': /* Clixon 'pseudo' user */
if (!strlen(optarg))
usage(h, argv[0]);
@@ -432,7 +452,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;
@@ -444,18 +472,26 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec, NULL) < 0)
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_dir(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ 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;
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 838f0c73..fc034abf 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.
@@ -32,7 +32,13 @@
***** END LICENSE BLOCK *****
*
- * Code for handling netconf rpc messages according to RFC 4741 and RFC 5277
+ * Code for handling netconf rpc messages according to RFC 4741,5277,6241
+ * All NETCONF protocol elements are defined in the following namespace:
+ * urn:ietf:params:xml:ns:netconf:base:1.0
+ * YANG defines an XML namespace for NETCONF operations,
+ * content, and the element. The name of this
+ * namespace is "urn:ietf:params:xml:ns:yang:1".
+ *
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@@ -50,6 +56,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -128,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");
@@ -180,7 +177,6 @@ netconf_get_config(clicon_handle h,
"type "
" ");
}
- ok: /* netconf error is not fatal */
retval = 0;
done:
return retval;
@@ -216,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;
@@ -304,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 "
@@ -358,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
*
*
@@ -583,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.
@@ -747,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) */
@@ -760,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;
@@ -769,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;
}
@@ -834,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:
@@ -863,18 +543,23 @@ netconf_application_rpc(clicon_handle h,
int retval = -1;
yang_spec *yspec = NULL; /* application yspec */
yang_stmt *yrpc = NULL;
+ yang_stmt *ymod = NULL;
yang_stmt *yinput;
yang_stmt *youtput;
cxobj *xoutput;
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){
@@ -882,36 +567,42 @@ netconf_application_rpc(clicon_handle h,
goto done;
}
cbuf_reset(cb);
- if (xml_namespace(xn) == NULL){
+ if (ys_module_by_xml(yspec, xn, &ymod) < 0)
+ goto done;
+ if (ymod == NULL){
xml_parse_va(xret, NULL, ""
"operation-failed "
"rpc "
"error "
"%s "
- "Not recognized "
+ "Not recognized module "
" ", xml_name(xn));
goto ok;
}
- cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
- /* Find yang rpc statement, return yang rpc statement if found */
- if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0)
- goto done;
+ yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
+ if ((yrpc==NULL) && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(xn, yspec, &yrpc) < 0) /* Y_RPC */
+ goto done;
+ }
/* Check if found */
if (yrpc != NULL){
/* 1. Check xn arguments with input statement. */
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
- if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
+ 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)
@@ -923,17 +614,30 @@ netconf_application_rpc(clicon_handle h,
else /* Send to backend */
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
- /* Sanity check of outgoing XML */
+ /* Sanity check of outgoing XML
+ * For now, skip outgoing checks.
+ * (1) Does not handle properly
+ * (2) Uncertain how validation errors should be logged/handled
+ */
+ if (0)
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, "/");
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
- if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
+ 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;
@@ -948,7 +652,6 @@ netconf_application_rpc(clicon_handle h,
return retval;
}
-
/*! The central netconf rpc dispatcher. Look at first tag and dispach to sub-functions.
* Call plugin handler if tag not found. If not handled by any handler, return
* error.
@@ -979,10 +682,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;
}
@@ -990,47 +708,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 ed7fdc43..d83f1a27 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:d: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"
@@ -492,10 +492,12 @@ usage(clicon_handle h,
"\t-D \tDebug level\n"
"\t-f \tConfiguration file (mandatory)\n"
"\t-l > \tLog on (s)yslog, (f)ile (syslog is default)\n"
+ "\t-p \tYang directory path (see CLICON_YANG_DIR)\n"
"\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)
);
@@ -513,19 +515,18 @@ main(int argc,
char *argv0 = argv[0];
FCGX_Request request;
FCGX_Request *r = &request;
- char c;
+ int c;
char *sockpath;
char *path;
clicon_handle h;
- char *yangspec=NULL;
char *dir;
char *tmp;
int logdst = CLICON_LOG_SYSLOG;
yang_spec *yspec = NULL;
yang_spec *yspecfg = NULL; /* For config XXX clixon bug */
- char *yang_filename = NULL;
char *stream_path;
int finish;
+ char *str;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@@ -596,13 +597,17 @@ main(int argc,
case 'f': /* config file */
case 'l': /* log */
break; /* see above */
+ case 'p' : /* yang dir path */
+ if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
+ goto done;
+ break;
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
break;
case 'y' : /* Load yang spec file (override yang main module) */
- yang_filename = optarg;
+ clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
case 'a': /* internal backend socket address family */
clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
@@ -612,6 +617,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;
@@ -619,10 +633,6 @@ main(int argc,
argc -= optind;
argv += optind;
- /* Overwrite yang module with -y option */
- if (yangspec)
- clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", yangspec);
-
/* Initialize plugins group */
if ((dir = clicon_restconf_dir(h)) != NULL)
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
@@ -632,27 +642,36 @@ main(int argc,
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
- /* Load main application yang specification either module or specific file
- * If -y is given, it overrides main module */
- if (yang_filename){
- if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec, NULL) < 0)
+
+ /* Load Yang modules
+ * 1. Load a yang module as a specific absolute filename */
+ if ((str = clicon_yang_main_file(h)) != NULL){
+ if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
- else if (yang_spec_parse_module(h, clicon_yang_module_main(h),
- clicon_yang_dir(h),
- clicon_yang_module_revision(h),
- yspec, NULL) < 0)
+ /* 2. Load a (single) main module */
+ if ((str = clicon_yang_module_main(h)) != NULL){
+ if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
+ yspec) < 0)
+ goto done;
+ }
+ /* 3. Load all modules in a directory */
+ 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;
/* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
- yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec, NULL)< 0)
+ 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", CLIXON_DATADIR, NULL, yspec, NULL)< 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 133f0ed9..fcc266a9 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? */
@@ -255,21 +270,45 @@ api_data_get2(clicon_handle h,
}
}
else{
- if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0)
- goto done;
- clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, (int)xlen);
+ if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0){
+ if (netconf_operation_failed_xml(&xerr, "application", 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;
+ }
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));
+ 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:
@@ -408,11 +447,13 @@ api_data_post(clicon_handle h,
yang_spec *yspec;
cxobj *xa;
cxobj *xret = NULL;
- cxobj *xretcom = NULL;
+ cxobj *xretcom = NULL; /* return from commit */
+ cxobj *xretdis = NULL; /* return from discard-changes */
cxobj *xerr = NULL; /* malloced must be freed */
- cxobj *xe;
+ cxobj *xe; /* dont free */
char *username;
-
+ int ret;
+
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
api_path, data);
@@ -427,14 +468,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 +501,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 +515,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 +533,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;
@@ -488,14 +568,23 @@ api_data_post(clicon_handle h,
}
/* Assume this is validation failed since commit includes validate */
cbuf_reset(cbx);
- cprintf(cbx, "", username?username:"");
+ /* commit/discard should be done automaticaly by the system, therefore
+ * recovery user is used here (edit-config but not commit may be permitted
+ by NACM */
+ cprintf(cbx, "", NACM_RECOVERY_USER);
cprintf(cbx, " ");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
- if (clicon_rpc_discard_changes(h) < 0)
+ cbuf_reset(cbx);
+ cprintf(cbx, "", username?username:"");
+ cprintf(cbx, " ");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
- if (api_return_err(h, r, xe, pretty, use_xml) < 0)
+ /* 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) /* Use original xe */
goto done;
goto ok;
}
@@ -512,6 +601,8 @@ api_data_post(clicon_handle h,
xml_free(xerr);
if (xretcom)
xml_free(xretcom);
+ if (xretdis)
+ xml_free(xretdis);
if (xtop)
xml_free(xtop);
if (xdata)
@@ -552,18 +643,19 @@ match_list_keys(yang_stmt *y,
char *keyd;
if (y->ys_keyword != Y_LIST &&y->ys_keyword != Y_LEAF_LIST)
- return -1;
+ goto done;
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((xkeya = xml_find(xapipath, keyname)) == NULL)
goto done; /* No key in api-path */
-
- keya = xml_body(xkeya);
+ if ((keya = xml_body(xkeya)) == NULL)
+ goto done;
if ((xkeyd = xml_find(xdata, keyname)) == NULL)
goto done; /* No key in data */
- keyd = xml_body(xkeyd);
+ if ((keyd = xml_body(xkeyd)) == NULL)
+ goto done;
if (strcmp(keya, keyd) != 0)
goto done; /* keys dont match */
}
@@ -623,10 +715,13 @@ api_data_put(clicon_handle h,
cxobj *xa;
char *api_path;
cxobj *xret = NULL;
- cxobj *xretcom = NULL;
+ cxobj *xretcom = NULL; /* return from commit */
+ cxobj *xretdis = NULL; /* return from discard-changes */
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);
@@ -642,25 +737,48 @@ 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.
@@ -668,17 +786,43 @@ api_data_put(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;
}
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)
@@ -689,11 +833,16 @@ api_data_put(clicon_handle h,
xml_name_set(xtop, "config");
}
else {
+ clicon_debug(1, "%s x:%s xbot:%s",__FUNCTION__, xml_name(x), xml_name(xbot));
/* Check same symbol in api-path as data */
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;
}
@@ -702,7 +851,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;
}
@@ -711,8 +864,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;
@@ -734,13 +894,22 @@ api_data_put(clicon_handle h,
goto ok;
}
cbuf_reset(cbx);
- cprintf(cbx, "", username?username:"");
+ /* commit/discard should be done automaticaly by the system, therefore
+ * recovery user is used here (edit-config but not commit may be permitted
+ by NACM */
+ cprintf(cbx, "", NACM_RECOVERY_USER);
cprintf(cbx, " ");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
- if (clicon_rpc_discard_changes(h) < 0)
+ cbuf_reset(cbx);
+ cprintf(cbx, "", username?username:"");
+ cprintf(cbx, " ");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
+ /* log errors from discard, but ignore */
+ if ((xpath_first(xretdis, "//rpc-error")) != NULL)
+ clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, use_xml) < 0)
goto done;
goto ok;
@@ -758,6 +927,8 @@ api_data_put(clicon_handle h,
xml_free(xerr);
if (xretcom)
xml_free(xretcom);
+ if (xretdis)
+ xml_free(xretdis);
if (xtop)
xml_free(xtop);
if (xdata)
@@ -791,7 +962,7 @@ api_data_patch(clicon_handle h,
return 0;
}
-/*! Generic REST DELETE method
+/*! Generic REST DELETE method translated to edit-config
* @param[in] h CLIXON handle
* @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
@@ -821,9 +992,12 @@ api_data_delete(clicon_handle h,
yang_spec *yspec;
enum operation_type op = OP_DELETE;
cxobj *xret = NULL;
- cxobj *xretcom = NULL;
+ cxobj *xretcom = NULL; /* return from commmit */
+ 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){
@@ -836,13 +1010,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;
@@ -857,21 +1044,30 @@ 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;
}
/* Assume this is validation failed since commit includes validate */
cbuf_reset(cbx);
- cprintf(cbx, "", username?username:"");
+ /* commit/discard should be done automaticaly by the system, therefore
+ * recovery user is used here (edit-config but not commit may be permitted
+ by NACM */
+ cprintf(cbx, "", NACM_RECOVERY_USER);
cprintf(cbx, " ");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
- if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
- if (clicon_rpc_discard_changes(h) < 0)
+ if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
+ cbuf_reset(cbx);
+ cprintf(cbx, "", NACM_RECOVERY_USER);
+ cprintf(cbx, " ");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
- if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
+ /* 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)
goto done;
goto ok;
}
@@ -887,6 +1083,8 @@ api_data_delete(clicon_handle h,
xml_free(xret);
if (xretcom)
xml_free(xretcom);
+ if (xretdis)
+ xml_free(xretdis);
if (xtop)
xml_free(xtop);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
@@ -912,6 +1110,11 @@ api_data_delete(clicon_handle h,
* data-model-specific RPC operations supported by the server. The
* server MAY omit this resource if no data-model-specific RPC
* operations are advertised.
+ * From ietf-restconf.yang:
+ * In XML, the YANG module namespace identifies the module:
+ *
+ * In JSON, the YANG module name identifies the module:
+ * { 'ietf-system:system-restart' : [null] }
*/
int
api_operations_get(clicon_handle h,
@@ -926,43 +1129,42 @@ api_operations_get(clicon_handle h,
{
int retval = -1;
yang_spec *yspec;
- yang_stmt *ym;
+ yang_stmt *ymod; /* yang module */
yang_stmt *yc;
- char *modname;
+ 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, "");
- ym = NULL;
- while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) {
- modname = ym->ys_argument;
- yc = NULL;
- while ((yc = yn_each((yang_node*)ym, yc)) != NULL) {
+ 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;
+ while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
if (yc->ys_keyword != Y_RPC)
continue;
- cprintf(cbx, "<%s:%s />", modname, yc->ys_argument);
+ 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, " ");
- clicon_debug(1, "%s xml:%s", __FUNCTION__, cbuf_get(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;
- }
- clicon_debug(1, "%s ret:%s", __FUNCTION__, cbuf_get(cbx));
+ 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");
@@ -979,6 +1181,289 @@ 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:
+ * ...
+ */
+#if 1
+ 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
+ /* Validate that exactly only tag */
+ 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 isempty;
+
+ // clicon_debug(1, "%s", __FUNCTION__);
+ 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 1
+ 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
+
+ /* Sanity check of outgoing XML
+ * For now, skip outgoing checks.
+ * (1) Does not handle properly
+ * (2) Uncertain how validation errors should be logged/handled
+ */
+ if (youtput!=NULL){
+ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
+#if 0
+ 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;
+ }
+#endif
+ }
+ /* Special case, no yang output (single - or empty body?)
+ * 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.
+ */
+ isempty = xml_child_nr_type(xoutput, CX_ELMNT) == 0 ||
+ (xml_child_nr_type(xoutput, CX_ELMNT) == 1 &&
+ (xok = xml_child_i_type(xoutput, 0, CX_ELMNT)) != NULL &&
+ strcmp(xml_name(xok),"ok")==0);
+ if (isempty) {
+ /* Internal error - invalid output from rpc handler */
+ FCGX_SetExitStatus(204, r->out); /* OK */
+ FCGX_FPrintF(r->out, "Status: 204 No Content\r\n");
+ FCGX_FPrintF(r->out, "\r\n");
+ 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
@@ -992,7 +1477,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,
@@ -1009,21 +1511,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;
@@ -1032,215 +1528,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 0
- {
- cbuf *c = cbuf_new();
- clicon_xml2cbuf(c, xtop, 0, 0);
- clicon_debug(1, "%s xtop:%s", __FUNCTION__, cbuf_get(c));
- cbuf_free(c);
- }
-#endif
- 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 */
- if (xml_apply(xbot, CX_ELMNT, xml_spec_populate, yinput) < 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, youtput) < 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 1
+ 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 incoming 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-reply/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-reply/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 1
+ 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;
-#if 1
- clicon_debug(1, "%s cbx:%s", __FUNCTION__, cbuf_get(cbx));
-#endif
- 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:
@@ -1249,16 +1735,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&5
+$as_echo "stdyangs is $enable_stdyangs" >&6; }
+
# Experimental: Curl publish notification stream to eg Nginx nchan.
# Check whether --enable-publish was given.
if test "${enable_publish+set}" = set; then :
@@ -4391,15 +4416,14 @@ fi
done
-# This is to find clixon system files in the source code and Makefile
+# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile
+# This directory should most probably be included in each application,
+# so each application designer may need to place CLIXON_DATADIR in their config
+# (last in yang dir list):
+# $CLIXON_DATADIR
CLIXON_DATADIR="${prefix}/share/clixon"
-cat >>confdefs.h <<_ACEOF
-#define CLIXON_DATADIR "${CLIXON_DATADIR}"
-_ACEOF
-
-
# Default location for config file
cat >>confdefs.h <<_ACEOF
@@ -4409,7 +4433,7 @@ _ACEOF
-ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile"
+ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -5122,7 +5146,10 @@ do
"datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;;
"util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;;
"yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;;
+ "yang/clixon/Makefile") CONFIG_FILES="$CONFIG_FILES yang/clixon/Makefile" ;;
+ "yang/standard/Makefile") CONFIG_FILES="$CONFIG_FILES yang/standard/Makefile" ;;
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+ "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
diff --git a/configure.ac b/configure.ac
index e6c08194..13cd7712 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
#
@@ -43,9 +43,9 @@ AC_INIT(lib/clixon/clixon.h.in)
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="3"
-CLIXON_VERSION_MINOR="8"
+CLIXON_VERSION_MINOR="9"
CLIXON_VERSION_PATCH="0"
-CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
+CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\""
# Fix to specific CLIgen version (eg 3.5) or head (3)
CLIGEN_VERSION="3"
@@ -91,6 +91,7 @@ AC_SUBST(LIBS)
AC_SUBST(SH_SUFFIX)
AC_SUBST(RANLIB)
AC_SUBST(with_restconf) # If yes, compile apps/restconf
+AC_SUBST(enable_stdyangs)
AC_SUBST(wwwdir,/www-data)
AC_SUBST(wwwuser,www-data)
#
@@ -145,6 +146,20 @@ if test "${with_cligen}"; then
test -d "$with_cligen" && CLIGEN_PREFIX="$with_cligen"
fi
+# Disable/enable standard Yang files.
+# If enable - include yang/standard/*.yang in clixon yang files (default)
+# If disable - get standard yang files from elsewhere
+AC_ARG_ENABLE(stdyangs, AS_HELP_STRING([--disable-stdyangs],[Include standard yang files in clixon install, default: yes]),[
+ if test "$enableval" = no; then
+ enable_stdyangs=no
+ else
+ enable_stdyangs=yes
+ fi
+ ],
+ [ enable_stdyangs=yes])
+
+AC_MSG_RESULT(stdyangs is $enable_stdyangs)
+
# Experimental: Curl publish notification stream to eg Nginx nchan.
AC_ARG_ENABLE(publish, AS_HELP_STRING([--enable-publish],[Enable publish of notification streams using SSE and curl]),[
if test "$enableval" = no; then
@@ -199,10 +214,13 @@ AC_CHECK_LIB(dl, dlopen)
AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp)
-# This is to find clixon system files in the source code and Makefile
+# CLIXON_DATADIR is where clixon installs the "system" yang files in yang/Makfile
+# This directory should most probably be included in each application,
+# so each application designer may need to place CLIXON_DATADIR in their config
+# (last in yang dir list):
+# $CLIXON_DATADIR
AC_SUBST(CLIXON_DATADIR)
CLIXON_DATADIR="${prefix}/share/clixon"
-AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${CLIXON_DATADIR}", [Clixon data dir for system yang files etc])
# Default location for config file
AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${CLIXON_DEFAULT_CONFIG}",[Location for apps to find default config file])
@@ -228,6 +246,9 @@ AC_OUTPUT(Makefile
datastore/text/Makefile
util/Makefile
yang/Makefile
+ yang/clixon/Makefile
+ yang/standard/Makefile
doc/Makefile
+ test/Makefile
)
diff --git a/datastore/Makefile.in b/datastore/Makefile.in
index 98bc8a0e..5adde268 100644
--- a/datastore/Makefile.in
+++ b/datastore/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
#
@@ -49,7 +49,6 @@ INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
with_restconf = @with_restconf@
-with_keyvalue = @with_keyvalue@
SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@@ -117,5 +116,5 @@ distclean: clean
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
-tags:
+TAGS:
find $(srcdir) -name '*.[chyl]' -print | etags -
diff --git a/datastore/README.md b/datastore/README.md
index a3283c6e..8f000d88 100644
--- a/datastore/README.md
+++ b/datastore/README.md
@@ -2,8 +2,7 @@
The Clixon datastore is a stand-alone XML based datastore. The idea is
to be able to use different datastores backends with the same
-API. There is currently a key-value plugin based on qdbm and a plain
-text-file datastore.
+API. There is currently only a plain text-file datastore.
The datastore is primarily designed to be used by Clixon but can be used
separately.
@@ -38,7 +37,7 @@ int xmldb_create(clicon_handle h, char *db);
To use the API, a client needs the following:
- A clicon handle.
-- A datastore plugin, such as a text.so or keyvalue.so. These are normally built and installed at Clixon make.
+- A datastore plugin, such as a text.so. These are normally built and installed at Clixon make.
- A directory where to store databases
- A yang specification. This needs to be parsed using the Clixon yang_parse() method.
diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c
index 82c0263e..b2a0a2a8 100644
--- a/datastore/datastore_client.c
+++ b/datastore/datastore_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.
@@ -31,14 +31,6 @@
***** END LICENSE BLOCK *****
- * Examples:
-
-./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip get /
-
-sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip put merge /interfaces/interface=eth66 'eth66 '
-
-sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip put merge / 'eth0 true '
-
*/
#ifdef HAVE_CONFIG_H
@@ -71,7 +63,7 @@ sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src
#include
/* Command line options to be passed to getopt(3) */
-#define DATASTORE_OPTS "hDd:p:b:y:m:"
+#define DATASTORE_OPTS "hDd:p:b:y:"
/*! usage
*/
@@ -85,8 +77,7 @@ usage(char *argv0)
"\t-d \t\tDatabase name. Default: running. Alt: candidate,startup\n"
"\t-b \tDatabase directory. Mandatory\n"
"\t-p \tDatastore plugin. Mandatory\n"
- "\t-y \tYang directory (where modules are stored). Mandatory\n"
- "\t-m \tYang module. Mandatory\n"
+ "\t-y \tYang file. Mandatory\n"
"and command is either:\n"
"\tget []\n"
"\tmget []\n"
@@ -108,15 +99,14 @@ usage(char *argv0)
int
main(int argc, char **argv)
{
- char c;
+ int c;
clicon_handle h;
char *argv0;
char *db = "running";
char *plugin = NULL;
char *cmd = NULL;
yang_spec *yspec = NULL;
- char *yangdir = NULL;
- char *yangmodule = NULL;
+ char *yangfilename = NULL;
char *dbdir = NULL;
int ret;
int pid;
@@ -158,15 +148,10 @@ main(int argc, char **argv)
usage(argv0);
dbdir = optarg;
break;
- case 'y': /* Yang directory */
+ case 'y': /* Yang file */
if (!optarg)
usage(argv0);
- yangdir = optarg;
- break;
- case 'm': /* Yang module */
- if (!optarg)
- usage(argv0);
- yangmodule = optarg;
+ yangfilename = optarg;
break;
}
/*
@@ -188,12 +173,8 @@ main(int argc, char **argv)
clicon_err(OE_DB, 0, "Missing dbdir -b option");
goto done;
}
- if (yangdir == NULL){
- clicon_err(OE_YANG, 0, "Missing yangdir -y option");
- goto done;
- }
- if (yangmodule == NULL){
- clicon_err(OE_YANG, 0, "Missing yang module -m option");
+ if (yangfilename == NULL){
+ clicon_err(OE_YANG, 0, "Missing yang filename -y option");
goto done;
}
/* Load datastore plugin */
@@ -206,7 +187,7 @@ main(int argc, char **argv)
if ((yspec = yspec_new()) == NULL)
goto done;
/* Parse yang spec from given file */
- if (yang_parse(h, NULL, yangmodule, yangdir, NULL, yspec, NULL) < 0)
+ if (yang_spec_parse_file(h, yangfilename, yspec) < 0)
goto done;
/* Set database directory option */
if (xmldb_setopt(h, "dbdir", dbdir) < 0)
@@ -258,14 +239,18 @@ main(int argc, char **argv)
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
usage(argv0);
}
+ _CLICON_XML_NS_STRICT = 0;
if (xml_parse_string(argv[2], NULL, &xt) < 0)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
- if ((cbret = cbuf_new()) == NULL)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
- if (xmldb_put(h, db, op, xt, cbret) < 0)
+ }
+ if (xmldb_put(h, db, op, xt, NULL, cbret) < 1)
goto done;
+
}
else if (strcmp(cmd, "copy")==0){
if (argc != 2)
diff --git a/datastore/keyvalue/clixon_chunk.c b/datastore/keyvalue/clixon_chunk.c
deleted file mode 100644
index 80db76da..00000000
--- a/datastore/keyvalue/clixon_chunk.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- *
- ***** BEGIN LICENSE BLOCK *****
-
- Copyright (C) 2009-2018 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 *****
-
- */
-/* Error handling: dont use clicon_err, treat as unix system calls. That is,
- ensure errno is set and return -1/NULL */
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* clicon */
-#include
-
-/* clicon */
-#include
-
-#include "clixon_chunk.h"
-
-/*
- * The chunk head array for the predefined chunk sizes.
- */
-static chunk_head_t chunk_heads[CHUNK_HEADS];
-
-/*
- * Did we initialize the chunk heads yet?
- */
-static int chunk_initialized = 0;
-
-
-/*
- * The pagesize of this system
- */
-static int chunk_pagesz;
-
-
-/*
- * List of chunk groups
- */
-static chunk_group_t *chunk_grp;
-
-/*
- * Hack to tell unchunk() not to remove chunk_group if empty
- */
-static int dont_unchunk_group;
-
-
-/*
- * Initialize chunk library
- */
-static void
-chunk_initialize ()
-{
- int pgs;
- register int idx;
-
- chunk_pagesz = getpagesize();
-
- bzero (&chunk_heads, sizeof(chunk_heads));
-
- for (idx = 0; idx < CHUNK_HEADS; idx++) {
- chunk_head_t *chead = &chunk_heads[idx];
-
-
- /*
- * Calculate the size of a block
- */
- pgs = (0x01lu << (CHUNK_BASE + idx)) / chunk_pagesz;
- if (pgs == 0)
- pgs = 1;
- chead->ch_blksz = pgs * chunk_pagesz;
-
-
- /*
- * Chunks per block is 1 for all size above a page. For sizes
- * (including the chunk header) less than a page it's as many
- * as fits
- */
- chead->ch_nchkperblk = chead->ch_blksz / (0x01lu << (CHUNK_BASE + idx));
-
-
- /*
- * Size of each chunk is:
- * (size + chnkhdr) * ncnkperblk = blksiz - blkhdr
- */
- chead->ch_size =
- (chead->ch_blksz / chead->ch_nchkperblk)
- - sizeof(chunk_t);
-
- }
-
- /* Zero misc variables */
- chunk_grp = NULL;
- dont_unchunk_group = 0;
-
- chunk_initialized = 1;
-}
-
-
-/*
- * chunk_new_block() - Allocate new block, initialize it and it's chunks.
- */
-static int
-chunk_new_block (chunk_head_t *chead)
-{
- register int idx;
- register char *c;
- chunk_block_t *blk;
- chunk_t *cnk;
-
- /* Map block header mem */
- blk = (chunk_block_t *)
- mmap(NULL, sizeof(chunk_block_t),
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (blk == MAP_FAILED)
- return -1;
- memset((void *)blk, 0, sizeof(*blk));
-
- /* Allocate chunk block */
- blk->cb_blk = (void *)
- mmap(NULL, chead->ch_blksz,
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
- if (blk->cb_blk == MAP_FAILED) {
- munmap(blk, chead->ch_blksz);
- return -1;
- }
- memset(blk->cb_blk, 0, chead->ch_blksz);
-
-
- /* Initialize chunk header */
- blk->cb_head = chead;
- INSQ(blk, chead->ch_blks);
- chead->ch_nblks++;
-
- /* Initialize chunks */
- c = ((char *)blk->cb_blk);
- for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
-
- cnk = (chunk_t *)c;
- cnk->c_blk = blk;
- INSQ(cnk, chead->ch_free);
- chead->ch_nfree++;
-
- c += (chead->ch_size + sizeof(chunk_t));
- }
-
-
- return 0;
-}
-
-/*
- * chunk_release_block() - Unqueue a block, it's chunks and free mem
- */
-static void
-chunk_release_block(chunk_block_t *cblk)
-{
- int idx;
- char *c;
- chunk_t *cnk;
- chunk_head_t *chead;
-
-
- chead = cblk->cb_head;
-
- /*
- * Dequeue block
- */
- DELQ(cblk, chead->ch_blks, chunk_block_t *);
- chead->ch_nblks--;
-
- /*
- * Dequeue all chunks in the block
- */
- c = (char *)cblk->cb_blk;
- for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
-
- cnk = (chunk_t *)c;
- DELQ(cnk, chead->ch_free, chunk_t *);
- chead->ch_nfree--;
-
- c += (chead->ch_size + sizeof(chunk_t));
- }
-
- /*
- * Free block
- */
- munmap((void *)cblk->cb_blk, chead->ch_blksz);
- munmap((void *)cblk, sizeof(*cblk));
-}
-
-
-
-/*
- * chunk_alloc() - Map new chunk of memory
- */
-static void *
-chunk_alloc(size_t len)
-{
- register int idx;
- chunk_head_t *chead;
- chunk_t *cnk;
-
-
- if (!len)
- return (void *)NULL;
-
-
-
- /* Find sufficient sized block head */
- for (idx = 0; idx < CHUNK_HEADS; idx++)
- if (chunk_heads[idx].ch_size >= len)
- break;
-
- /* Too large chunk? */
- if (idx >= CHUNK_HEADS) {
- errno = ENOMEM;
- return (void *)NULL;
- }
-
- chead = &chunk_heads[idx];
-
-
- /* Get new block if necessary */
- if (!chead->ch_nfree)
- if (chunk_new_block(chead))
- return (void *)NULL;
-
-
- /* Move a free chunk to the in-use list */
- cnk = chead->ch_free;
- DELQ(cnk, chead->ch_free, chunk_t *);
- chead->ch_nfree--;
- INSQ(cnk, chead->ch_cnks);
- /* Add reference to the corresponding block */
- cnk->c_blk->cb_ref++;
-
-#ifdef CHUNK_DIAG
- /* Clear diag info */
- bzero((void *)&cnk->c_diag, sizeof(cnk->c_diag));
-#endif /* CHUNK_DIAG */
-
- /* Return pointer to first byte after the chunk header */
- return (void *) (cnk + 1);
-}
-
-
-/*
- * chunk() - Map new chunk of memory in group
- */
-void *
-#ifdef CHUNK_DIAG
-_chunk(size_t len, const char *name, const char *file, int line)
-#else
-chunk(size_t len, const char *name)
-#endif
-{
- int newgrp = 0;
- void *ptr = NULL;
- chunk_t *cnk;
- chunk_group_t *tmp;
- chunk_group_t *grp = NULL;
- chunk_grpent_t *ent = NULL;
-
- /* Make sure chunk_heads are initialized */
- if (!chunk_initialized)
- chunk_initialize();
-
- if (!len)
- return (void *)NULL;
-
- /* Get actual chunk
- */
- ptr = chunk_alloc(len);
- if (!ptr)
- goto error;
- cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
-
-#ifdef CHUNK_DIAG
- /* Store who reuested us
- */
- cnk->c_diag.cd_file = file;
- cnk->c_diag.cd_line = line;
-#endif /* CHUNK_DIAG */
-
- /* No name given. Get an ungrouped chunk
- */
- if (!name)
- return ptr;
-
-
- /* Try to find already existing entry
- */
- if (chunk_grp) {
- tmp = chunk_grp;
- do {
- if (!strcmp(tmp->cg_name, name)) {
- grp = tmp;
- break;
- }
-
- tmp = NEXTQ(chunk_group_t *, tmp);
-
- } while (tmp != chunk_grp);
- }
-
- /* New group.
- */
- if ( !grp ) {
-
- grp = (chunk_group_t *) chunk_alloc(sizeof(chunk_group_t));
- if (!grp)
- goto error;
-
- bzero(grp, sizeof(chunk_group_t));
-
- grp->cg_name = (char *) chunk_alloc(strlen(name) + 1);
- if (!grp->cg_name)
- goto error;
- bcopy(name, grp->cg_name, strlen(name)+1);
- newgrp = 1;
- }
-
-
- /* Get new entry.
- */
- ent = (chunk_grpent_t *) chunk_alloc(sizeof(chunk_grpent_t));
- if (!ent)
- goto error;
- bzero(ent, sizeof(chunk_grpent_t));
-
- /* Now put everything together
- */
- cnk->c_grpent = ent;
-
- ent->ce_cnk = cnk;
- ent->ce_grp = grp;
-
- INSQ(ent, grp->cg_ent);
- if (newgrp)
- INSQ(grp, chunk_grp);
-
- return (ptr);
-
- error:
- if (grp && newgrp) {
- if (grp->cg_name)
- unchunk(grp->cg_name);
- unchunk(grp);
- }
- if (ent)
- unchunk(ent);
- if (ptr)
- unchunk(ptr);
-
- return (void *) NULL;
-}
-
-
-/*
- * rechunk() - Resize previously allocated chunk.
- */
-void *
-#ifdef CHUNK_DIAG
-_rechunk(void *ptr, size_t len, const char *name, const char *file, int line)
-#else
-rechunk(void *ptr, size_t len, const char *name)
-#endif
-{
- int idx;
- void *new;
- chunk_t *cnk;
- chunk_t *newcnk;
- chunk_head_t *chead;
- chunk_head_t *newchead;
-
-
- /* No previoud chunk, get new
- */
- if (!ptr) {
-#ifdef CHUNK_DIAG
- return _chunk(len, name, file, line);
-#else
- return chunk(len, name);
-#endif
- }
-
- /* Zero length, free chunk
- */
- if (len == 0) {
- unchunk(ptr);
- return (void *) NULL;
- }
-
- /* Rewind pointer to beginning of chunk header
- */
- cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
- chead = cnk->c_blk->cb_head;
-
- /* Find sufficient sized block head
- */
- for (idx = 0; idx < CHUNK_HEADS; idx++)
- if (chunk_heads[idx].ch_size >= len)
- break;
- /* Too large chunk? */
- if (idx >= CHUNK_HEADS) {
- errno = ENOMEM;
- return (void *)NULL;
- }
-
- /* Check if chunk size remains unchanged
- */
- if (chunk_heads[idx].ch_size == chead->ch_size)
- return (ptr);
-
- /* Get new chunk
- */
-#ifdef CHUNK_DIAG
- new = _chunk(len, name, file, line);
-#else
- new = chunk(len, name);
-#endif
- if (!new)
- return (void *) NULL;
- newcnk = (chunk_t *) (((char *)new) - sizeof(chunk_t));
- newchead = newcnk->c_blk->cb_head;
-
- /* Copy contents to new chunk
- */
- bcopy(ptr, new, MIN(newchead->ch_size, chead->ch_size));
-
- /* Free old chunk
- */
- unchunk(ptr);
-
-
- return (new);
-}
-
-/*
- * unchunk() - Release chunk
- */
-void
-unchunk(void *ptr)
-{
- chunk_t *cnk;
- chunk_head_t *chead;
- chunk_block_t *cblk;
- chunk_grpent_t *ent;
- chunk_group_t *grp;
-
- if (!chunk_initialized)
- return;
-
- /* Rewind pointer to beginning of chunk header
- */
- cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
- cblk = cnk->c_blk;
- chead = cblk->cb_head;
-
- /* Move chunk back to free list
- */
- DELQ(cnk, chead->ch_cnks, chunk_t *);
- INSQ(cnk, chead->ch_free);
- chead->ch_nfree++;
-
- /* If chunk is grouped, remove from group.
- */
- ent = cnk->c_grpent;
- if (ent) {
- grp = ent->ce_grp;
- DELQ(ent, grp->cg_ent, chunk_grpent_t *);
- unchunk(ent);
- cnk->c_grpent = NULL;
-
- /* Group empty? */
- if (!dont_unchunk_group && !grp->cg_ent) {
- DELQ(grp, chunk_grp, chunk_group_t *);
- unchunk(grp->cg_name);
- unchunk(grp);
- }
- }
-
- /* Check block refs is nil, if so free it
- */
- cblk->cb_ref--;
- if (cblk->cb_ref == 0)
- chunk_release_block (cblk);
-}
-
-
-/*
- * unchunk_group() - Release all group chunks.
- */
-void
-unchunk_group(const char *name)
-{
- chunk_group_t *tmp;
- chunk_group_t *grp = NULL;
- chunk_t *cnk;
-
- if (!chunk_initialized)
- return;
-
- /* Try to find already existing entry
- */
- if (chunk_grp) {
- tmp = chunk_grp;
- do {
- if (!strcmp(tmp->cg_name, name)) {
- grp = tmp;
- break;
- }
-
- tmp = NEXTQ(chunk_group_t *, tmp);
-
- } while (tmp != chunk_grp);
- }
- if (!grp)
- return;
-
-
- /* Walk through all chunks in group an free them
- */
- dont_unchunk_group = 1;
- while (grp->cg_ent) {
- cnk = grp->cg_ent->ce_cnk;
- unchunk((chunk_t *)(((char *)cnk) + sizeof(chunk_t)));
- }
- dont_unchunk_group = 0;
-
-
- /* Remove group from list and free it
- */
- DELQ(grp, chunk_grp, chunk_group_t *);
- unchunk(grp->cg_name);
- unchunk(grp);
-}
-
-/*
- * chunkdup() - Copy block of data to a new chunk of memory in group
- */
-void *
-#ifdef CHUNK_DIAG
-_chunkdup(const void *ptr, size_t len, const char *name, const char *file, int line)
-#else
-chunkdup(const void *ptr, size_t len, const char *name)
-#endif
-{
- void *new;
-
- /* No input data or no length
- */
- if (!ptr || len <= 0)
- return (void *)NULL;
-
- /* Get new chunk
- */
-#ifdef CHUNK_DIAG
- new = _chunk(len, name, file, line);
-#else
- new = chunk(len, name);
-#endif
- if (!new)
- return (void *)NULL;
-
- /* Copy data to new chunk
- */
- memcpy(new, ptr, len);
-
- return (new);
-}
-
-/*
- * chunksize() - Return size of memory chunk.
- */
-size_t
-chunksize(void *ptr)
-{
- chunk_t *cnk;
- chunk_head_t *chead;
- chunk_block_t *cblk;
-
- if (!chunk_initialized)
- return -1;
-
- /* Rewind pointer to beginning of chunk header
- */
- cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
- cblk = cnk->c_blk;
- chead = cblk->cb_head;
-
- return chead->ch_size;
-}
-
-/*
- * chunk_strncat() - Concatenate 'n' characters to a chunk allocated string. If
- * 'n' is zero, do the whole src string.
- *
- */
-char *
-#ifdef CHUNK_DIAG
-_chunk_strncat(const char *dst, const char *src, size_t n, const char *name,
- const char *file, int line)
-#else
-chunk_strncat(const char *dst, const char *src, size_t n, const char *name)
-#endif
-{
- size_t len;
- char *new;
- void *ptr = (void *)dst;
-
- if (n == 0) /* zero length means cat whole string */
- n = strlen(src);
- len = (dst ? strlen(dst) : 0) + n + 1;
-#ifdef CHUNK_DIAG
- ptr = _rechunk(ptr, len, name, file, line);
-#else
- ptr = rechunk(ptr, len, name);
-#endif
- if (ptr == NULL)
- return NULL;
-
- new = (char *)ptr;
- new += strlen(new);
- while (n-- > 0 && *src)
- *new++ = *src++;
- *new = '\0';
-
- return (char *)ptr;
-}
-
-/*
- * chunk_sprintf() - Format string into new chunk.
- */
-char *
-#ifdef CHUNK_DIAG
-_chunk_sprintf(const char *name, const char *file,
- int line, const char *fmt, ...)
-#else
-chunk_sprintf(const char *name, char *fmt, ...)
-#endif
-{
- size_t len;
- char *str;
- va_list args;
-
- /* Calculate formatted string length */
- va_start(args, fmt);
- len = vsnprintf(NULL, 0, fmt, args) + 1;
- va_end(args);
-
- /* get chunk */
-#ifdef CHUNK_DIAG
- str = _chunk(len, name, file, line);
-#else
- str = chunk(len, name);
-#endif
- if (str == NULL)
- return NULL;
-
- /* Format string */
- va_start(args, fmt);
- len = vsnprintf(str, len, fmt, args);
- va_end(args);
-
- return str;
-}
-
-#ifdef CHUNK_DIAG
-/*
- * chunk_check() - Report all non-freed chunk for given group (if any)
- */
-void
-chunk_check(FILE *fout, const char *name)
-{
- int idx;
- chunk_t *cnk;
- chunk_group_t *tmp;
- chunk_group_t *grp = NULL;
- chunk_grpent_t *ent;
-
- if (!chunk_initialized)
- return;
- /* No name given, walk through everything
- */
- if (name == (const char *)NULL) {
- for (idx = 0; idx < CHUNK_HEADS; idx++) {
- chunk_head_t *chead = &chunk_heads[idx];
- cnk = chead->ch_cnks;
- if (cnk == (chunk_t *)NULL)
- continue;
-
- do {
-
- /* If no file name it's an internal chunk */
- if (cnk->c_diag.cd_file)
- clicon_debug(0,
- "%s:%d,\t%zu bytes (%p), group \"%s\"\n",
- cnk->c_diag.cd_file,
- cnk->c_diag.cd_line,
- cnk->c_blk->cb_head->ch_size,
- (cnk +1),
- cnk->c_grpent ?
- cnk->c_grpent->ce_grp->cg_name :
- "NULL");
-
- cnk = NEXTQ(chunk_t *, cnk);
-
- } while (cnk != chead->ch_cnks);
- }
- }
-
- /* Walk through group
- */
- else {
-
-
- /* Try to find already existing entry
- */
- if (chunk_grp) {
- tmp = chunk_grp;
- do {
- if (!strcmp(tmp->cg_name, name)) {
- grp = tmp;
- break;
- }
-
- tmp = NEXTQ(chunk_group_t *, tmp);
-
- } while (tmp != chunk_grp);
- }
- if (!grp)
- return;
-
- ent = grp->cg_ent;
- do {
- cnk = ent->ce_cnk;
-
- fprintf(fout ? fout : stdout,
- "%s:%d,\t%zu bytes (%p), group \"%s\"\n",
- cnk->c_diag.cd_file,
- cnk->c_diag.cd_line,
- cnk->c_blk->cb_head->ch_size,
- (cnk +1),
- cnk->c_grpent ?
- cnk->c_grpent->ce_grp->cg_name :
- "NULL");
-
- ent = NEXTQ(chunk_grpent_t *, ent);
- } while (ent != grp->cg_ent);
- }
-}
-#else /* CHUNK_DIAG */
-void
-chunk_check(FILE *fout, const char *name)
-{
-}
-#endif /* CHUNK_DIAG */
diff --git a/datastore/keyvalue/clixon_chunk.h b/datastore/keyvalue/clixon_chunk.h
deleted file mode 100644
index 81a8f18b..00000000
--- a/datastore/keyvalue/clixon_chunk.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- *
- ***** BEGIN LICENSE BLOCK *****
-
- Copyright (C) 2009-2018 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 *****
-
- *
- */
-
-#ifndef _CLIXON_CHUNK_H_
-#define _CLIXON_CHUNK_H_
-
-
-/*
- * Compile with chunk diagnostics. XXX Should be in Makefile.in ??
- */
-#undef CHUNK_DIAG
-
-/*
- * Base number of bits to shift getting the size of a chunk_head.
- */
-#define CHUNK_BASE 6
-
-/*
- * Number of predefined chunk sizes. I.e. the number of chunk heads in the
- * chunk_heads vector.
- */
-#define CHUNK_HEADS (32 - CHUNK_BASE)
-
-
-#ifdef CHUNK_DIAG
-/*
- * Chunk diagnostics
- */
-typedef struct _chunk_diag_t {
- const char *cd_file; /* File which requested chunk */
-
- int cd_line; /* Line in requesting file */
-
-} chunk_diag_t;
-#endif /* CHUNK_DIAG */
-
-/*
- * The block header.
- */
-struct _chunk_head_t;
-typedef struct _chunk_head_t chunk_head_t;
-
-typedef struct _chunk_block_t {
- qelem_t cb_qelem; /* Circular queue of blocks */
-
- chunk_head_t *cb_head; /* The chunk head I belong to */
-
- void *cb_blk; /* Allocated memory block */
-
- uint16_t cb_ref; /* Number of used chunks of block */
-
-} chunk_block_t;
-
-
-/*
- * The chunk header.
- */
-struct _chunk_grpent_t;
-typedef struct _chunk_grpent_t chunk_grpent_t;
-typedef struct _chunk_t {
- qelem_t c_qelem; /* Circular queue of chunks */
-
- chunk_block_t *c_blk; /* The block I belong to */
-
-#ifdef CHUNK_DIAG
- chunk_diag_t c_diag; /* The diagnostics structure */
-#endif /* CHUNK_DIAG */
-
- chunk_grpent_t *c_grpent;
-} chunk_t;
-
-/*
- * The head of a chunk size. Each predefined size has it's own head keeping
- * track of all blocks and chunks for the size.
- */
-struct _chunk_head_t {
- size_t ch_size; /* Chunk size */
- int ch_nchkperblk; /* Number pf chunks per block */
-
- size_t ch_blksz; /* Size of a block */
- int ch_nblks; /* Number of allocated blocks */
-
- chunk_block_t *ch_blks; /* Circular list of blocks */
-
- chunk_t *ch_cnks; /* Circular list of chunks in use */
-
- size_t ch_nfree; /* Number of free chunks */
- chunk_t *ch_free; /* Circular list of free chunks */
-
-};
-
-
-/*
- * The chunk group structure.
- */
-typedef struct _chunk_group_t {
- qelem_t cg_qelem; /* List of chunk groups */
-
- char *cg_name; /* Name of group */
-
- chunk_grpent_t *cg_ent; /* List of chunks in the group */
-
-} chunk_group_t;
-
-
-/*
- * The chunk group entry structure.
- */
-struct _chunk_grpent_t {
- qelem_t ce_qelem; /* Circular list of entries */
-
- chunk_group_t *ce_grp; /* The group I belong to */
-
- chunk_t *ce_cnk; /* Pointer to the chunk */
-};
-
-/*
- * Public function declarations
- */
-#ifdef CHUNK_DIAG
-void *_chunk (size_t, const char *, const char *, int);
-#define chunk(siz,label) _chunk((siz),(label),__FILE__,__LINE__)
-void *_rechunk (void *, size_t, const char *, const char *, int);
-#define rechunk(ptr,siz,label) _rechunk((ptr),(siz),(label),__FILE__,__LINE__)
-void *_chunkdup (const void *, size_t, const char *, const char *, int);
-#define chunkdup(ptr,siz,label) _chunkdup((ptr),(siz),(label),__FILE__,__LINE__)
-char *_chunk_strncat (const char *, const char *, size_t, const char *, const char *, int);
-#define chunk_strncat(str,new,n,label) _chunk_strncat((str),(new),(n),(label),__FILE__,__LINE__)
-char *_chunk_sprintf (const char *, const char *, int, const char *, ...);
-#define chunk_sprintf(label,fmt,...) _chunk_sprintf((label),__FILE__,__LINE__,(fmt),__VA_ARGS__)
-#else /* CHUNK_DIAG */
-void *chunk (size_t, const char *);
-void *rechunk (void *, size_t, const char *);
-void *chunkdup (const void *, size_t, const char *);
-char *chunk_strncat (const char *, const char *, size_t, const char *);
-char *chunk_sprintf (const char *, char *, ...);
-#endif /* CHUNK_DIAG */
-void unchunk (void *);
-void unchunk_group (const char *);
-void chunk_check (FILE *, const char *);
-size_t chunksize (void *);
-
-
-#endif /* _CLIXON_CHUNK_H_ */
diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c
deleted file mode 100644
index 39a11134..00000000
--- a/datastore/keyvalue/clixon_keyvalue.c
+++ /dev/null
@@ -1,1130 +0,0 @@
-/*
- *
- ***** BEGIN LICENSE BLOCK *****
-
- Copyright (C) 2009-2018 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 *****
- */
-/*
- * An xml database consists of key-value pairs for xml-trees.
- * Each node in an xml-tree has a key and an optional value.
- * The key (xmlkey) is constructed from the xml node name concatenated
- * with its ancestors and any eventual list keys.
- * A xmlkeyfmt is a help-structure used when accessing the XML database.
- * It consists of an xmlkey but with the key fields replaced with wild-chars(%s)
- * Example: /aaa/bbb/%s/%s/ccc
- * Such an xmlkeyfmt can be obtained from a yang-statement by following
- * its ancestors to the root module. If one of the ancestors is a list,
- * a wildchar (%s) is inserted for each key.
- * These xmlkeyfmt keys are saved and used in cli callbacks such as when
- * modifying syntax (eg cli_merge/cli_delete) or when completing for sub-symbols
- * In this case, the variables are set and the wildcards can be instantiated.
- * An xml tree can then be formed that can be used to the xmldb_get() or
- * xmldb_put() functions.
- * The relations between the functions and formats are as follows:
- *
- * +-----------------+ +-----------------+
- * | yang-stmt | yang2api_path_fmt | api_path_fmt | api_path_fmt2xpath
- * | list aa,leaf k | ----------------->| /aa=%s |---------------->
- * +-----------------+ +-----------------+
- * |
- * | api_path_fmt2api_path
- * | k=17
- * v
- * +-------------------+ +-----------------+
- * | xml-tree/cxobj | xmlkey2xml |api_path RFC3986|
- * | 17 | <------------- | /aa=17 |
- * +-------------------+ +-----------------+
- *
- * Alternative for xmlkeyfmt would be eg:
- * RESTCONF: /interfaces/interface=%s/ipv4/address/ip=%s (used)
- * XPATH: /interfaces/interface[name='%s']/ipv4/address/[ip'=%s']
- *
- * Paths through the code (for coverage)
- * cli_callback_generate +----------------+
- * cli_expand_var_generate | yang2api_path_fmt |
- * yang -------------> | |
- * +----------------+
- * dependency on clixon handle:
- * clixon_xmldb_dir()
- * clicon_dbspec_yang(h)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "clixon_config.h" /* generated by config & autoconf */
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* cligen */
-#include
-
-/* clicon */
-#include
-
-#include "clixon_chunk.h"
-#include "clixon_qdb.h"
-#include "clixon_keyvalue.h"
-
-#define handle(xh) (assert(kv_handle_check(xh)==0),(struct kv_handle *)(xh))
-
-/* Magic to ensure plugin sanity. */
-#define KV_HANDLE_MAGIC 0xfa61a402
-
-/*! Internal structure of keyvalue datastore handle.
- */
-struct kv_handle {
- int kh_magic; /* magic */
- char *kh_dbdir; /* Directory of database files */
- yang_spec *kh_yangspec; /* Yang spec if this datastore */
-};
-
-/*! Check struct magic number for sanity checks
- * return 0 if OK, -1 if fail.
- */
-static int
-kv_handle_check(xmldb_handle xh)
-{
- /* Dont use handle macro to avoid recursion */
- struct kv_handle *kh = (struct kv_handle *)(xh);
-
- return kh->kh_magic == KV_HANDLE_MAGIC ? 0 : -1;
-}
-
-/*! Database locking for candidate and running non-persistent
- * Store an integer for running and candidate containing
- * the session-id of the client holding the lock.
- */
-static int _running_locked = 0;
-static int _candidate_locked = 0;
-static int _startup_locked = 0;
-
-/*! Translate from symbolic database name to actual filename in file-system
- * @param[in] xh XMLDB handle
- * @param[in] db Symbolic database name, eg "candidate", "running"
- * @param[out] filename Filename. Unallocate after use with free()
- * @retval 0 OK
- * @retval -1 Error
- * @note Could need a way to extend which databases exists, eg to register new.
- * The currently allowed databases are:
- * candidate, tmp, running, result
- * The filename reside in CLICON_XMLDB_DIR option
- */
-static int
-kv_db2file(struct kv_handle *kh,
- const char *db,
- char **filename)
-{
- int retval = -1;
- cbuf *cb;
- char *dir;
-
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- if ((dir = kh->kh_dbdir) == NULL){
- clicon_err(OE_XML, errno, "dbdir not set");
- goto done;
- }
- if (strcmp(db, "running") != 0 &&
- strcmp(db, "candidate") != 0 &&
- strcmp(db, "startup") != 0 &&
- strcmp(db, "tmp") != 0){
- clicon_err(OE_XML, 0, "No such database: %s", db);
- goto done;
- }
- cprintf(cb, "%s/%s_db", dir, db);
- if ((*filename = strdup4(cbuf_get(cb))) == NULL){
- clicon_err(OE_UNIX, errno, "strdup");
- goto done;
- }
- retval = 0;
- done:
- if (cb)
- cbuf_free(cb);
- return retval;
-}
-
-/*! Help function to append key values from an xml list to a cbuf
- * Example, a yang node x with keys a and b results in "x/a/b"
- */
-static int
-append_listkeys(cbuf *ckey,
- cxobj *xt,
- yang_stmt *ys)
-{
- int retval = -1;
- yang_stmt *ykey;
- cxobj *xkey;
- cg_var *cvi;
- cvec *cvk = NULL; /* vector of index keys */
- char *keyname;
- char *bodyenc;
- int i=0;
-
- cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
- cvi = NULL;
- /* Iterate over individual keys */
- while ((cvi = cvec_each(cvk, cvi)) != NULL) {
- keyname = cv_string_get(cvi);
- if ((xkey = xml_find(xt, keyname)) == NULL){
- clicon_err(OE_XML, errno, "XML list node \"%s\" does not have key \"%s\" child",
- xml_name(xt), keyname);
- goto done;
- }
- if (uri_percent_encode(&bodyenc, "%s", xml_body(xkey)) < 0)
- goto done;
- if (i++)
- cprintf(ckey, ",");
- else
- cprintf(ckey, "=");
- cprintf(ckey, "%s", bodyenc);
- free(bodyenc);
- bodyenc = NULL;
- }
- retval = 0;
- done:
- return retval;
-}
-
-/*! Help function to create xml key values
- * @param[in,out] x Parent
- * @param[in] ykey
- * @param[in] arg
- * @param[in] keyname yang key name
- */
-static int
-create_keyvalues(cxobj *x,
- yang_stmt *ykey,
- char *arg,
- char *keyname)
-{
- int retval = -1;
- cxobj *xn;
- cxobj *xb;
-
- /* Check if key node exists */
- if ((xn = xml_new_spec(keyname, x, ykey)) == NULL)
- goto done;
- if ((xb = xml_new("body", xn)) == NULL)
- goto done;
- xml_type_set(xb, CX_BODY);
- xml_value_set(xb, arg);
- retval = 0;
- done:
- return retval;
-}
-
-
-/*!
- * @param[in] xk xmlkey
- * @param[out] xt XML tree as result
- * XXX cannot handle top-level list
- */
-static int
-get(char *dbname,
- yang_spec *ys,
- char *xk,
- char *val,
- cxobj *xt)
-{
- int retval = -1;
- char **vec = NULL;
- int nvec;
- char **valvec = NULL;
- int nvalvec;
- int i;
- int j;
- char *name;
- char *restval;
- yang_stmt *y;
- cxobj *x;
- cxobj *xc;
- cxobj *xb;
- yang_stmt *ykey;
- cg_var *cvi;
- cvec *cvk = NULL; /* vector of index keys */
- char *keyname;
- char *arg;
- char *argdec;
- cbuf *cb;
-
- // clicon_debug(1, "%s xkey:%s val:%s", __FUNCTION__, xk, val);
- x = xt;
- if (xk == NULL || *xk!='/'){
- clicon_err(OE_DB, 0, "Invalid key: %s", xk);
- goto done;
- }
- if ((vec = clicon_strsep(xk, "/", &nvec)) == NULL)
- goto done;
- /* Element 0 is NULL '/',
- Element 1 is top symbol and needs to find subs in all modules:
- spec->module->syntaxnode
- */
- if (nvec < 2){
- clicon_err(OE_XML, 0, "Malformed key: %s", xk);
- goto done;
- }
- i = 1;
- while (i name:x restval=1,2 */
- if ((restval = index(name, '=')) != NULL){
- *restval = '\0';
- restval++;
- }
- if (i == 1){ /* spec->module->node */
- if ((y = yang_find_topnode(ys, name, YC_DATANODE)) == NULL){
- clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
- goto done;
- }
- }
- else
- if ((y = yang_find_datanode((yang_node*)y, name)) == NULL){
- clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
- goto done;
- }
- switch (y->ys_keyword){
- case Y_LEAF_LIST:
- /*
- * If xml element is a leaf-list, then the next element is expected to
- * be a value
- */
- if (uri_percent_decode(&argdec, restval) < 0)
- goto done;
- if ((xc = xml_find(x, name))==NULL ||
- (xb = xml_find(xc, argdec))==NULL){
- if ((xc = xml_new_spec(name, x, y)) == NULL)
- goto done;
- /* Assume body is created at end of function */
- }
- free(argdec);
- argdec = NULL;
- break;
- case Y_LIST:
- /*
- * If xml element is a list, then the next element(s) is expected to be
- * a key value. Check if this key value is already in the xml tree,
- * otherwise create it.
- */
- if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
- clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
- __FUNCTION__, y->ys_argument);
- goto done;
- }
- /* The value is a list of keys: [ ]* */
- if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
- goto done;
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- cvi = NULL;
- /* Iterate over individual yang keys */
- cprintf(cb, "%s", name);
- if (valvec)
- free(valvec);
- if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
- goto done;
- if (cvec_len(cvk)!=nvalvec){
- retval = 0;
- goto done;
- }
- j = 0;
- while ((cvi = cvec_each(cvk, cvi)) != NULL){
- if (j>=nvalvec)
- break;
- arg = valvec[j++];
- if (uri_percent_decode(arg, &argdec) < 0)
- goto done;
- cprintf(cb, "[%s='%s']", cv_string_get(cvi), argdec);
- free(argdec);
- argdec=NULL;
- }
- if ((xc = xpath_first(x, cbuf_get(cb))) == NULL){
- if ((xc = xml_new_spec(name, x, y)) == NULL)
- goto done;
- cvi = NULL;
- // i -= cvec_len(cvk);
- /* Iterate over individual yang keys */
- j=0;
- while ((cvi = cvec_each(cvk, cvi)) != NULL) {
- if (j>=nvalvec)
- break;
- arg = valvec[j++];
- keyname = cv_string_get(cvi);
- if (uri_percent_decode(arg, &argdec) < 0)
- goto done;
- if (create_keyvalues(xc,
- ykey,
- argdec,
- keyname) < 0)
- goto done;
- free(argdec);
- argdec = NULL;
- } /* while */
- }
- if (cb){
- cbuf_free(cb);
- cb = NULL;
- }
- if (cvk){
- cvec_free(cvk);
- cvk = NULL;
- }
- break;
- case Y_LEAF:
- case Y_CONTAINER:
- default:
- if ((xc = xml_find(x, name))==NULL)
- if ((xc = xml_new_spec(name, x, y)) == NULL)
- goto done;
- break;
- } /* switch */
- x = xc;
- i++;
- }
- if (val && xml_body(x)==NULL){
- if ((x = xml_new("body", x)) == NULL)
- goto done;
- xml_type_set(x, CX_BODY);
- xml_value_set(x, val);
- }
- if(debug>1){
- fprintf(stderr, "%s %s\n", __FUNCTION__, xk);
- clicon_xml2file(stderr, xt, 0, 1);
- }
- retval = 0;
- done:
- if (vec)
- free(vec);
- if (valvec)
- free(valvec);
- if (cvk)
- cvec_free(cvk);
- return retval;
-}
-
-/*! Connect to a datastore plugin
- * @retval handle Use this handle for other API calls
- * @retval NULL Error
- * @note You can do several connects, and have multiple connections to the same
- * datastore
- */
-xmldb_handle
-kv_connect(void)
-{
- struct kv_handle *kh;
- xmldb_handle xh = NULL;
- int size;
-
- size = sizeof(struct kv_handle);
- if ((kh = malloc(size)) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(kh, 0, size);
- kh->kh_magic = KV_HANDLE_MAGIC;
- xh = (xmldb_handle)kh;
- done:
- return xh;
-}
-
-/*! Disconnect from a datastore plugin and deallocate handle
- * @param[in] handle Disconect and deallocate from this handle
- * @retval 0 OK
- */
-int
-kv_disconnect(xmldb_handle xh)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
-
- if (kh){
- if (kh->kh_dbdir)
- free(kh->kh_dbdir);
- free(kh);
- }
- retval = 0;
- // done:
- return retval;
-}
-
-/*! Get value of generic plugin option. Type of value is givenby context
- * @param[in] xh XMLDB handle
- * @param[in] optname Option name
- * @param[out] value Pointer to Value of option
- * @retval 0 OK
- * @retval -1 Error
- */
-int
-kv_getopt(xmldb_handle xh,
- char *optname,
- void **value)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
-
- if (strcmp(optname, "yangspec") == 0)
- *value = kh->kh_yangspec;
- else if (strcmp(optname, "dbdir") == 0)
- *value = kh->kh_dbdir;
- else{
- clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
- goto done;
- }
- retval = 0;
- done:
- return retval;
-}
-
-/*! Set value of generic plugin option. Type of value is givenby context
- * @param[in] xh XMLDB handle
- * @param[in] optname Option name
- * @param[in] value Value of option
- * @retval 0 OK
- * @retval -1 Error
- */
-int
-kv_setopt(xmldb_handle xh,
- char *optname,
- void *value)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
-
- if (strcmp(optname, "yangspec") == 0)
- kh->kh_yangspec = (yang_spec*)value;
- else if (strcmp(optname, "dbdir") == 0){
- if (value && (kh->kh_dbdir = strdup((char*)value)) == NULL){
- clicon_err(OE_UNIX, 0, "strdup");
- goto done;
- }
- }
- else{
- clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
- goto done;
- }
- retval = 0;
- done:
- return retval;
-}
-
-/*! Get content of database using xpath. return a set of matching sub-trees
- * The function returns a minimal tree that includes all sub-trees that match
- * xpath.
- * This is a clixon datastore plugin of the the xmldb api
- * @see xmldb_get
- */
-int
-kv_get(xmldb_handle xh,
- const char *db,
- char *xpath,
- int config,
- cxobj **xtop)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- yang_spec *yspec;
- char *dbfile = NULL;
- cxobj **xvec = NULL;
- size_t xlen;
- int i;
- int npairs;
- struct db_pair *pairs;
- cxobj *xt = NULL;
-
- clicon_debug(2, "%s", __FUNCTION__);
- if (kv_db2file(kh, db, &dbfile) < 0)
- goto done;
- if (dbfile==NULL){
- clicon_err(OE_XML, 0, "dbfile NULL");
- goto done;
- }
- if ((yspec = kh->kh_yangspec) == NULL){
- clicon_err(OE_YANG, ENOENT, "No yang spec");
- goto done;
- }
- /* Read in complete database (this can be optimized) */
- if ((npairs = db_regexp(dbfile, "", __FUNCTION__, &pairs, 0)) < 0)
- goto done;
- if ((xt = xml_new_spec("config", NULL, yspec)) == NULL)
- goto done;
- /* Translate to complete xml tree */
- for (i = 0; i < npairs; i++) {
- if (get(dbfile,
- yspec,
- pairs[i].dp_key, /* xml key */
- pairs[i].dp_val, /* may be NULL */
- xt) < 0)
- goto done;
- }
- if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
- goto done;
- /* If vectors are specified then filter out everything else,
- * otherwise return complete tree.
- */
- if (xvec != NULL){
- for (i=0; i1)
- clicon_xml2file(stderr, xt, 0, 1);
- *xtop = xt;
- retval = 0;
- done:
- if (dbfile)
- free(dbfile);
- if (xvec)
- free(xvec);
- unchunk_group(__FUNCTION__);
- return retval;
-
-}
-
-/*! Add data to database internal recursive function
- * @param[in] dbfile Name of database to search in (filename incl dir path)
- * @param[in] xt xml-node.
- * @param[in] ys Yang statement corresponding to xml-node
- * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
- * @param[in] xkey0 aggregated xmlkey
- * @retval 0 OK
- * @retval -1 Error
- * @note XXX op only supports merge
- */
-static int
-put(char *dbfile,
- cxobj *xt,
- yang_stmt *ys,
- enum operation_type op,
- const char *xk0)
-{
- int retval = -1;
- cxobj *x = NULL;
- char *xk;
- cbuf *cbxk = NULL;
- char *body;
- yang_stmt *y;
- int exists;
- char *bodyenc=NULL;
- char *opstr;
-
- clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
- if (debug){
- xml_print(stderr, xt);
- // yang_print(stderr, (yang_node*)ys);
- }
- if ((opstr = xml_find_value(xt, "operation")) != NULL)
- if (xml_operation(opstr, &op) < 0)
- goto done;
- body = xml_body(xt);
- if ((cbxk = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- cprintf(cbxk, "%s/%s", xk0, xml_name(xt));
- switch (ys->ys_keyword){
- case Y_LIST: /* Note: can have many keys */
- if (append_listkeys(cbxk, xt, ys) < 0)
- goto done;
- break;
- case Y_LEAF_LIST:
- if (uri_percent_encode(&bodyenc, "%s", body) < 0)
- goto done;
- cprintf(cbxk, "=%s", bodyenc);
- break;
- default:
- break;
- }
- xk = cbuf_get(cbxk);
- // fprintf(stderr, "%s %s\n", key, body?body:"");
- /* Write to database, key and a vector of variables */
- switch (op){
- case OP_CREATE:
- if ((exists = db_exists(dbfile, xk)) < 0)
- goto done;
- if (exists == 1){
- clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk);
- goto done;
- }
- case OP_MERGE:
- case OP_REPLACE:
- if (db_set(dbfile, xk, body?body:NULL, body?strlen(body)+1:0) < 0)
- goto done;
- break;
- case OP_DELETE:
- if ((exists = db_exists(dbfile, xk)) < 0)
- goto done;
- if (exists == 0){
- clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
- goto done;
- }
- case OP_REMOVE:
- switch (ys->ys_keyword){
- case Y_LIST:
- case Y_CONTAINER:{
- struct db_pair *pairs;
- int npairs;
- cbuf *cbrx;
- int i;
-
- if ((cbrx = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- cprintf(cbrx, "^%s.*$", xk);
- if ((npairs = db_regexp(dbfile, cbuf_get(cbrx), __FUNCTION__,
- &pairs, 0)) < 0)
- goto done;
- /* Translate to complete xml tree */
- for (i = 0; i < npairs; i++)
- if (db_del(dbfile, pairs[i].dp_key) < 0)
- goto done;
- if (cbrx)
- cbuf_free(cbrx);
- /* Skip recursion, we have deleted whole subtree */
- retval = 0;
- goto done;
- break;
- }
- default:
- if (db_del(dbfile, xk) < 0)
- goto done;
- break;
- }
-
- break;
- case OP_NONE:
- break;
- }
- /* For every node, create a key with values */
- while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
- if ((y = yang_find_datanode((yang_node*)ys, xml_name(x))) == NULL){
- clicon_err(OE_UNIX, 0, "No yang node found: %s", xml_name(x));
- goto done;
- }
- if (put(dbfile, x, y, op, xk) < 0)
- goto done;
- }
- retval = 0;
- done:
- if (cbxk)
- cbuf_free(cbxk);
- if (bodyenc)
- free(bodyenc);
- unchunk_group(__FUNCTION__);
- return retval;
-}
-
-/*! Modify database provided an xml tree and an operation
- * This is a clixon datastore plugin of the the xmldb api
- * @see xmldb_put
- */
-int
-kv_put(xmldb_handle xh,
- const char *db,
- enum operation_type op,
- cxobj *xt)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- cxobj *x = NULL;
- yang_stmt *ys;
- yang_spec *yspec;
- char *dbfilename = NULL;
-
- if ((yspec = kh->kh_yangspec) == NULL){
- clicon_err(OE_YANG, ENOENT, "No yang spec");
- goto done;
- }
- if (kv_db2file(kh, db, &dbfilename) < 0)
- goto done;
- if (op == OP_REPLACE){
- if (db_delete(dbfilename) < 0)
- goto done;
- if (db_init(dbfilename) < 0)
- goto done;
- }
- // clicon_log(LOG_WARNING, "%s", __FUNCTION__);
- while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
- if ((ys = yang_find_topnode(yspec, xml_name(x), YC_DATANODE)) == NULL){
- clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
- goto done;
- }
- if (put(dbfilename, /* database name */
- x, /* xml root node */
- ys, /* yang statement of xml node */
- op, /* operation, eg merge/delete */
- "" /* aggregate xml key */
- ) < 0)
- goto done;
- }
- retval = 0;
- done:
- if (dbfilename)
- free(dbfilename);
- return retval;
-}
-
-/*! Copy database from db1 to db2
- * @param[in] xh XMLDB handle
- * @param[in] from Source database copy
- * @param[in] to Destination database
- * @retval -1 Error
- * @retval 0 OK
- */
-int
-kv_copy(xmldb_handle xh,
- const char *from,
- const char *to)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- char *fromfile = NULL;
- char *tofile = NULL;
-
- /* XXX lock */
- if (kv_db2file(kh, from, &fromfile) < 0)
- goto done;
- if (kv_db2file(kh, to, &tofile) < 0)
- goto done;
- if (clicon_file_copy(fromfile, tofile) < 0)
- goto done;
- retval = 0;
- done:
- if (fromfile)
- free(fromfile);
- if (tofile)
- free(tofile);
- return retval;
-}
-
-/*! Lock database
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @param[in] pid Process id
- * @retval -1 Error
- * @retval 0 OK
- */
-int
-kv_lock(xmldb_handle xh,
- const char *db,
- int pid)
-{
- int retval = -1;
- // struct kv_handle *kh = handle(xh);
- if (strcmp("running", db) == 0)
- _running_locked = pid;
- else if (strcmp("candidate", db) == 0)
- _candidate_locked = pid;
- else if (strcmp("startup", db) == 0)
- _startup_locked = pid;
- else{
- clicon_err(OE_DB, 0, "No such database: %s", db);
- goto done;
- }
- clicon_debug(1, "%s: locked by %u", db, pid);
- retval = 0;
- done:
- return retval;
-}
-
-/*! Unlock database
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @param[in] pid Process id
- * @retval -1 Error
- * @retval 0 OK
- * Assume all sanity checks have been made
- */
-int
-kv_unlock(xmldb_handle xh,
- const char *db)
-{
- int retval = -1;
- // struct kv_handle *kh = handle(xh);
- if (strcmp("running", db) == 0)
- _running_locked = 0;
- else if (strcmp("candidate", db) == 0)
- _candidate_locked = 0;
- else if (strcmp("startup", db) == 0)
- _startup_locked = 0;
- else{
- clicon_err(OE_DB, 0, "No such database: %s", db);
- goto done;
- }
- retval = 0;
- done:
- return retval;
-}
-
-/*! Unlock all databases locked by pid (eg process dies)
- * @param[in] xh XMLDB handle
- * @param[in] pid Process / Session id
- * @retval -1 Error
- * @retval 0 Ok
- */
-int
-kv_unlock_all(xmldb_handle xh,
- int pid)
-{
- // struct kv_handle *kh = handle(xh);
-
- if (_running_locked == pid)
- _running_locked = 0;
- if (_candidate_locked == pid)
- _candidate_locked = 0;
- if (_startup_locked == pid)
- _startup_locked = 0;
- return 0;
-}
-
-/*! Check if database is locked
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @retval -1 Error
- * @retval 0 Not locked
- * @retval >0 Id of locker
- */
-int
-kv_islocked(xmldb_handle xh,
- const char *db)
-{
- int retval = -1;
- // struct kv_handle *kh = handle(xh);
-
- if (strcmp("running", db) == 0)
- retval = _running_locked;
- else if (strcmp("candidate", db) == 0)
- retval = _candidate_locked;
- else if (strcmp("startup", db) == 0)
- retval = _startup_locked;
- else
- clicon_err(OE_DB, 0, "No such database: %s", db);
- return retval;
-}
-
-/*! Check if db exists
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @retval -1 Error
- * @retval 0 No it does not exist
- * @retval 1 Yes it exists
- */
-int
-kv_exists(xmldb_handle xh,
- const char *db)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- char *filename = NULL;
- struct stat sb;
-
- if (kv_db2file(kh, db, &filename) < 0)
- goto done;
- if (lstat(filename, &sb) < 0)
- retval = 0;
- else
- retval = 1;
- done:
- if (filename)
- free(filename);
- return retval;
-}
-
-/*! Delete database. Remove file
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @retval -1 Error
- * @retval 0 OK
- */
-int
-kv_delete(xmldb_handle xh,
- const char *db)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- char *filename = NULL;
-
- if (kv_db2file(kh, db, &filename) < 0)
- goto done;
- if (db_delete(filename) < 0)
- goto done;
- retval = 0;
- done:
- if (filename)
- free(filename);
- return retval;
-}
-
-/*! Create / Initialize database
- * @param[in] xh XMLDB handle
- * @param[in] db Database
- * @retval 0 OK
- * @retval -1 Error
- */
-int
-kv_create(xmldb_handle xh,
- const char *db)
-{
- int retval = -1;
- struct kv_handle *kh = handle(xh);
- char *filename = NULL;
-
- if (kv_db2file(kh, db, &filename) < 0)
- goto done;
- if (db_init(filename) < 0)
- goto done;
- retval = 0;
- done:
- if (filename)
- free(filename);
- return retval;
-}
-
-/*! plugin init function */
-int
-kv_plugin_exit(void)
-{
- return 0;
-}
-
-static const struct xmldb_api api;
-
-/*! plugin init function */
-void *
-clixon_xmldb_plugin_init(int version)
-{
- if (version != XMLDB_API_VERSION){
- clicon_err(OE_DB, 0, "Invalid version %d expected %d",
- version, XMLDB_API_VERSION);
- goto done;
- }
- return (void*)&api;
- done:
- return NULL;
-}
-
-static const struct xmldb_api api = {
- 1,
- XMLDB_API_MAGIC,
- clixon_xmldb_plugin_init,
- kv_plugin_exit,
- kv_connect,
- kv_disconnect,
- kv_getopt,
- kv_setopt,
- kv_get,
- kv_put,
- kv_copy,
- kv_lock,
- kv_unlock,
- kv_unlock_all,
- kv_islocked,
- kv_exists,
- kv_delete,
- kv_create,
-};
-
-
-#if 0 /* Test program */
-/*
- * Turn this on to get an xpath test program
- * Usage: clicon_xpath []
- * read xml from input
- * Example compile:
- gcc -g -o keyvalue -I. -I../../lib ./clixon_keyvalue.c clixon_chunk.c clixon_qdb.c -lclixon -lcligen -lqdbm
-*/
-
-/*! Raw dump of database, just keys and values, no xml interpretation
- * @param[in] f File
- * @param[in] dbfile File-name of database. This is a local file
- * @param[in] rxkey Key regexp, eg "^.*$"
- * @note This function can only be called locally.
- */
-int
-main(int argc,
- char **argv)
-{
- int retval = -1;
- int npairs;
- struct db_pair *pairs;
- char *rxkey = NULL;
- char *dbfilename;
-
- if (argc != 2 && argc != 3){
- fprintf(stderr, "usage: %s [rxkey]\n", argv[0]);
- goto done;
- }
- dbfilename = argv[1];
- if (argc == 3)
- rxkey = argv[2];
- else
- rxkey = "^.*$"; /* Default is match all */
-
- /* Get all keys/values for vector */
- if ((npairs = db_regexp(dbfilename, rxkey, __FUNCTION__, &pairs, 0)) < 0)
- goto done;
-
- for (npairs--; npairs >= 0; npairs--)
- fprintf(stdout, "%s %s\n", pairs[npairs].dp_key,
- pairs[npairs].dp_val?pairs[npairs].dp_val:"");
- retval = 0;
- done:
- unchunk_group(__FUNCTION__);
- return retval;
-}
-
-#endif /* Test program */
diff --git a/datastore/keyvalue/clixon_qdb.c b/datastore/keyvalue/clixon_qdb.c
deleted file mode 100644
index b80daf71..00000000
--- a/datastore/keyvalue/clixon_qdb.c
+++ /dev/null
@@ -1,588 +0,0 @@
-/*
- *
- ***** BEGIN LICENSE BLOCK *****
-
- Copyright (C) 2009-2018 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 *****
-
- * @note Some unclarities with locking. man dpopen defines the following flags
- * with dpopen:
- * `DP_ONOLCK', which means it opens a database file without
- * file locking,
- * `DP_OLCKNB', which means locking is performed without blocking.
- *
- * While connecting as a writer, an exclusive lock is invoked to
- * the database file. While connecting as a reader, a shared lock is
- * invoked to the database file. The thread blocks until the lock is
- * achieved. If `DP_ONOLCK' is used, the application is responsible
- * for exclusion control.
- * The code below uses for
- * write, delete: DP_OLCKNB
- * read: DP_OLCKNB
- * This means that a write fails if one or many reads are occurring, and
- * a read or write fails if a write is occurring, and
- * QDBM allows a single write _or_ multiple readers, but
- * not both. This is obviously extremely limiting.
- * NOTE, the locking in netconf and xmldb is a write lock.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "clixon_config.h"
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef HAVE_DEPOT_H
-#include /* qdb api */
-#else /* HAVE_QDBM_DEPOT_H */
-#include /* qdb api */
-#endif
-
-#include
-
-/* clicon */
-#include
-
-#include "clixon_chunk.h"
-#include "clixon_qdb.h"
-
-/*! Initialize database
- * @param[in] file database file
- * @param[in] omode see man dpopen
- */
-static int
-db_init_mode(char *file,
- int omode)
-{
- DEPOT *dp;
-
- /* Open database for writing */
- if ((dp = dpopen(file, omode | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "dpopen(%s): %s",
- file,
- dperrmsg(dpecode));
- return -1;
- }
- clicon_debug(1, "db_init(%s)", file);
- if (dpclose(dp) == 0){
- clicon_err(OE_DB, errno, "db_set: dpclose: %s",
- dperrmsg(dpecode));
- return -1;
- }
- return 0;
-}
-
-/*! Open database for reading and writing
- * @param[in] file database file
- */
-int
-db_init(char *file)
-{
- return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */
-}
-
-/*! Remove database by removing file, if it exists *
- * @param[in] file database file
- */
-int
-db_delete(char *file)
-{
- struct stat sb;
-
- if (stat(file, &sb) < 0){
- return 0;
- }
- if (unlink(file) < 0){
- clicon_err(OE_DB, errno, "unlink %s", file);
- return -1;
- }
- return 0;
-}
-
-/*! Write data to database
- * @param[in] file database file
- * @param[in] key database key
- * @param[out] data Buffer containing content
- * @param[out] datalen Length of buffer
- * @retval 0 if OK: value returned. If not found, zero string returned
- * @retval -1 on error
- */
-int
-db_set(char *file,
- char *key,
- void *data,
- size_t datalen)
-{
- DEPOT *dp;
-
- /* Open database for writing */
- if ((dp = dpopen(file, DP_OWRITER|DP_OLCKNB , 0)) == NULL){
- clicon_err(OE_DB, errno, "db_set: dpopen(%s): %s",
- file,
- dperrmsg(dpecode));
- return -1;
- }
- clicon_debug(2, "%s: db_put(%s, len:%d)",
- file, key, (int)datalen);
- if (dpput(dp, key, -1, data, datalen, DP_DOVER) == 0){
- clicon_err(OE_DB, errno, "%s: db_set: dpput(%s, %d): %s",
- file,
- key,
- datalen,
- dperrmsg(dpecode));
- dpclose(dp);
- return -1;
- }
- if (dpclose(dp) == 0){
- clicon_err(OE_DB, 0, "db_set: dpclose: %s", dperrmsg(dpecode));
- return -1;
- }
- return 0;
-}
-
-/*! Get data from database
- * @param[in] file database file
- * @param[in] key database key
- * @param[out] data Pre-allocated buffer where data corresponding key is placed
- * @param[out] datalen Length of pre-allocated buffer
- * @retval 0 if OK: value returned. If not found, zero string returned
- * @retval -1 on error
- * @see db_get_alloc Allocates memory
- */
-int
-db_get(char *file,
- char *key,
- void *data,
- size_t *datalen)
-{
- DEPOT *dp;
- int len;
-
- /* Open database for readinf */
- if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "%s: db_get(%s, %d): dpopen: %s",
- file,
- key,
- datalen,
- dperrmsg(dpecode));
- return -1;
- }
- len = dpgetwb(dp, key, -1, 0, *datalen, data);
- if (len < 0){
- if (dpecode == DP_ENOITEM){
- data = NULL;
- *datalen = 0;
- }
- else{
- clicon_err(OE_DB, errno, "db_get: dpgetwb: %s (%d)",
- dperrmsg(dpecode), dpecode);
- dpclose(dp);
- return -1;
- }
- }
- else
- *datalen = len;
- clicon_debug(2, "db_get(%s, %s)=%s", file, key, (char*)data);
- if (dpclose(dp) == 0){
- clicon_err(OE_DB, errno, "db_get: dpclose: %s", dperrmsg(dpecode));
- return -1;
- }
- return 0;
-}
-
-/*! Get data from database and allocates memory
- * Similar to db_get but returns a malloced pointer to the data instead
- * of copying data to pre-allocated buffer. This is necessary if the
- * length of the data is not known when calling the function.
- * @param[in] file database file
- * @param[in] key database key
- * @param[out] data Allocated buffer where data corresponding key is placed
- * @param[out] datalen Length of pre-allocated buffer
- * @retval 0 if OK: value returned. If not found, zero string returned
- * @retval -1 on error
- * @note: *data needs to be freed after use.
- * @code
- * char *lvec = NULL;
- * size_t len = 0;
- * if (db_get-alloc(dbname, "a.0", &val, &vlen) == NULL)
- * return -1;
- * ..do stuff..
- * if (val) free(val);
- * @endcode
- * @see db_get Pre-allocates memory
- */
-int
-db_get_alloc(char *file,
- char *key,
- void **data,
- size_t *datalen)
-{
- DEPOT *dp;
- int len;
-
- /* Open database for writing */
- if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "%s: dpopen(%s): %s",
- __FUNCTION__,
- file,
- dperrmsg(dpecode));
- return -1;
- }
- if ((*data = dpget(dp, key, -1, 0, -1, &len)) == NULL){
- if (dpecode == DP_ENOITEM){
- *datalen = 0;
- *data = NULL;
- len = 0;
- }
- else{
- /* No entry vs error? */
- clicon_err(OE_DB, errno, "db_get_alloc: dpgetwb: %s (%d)",
- dperrmsg(dpecode), dpecode);
- dpclose(dp);
- return -1;
- }
- }
- *datalen = len;
- if (dpclose(dp) == 0){
- clicon_err(OE_DB, errno, "db_get_alloc: dpclose: %s", dperrmsg(dpecode));
- return -1;
- }
- return 0;
-}
-
-/*! Delete database entry
- * @param[in] file database file
- * @param[in] key database key
- * @retval -1 on failure,
- * @retval 0 if key did not exist
- * @retval 1 if successful.
- */
-int
-db_del(char *file, char *key)
-{
- int retval = 0;
- DEPOT *dp;
-
- /* Open database for writing */
- if ((dp = dpopen(file, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "db_del: dpopen(%s): %s",
- file,
- dperrmsg(dpecode));
- return -1;
- }
- if (dpout(dp, key, -1)) {
- retval = 1;
- }
- if (dpclose(dp) == 0){
- clicon_err(OE_DB, errno, "db_del: dpclose: %s", dperrmsg(dpecode));
- return -1;
- }
- return retval;
-}
-
-/*! Check if entry in database exists
- * @param[in] file database file
- * @param[in] key database key
- * @retval 1 if key exists in database
- * @retval 0 key does not exist in database
- * @retval -1 error
- */
-int
-db_exists(char *file,
- char *key)
-{
- DEPOT *dp;
- int len;
-
- /* Open database for reading */
- if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "%s: dpopen: %s",
- __FUNCTION__, dperrmsg(dpecode));
- return -1;
- }
-
- len = dpvsiz(dp, key, -1);
- if (len < 0 && dpecode != DP_ENOITEM)
- clicon_err(OE_DB, errno, "^s: dpvsiz: %s (%d)",
- __FUNCTION__, dperrmsg(dpecode), dpecode);
-
- if (dpclose(dp) == 0) {
- clicon_err(OE_DB, errno, "%s: dpclose: %s", dperrmsg(dpecode),__FUNCTION__);
- return -1;
- }
-
- return (len < 0) ? 0 : 1;
-}
-
-/*! Return all entries in database that match a regular expression.
- * @param[in] file database file
- * @param[in] regexp regular expression for database keys
- * @param[in] label for memory/chunk allocation
- * @param[out] pairs Vector of database keys and values
- * @param[in] noval If set don't retreive values, just keys
- * @retval -1 on error
- * @retval n Number of pairs
- * @code
- * struct db_pair *pairs;
- * int npairs;
- * if ((npairs = db_regexp(dbname, "^/test/kalle$", __FUNCTION__,
- * &pairs, 0)) < 0)
- * err;
- *
- * @endcode
- */
-int
-db_regexp(char *file,
- char *regexp,
- const char *label,
- struct db_pair **pairs,
- int noval)
-{
- int npairs;
- int status;
- int retval = -1;
- int vlen = 0;
- char *key = NULL;
- void *val = NULL;
- char errbuf[512];
- struct db_pair *pair;
- struct db_pair *newpairs;
- regex_t iterre;
- DEPOT *iterdp = NULL;
- regmatch_t pmatch[1];
- size_t nmatch = 1;
-
- npairs = 0;
- *pairs = NULL;
-
- if (regexp) {
- if ((status = regcomp(&iterre, regexp, REG_EXTENDED)) != 0) {
- regerror(status, &iterre, errbuf, sizeof(errbuf));
- clicon_err(OE_DB, errno, "%s: regcomp: %s", __FUNCTION__, errbuf);
- return -1;
- }
- }
-
- /* Open database for reading */
- if ((iterdp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, 0, "%s: dpopen(%s): %s",
- __FUNCTION__, file, dperrmsg(dpecode));
- goto quit;
- }
-
- /* Initiate iterator */
- if(dpiterinit(iterdp) == 0) {
- clicon_err(OE_DB, errno, "%s: dpiterinit: %s", __FUNCTION__, dperrmsg(dpecode));
- goto quit;
- }
-
- /* Iterate through DB */
- while((key = dpiternext(iterdp, NULL)) != NULL) {
-
- if (regexp && regexec(&iterre, key, nmatch, pmatch, 0) != 0) {
- free(key);
- continue;
- }
-
- /* Retrieve value if required */
- if ( ! noval) {
- if((val = dpget(iterdp, key, -1, 0, -1, &vlen)) == NULL) {
- clicon_log(LOG_WARNING, "%s: dpget: %s", __FUNCTION__, dperrmsg(dpecode));
- goto quit;
- }
- }
-
- /* Resize and populate resulting array */
- newpairs = rechunk(*pairs, (npairs+1) * sizeof(struct db_pair), label);
- if (newpairs == NULL) {
- clicon_err(OE_DB, errno, "%s: rechunk", __FUNCTION__);
- goto quit;
- }
- pair = &newpairs[npairs];
- memset(pair, 0, sizeof(*pair));
-
- pair->dp_key = chunk_sprintf(label, "%s", key);
- if (regexp)
- pair->dp_matched = chunk_sprintf(label, "%.*s",
- pmatch[0].rm_eo - pmatch[0].rm_so,
- key + pmatch[0].rm_so);
- else
- pair->dp_matched = chunk_sprintf(label, "%s", key);
- if (pair->dp_key == NULL || pair->dp_matched == NULL) {
- clicon_err(OE_DB, errno, "%s: chunk_sprintf");
- goto quit;
- }
- if ( ! noval) {
- if (vlen){
- pair->dp_val = chunkdup(val, vlen, label);
- if (pair->dp_val == NULL) {
- clicon_err(OE_DB, errno, "%s: chunkdup", __FUNCTION__);
- goto quit;
- }
- }
- pair->dp_vlen = vlen;
- free(val);
- val = NULL;
- }
-
- (*pairs) = newpairs;
- npairs++;
- free(key);
- }
-
- retval = npairs;
-
-quit:
- if (key)
- free(key);
- if (val)
- free(val);
- if (regexp)
- regfree(&iterre);
- if (iterdp)
- dpclose(iterdp);
- if (retval < 0)
- unchunk_group(label);
-
- return retval;
-}
-
-/*! Sanitize regexp string. Escape '\' etc.
- */
-char *
-db_sanitize(char *rx, const char *label)
-{
- char *new;
- char *k, *p, *s;
-
- k = chunk_sprintf(__FUNCTION__, "%s", "");
- p = rx;
- while((s = strstr(p, "\\"))) {
- if ((k = chunk_sprintf(__FUNCTION__, "%s%.*s\\\\", k, s-p, p)) == NULL)
- goto quit;
- p = s+1;
- }
- if ((k = chunk_strncat(k, p, strlen(p), __FUNCTION__)) == NULL)
- goto quit;
-
- new = (char *)chunkdup(k, strlen(k)+1, label);
- unchunk_group(__FUNCTION__);
- return new;
-
- quit:
- unchunk_group(__FUNCTION__);
- return NULL;
-}
-
-#if 0 /* Test program */
-/*
- * Turn this on to get an xpath test program
- * Usage: clicon_xpath []
- * read xml from input
- * Example compile:
- gcc -g -o qdb -I. -I../clixon ./clixon_qdb.c -lclixon -lcligen -lqdbm
-*/
-
-static int
-usage(char *argv0)
-{
- fprintf(stderr, "usage:\n");
- fprintf(stderr, "\t%s init \n", argv0);
- fprintf(stderr, "\t%s read \n", argv0);
- fprintf(stderr, "\t%s write \n", argv0);
- fprintf(stderr, "\t%s openread \n", argv0);
- fprintf(stderr, "\t%s openwrite \n", argv0);
- exit(0);
-}
-
-int
-main(int argc, char **argv)
-{
- char *verb;
- char *filename;
- char *key;
- char *val;
- size_t len;
- DEPOT *dp;
-
- if (argc < 3)
- usage(argv[0]);
- clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
- verb = argv[1];
- filename = argv[2];
- if (strcmp(verb, "init")==0){
- db_init(filename);
- }
- else if (strcmp(verb, "read")==0){
- if (argc < 4)
- usage(argv[0]);
- key = argv[3];
- db_get_alloc(filename, key, (void**)&val, &len);
- fprintf(stdout, "%s\n", val);
- }
- else if (strcmp(verb, "write")==0){
- if (argc < 5)
- usage(argv[0]);
- key = argv[3];
- val = argv[4];
- db_set(filename, key, val, strlen(val)+1);
- }
- else if (strcmp(verb, "openread")==0){
- if ((dp = dpopen(filename, DP_OREADER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "dbopen: %s",
- dperrmsg(dpecode));
- return -1;
- }
- sleep(1000000);
- }
- else if (strcmp(verb, "openwrite")==0){
- if ((dp = dpopen(filename, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
- clicon_err(OE_DB, errno, "dbopen: %s",
- dperrmsg(dpecode));
- return -1;
- }
- sleep(1000000);
- }
- return 0;
-}
-
-#endif /* Test program */
-
-
diff --git a/datastore/text/Makefile.in b/datastore/text/Makefile.in
index 23c2a7bf..9eadb121 100644
--- a/datastore/text/Makefile.in
+++ b/datastore/text/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/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c
index bee21bd0..277e0373 100644
--- a/datastore/text/clixon_xmldb_text.c
+++ b/datastore/text/clixon_xmldb_text.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.
@@ -88,6 +88,8 @@ struct text_handle {
Assumes single backend*/
char *th_format; /* Datastroe format: xml / json */
int th_pretty; /* Store xml/json pretty-printed. */
+ char *th_nacm_mode;
+ cxobj *th_nacm_xtree;
};
/* Struct per database in hash */
@@ -207,6 +209,8 @@ text_disconnect(xmldb_handle xh)
}
hash_free(th->th_dbs);
}
+ if (th->th_nacm_mode)
+ free(th->th_nacm_mode);
free(th);
}
retval = 0;
@@ -239,6 +243,10 @@ text_getopt(xmldb_handle xh,
*value = th->th_format;
else if (strcmp(optname, "pretty") == 0)
*value = &th->th_pretty;
+ else if (strcmp(optname, "nacm_mode") == 0)
+ *value = &th->th_nacm_mode;
+ else if (strcmp(optname, "nacm_xtree") == 0)
+ *value = th->th_nacm_xtree;
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
@@ -288,6 +296,15 @@ text_setopt(xmldb_handle xh,
else if (strcmp(optname, "pretty") == 0){
th->th_pretty = (intptr_t)value;
}
+ else if (strcmp(optname, "nacm_mode") == 0){
+ if (value && (th->th_nacm_mode = strdup((char*)value)) == NULL){
+ clicon_err(OE_UNIX, 0, "strdup");
+ goto done;
+ }
+ }
+ else if (strcmp(optname, "nacm_xtree") == 0){
+ th->th_nacm_xtree = (cxobj*)value;
+ }
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
@@ -358,6 +375,16 @@ xml_copy_marked(cxobj *x0,
assert(x0 && x1);
yt = xml_spec(x0); /* can be null */
+ /* Copy all attributes */
+ x = NULL;
+ while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
+ name = xml_name(x);
+ if ((xcopy = xml_new(name, x1, xml_spec(x))) == NULL)
+ goto done;
+ if (xml_copy(x, xcopy) < 0)
+ goto done;
+ }
+
/* Go through children to detect any marked nodes:
* (3) Special case: key nodes in lists are copied if any
* node in list is marked
@@ -411,10 +438,17 @@ xml_copy_marked(cxobj *x0,
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* This is a clixon datastore plugin of the the xmldb api
- * @see xmldb_get
+ * @param[in] h Clicon handle
+ * @param[in] dbname Name of database to search in (filename including dir path
+ * @param[in] xpath String with XPATH syntax. or NULL for all
+ * @param[in] config If set only configuration data, else also state
+ * @param[out] xret Single return XML tree. Free with xml_free()
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see xmldb_get the generic API function
*/
int
-text_get(xmldb_handle xh,
+text_get(xmldb_handle xh,
const char *db,
char *xpath,
int config,
@@ -424,6 +458,7 @@ text_get(xmldb_handle xh,
char *dbfile = NULL;
yang_spec *yspec;
cxobj *xt = NULL;
+ cxobj *x;
int fd = -1;
cxobj **xvec = NULL;
size_t xlen;
@@ -470,9 +505,12 @@ text_get(xmldb_handle xh,
if (singleconfigroot(xt, &xt) < 0)
goto done;
}
+ /* XXX: should we validate file if read from disk?
+ * Argument against: we may want to have a semantically wrong file and wish
+ * to edit?
+ */
} /* xt == NULL */
/* Here xt looks like: ... */
-
if (xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
@@ -482,11 +520,13 @@ text_get(xmldb_handle xh,
*/
if (xvec != NULL)
for (i=0; ith_cache)
- xml_apply_ancestor(xvec[i], (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
+ x = xvec[i];
+ xml_flag_set(x, XML_FLAG_MARK);
+ if (1 || th->th_cache)
+ xml_apply_ancestor(x, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
+
if (th->th_cache){
/* Copy the matching parts of the (relevant) XML tree.
* If cache was NULL, also write to datastore cache
@@ -520,22 +560,22 @@ text_get(xmldb_handle xh,
/* reset flag */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
+
/* filter out state (operations) data if config not set. Mark all nodes
that are not config data */
- if (config && xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
- goto done;
- /* Remove (prune) nodes that are marked (that does not pass test) */
- if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
- goto done;
+ if (config){
+ if (xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
+ goto done;
+ /* Remove (prune) nodes that are marked (that does not pass test) */
+ if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
+ goto done;
+ }
/* Add default values (if not set) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
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,34 +595,49 @@ 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[in] username User name of requestor for nacm
+ * @param[in] xnacm NACM XML tree
+ * @param[in] permit If set, NACM has permitted this tree on an upper level
+ * @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,
enum operation_type op,
+ char *username,
+ cxobj *xnacm,
+ int permit,
cbuf *cbret)
{
int retval = -1;
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 */
+ char *x0bstr; /* mod body string */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
+ int ret;
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
@@ -598,15 +653,29 @@ 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:
case OP_REPLACE:
if (x0==NULL){
+ if ((op != OP_NONE) && !permit && xnacm){
+ if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
// 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)
@@ -623,24 +692,39 @@ text_modify(cxobj *x0,
}
}
if (x1bstr){
- if ((x0b = xml_body_get(x0)) == NULL){
- if ((x0b = xml_new("body", x0, NULL)) == NULL)
- goto done;
- xml_type_set(x0b, CX_BODY);
+ if ((x0b = xml_body_get(x0)) != NULL){
+ x0bstr = xml_value(x0b);
+ if (x0bstr==NULL || strcmp(x0bstr, x1bstr)){
+ if ((op != OP_NONE) && !permit && xnacm){
+ if ((ret = nacm_datanode_write(NULL, x1,
+ x0bstr==NULL?NACM_CREATE:NACM_UPDATE,
+ username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ }
+ if (xml_value_set(x0b, x1bstr) < 0)
+ goto done;
+ }
}
- if (xml_value_set(x0b, x1bstr) < 0)
- goto done;
}
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){
- xml_purge(x0);
+ if ((op != OP_NONE) && !permit && xnacm){
+ if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ }
+ if (xml_purge(x0) < 0)
+ goto done;
}
break;
default:
@@ -653,9 +737,16 @@ 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 (xnacm && !permit){
+ if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
if (x0){
xml_purge(x0);
x0 = NULL;
@@ -663,10 +754,21 @@ text_modify(cxobj *x0,
case OP_MERGE: /* fall thru */
case OP_NONE:
/* Special case: anyxml, just replace tree,
- See 7.10.3 of RFC6020bis */
+ See rfc6020 7.10.3:n
+ An anyxml node is treated as an opaque chunk of data. This data
+ can be modified in its entirety only.
+ Any "operation" attributes present on subelements of an anyxml
+ node are ignored by the NETCONF server.*/
if (y0->yn_keyword == Y_ANYXML){
if (op == OP_NONE)
break;
+ if (xnacm && op==OP_MERGE && !permit){
+ if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
if (x0){
xml_purge(x0);
}
@@ -677,13 +779,28 @@ text_modify(cxobj *x0,
break;
}
if (x0==NULL){
+ if (xnacm && op==OP_MERGE && !permit){
+ if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
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,59 +816,91 @@ 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,
+ username, xnacm, permit, 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)
- xml_purge(x0);
+ if (x0){
+ if (xnacm){
+ if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ }
+ if (xml_purge(x0) < 0)
+ goto done;
+ }
break;
default:
break;
} /* 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[in] username User name of requestor for nacm
+ * @param[in] xnacm NACM XML tree
+ * @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
-text_modify_top(cxobj *x0,
+text_modify_top(struct text_handle *th,
+ cxobj *x0,
cxobj *x1,
yang_spec *yspec,
enum operation_type op,
+ char *username,
+ cxobj *xnacm,
cbuf *cbret)
{
int retval = -1;
@@ -759,7 +908,10 @@ text_modify_top(cxobj *x0,
cxobj *x0c; /* base child */
cxobj *x1c; /* mod child */
yang_stmt *yc; /* yang child */
+ yang_stmt *ymod;/* yang module */
char *opstr;
+ int ret;
+ int permit = 0;
/* Assure top-levels are 'config' */
assert(x0 && strcmp(xml_name(x0),"config")==0);
@@ -770,62 +922,104 @@ text_modify_top(cxobj *x0,
if (xml_operation(opstr, &op) < 0)
goto done;
/* Special case if x1 is empty, top-level only */
- if (xml_child_nr(x1) == 0){
- if (xml_child_nr(x0)) /* base tree not empty */
+ if (xml_child_nr_type(x1, CX_ELMNT) == 0){
+ if (xml_child_nr_type(x0, CX_ELMNT)){ /* base tree not empty */
switch(op){
case OP_DELETE:
case OP_REMOVE:
case OP_REPLACE:
- x0c = NULL;
- while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL)
- xml_purge(x0c);
+ if (xnacm){
+ if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
+ while ((x0c = xml_child_i(x0, 0)) != 0)
+ if (xml_purge(x0c) < 0)
+ goto done;
break;
default:
break;
}
+ }
else /* base tree empty */
switch(op){
+#if 0 /* According to RFC6020 7.5.8 you cant delete a non-existing object.
+ On the other hand, the top-level cannot be removed anyway.
+ Additionally, I think this is irritating so I disable it.
+ I.e., curl -u andy:bar -sS -X DELETE http://localhost/restconf/data
+ */
case OP_DELETE:
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
- goto ok;
+ goto fail;
break;
+#endif
default:
break;
}
}
/* Special case top-level replace */
- if (op == OP_REPLACE || op == OP_DELETE){
- x0c = NULL;
+ else if (op == OP_REPLACE || op == OP_DELETE){
+ if (xnacm && !permit){
+ if ((ret = nacm_datanode_write(NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
+ goto done;
+ if (ret == 0)
+ goto fail;
+ permit = 1;
+ }
while ((x0c = xml_child_i(x0, 0)) != 0)
- xml_purge(x0c);
+ if (xml_purge(x0c) < 0)
+ goto done;
}
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
/* Get yang spec of the child */
- if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
- clicon_err(OE_YANG, ENOENT, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?",
- xml_name(x1), x1cname);
+ yc = NULL;
+ if (ys_module_by_xml(yspec, x1c, &ymod) <0)
goto done;
+ if (ymod != NULL)
+ yc = yang_find_datanode((yang_node*)ymod, x1cname);
+ if (yc == NULL && !_CLICON_XML_NS_STRICT){
+ if (xml_yang_find_non_strict(x1c, yspec, &yc) < 0)
+ goto done;
+ }
+ if (yc == NULL){
+ if (netconf_unknown_element(cbret, "application", x1cname, "Unassigned yang spec") < 0)
+ goto done;
+ 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,
+ username, xnacm, permit, 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:
@@ -854,7 +1048,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;
@@ -864,13 +1058,23 @@ xml_container_presence(cxobj *x,
/*! Modify database provided an xml tree and an operation
* This is a clixon datastore plugin of the the xmldb api
- * @see xmldb_put
+ * @param[in] h CLICON handle
+ * @param[in] db running or candidate
+ * @param[in] op Top-level operation, can be superceded by other op in tree
+ * @param[in] x1 xml-tree. Top-level symbol is dummy
+ * @param[in] username User name for nacm
+ * @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
+ * @see xmldb_put the generic API function
*/
int
text_put(xmldb_handle xh,
const char *db,
enum operation_type op,
cxobj *x1,
+ char *username,
cbuf *cbret)
{
int retval = -1;
@@ -882,14 +1086,12 @@ 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;
+ cxobj *xnacm = NULL;
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");
@@ -942,24 +1144,41 @@ text_put(xmldb_handle xh,
xml_name(x0));
goto done;
}
-
- /* Add yang specification backpointer to all XML nodes */
- /* XXX: where is this created? Add yspec */
- if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
- goto done;
#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
+#if 1
+ {
+ char *mode;
+ cxobj *xnacm0 = NULL;
+
+ mode = th->th_nacm_mode;
+ if (mode){
+ if (strcmp(mode, "external")==0)
+ xnacm0 = th->th_nacm_xtree;
+ else if (strcmp(mode, "internal")==0)
+ xnacm0 = x0;
+ }
+ if (xnacm0 != NULL &&
+ (xnacm = xpath_first(xnacm0, "nacm")) != NULL){
+ /* Pre-NACM access step */
+ if ((ret = nacm_access(mode, xnacm, username)) < 0)
+ goto done;
+ }
+ /* Here assume if xnacm is set (actually may be ret==0?) do NACM */
+ }
+
#endif
/*
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
- if (text_modify_top(x0, x1, yspec, op, cbret) < 0)
+ if ((ret = text_modify_top(th, x0, x1, yspec, op, username, xnacm, 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)
@@ -974,7 +1193,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 */
@@ -1009,11 +1228,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)
@@ -1025,6 +1241,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
@@ -1367,10 +1586,11 @@ main(int argc,
enum operation_type op;
char *cmd;
char *db;
- char *yangdir;
- char *yangmod;
+ char *yangmod; /* yang file */
yang_spec *yspec = NULL;
clicon_handle h;
+ cbuf *cbret = NULL;
+ int ret;
if ((h = clicon_handle_init()) == NULL)
goto done;
@@ -1381,12 +1601,11 @@ main(int argc,
}
cmd = argv[1];
db = argv[2];
- yangdir = argv[3];
yangmod = argv[4];
db_init(db);
if ((yspec = yspec_new()) == NULL)
goto done
- if (yang_parse(h, NULL, yangmod, yangdir, NULL, yspec) < 0)
+ if (yang_spec_parse_module(h, yangmod, NULL, yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){
if (argc < 5)
@@ -1420,13 +1639,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..ed365634 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.
@@ -40,7 +40,7 @@
* Prototypes
*/
int text_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop);
-int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
+int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(xmldb_handle h, const char *from, const char *to);
int text_lock(xmldb_handle h, const char *db, int pid);
diff --git a/doc/FAQ.md b/doc/FAQ.md
index 25727e98..f15ee42c 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,70 +72,37 @@ grep clicon /etc/group
clicon:x:1001:,www-data
```
-## What about reference documentation?
-Clixon uses Doxygen for reference documentation.
-Build using 'make doc' and aim your browser at doc/html/index.html.
+## How do I use the CLI?
-## How is configuration data stored?
-Configuration data is stored in an XML datastore. In the example the
-datastore are regular files found in /usr/local/var/example/.
-
-## What is validate and commit?
-Clixon follows netconf in its validate and commit semantics.
-In short, you edit a 'candidate' configuration, which is first
-'validated' for consistency and then 'committed' to the 'running'
-configuration.
-
-A clixon developer writes commit functions to incrementaly upgrade a
-system state based on configuration changes. Writing commit callbacks
-is the core functionality of a clixon system.
-
-## What is a Clixon configuration file?
-
-Clixon options are stored in an XML configuration file. The default
-configuration file is /usr/local/etc/clixon.xml. The example
-configuration file is installed at /usr/local/etc/example.xml. The
-YANG specification for the configuration file is clixon-config.yang.
-
-You can change where Clixon looks for the configuration FILE as follows:
- - Provide -f FILE option when starting a program (eg clixon_backend -f FILE)
- - Provide --with-configfile=FILE when configuring
- - Provide --with-sysconfig= when configuring, then FILE is /clixon.xml
- - Provide --sysconfig= when configuring then FILE is /etc/clixon.xml
- - FILE is /usr/local/etc/clixon.xml
-
-## How do I enable Yang features?
-
-Yang models have features, and parts of a specification can be
-conditional using the if-feature statement. In Clixon, features are
-enabled in the configuration file using .
-
-The example below shows enabling a specific feature; enabling all features in module; and enabling all features in all modules, respectively:
+The easiest way to use Clixon is via the CLI. Once the backend is started
+Example:
```
- ietf-routing:router-id
- ietf-routing:*
- *:*
+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
```
-Features can be probed by using RFC 7895 Yang module library which provides
-information on all modules and which features are enabled.
-
-## Can I run Clixon as docker containers?
-
-Yes, the example works as docker containers as well. There should be a
-prepared container in docker hub for the example where the backend and
-CLI is bundled.
-```
-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
+clixon_netconf -qf /usr/local/etc/example.xml
+ ]]>]]>
+eth9 ex:eth true ]]>]]>
```
However, more useful is to run clixon_netconf as an SSH
@@ -170,18 +137,118 @@ Start nginx daemon
```
sudo /etc/init.d/nginx start
```
-
-Example:
+Start the clixon restconf daemon
```
- curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
+sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
+```
+
+Then acess:
+```
+ curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type
[
{
- "type": "eth"
+ "ietf-interfaces:type": "ex:eth"
}
]
```
-Read more in the (restconf)[../apps/restconf] docs.
+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.
+Build it in the doc directory and point the browser to `.../clixon/doc/html/index.html` as follows:
+```
+> cd doc
+> make doc
+> make graphs # detailed callgraphs
+```
+## How is configuration data stored?
+Configuration data is stored in an XML datastore. In the example the
+datastore are regular files found in /usr/local/var/example/.
+
+## What is validate and commit?
+Clixon follows netconf in its validate and commit semantics.
+In short, you edit a 'candidate' configuration, which is first
+'validated' for consistency and then 'committed' to the 'running'
+configuration.
+
+A clixon developer writes commit functions to incrementaly upgrade a
+system state based on configuration changes. Writing commit callbacks
+is the core functionality of a clixon system.
+
+## What is a Clixon configuration file?
+
+Clixon options are stored in an XML configuration file. The default
+configuration file is /usr/local/etc/clixon.xml. The example
+configuration file is installed at /usr/local/etc/example.xml. The
+YANG specification for the configuration file is clixon-config.yang.
+
+## How are Clixon configuration files found?
+
+Clixon by default finds its configuration file at `/usr/local/etc/clixon.xml`. However, you can modify this location as follows:
+ - Provide -f FILE option when starting a program (eg clixon_backend -f FILE)
+ - Provide --with-configfile=FILE when configuring
+ - Provide --with-sysconfig= when configuring. Then FILE is /clixon.xml
+ - Provide --sysconfig= when configuring. Then FILE is /etc/clixon.xml
+ - FILE is /usr/local/etc/clixon.xml
+
+## Can I modify clixon options at runtime?
+
+Yes, when you start a clixon program, you can supply the `-o` option to modify the configuration specified in the configuration file. Options that are leafs are overriden, whereas options that are leaf-lists are added to.
+
+Example, add the "usr/local/share/ietf" directory to the list of directories where yang files are searched for:
+```
+ clixon_cli -o CLICON_YANG_DIR=/usr/local/share/ietf
+```
+
+## How are Yang files found?
+
+Yang files contain the configuration specification. A Clixon
+application loads yang files and clixon itself loads system yang
+files. When Yang files are loaded modules are imported and submodules
+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 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.
+
+Note that the special `CLIXON_DATADIR`, by default `/usr/local/share/clixon` should be included in the yang dir path for Clixon system files to be found.
+
+You can combine the options, however, if there are different variants
+of the same module, more specific options override less
+specific. The precedence of the options are as follows:
+- `CLICON_YANG_MAIN_FILE`
+- `CLICON_YANG_MODULE_MAIN`
+- `CLICON_YANG_MAIN_DIR`
+
+## How do I enable Yang features?
+
+Yang models have features, and parts of a specification can be
+conditional using the if-feature statement. In Clixon, features are
+enabled in the configuration file using .
+
+The example below shows enabling a specific feature; enabling all features in module; and enabling all features in all modules, respectively:
+```
+ ietf-routing:router-id
+ ietf-routing:*
+ *:*
+```
+
+Features can be probed by using RFC 7895 Yang module library which provides
+information on all modules and which features are enabled.
+
+## Can I run Clixon as docker containers?
+
+Yes, the example works as docker containers as well. There should be a
+prepared container in docker hub for the example where the backend and
+CLI is bundled.
+```
+sudo docker run -td olofhagsand/clixon_example
+```
+Look in the example documentation for more info.
## Does Clixon support event streams?
@@ -202,7 +269,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 ]]>]]>
...
@@ -211,11 +278,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.
@@ -239,6 +306,7 @@ You may also add a default method in the configuration file:
Yes. Systemd example files are provide for the backend and the
restconf daemon as part of the [example](../example/systemd).
+
## How can I add extra XML?
There are two ways to add extra XML to running database after start. Note that this XML is not "committed" into running.
@@ -246,7 +314,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:
@@ -258,12 +326,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.
@@ -349,7 +418,7 @@ Please look at the example for an example on how to write a state data callback.
A YANG RPC is an application specific operation. Example:
```
- rpc fib-route {
+ rpc example-rpc {
input {
leaf inarg { type string; }
}
@@ -358,7 +427,7 @@ A YANG RPC is an application specific operation. Example:
}
}
```
-which defines the fib-route operation present in the example (the arguments have been changed).
+which defines the example-rpc operation present in the example (the arguments have been changed).
Clixon automatically relays the RPC to the clixon backend. To
implement the RFC, you need to register an RPC callback in the backend plugin:
@@ -368,18 +437,18 @@ int
clixon_plugin_init(clicon_handle h)
{
...
- rpc_callback_register(h, fib_route, NULL, "fib-route");
+ rpc_callback_register(h, example_rpc, NULL, "example-rpc");
...
}
```
And then define the callback itself:
```
static int
-fib_route(clicon_handle h, /* Clicon handle */
- cxobj *xe, /* Request: */
- cbuf *cbret, /* Reply eg ... */
- void *arg, /* Client session */
- void *regarg) /* Argument given at register */
+example_rpc(clicon_handle h, /* Clicon handle */
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* Client session */
+ void *regarg) /* Argument given at register */
{
cprintf(cbret, " ");
return 0;
@@ -406,10 +475,18 @@ To authenticate, the callback needs to return the value 1 and supply a username.
See [../apps/example/example_restconf.c] example_restconf_credentials() for
an example of HTTP basic auth.
+## What about access control?
+
+Clixon has experimental support of the Network Configuration Access
+Control Model defined in [RFC8341](https://tools.ietf.org/html/rfc8341)
+
+Incoming RPC and data node access points are supported with some
+limitations. See the (README)(../README.md) for more information.
+
## How do I write a CLI translator function?
The CLI can perform variable translation. This is useful if you want to
-prcess the input, such as hashing, encrypting or in other way
+process the input, such as hashing, encrypting or in other way
translate the input.
Yang example:
diff --git a/doc/Makefile.in b/doc/Makefile.in
index 7b1ce519..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
#
@@ -44,11 +44,13 @@ all: $(SUBDIRS) doc
echo "Build doxygen doc: make doc"
# Regular doxygen documentation
+# Need to install doxygen
doc:
doxygen Doxyfile # generates html dir
echo "Build doxygen graphs: make graphs"
# doxygen documentation with callgraphs
+# Need to install graphviz
graphs:
doxygen Doxyfile.graphs # generates html dir + call graphs (takes time)
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 23d0a7e8..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
#
@@ -55,7 +55,7 @@ distclean: clean
rm -f Makefile *~ .depend
docker:
- sudo docker build -t $(IMAGE) .
+ sudo docker build -t $(IMAGE) . # --no-cache
echo "cd ../example; make docker to build example application"
push:
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..e4e60549 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
#
@@ -43,6 +43,8 @@ localstatedir = @localstatedir@
libdir = @exec_prefix@/lib
APPNAME = example
+# Here is where example yang appears
+CLIXON_DATADIR = @CLIXON_DATADIR@
# Install here if you want default clixon location:
CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@
@@ -70,16 +72,7 @@ all: $(PLUGINS)
CLISPECS = $(APPNAME)_cli.cli
-YANGSPECS = $(APPNAME).yang
-YANGSPECS += ietf-yang-types@2013-07-15.yang
-YANGSPECS += ietf-inet-types@2013-07-15.yang
-YANGSPECS += ietf-interfaces@2014-05-08.yang
-YANGSPECS += ietf-ip@2014-06-16.yang
-YANGSPECS += ietf-routing@2014-10-26.yang
-YANGSPECS += ietf-ipv4-unicast-routing@2014-10-26.yang
-YANGSPECS += ietf-ipv6-unicast-routing@2014-10-26.yang
-YANGSPECS += ietf-ipsec@2016-03-09.yang
-YANGSPECS += iana-if-type@2014-05-08.yang
+YANGSPECS = clixon-example@2019-01-13.yang
# Backend plugin
BE_SRC = $(APPNAME)_backend.c
@@ -125,7 +118,7 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NET
install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir)
# install -m 0644 $(APPNAME).xml $(DESTDIR)$(CLIXON_DEFAULT_CONFIG)
install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
- install -m 0644 $(YANGSPECS) $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
+ install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(CLIXON_DATADIR)
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli
install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend
diff --git a/example/README.md b/example/README.md
index 6fdc8641..8e9c61f0 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,20 +1,33 @@
# Clixon example
-This directory contains a Clixon example which includes a simple
-routing 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:
+ * [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)[../yang/clixon-config@2018-10-21.yang] for the documentation of all available fields.
+* `clixon-example@2019-01-13.yang` The yang spec of the example.
+* `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 example RPC call (`example_client_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
@@ -48,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
@@ -66,38 +106,181 @@ Send restconf command
]]>]]>
```
-## Getting data using netconf
+### Getting data using netconf
```
]]>]]>
]]>]]>
]]>]]>
- ]]>]]>
- ]]>]]>
+eth9 ex:eth ]]>]]>
+ ]]>]]>
]]>]]>
```
+## Restconf
+
+Setup a web/reverse-proxy server.
+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
+```
+Start the clixon restconf daemon
+```
+sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
+```
+then access using curl or wget:
+```
+ curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type
+```
## Streams
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:
+```
+clixon_cli -f /usr/local/etc/example.xml
+cli> rpc ipv4
+ipv4 42
+```
+Example using Netconf:
+```
+clixon_netconf -qf /usr/local/etc/example.xml
+ipv4 ]]>]]>
+ipv4 42 ]]>]]>
+```
+Restconf (assuming nginx started):
+```
+sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data&
+curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}'
+{
+ "clixon-example:output": {
+ "x": "ipv4",
+ "y": "42"
+ }
+}
+```
+
+### Details
+
+The example works by defining an RPC in clixon-example.yang:
+```
+ rpc example {
+ description "Some example input/output for testing RFC7950 7.14.
+ RPC simply echoes the input for debugging.";
+ input {
+ leaf x {
+ ...
+```
+
+In the CLI a netconf rpc call is constructed and sent to the backend: See `example_client_rpc()` in [example_cli.c] CLI plugin.
+
+The clixon backend plugin [example_backend.c] reveives the netconf call and replies. This is made byregistering a callback handling handling the RPC:
+```
+static int
+example_rpc(clicon_handle h,
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* Client session */
+ void *regarg) /* Argument given at register */
+{
+ /* code that echoes the request */
+ return 0;
+}
+int
+clixon_plugin_init(clicon_handle h)
+{
+...
+ rpc_callback_register(h, example_rpc, NULL, "example");
+...
+}
+```
+
+## 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.
@@ -123,102 +306,8 @@ clixon_plugin_api *
clixon_plugin_init(clicon_handle h)
{
/* Optional callback registration for RPC calls */
- rpc_callback_register(h, fib_route, NULL, "fib-route");
+ rpc_callback_register(h, example_rpc, NULL, "example");
/* Return plugin API */
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: adm1, wilma, and guest, according to the examples in Appendix A in the RFC.
-* 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/clixon-example@2019-01-13.yang b/example/clixon-example@2019-01-13.yang
new file mode 100644
index 00000000..da038f31
--- /dev/null
+++ b/example/clixon-example@2019-01-13.yang
@@ -0,0 +1,142 @@
+module clixon-example {
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix ex;
+ revision 2019-01-13 {
+ description
+ "Released in Clixon 3.9";
+ }
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-ip {
+ prefix ip;
+ }
+ import iana-if-type {
+ prefix ianaift;
+ }
+ /* Example interface type for tests, local callbacks, etc */
+ identity eth {
+ base if:interface-type;
+ }
+ identity loopback {
+ base if:interface-type;
+ }
+ /* Translation function example - See also example_cli */
+ list translate{
+ key k;
+ leaf k{
+ type string;
+ }
+ leaf value{
+ type string;
+ }
+ }
+ /* State data (not config) for the example application*/
+ container state {
+ config false;
+ description "state data for the example application (must be here for example get operation)";
+ leaf-list op {
+ type string;
+ }
+ }
+ /* Example notification as used in RFC 5277 and RFC 8040 */
+ notification event {
+ description "Example notification event.";
+ leaf event-class {
+ type string;
+ description "Event class identifier.";
+ }
+ container reportingEntity {
+ description "Event specific information.";
+ leaf card {
+ type string;
+ description "Line card identifier.";
+ }
+ }
+ leaf severity {
+ type string;
+ description "Event severity description.";
+ }
+ }
+ 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 x {
+ type string;
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ }
+ }
+ rpc empty {
+ description "Smallest possible RPC with no input or output sections";
+ }
+ rpc optional {
+ description "Small RPC with optional input and output";
+ input {
+ leaf x {
+ type string;
+ }
+ }
+ output {
+ leaf x {
+ type string;
+ }
+ }
+ }
+ rpc example {
+ description "Some example input/output for testing RFC7950 7.14.
+ RPC simply echoes the input for debugging.";
+ input {
+ 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.xml b/example/example.xml
index c222f6ce..aaf202b0 100644
--- a/example/example.xml
+++ b/example/example.xml
@@ -1,8 +1,8 @@
/usr/local/etc/example.xml
*:*
- /usr/local/share/example/yang
- example
+ /usr/local/share/clixon
+ clixon-example
example
/usr/local/lib/example/backend
/usr/local/lib/example/netconf
diff --git a/example/example.yang b/example/example.yang
deleted file mode 100644
index 1bba52d3..00000000
--- a/example/example.yang
+++ /dev/null
@@ -1,73 +0,0 @@
-module example {
- prefix ex;
- import ietf-interfaces {
- prefix if;
- }
- import ietf-ip {
- prefix ip;
- }
- import ietf-routing {
- prefix rt;
- }
- import iana-if-type {
- prefix ianaift;
- }
- description
- "Example code that includes ietf-ip and ietf-routing";
- /* Example interface type for tests, local callbacks, etc */
- identity eth {
- base if:interface-type;
- }
- identity loopback {
- base if:interface-type;
- }
- /* Translation function example - See also example_cli */
- list translate{
- leaf value{
- type string;
- }
- }
- /* State data (not config) for the example application*/
- container state {
- config false;
- description "state data for the example application (must be here for example get operation)";
- leaf-list op {
- type string;
- }
- }
- /* Example notification as used in RFC 5277 and RFC 8040 */
- notification event {
- description "Example notification event.";
- leaf event-class {
- type string;
- description "Event class identifier.";
- }
- container reportingEntity {
- description "Event specific information.";
- leaf card {
- type string;
- description "Line card identifier.";
- }
- }
- leaf severity {
- type string;
- description "Event severity description.";
- }
- }
- 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;
- }
- }
- }
-}
diff --git a/example/example_backend.c b/example/example_backend.c
index 3a420a0e..2e074064 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)
@@ -119,37 +119,6 @@ example_stream_timer_setup(clicon_handle h)
return event_reg_timeout(t, example_stream_timer, h, "example stream timer");
}
-/*! IETF Routing fib-route rpc
- * @see ietf-routing@2014-10-26.yang (fib-route)
- */
-static int
-fib_route(clicon_handle h, /* Clicon handle */
- cxobj *xe, /* Request: */
- cbuf *cbret, /* Reply eg ... */
- void *arg, /* Client session */
- void *regarg) /* Argument given at register */
-{
- cprintf(cbret, ""
- "ipv4 "
- "2.3.4.5 "
- " ");
- return 0;
-}
-
-/*! IETF Routing route-count rpc
- * @see ietf-routing@2014-10-26.yang (route-count)
- */
-static int
-route_count(clicon_handle h,
- cxobj *xe, /* Request: */
- cbuf *cbret, /* Reply eg ... */
- void *arg,
- void *regarg) /* Argument given at register */
-{
- cprintf(cbret, "42 ");
- return 0;
-}
-
/*! Smallest possible RPC declaration for test
* Yang/XML:
* If the RPC operation invocation succeeded and no output parameters
@@ -157,16 +126,50 @@ 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, "");
+ if (!xml_child_nr_type(xe, CX_ELMNT))
+ cprintf(cbret, " ");
+ else 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,8 +199,10 @@ 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 "
+ "41 "
+ "43 " /* should not be ordered */
" ", NULL, &xstate) < 0)
goto done;
retval = 0;
@@ -225,19 +230,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, clicon_username_get(h), 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,26 +333,28 @@ clixon_plugin_init(clicon_handle h)
if (example_stream_timer_setup(h) < 0)
goto done;
- /* 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;
- 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,
+ /* Register callback for routing rpc calls
+ */
+ /* From example.yang (clicon) */
+ if (rpc_callback_register(h, empty_rpc,
NULL,
"empty"/* Xml tag when callback is made */
) < 0)
goto done;
+ /* Same as example but with optional input/output */
+ if (rpc_callback_register(h, example_rpc,
+ NULL,
+ "optional"/* 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;
done:
return NULL;
}
-
diff --git a/example/example_backend_nacm.c b/example/example_backend_nacm.c
index 3814a975..eae31132 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.
@@ -75,7 +75,7 @@ nacm_statedata(clicon_handle h,
cxobj **xvec = NULL;
/* Example of (static) statedata, real code would poll state */
- if (xml_parse_string(""
+ if (xml_parse_string(""
"0 "
"0 "
"0 "
diff --git a/example/example_cli.c b/example/example_cli.c
index 99c38368..10e122ee 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.
@@ -71,7 +71,6 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
"/interfaces/interface[name='eth0']",
&xret) < 0)
goto done;
-
xml_print(stdout, xret);
retval = 0;
done:
@@ -80,25 +79,25 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
return retval;
}
-/*! Example "downcall": ietf-routing fib-route RPC */
+/*! Example "downcall", ie initiate an RPC to the backend */
int
-fib_route_rpc(clicon_handle h,
- cvec *cvv,
- cvec *argv)
+example_client_rpc(clicon_handle h,
+ cvec *cvv,
+ cvec *argv)
{
int retval = -1;
- cg_var *instance;
+ cg_var *cva;
cxobj *xtop = NULL;
cxobj *xrpc;
cxobj *xret = NULL;
cxobj *xerr;
/* 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 ",
+ cva = cvec_find(cvv, "a"); /* get a cligen variable from vector */
+ /* Create XML for example netconf RPC */
+ if (xml_parse_va(&xtop, NULL, "%s ",
clicon_username_get(h),
- cv_string_get(instance)) < 0)
+ cv_string_get(cva)) < 0)
goto done;
/* Skip top-level */
xrpc = xml_child_i(xtop, 0);
@@ -110,7 +109,12 @@ fib_route_rpc(clicon_handle h,
goto done;
}
/* Print result */
- xml_print(stdout, xml_child_i(xret, 0));
+ clicon_xml2file(stdout, xml_child_i(xret, 0), 0, 0);
+ fprintf(stdout,"\n");
+
+ /* pretty-print:
+ 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 1856c8c7..52c2bcf7 100644
--- a/example/example_cli.cli
+++ b/example/example_cli.cli
@@ -7,10 +7,10 @@ CLICON_PLUGIN="example_cli";
translate value (),cli_set("/translate/value");
# Note, when switching to PT, change datamodel to only @datamodel
-set @datamodel:example, cli_set();
-merge @datamodel:example, cli_merge();
-create @datamodel:example, cli_create();
-delete("Delete a configuration item") @datamodel:example, cli_del();
+set @datamodel, cli_set();
+merge @datamodel, cli_merge();
+create @datamodel, cli_create();
+delete("Delete a configuration item") @datamodel, cli_del();
validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit();
@@ -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();
@@ -40,19 +40,19 @@ show("Show a particular state of the system"){
}
configuration("Show configuration"), cli_show_config("candidate", "text", "/");{
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{
- @datamodel:example, cli_show_auto("candidate", "text");
+ @datamodel, cli_show_auto("candidate", "text");
}
cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/");{
- @datamodel:example, cli_show_auto("candidate", "cli");
+ @datamodel, cli_show_auto("candidate", "cli");
}
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{
- @datamodel:example, cli_show_auto("candidate", "netconf");
+ @datamodel, cli_show_auto("candidate", "netconf");
}
text("Show configuration as text"), cli_show_config("candidate","text","/");{
- @datamodel:example, cli_show_auto("candidate", "text");
+ @datamodel, cli_show_auto("candidate", "text");
}
json("Show configuration as JSON"), cli_show_config("candidate", "json", "/");{
- @datamodel:example, cli_show_auto("candidate", "json");
+ @datamodel, cli_show_auto("candidate", "json");
}
}
}
@@ -63,7 +63,7 @@ load("Load configuration from XML file") ("Filename (local file
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
}
example("This is a comment") ("Just a random number"), mycallback("myarg");
-rpc("ex:fib-route rpc") ("routing instance"), fib_route_rpc("myarg");
+rpc("example rpc") ("routing instance"), example_client_rpc("");
notify("Get notifications from backend"), cli_notify("EXAMPLE", "1", "text");
no("Negate") notify("Get notifications from backend"), cli_notify("EXAMPLE", "0", "xml");
lock,cli_lock("candidate");
diff --git a/example/example_netconf.c b/example/example_netconf.c
index 9993e851..5a5710f0 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.
@@ -65,13 +65,34 @@ plugin_exit(clicon_handle h)
/*! Local example netconf rpc callback
*/
int netconf_client_rpc(clicon_handle h,
- cxobj *xn,
+ cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
- clicon_debug(1, "%s restconf", __FUNCTION__);
- cprintf(cbret, "ok ");
+ 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, "");
+ if (!xml_child_nr_type(xe, CX_ELMNT))
+ cprintf(cbret, " ");
+ else 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;
+
return 0;
}
diff --git a/example/example_restconf.c b/example/example_restconf.c
index 6cc6323d..e6e395c5 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.
@@ -191,7 +191,7 @@ b64_decode(const char *src,
* @retval -1 Fatal error
* @retval 0 Unauth
* @retval 1 Auth
- * @note: Three hardwired users: adm1, wilma, guest w password "bar".
+ * @note: Three hardwired users: andy, wilma, guest w password "bar".
* Enabled by passing -- -a to the main function
*/
int
@@ -237,9 +237,9 @@ example_restconf_credentials(clicon_handle h,
/* Here get auth sub-tree whjere all the users are */
if ((cb = cbuf_new()) == NULL)
goto done;
- /* Hardcoded user/passwd */
- if (strcmp(user, "wilma")==0 || strcmp(user, "adm1")==0 ||
- strcmp(user, "quest")==0){
+ /* XXX Three hardcoded user/passwd (from RFC8341 A.1)*/
+ if (strcmp(user, "wilma")==0 || strcmp(user, "andy")==0 ||
+ strcmp(user, "guest")==0){
passwd2 = "bar";
}
if (strcmp(passwd, passwd2))
@@ -268,28 +268,46 @@ example_restconf_credentials(clicon_handle h,
*/
int
restconf_client_rpc(clicon_handle h,
- cxobj *xn,
+ cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
- // FCGX_Request *r = (FCGX_Request *)arg;
- clicon_debug(1, "%s", __FUNCTION__);
- cprintf(cbret, "ok ");
- return 0;
+ 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, "");
+ if (!xml_child_nr_type(xe, CX_ELMNT))
+ cprintf(cbret, " ");
+ else 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;
}
/*! Start example restonf plugin. Set authentication method
* Arguments are argc/argv after --
* Currently defined: -a enable http basic authentication
- * Note hardwired users adm1, wilma and guest
+ * @note There are three hardwired users andy, wilma and guest from RFC8341 A.1
*/
int
example_restconf_start(clicon_handle h,
int argc,
char **argv)
{
- char c;
+ int c;
clicon_debug(1, "%s argc:%d", __FUNCTION__, argc);
optind = 1;
diff --git a/example/ietf-interfaces@2014-05-08.yang b/example/ietf-interfaces@2014-05-08.yang
deleted file mode 100644
index 217bd979..00000000
--- a/example/ietf-interfaces@2014-05-08.yang
+++ /dev/null
@@ -1,698 +0,0 @@
- module ietf-interfaces {
-
- namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
- prefix if;
-
- import ietf-yang-types {
- prefix yang;
- }
-
- organization
- "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
-
- contact
- "WG Web:
- WG List:
-
- WG Chair: Thomas Nadeau
-
-
- WG Chair: Juergen Schoenwaelder
-
-
- Editor: Martin Bjorklund
- ";
-
- description
- "This module contains a collection of YANG definitions for
- managing network interfaces.
-
- Copyright (c) 2014 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 7223; see
- the RFC itself for full legal notices.";
-
- revision 2014-05-08 {
- description
- "Initial revision.";
- reference
- "RFC 7223: A YANG Data Model for Interface Management";
- }
-
- /*
- * Typedefs
- */
-
- typedef interface-ref {
- type leafref {
- path "/if:interfaces/if:interface/if:name";
- }
- description
- "This type is used by data models that need to reference
- configured interfaces.";
- }
-
- typedef interface-state-ref {
- type leafref {
- path "/if:interfaces-state/if:interface/if:name";
- }
- description
- "This type is used by data models that need to reference
- the operationally present interfaces.";
- }
-
- /*
- * Identities
- */
-
- identity interface-type {
- description
- "Base identity from which specific interface types are
- derived.";
- }
-
- /*
- * Features
- */
-
- feature arbitrary-names {
- description
- "This feature indicates that the device allows user-controlled
- interfaces to be named arbitrarily.";
- }
-
- feature pre-provisioning {
- description
- "This feature indicates that the device supports
- pre-provisioning of interface configuration, i.e., it is
- possible to configure an interface whose physical interface
- hardware is not present on the device.";
- }
-
- feature if-mib {
- description
- "This feature indicates that the device implements
- the IF-MIB.";
- reference
- "RFC 2863: The Interfaces Group MIB";
- }
-
- /*
- * Configuration data nodes
- */
-
- container interfaces {
- description
- "Interface configuration parameters.";
-
- list interface {
- key "name";
-
- description
- "The list of configured interfaces on the device.
-
- The operational state of an interface is available in the
- /interfaces-state/interface list. If the configuration of a
- system-controlled interface cannot be used by the system
- (e.g., the interface hardware present does not match the
- interface type), then the configuration is not applied to
- the system-controlled interface shown in the
- /interfaces-state/interface list. If the configuration
- of a user-controlled interface cannot be used by the system,
- the configured interface is not instantiated in the
- /interfaces-state/interface list.";
-
- leaf name {
- type string;
- description
- "The name of the interface.
-
- A device MAY restrict the allowed values for this leaf,
- possibly depending on the type of the interface.
-
- For system-controlled interfaces, this leaf is the
- device-specific name of the interface. The 'config false'
- list /interfaces-state/interface contains the currently
- existing interfaces on the device.
-
- If a client tries to create configuration for a
- system-controlled interface that is not present in the
- /interfaces-state/interface list, the server MAY reject
- the request if the implementation does not support
- pre-provisioning of interfaces or if the name refers to
- an interface that can never exist in the system. A
- NETCONF server MUST reply with an rpc-error with the
- error-tag 'invalid-value' in this case.
-
- If the device supports pre-provisioning of interface
- configuration, the 'pre-provisioning' feature is
- advertised.
-
- If the device allows arbitrarily named user-controlled
- interfaces, the 'arbitrary-names' feature is advertised.
-
- When a configured user-controlled interface is created by
- the system, it is instantiated with the same name in the
- /interface-state/interface list.";
- }
-
- leaf description {
- type string;
- description
- "A textual description of the interface.
-
- A server implementation MAY map this leaf to the ifAlias
- MIB object. Such an implementation needs to use some
- mechanism to handle the differences in size and characters
- allowed between this leaf and ifAlias. The definition of
- such a mechanism is outside the scope of this document.
-
- Since ifAlias is defined to be stored in non-volatile
- storage, the MIB implementation MUST map ifAlias to the
- value of 'description' in the persistently stored
- datastore.
-
- Specifically, if the device supports ':startup', when
- ifAlias is read the device MUST return the value of
- 'description' in the 'startup' datastore, and when it is
- written, it MUST be written to the 'running' and 'startup'
- datastores. Note that it is up to the implementation to
-
- decide whether to modify this single leaf in 'startup' or
- perform an implicit copy-config from 'running' to
- 'startup'.
-
- If the device does not support ':startup', ifAlias MUST
- be mapped to the 'description' leaf in the 'running'
- datastore.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifAlias";
- }
-
- leaf type {
- type identityref {
- base interface-type;
- }
- mandatory true;
- description
- "The type of the interface.
-
- When an interface entry is created, a server MAY
- initialize the type leaf with a valid value, e.g., if it
- is possible to derive the type from the name of the
- interface.
-
- If a client tries to set the type of an interface to a
- value that can never be used by the system, e.g., if the
- type is not supported or if the type does not match the
- name of the interface, the server MUST reject the request.
- A NETCONF server MUST reply with an rpc-error with the
- error-tag 'invalid-value' in this case.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifType";
- }
-
- leaf enabled {
- type boolean;
- default "true";
- description
- "This leaf contains the configured, desired state of the
- interface.
-
- Systems that implement the IF-MIB use the value of this
- leaf in the 'running' datastore to set
- IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
- has been initialized, as described in RFC 2863.
-
- Changes in this leaf in the 'running' datastore are
- reflected in ifAdminStatus, but if ifAdminStatus is
- changed over SNMP, this leaf is not affected.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
- }
-
- leaf link-up-down-trap-enable {
- if-feature if-mib;
- type enumeration {
- enum enabled {
- value 1;
- }
- enum disabled {
- value 2;
- }
- }
- description
- "Controls whether linkUp/linkDown SNMP notifications
- should be generated for this interface.
-
- If this node is not configured, the value 'enabled' is
- operationally used by the server for interfaces that do
- not operate on top of any other interface (i.e., there are
- no 'lower-layer-if' entries), and 'disabled' otherwise.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifLinkUpDownTrapEnable";
- }
- }
- }
-
- /*
- * Operational state data nodes
- */
-
- container interfaces-state {
- config false;
- description
- "Data nodes for the operational state of interfaces.";
-
- list interface {
- key "name";
-
- description
- "The list of interfaces on the device.
-
- System-controlled interfaces created by the system are
- always present in this list, whether they are configured or
- not.";
-
- leaf name {
- type string;
- description
- "The name of the interface.
-
- A server implementation MAY map this leaf to the ifName
- MIB object. Such an implementation needs to use some
- mechanism to handle the differences in size and characters
- allowed between this leaf and ifName. The definition of
- such a mechanism is outside the scope of this document.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifName";
- }
-
- leaf type {
- type identityref {
- base interface-type;
- }
- mandatory true;
- description
- "The type of the interface.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifType";
- }
-
- leaf admin-status {
- if-feature if-mib;
- type enumeration {
- enum up {
- value 1;
- description
- "Ready to pass packets.";
- }
- enum down {
- value 2;
- description
- "Not ready to pass packets and not in some test mode.";
- }
-
- enum testing {
- value 3;
- description
- "In some test mode.";
- }
- }
- mandatory true;
- description
- "The desired state of the interface.
-
- This leaf has the same read semantics as ifAdminStatus.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
- }
-
- leaf oper-status {
- type enumeration {
- enum up {
- value 1;
- description
- "Ready to pass packets.";
- }
- enum down {
- value 2;
- description
- "The interface does not pass any packets.";
- }
- enum testing {
- value 3;
- description
- "In some test mode. No operational packets can
- be passed.";
- }
- enum unknown {
- value 4;
- description
- "Status cannot be determined for some reason.";
- }
- enum dormant {
- value 5;
- description
- "Waiting for some external event.";
- }
- enum not-present {
- value 6;
- description
- "Some component (typically hardware) is missing.";
- }
-
- enum lower-layer-down {
- value 7;
- description
- "Down due to state of lower-layer interface(s).";
- }
- }
- mandatory true;
- description
- "The current operational state of the interface.
-
- This leaf has the same semantics as ifOperStatus.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifOperStatus";
- }
-
- leaf last-change {
- type yang:date-and-time;
- description
- "The time the interface entered its current operational
- state. If the current state was entered prior to the
- last re-initialization of the local network management
- subsystem, then this node is not present.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifLastChange";
- }
-
- leaf if-index {
- if-feature if-mib;
- type int32 {
- range "1..2147483647";
- }
- mandatory true;
- description
- "The ifIndex value for the ifEntry represented by this
- interface.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifIndex";
- }
-
- leaf phys-address {
- type yang:phys-address;
- description
- "The interface's address at its protocol sub-layer. For
- example, for an 802.x interface, this object normally
- contains a Media Access Control (MAC) address. The
- interface's media-specific modules must define the bit
- and byte ordering and the format of the value of this
- object. For interfaces that do not have such an address
- (e.g., a serial line), this node is not present.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
- }
-
- leaf-list higher-layer-if {
- type interface-state-ref;
- description
- "A list of references to interfaces layered on top of this
- interface.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifStackTable";
- }
-
- leaf-list lower-layer-if {
- type interface-state-ref;
- description
- "A list of references to interfaces layered underneath this
- interface.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifStackTable";
- }
-
- leaf speed {
- type yang:gauge64;
- units "bits/second";
- description
- "An estimate of the interface's current bandwidth in bits
- per second. For interfaces that do not vary in
- bandwidth or for those where no accurate estimation can
- be made, this node should contain the nominal bandwidth.
- For interfaces that have no concept of bandwidth, this
- node is not present.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifSpeed, ifHighSpeed";
- }
-
- container statistics {
- description
- "A collection of interface-related statistics objects.";
-
- leaf discontinuity-time {
- type yang:date-and-time;
- mandatory true;
- description
- "The time on the most recent occasion at which any one or
- more of this interface's counters suffered a
- discontinuity. If no such discontinuities have occurred
- since the last re-initialization of the local management
- subsystem, then this node contains the time the local
- management subsystem re-initialized itself.";
- }
-
- leaf in-octets {
- type yang:counter64;
- description
- "The total number of octets received on the interface,
- including framing characters.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
- }
-
- leaf in-unicast-pkts {
- type yang:counter64;
- description
- "The number of packets, delivered by this sub-layer to a
- higher (sub-)layer, that were not addressed to a
- multicast or broadcast address at this sub-layer.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
- }
-
- leaf in-broadcast-pkts {
- type yang:counter64;
- description
- "The number of packets, delivered by this sub-layer to a
- higher (sub-)layer, that were addressed to a broadcast
- address at this sub-layer.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifHCInBroadcastPkts";
- }
-
- leaf in-multicast-pkts {
- type yang:counter64;
- description
- "The number of packets, delivered by this sub-layer to a
- higher (sub-)layer, that were addressed to a multicast
- address at this sub-layer. For a MAC-layer protocol,
- this includes both Group and Functional addresses.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifHCInMulticastPkts";
- }
-
- leaf in-discards {
- type yang:counter32;
- description
- "The number of inbound packets that were chosen to be
- discarded even though no errors had been detected to
- prevent their being deliverable to a higher-layer
- protocol. One possible reason for discarding such a
- packet could be to free up buffer space.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
-
- reference
- "RFC 2863: The Interfaces Group MIB - ifInDiscards";
- }
-
- leaf in-errors {
- type yang:counter32;
- description
- "For packet-oriented interfaces, the number of inbound
- packets that contained errors preventing them from being
- deliverable to a higher-layer protocol. For character-
- oriented or fixed-length interfaces, the number of
- inbound transmission units that contained errors
- preventing them from being deliverable to a higher-layer
- protocol.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifInErrors";
- }
-
- leaf in-unknown-protos {
- type yang:counter32;
- description
- "For packet-oriented interfaces, the number of packets
- received via the interface that were discarded because
- of an unknown or unsupported protocol. For
- character-oriented or fixed-length interfaces that
- support protocol multiplexing, the number of
- transmission units received via the interface that were
- discarded because of an unknown or unsupported protocol.
- For any interface that does not support protocol
- multiplexing, this counter is not present.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
- }
- leaf out-octets {
- type yang:counter64;
- description
- "The total number of octets transmitted out of the
- interface, including framing characters.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
- }
-
- leaf out-unicast-pkts {
- type yang:counter64;
- description
- "The total number of packets that higher-level protocols
- requested be transmitted, and that were not addressed
- to a multicast or broadcast address at this sub-layer,
- including those that were discarded or not sent.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
- }
-
- leaf out-broadcast-pkts {
- type yang:counter64;
- description
- "The total number of packets that higher-level protocols
- requested be transmitted, and that were addressed to a
- broadcast address at this sub-layer, including those
- that were discarded or not sent.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifHCOutBroadcastPkts";
- }
-
- leaf out-multicast-pkts {
- type yang:counter64;
- description
- "The total number of packets that higher-level protocols
- requested be transmitted, and that were addressed to a
- multicast address at this sub-layer, including those
- that were discarded or not sent. For a MAC-layer
- protocol, this includes both Group and Functional
- addresses.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB -
- ifHCOutMulticastPkts";
- }
-
- leaf out-discards {
- type yang:counter32;
- description
- "The number of outbound packets that were chosen to be
- discarded even though no errors had been detected to
- prevent their being transmitted. One possible reason
- for discarding such a packet could be to free up buffer
- space.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
- }
-
- leaf out-errors {
- type yang:counter32;
- description
- "For packet-oriented interfaces, the number of outbound
- packets that could not be transmitted because of errors.
- For character-oriented or fixed-length interfaces, the
- number of outbound transmission units that could not be
- transmitted because of errors.
-
- Discontinuities in the value of this counter can occur
- at re-initialization of the management system, and at
- other times as indicated by the value of
- 'discontinuity-time'.";
- reference
- "RFC 2863: The Interfaces Group MIB - ifOutErrors";
- }
- }
- }
- }
- }
-
diff --git a/example/ietf-ipsec@2016-03-09.yang b/example/ietf-ipsec@2016-03-09.yang
deleted file mode 100644
index b8beed47..00000000
--- a/example/ietf-ipsec@2016-03-09.yang
+++ /dev/null
@@ -1,3672 +0,0 @@
- module ietf-ipsec {
- namespace "urn:ietf:params:xml:ns:yang:ietf-ipsec";
- prefix "eipsec";
-
- import ietf-inet-types {
- prefix inet;
- }
-
- import ietf-yang-types {
- prefix yang;
- }
-
- organization "Ericsson AB.
- Huawei Technologies India Pvt Ltd.";
-
- contact "Web: ";
-
- description
- "This YANG module defines the configuration and operational
- state data for Internet Protocol Security (IPSec) on
- IETF draft.
- Copyright (c) 2016 Ericsson AB.
- All rights reserved.";
-
- revision 2016-03-09 {
- description
- "Third revision.
- Fix YANG compiling error because it used internal
- data model econtext and should be removed in the
- draft.
- Fix warnings.
- Run validation on
- http://www.netconfcentral.org/yangdumpresults";
- reference
- "Update since second revision.";
- }
- revision 2015-09-13 {
- description
- "Second revision.";
- reference
- "updates since initial revision.
- combining:
- draft-tran-ipecme-yang-ipsec-00.
- draft-wang-ipsecme-ike-yang-00.
- draft-wang-ipsecme-ipsec-yang-00.";
- }
- revision 2015-05-14 {
- description
- "Initial revision.";
- reference
- "May 14, 2015 draft-tran-ipecme-yang-ipsec-00.
- May 22, 2015 draft-wang-ipsecme-ike-yang-00.
- June 15, 2015 draft-wang-ipsecme-ipsec-yang-00.";
- }
- /*--------------------*/
- /* Feature */
- /*--------------------*/
-
- feature ikev1 {
- description
- "Feature IKEv1";
- }
-
- feature ike-proposal-state {
- description
- "IKEv2 Proposal Operational State";
- }
-
- feature ike-policy-state {
- description
- "IKEv1 Policy Operational State";
- }
-
- feature ikev1-state {
- description
- "IKEv1 Operational State";
- }
-
- feature ike-reauth-failure {
- description
- "IKEv1 Reauthorization Failure";
- }
-
- feature ike-rekey-failure {
- description
- "IKEv1 Rekey Failure";
- }
-
-
- feature ikev2 {
- description
- "Feature IKEv2";
-
- }
-
- feature ikev2-global {
- description
- "Feature IKEv2 Global Parameters";
-
- }
-
- feature ikev2-peer {
- description
- "Feature IKEv2 Peer";
-
- }
-
- feature ikev2-proposal {
- description
- "Feature IKEv2 Proposal";
-
- }
-
- feature ikev2-policy {
- description
- "Feature IKEv2 Policy";
-
- }
-
- feature ikev2-proposal-state {
- description
- "IKEv2 Proposal Operational State";
- }
-
- feature ikev2-state {
- description
- "IKEv2 Operational State";
- }
-
- feature ipsec {
- description
- "Feature IPsec";
-
- }
-
- feature ipsec-acl {
- description
- "Feature IPsec ACL";
- }
- feature ipsec-sad {
- description
- "Feature IPsec SAD";
-
- }
-
- feature ipsec-proposal {
- description
- "Feature IPsec Proposal";
-
- }
-
- feature ipsec-spd {
- description
- "Feature IPsec SPD";
-
- }
-
- feature ipsec-policy-state {
- description
- "IPsec Policy Operational State";
- }
-
- feature ipsec-proposal-state {
- description
- "IPsec Proposal Operational State";
- }
-
- feature ipsec-alarms-state {
- description
- "IPsec Alarm State Operational State";
- }
-
- feature ipsec-sa-ah-state {
- description
- "IPsec SA AH Operational State";
- }
-
- feature ipsec-sa-state {
- description
- "IPsec SA Operational State";
- }
-
- feature ipsec-tunnel {
- description
- "IPsec Tunnel";
- }
-
- feature ipsec-local-address-range {
- description
- "IPsec Local Address Range";
- }
-
- feature ipsec-remote-address-range {
- description
- "IPsec Remote Address Range";
- }
-
- feature ipsec-next-protocol-range {
- description
- "IPsec Next Protocol Range";
- }
-
- feature ipsec-local-port-range {
- description
- "IPsec Local Port Range";
- }
-
- feature ipsec-remote-port-range {
- description
- "IPsec Remote Port Range";
- }
-
- feature ipsec-ah-authentication {
- description
- "IPsec AH Authentication";
- }
-
- feature ipsec-esp-integrity {
- description
- "IPsec ESP Integrity";
- }
-
- feature ipsec-esp-encrypt {
- description
- "IPsec ESP encryption";
- }
-
- feature ipsec-stat {
- description
- "IPsec Stats";
- }
-
- feature ipsec-state {
- description
- "IPsec Operational State";
- }
-
- feature ipsec-rekey-failure {
- description
- "IPsec Rekey Failure";
- }
-
- feature ipsec-redundancy {
- description
- "IPsec Redundancy State";
- }
-
- feature sad {
- description
- "Security Association (SA) Database";
- }
-
- feature spd {
- description
- "Security Policy Database";
- }
-
- feature ipsec-global-stats {
- description
- "IPsec Global Stats";
- }
-
- feature clear-ipsec-group {
- description
- "Clear IPsec group";
- }
-
- feature clear-ike-group {
- description
- "Clear IKE group";
- }
-
- feature clear-ikev2-group {
- description
- "Clear IKEv2 group";
- }
-
- feature reset-ipv4 {
- description
- "Reset IPv4";
- }
-
- feature reset-ipv6 {
- description
- "Reset IPv6";
- }
-
- feature reset-global {
- description
- "Reset Global";
- }
-
- feature peer-authentication-failure {
- description
- "Peer Authentication Failure";
- }
- /*--------------------*/
- /* Typedefs */
- /*--------------------*/
-
- typedef authentication-method-t {
- type enumeration {
- enum psk {
- value 0;
- description
- "Pre-Sharing Keys.";
- }
- enum certificate {
- value 1;
- description
- "Certificate.";
- }
- }
- description
- "Available authentication methods.";
- }
-
- /* IKEv2 Exchange Types (ET) */
- typedef ikev2-exchange-type-t {
- type enumeration {
- enum ikev2-et-ike-sa-init {
- value 34;
- description
- "ikev2-et-ike-sa-init - RFC 7296.";
- }
- enum ikev2-et-ike-auth {
- value 35;
- description
- "ikev2-et-ike-auth - RFC 7296.";
- }
- enum ikev2-et-create-child-sa {
- value 36;
- description
- "ikev2-et-create-child-sa - RFC 7296.";
- }
- enum ikev2-et-informational {
- value 37;
- description
- "ikev2-et-informational - RFC 7296.";
- }
- enum ikev2-et-ike-session-resume {
- value 38;
- description
- "ikev2-et-ike-session-resume - RFC 7296.";
- }
- enum ikev2-et-gsa-auth {
- value 39;
- description
- "ikev2-et-gsa-auth - RFC 7296.";
- }
- enum ikev2-et-gsa-registration {
- value 40;
- description
- "ikev2-et-gsa-registration - RFC 7296.";
- }
- enum ikev2-et-gsa-rekey {
- value 41;
- description
- "ikev2-et-gsa-rekey - RFC 7296.";
- }
- }
- description
- "IKEv2 Exchange Types (ET).";
- }
-
- /* Transform Type Values (TTV), RFC 7296 */
- typedef transform-type-value-t {
- type enumeration {
- enum ttv-reserved-0 {
- value 0;
- description
- "ttv-reserved-0 - Transform Type Value Reserved "+
- "(RFC 7296).";
- }
- enum ttv-encr {
- value 1;
- description
- "ttv-encr - Transform Type Value 1,
- Encryption Algorithm "+
- "(ENCR) used in IKE and ESP.";
- }
- enum ttv-prf {
- value 2;
- description
- "ttv-prf - Transform Type Value 2, "+
- "Pseudo-Random Function(PRF) used in IKE.";
- }
- enum ttv-integ {
- value 3;
- description
- "ttv-integ - Transform Type Value 3, Integrity Algorithm"+
- " (INTEG) used in IKE, AH, optional ESP.";
- }
- enum ttv-dh {
- value 4;
- description
- "ttv-dh - Transform Type Value 4, Diffie-Hellman (DH) "+
- "used in IKE, optional AH and ESP.";
- }
- enum ttv-esn {
- value 5;
- description
- "ttv-esn - Transform Type Value 5, Extended Sequence "+
- "Numbers (ESN) used in AH and ESP.";
- }
- }
- description
- "Transform Type Values (RFC 7296).";
- }
-
- /* IKEv2 Transform Attribute Types (TAT) */
- typedef ikev2-transform-attribute-type-t {
- type enumeration {
- enum ikev2-tat-reserved-0 {
- value 0;
- description
- "ikev2-tat-reserved-0 - IKEv2 Transform Attribute "+
- "Type Reserved-0 (RFC 7296).";
- }
- enum ikev2-tat-reserved-1 {
- value 1;
- description
- "ikev2-tat-reserved-1 - IKEv2 Transform Attribute "+
- "Type Reserved-1 (RFC 7296).";
- }
- enum ikev2-tat-reserved-13 {
- value 13;
- description
- "ikev2-tat-reserved-13 - IKEv2 Transform Attribute "+
- "Type Reserved-13 (RFC 7296).";
- }
- enum ikev2-tat-key-length {
- value 41;
- description
- "ikev2-tat-key-length - IKEv2 Transform Attribute "+
- "Type KEY LENGTH (in bits) (RFC 7296).";
- }
- }
- description
- "IKEv2 Transform Attribute Types (TAT) (RFC 7296).";
- }
-
- /* Transform Type 1 (Encryption Algorithm Transform IDs) */
- typedef ike-encryption-algorithm-t {
- type enumeration {
- enum encr-reserved-0 {
- value 0;
- description
- "encr-reserved-0 --> RFC_5996.";
- }
- enum encr-des-iv4 {
- value 1;
- description
- "encr-des-iv4 --> RFC_5996.";
- }
- enum encr-des {
- value 2;
- description
- "encr-des --> RFC_5996.";
- }
- enum encr-3des {
- value 3;
- description
- "encr-3des --> RFC_5996.";
- }
- enum encr-rc5 {
- value 4;
- description
- "encr-rc5 --> RFC_5996.";
- }
- enum encr-idea {
- value 5;
- description
- "encr-idea --> RFC_5996.";
- }
- enum encr-cast {
- value 6;
- description
- "encr-cast --> RFC_5996.";
- }
- enum encr-blowfish {
- value 7;
- description
- "encr-blowfish --> RFC_5996.";
- }
- enum encr-3idea {
- value 8;
- description
- "encr-3idea --> RFC_5996.";
- }
- enum encr-des-iv32 {
- value 9;
- description
- "encr-des-iv32 --> RFC_5996.";
- }
- enum encr-reserved-10 {
- value 10;
- description
- "encr-reserved-10 --> RFC_5996.";
- }
- enum encr-null {
- value 11;
- description
- "encr-null --> RFC_5996.";
- }
- enum encr-aes-cbc {
- value 12;
- description
- "encr-aes-cbc --> RFC_5996.";
- }
- enum encr-aes-ctr {
- value 13;
- description
- "encr-aes-ctr --> RFC_5996.";
- }
- enum encr-aes-ccm-8 {
- value 14;
- description
- "encr-aes-ccm-8 --> RFC_5996.";
- }
- enum encr-aes-ccm-12 {
- value 15;
- description
- "encr-aes-ccm-12 --> RFC_5996.";
- }
- enum encr-aes-ccm-16 {
- value 16;
- description
- "encr-aes-ccm-16 --> RFC_5996.";
- }
- enum encr-reserved-17 {
- value 17;
- description
- "encr-reserved-17 --> RFC_5996.";
- }
- enum encr-aes-gcm-8-icv {
- value 18;
- description
- "encr-aes-gcm-8-icv --> RFC_5996.";
- }
- enum encr-aes-gcm-12-icv {
- value 19;
- description
- "encr-aes-gcm-12-icv --> RFC_5996.";
- }
- enum encr-aes-gcm-16-icv {
- value 20;
- description
- "encr-aes-gcm-16-icv--> RFC_5996.";
- }
- enum encr-null-auth-aes-gmac {
- value 21;
- description
- "encr-null-auth-aes-gmac --> RFC_5996.";
- }
- enum encr-ieee-p1619-xts-aes {
- value 22;
- description
- "encr-ieee-p1619-xts-aes --> Reserved for "+
- "IEEE P1619 XTS-AES.";
- }
- enum encr-camellia-cbc {
- value 23;
- description
- "encr-camellia-cbc --> RFC_5996.";
- }
- enum encr-camellia-ctr {
- value 24;
- description
- "encr-camellia-ctr --> RFC_5996.";
- }
- enum encr-camellia-ccm-8-icv {
- value 25;
- description
- "encr-camellia-ccm-8-icv --> RFC_5996.";
- }
- enum encr-camellia-ccm-12-icv {
- value 26;
- description
- "encr-camellia-ccm-12-icv --> RFC_5996.";
- }
- enum encr-camellia-ccm-16-icv {
- value 27;
- description
- "encr-camellia-ccm-16-icv --> RFC_5996.";
- }
- enum encr-aes-cbc-128 {
- value 1024;
- description
- "encr-aes-cbc-128 --> RFC_5996.";
- }
- enum encr-aes-cbc-192 {
- value 1025;
- description
- "encr-aes-cbc-192 --> RFC_5996.";
- }
- enum encr-aes-cbc-256 {
- value 1026;
- description
- "encr-aes-cbc-256 --> RFC_5996.";
- }
- enum encr-blowfish-128 {
- value 1027;
- description
- "encr-blowfish-128 --> RFC_5996.";
- }
- enum encr-blowfish-192 {
- value 1028;
- description
- "encr-blowfish-192 --> RFC_5996.";
- }
- enum encr-blowfish-256 {
- value 1029;
- description
- "encr-blowfish-256 --> RFC_5996.";
- }
- enum encr-blowfish-448 {
- value 1030;
- description
- "encr-blowfish-448 --> RFC_5996.";
- }
- enum encr-camellia-128 {
- value 1031;
- description
- "encr-camellia-128 --> RFC_5996.";
- }
- enum encr-camellia-192 {
- value 1032;
- description
- "encr-camellia-192 --> RFC_5996.";
- }
- enum encr-camellia-256 {
- value 1033;
- description
- "encr-camellia-256 --> RFC_5996.";
- }
- }
- description
- "Transform Type 1 - Internet Key Exchange (IKE) "+
- "encryption algorithms.";
- }
-
- /* Transform Type 2 (Pseudo-Random Function PRF) */
- typedef pseudo-random-function-t {
- type enumeration {
- enum prf-reserved-0 {
- value 0;
- description
- "prf-reserved-0 --> RFC_2104.";
- }
- enum prf-hmac-md5 {
- value 1;
- description
- "prf-hmac-md5 --> RFC_2104.";
- }
- enum prf-hmac-sha1 {
- value 2;
- description
- "prf-hmac-sha1 --> RFC2104.";
- }
- enum prf-hmac-tiger {
- value 3;
- description
- "prf-hmac-tiger --> RFC2104.";
- }
- enum prf-aes128-xcbc {
- value 4;
- description
- "prf-aes128-xcbc --> RFC_4434.";
- }
- enum prf-hmac-sha2-256 {
- value 5;
- description
- "prf-hmac-sha2-256 --> RFC_4434.";
- }
- enum prf-hmac-sha2-384 {
- value 6;
- description
- "prf-hmac-sha2-384 --> RFC_4434.";
- }
- enum prf-hmac-sha2-512 {
- value 7;
- description
- "prf-hmac-sha2-512 --> RFC_4434.";
- }
- enum prf-aes128-cmac {
- value 8;
- description
- "prf-aes128-cmac --> RFC_4615.";
- }
- }
- description
- "Available Pseudo-Random Functions (PRF).";
- }
-
- /* Transform Type 3 (Integrity Algorithm) */
- typedef ike-integrity-algorithm-t {
- type enumeration {
- enum auth-none {
- value 0;
- description
- "auth-none --> RFC_5996.";
- }
- enum auth-hmac-md5-96 {
- value 1;
- description
- "auth-hmac-md5-96 --> RFC_5996.";
- }
- enum auth-hmac-sha1-96 {
- value 2;
- description
- "auth-hmac-sha1-96 --> RFC_5996.";
- }
- enum auth-des-mac {
- value 3;
- description
- "auth-des-mac --> RFC_5996.";
- }
- enum auth-kpdk-md5 {
- value 4;
- description
- "auth-kpdk-md5 --> RFC_5996.";
- }
- enum auth-aes-xcbc-96 {
- value 5;
- description
- "auth-aes-xcbc-96 --> RFC_5996.";
- }
- enum auth-hmac-md5-128 {
- value 6;
- description
- "auth-hmac-md5-128 --> RFC_5996.";
- }
- enum auth-hmac-sha1-160 {
- value 7;
- description
- "auth-hmac-sha1-160 --> RFC_5996.";
- }
- enum auth-aes-cmac-96 {
- value 8;
- description
- "auth-aes-cmac-96 --> RFC_5996.";
- }
- enum auth-aes-128-gmac {
- value 9;
- description
- "auth-aes-128-gmac --> RFC_5996.";
- }
- enum auth-aes-192-gmac {
- value 10;
- description
- "auth-aes-192-gmac --> RFC_5996.";
- }
- enum auth-aes-256-gmac {
- value 11;
- description
- "auth-aes-256-gmac --> RFC_5996.";
- }
- enum auth-hmac-sha2-256-128 {
- value 12;
- description
- "auth-hmac-sha2-256-128 --> RFC_5996.";
- }
- enum auth-hmac-sha2-384-192 {
- value 13;
- description
- "auth-hmac-sha2-384-192 --> RFC_5996.";
- }
- enum auth-hmac-sha2-512-256 {
- value 14;
- description
- "auth-hmac-sha2-512-256 --> RFC_5996.";
- }
- enum auth-hmac-sha2-256-96 {
- value 1024;
- description
- "auth-hmac-sha2-256-96.";
- }
- }
- description
- "Transform Type 3 - Internet Key Exchange (IKE) "+
- "Integrity Algorithms.";
- }
-
- /* Transform Type 4 (Diffie-Hellman Group) */
- typedef diffie-hellman-group-t {
- type enumeration {
- enum group-none {
- value 0;
- description
- "group-none --> RFC_5996.";
- }
- enum modp-768-group-1 {
- value 1;
- description
- "modp-768-group-1 --> RFC_5996.";
- }
- enum modp-1024-group-2 {
- value 2;
- description
- "modp-1024-group-2 --> RFC_5996.";
- }
- enum modp-1536-group-5 {
- value 5;
- description
- "modp-1536-group-5 --> RFC_3526.";
- }
- enum modp-2048-group-14 {
- value 14;
- description
- "modp-2048-group-14 --> RFC_3526.";
- }
- enum modp-3072-group-15 {
- value 15;
- description
- "modp-3072-group-15 --> RFC_3526.";
- }
- enum modp-4096-group-16 {
- value 16;
- description
- "modp-4096-group-16 --> RFC_3526.";
- }
- enum modp-6144-group-17 {
- value 17;
- description
- "modp-6144-group-17 --> RFC_3526.";
- }
- enum modp-8192-group-18 {
- value 18;
- description
- "modp-8192-group-18 --> RFC_3526.";
- }
- enum recp-256-group-19 {
- value 19;
- description
- "recp-256-group-19 --> RFC_6989. 256-bit"+
- " Random ECP Group.";
- }
- enum recp-384-group-20 {
- value 20;
- description
- "recp-384-group-20 --> RFC_6989. 384-bit"+
- " Random ECP Group.";
- }
- enum recp-521-group-21 {
- value 21;
- description
- "recp-521-group-21 --> RFC_6989. 521-bit"+
- " Random ECP Group.";
- }
- enum modp-1024-160-pos-group-22 {
- value 22;
- description
- "modp-1024-160-pos-group-22 --> RFC_6989."+
- " 1024-bit MODP Group with"+
- " 160-bit Prime Order Subgroup (POS).";
- }
- enum modp-2048-224-pos-group-23 {
- value 23;
- description
- "modp-2048-224-pos-group-23 --> RFC_6989."+
- " 2048-bit MODP Group with"+
- " 224-bit Prime Order Subgroup (POS).";
- }
- enum modp-2048-256-pos-group-24 {
- value 24;
- description
- "modp-2048-256-pos-group-24 --> RFC_6989."+
- " 2048-bit MODP Group with"+
- " 256-bit Prime Order Subgroup (POS).";
- }
- enum recp-192-group-25 {
- value 25;
- description
- "recp-192-group-25 --> RFC_6989."+
- " 192-bit Random ECP Group.";
- }
- enum recp-224-group-26 {
- value 26;
- description
- "recp-224-group-26 --> RFC_6989."+
- " 224-bit Random ECP Group.";
- }
- }
- description
- "Diffie-Hellman Groups (RFC 5996).";
- }
-
-
- /* Transform Type 5 (Extended Sequence Numbers
- Transform ESN IDs) */
- typedef extended-sequence-number-t {
- type enumeration {
- enum esn-none {
- value 0;
- description
- "esn-none - Extended Sequence Number None --> RFC_7296.";
- }
- enum esn-1 {
- value 1;
- description
- "esn-1 - Extended Sequence Number --> RFC_7296.";
- }
- }
- description
- "Extended Sequence Number (RFC 7296).";
- }
-
-
- typedef connection-type-t {
- type enumeration {
- enum initiator-only {
- value 0;
- description
- "initiator-only: ME will act as initiator for"+
- " bringing up IKEv2"+
- " session with its IKE peer.";
- }
- enum responder-only {
- value 1;
- description
- "responder-only: ME will act as responder for"+
- " bringing up IKEv2"+
- " session with its IKE peer.";
- }
- enum both {
- value 2;
- description
- "both: ME can act as initiator or responder.";
- }
- }
- description
- "Connection type for IKE session.";
- }
-
- typedef transport-protocol-name-t {
- type enumeration {
- enum tcp {
- value 1;
- description
- "Transmission Control Protocol (TCP) Transport Protocol.";
- }
- enum udp {
- value 2;
- description
- "User Datagram Protocol (UDP) Transport Protocol";
- }
- enum sctp {
- value 3;
- description
- "Stream Control Transmission Protocol (SCTP) Transport "+
- "Protocol";
- }
- enum icmp {
- value 4;
- description
- "Internet Control Message Protocol (ICMP) Transport "+
- "Protocol";
- }
- }
- description
- "Enumeration of well known transport protocols.";
- }
-
- typedef preshared-key-t {
- type string;
- description
- "Derived string used as Pre-Shared Key.";
- }
-
- typedef pad-type-t {
- type enumeration {
- enum dns-name {
- value 1;
- description
- "DNS name (specific or partial)";
- }
- enum distinguished-name {
- value 2;
- description
- "Distinguished Name (complete or sub-tree constrained)";
- }
- enum rfc-822 {
- value 3;
- description
- "RFC 822 email address (complete or partially qualified)";
- }
- enum ipv4-range {
- value 4;
- description
- "IPv4 Address Range";
- }
- enum ipv6-range {
- value 5;
- description
- "IPv6 Address Range";
- }
- enum key-id {
- value 6;
- description
- "Key ID (exact match only)";
- }
- }
- description
- "PAD Type";
- }
-
-
- /*-------------------------------------------------- */
- /* draft-wang-ipsecme-ipsec-yang-00: ietf-ipsec-type */
- /*-------------------------------------------------- */
- typedef ipsec-mode {
- type enumeration {
- enum "transport" {
- description
- "Transport mode";
- }
- enum "tunnel" {
- description
- "Tunnel mode";
- }
- }
- description
- "type define of ipsec mode";
- }
-
- typedef ipsec-protocol {
- type enumeration {
- enum "ah" {
- description
- "AH Protocol";
- }
- enum "esp" {
- description
- "ESP Protocol";
- }
- }
- description
- "type define of ipsec security protocol";
- }
-
- typedef ipsec-spi {
- type uint32 {
- range "1..max";
- }
- description
- "SPI";
- }
-
- typedef ipsec-spd-name {
- type enumeration {
- enum id_rfc_822_addr {
- description
- "Fully qualified user name string.";
- }
- enum id_fqdn {
- description
- "Fully qualified DNS name.";
- }
- enum id_der_asn1_dn {
- description
- "X.500 distinguished name.";
- }
- enum id_key {
- description
- "IKEv2 Key ID.";
- }
- }
- description
- "IPsec SPD name type";
- }
-
- typedef ipsec-traffic-direction {
- type enumeration {
- enum inbound {
- description
- "Inbound traffic";
- }
- enum outbound {
- description
- "Outbound traffic";
- }
- }
- description
- "IPsec traffic direction";
- }
-
- typedef ipsec-spd-operation {
- type enumeration {
- enum protect {
- description
- "PROTECT the traffic with IPsec";
- }
- enum bypass {
- description
- "BYPASS the traffic";
- }
- enum discard {
- description
- "DISCARD the traffic";
- }
- }
- description
- "The operation when traffic matches IPsec security policy";
- }
-
-
- /*---------------------------------------------------- */
- /* draft-wang-ipsecme-ipsec-yang-00: ietf-ipsec-crypto */
- /*---------------------------------------------------- */
- typedef ipsec-authentication-algorithm {
- type enumeration {
- enum "null" {
- value 0;
- description
- "null";
- }
- enum "md5" {
- value 1;
- description
- "MD5 authentication algorithm";
- }
- enum "sha1" {
- value 2;
- description
- "SHA1 authentication algorithm";
- }
- enum "sha2-256" {
- value 3;
- description
- "SHA2-256 authentication algorithm";
- }
- enum "sha2-384" {
- value 4;
- description
- "SHA2-384 authentication algorithm";
- }
- enum "sha2-512" {
- value 5;
- description
- "SHA2-512 authentication algorithm";
- }
- }
- description
- "typedef for ipsec authentication algorithm";
- }
-
- typedef ipsec-encryption-algorithm {
- type enumeration {
- enum "null" {
- description
- "null";
- }
- enum "des" {
- description
- "DES encryption algorithm";
- }
- enum "3des" {
- description
- "3DES encryption algorithm";
- }
- enum "aes-128" {
- description
- "AES-128 encryption algorithm";
- }
- enum "aes-192" {
- description
- "AES-192 encryption algorithm";
- }
- enum "aes-256" {
- description
- "AES-256 encryption algorithm";
- }
- }
- description
- "typedef for ipsec encryption algorithm";
- }
-
- /*-------------------------------------------------- */
- /* draft-wang-ipsecme-ike-yang-00: ietf-ipsec-type */
- /*-------------------------------------------------- */
- typedef ike-integrity-algorithm {
- type enumeration {
- enum "hmac-md5-96" {
- description
- "HMAC-MD5-96 Integrity Algorithm";
- }
- enum "hmac-sha1-96" {
- description
- "HMAC-SHA1-96 Integrity Algorithm";
- }
- enum "hmac-sha2-256" {
- description
- "HMAC-SHA2-256 Integrity Algorithm";
- }
- enum "hmac-sha2-384" {
- description
- "HMAC-SHA2-384 Integrity Algorithm";
- }
- enum "hmac-sha2-512" {
- description
- "HMAC-SHA2-512 Integrity Algorithm";
- }
- }
- description
- "typedef for ike integrity algorithm.";
- }
-
- typedef ike-encryption-algorithm {
- type enumeration {
- enum "des-cbc" {
- description
- "DES-CBC Encryption algorithm";
- }
- enum "3des-cbc" {
- description
- "3DES-CBC Encryption algorithm";
- }
- enum "aes-cbc-128" {
- description
- "AES-CBC-128 Encryption algorithm";
- }
- enum "aes-cbc-192" {
- description
- "AES-CBC-192 Encryption algorithm";
- }
- enum "aes-cbc-256" {
- description
- "AES-CBC-256 Encryption algorithm";
- }
- }
- description
- "typedef for ike encryption algorithm.";
- }
-
- typedef ike-prf-algorithm {
- type enumeration {
- enum "hmac-md5-96" {
- description
- "HMAC-MD5-96 PRF Algorithm";
- }
- enum "hmac-sha1-96" {
- description
- "HMAC-SHA1-96 PRF Algorithm";
- }
- enum "hmac-sha2-256" {
- description
- "HMAC-SHA2-256 PRF Algorithm";
- }
- enum "hmac-sha2-384" {
- description
- "HMAC-SHA2-384 PRF Algorithm";
- }
- enum "hmac-sha2-512" {
- description
- "HMAC-SHA2-512 PRF Algorithm";
- }
- }
- description
- "typedef for ike prf algorithm.";
- }
-
- typedef ike-dh-group {
- type enumeration {
- enum "dh-group-none" {
- description
- "None Diffie-Hellman group";
- }
- enum "dh-group-1" {
- description
- "768 bits Diffie-Hellman group";
- }
- enum "dh-group-2" {
- description
- "1024 bits Diffie-Hellman group";
- }
- enum "dh-group-5" {
- description
- "1536 bits Diffie-Hellman group";
- }
- enum "dh-group-14" {
- description
- "2048 bits Diffie-Hellman group";
- }
- }
- description
- "typedef for ike dh group";
- }
-
-
- typedef ike-peer-name-ref {
- type leafref {
- path "/ikev2/ike-peer/ike-peer-entries/peer-name";
- }
- description "reference to ike peer name";
- }
-
- typedef ike-proposal-number-ref {
- type leafref {
- path "/ikev2/proposal/name";
- }
- description "reference to ike proposal name";
- }
-
- typedef ipsec-proposal-name-ref{
- type leafref {
- path "/ipsec/proposal/ipsec-proposal/name";
- }
- description "reference to ike proposal name";
- }
-
- typedef ike-auth-method {
- type enumeration {
- enum pre-share {
- description
- "Select pre-shared key message as the
- authentication method";
- }
- enum rsa-digital-signature {
- description
- "Select rsa digital signature as the
- authentication method";
- }
- enum dss-digital-signature {
- description
- "Select dss digital signature as the
- authentication method";
- }
- }
- description "IKE authentication methods";
- }
-
- /*--------------------*/
- /* grouping */
- /*--------------------*/
-
- /* The following groupings are used in both configuration data
- and operational state data */
- grouping name-grouping {
- description
- "This grouping provides a leaf identifying the name.";
- leaf name {
- type string;
- description
- "Name of a identifying.";
- }
- leaf description {
- type string;
- description
- "Specify the description.";
- }
- }
-
- grouping sequence-number-grouping {
- description
- "This grouping provides a leaf identifying
- a sequence number.";
- leaf sequence-number {
- type uint32 {
- range "1..4294967295";
- }
- description
- "Specify the sequence number.";
- }
- }
-
- grouping description-grouping {
- description
- "description for free use.";
- leaf description {
- type string;
- description
- "description for free use.";
- }
- }
-
- grouping traffic-selector-grouping {
- description
- "Traffic selector to be used for SA negotiation.";
- leaf traffic-selector-id {
- type string;
- mandatory true;
- description
- "Traffic selector identifier.";
- }
- leaf protocol-name {
- type transport-protocol-name-t;
- description
- "Specifies the protocol selector.";
- }
- leaf address-range {
- type string;
- mandatory true;
- description
- "Specifies the IPv4 or IPv6 address range.";
- }
- }
-
-
-
- grouping ike-general-proposal-grouping {
- description
- "IKE proposal.";
- leaf name {
- type string;
- mandatory true;
- description
- "IKE Proposal identify.";
- }
- leaf description {
- type string;
- description
- "Specify the description.";
- }
-
- leaf dh-group {
- type diffie-hellman-group-t;
- mandatory true;
- description
- "Specifies a Diffie-Hellman group.";
- }
- container encryption {
- description
- "Specify IKE Proposal encryption configuration";
- leaf algorithm {
- type ike-encryption-algorithm-t;
- description
- "Specifies an Encryption Algorithm.";
- }
- }
- }
-
- grouping ike-proposal-grouping {
- description
- "Configure the IKE Proposal";
- uses ike-general-proposal-grouping;
-
- leaf lifetime {
- type uint32;
- mandatory true;
- description
- "Configure lifetime for IKE SAs
- 0: for no timeout.
- 300 .. 99999999: IKE SA lifetime in seconds.";
- }
- container authentication {
- description
- "Specify IKE Proposal authentication configuration";
- leaf algorithm {
- type ike-integrity-algorithm-t;
- description
- "Specify the authentication algorithm";
- }
- leaf preshared-key {
- type empty;
- description
- "Use pre-shared key based authentication";
- }
- leaf rsa-signature {
- type empty;
- description
- "Use signature based authentication by using
- PKI certificates";
- }
- }
- }
-
- grouping ikev2-proposal-grouping {
- description
- "Holds an IKEv2 transform proposal used during "+
- "IKEv2 SA negotiation. Multiple IKEv2 Transforms "+
- " can be proposed during an IKEv2 session initiation "+
- "in an ordered list.";
- uses ike-general-proposal-grouping;
-
- leaf pseudo-random-function {
- type pseudo-random-function-t;
- mandatory true;
- description
- "Specifies Pseudo Random Function for IKEv2 key exchange";
- }
- container authentication {
- description
- "Specify IKEv2 Proposal authentication configuration";
- leaf algorithm {
- type ike-integrity-algorithm-t;
- description
- "Specify the authentication algorithm";
- }
- }
- }
-
- grouping ipsec-proposal-grouping {
- description
- "Configure IPSec Proposal";
- leaf name {
- type string;
- mandatory true;
- description
- "IPSec proposal identifier.";
- }
- leaf ah {
- type ike-integrity-algorithm-t;
- description
- "Configure Authentication Header (AH).";
- }
- container esp {
- description
- "Configure Encapsulating Security Payload (ESP).";
- leaf authentication {
- type ike-integrity-algorithm-t;
- description
- "Configure ESP authentication";
- }
- leaf encryption {
- type ike-encryption-algorithm-t;
- description
- "Configure ESP encryption";
- }
- }
- leaf ip-comp{
- type empty;
- description
- "Enable IPSec proposal IP-COMP which uses the IP Payload "+
- "compression protocol to compress IP Security (IPSec) "+
- "packets before encryption";
- }
- container lifetime {
- description
- "Configure lifetime for IPSEC SAs";
- leaf kbytes {
- type uint32 {
- range "128..2147483647";
- }
- description
- "Enter lifetime kbytes for IPSEC SAs";
- }
- leaf seconds {
- type uint32 {
- range "300..99999999";
- }
- description
- "Enter lifetime seconds for IPSEC SAs
- 0: lifetime of 0 for no timeout
- 300..99999999: IPSec SA lifetime in seconds";
- }
- }
- }
-
- grouping identity-grouping {
- description
- "Identification type. It is an union identity, "+
- "possible type as follows: "+
- "a) ID_FQDN: A fully-qualified domain name string. "+
- " An example of a ID_FQDN is, example.com. "+
- " The string MUST not contain any terminators "+
- "(e.g., NULL, CR, etc.). "+
- "b) ID_RFC822_ADDR: A fully-qualified RFC822 email "+
- " address string, An example of a ID_RFC822_ADDR is, "+
- " jsmith@example.com. The string MUST not contain "+
- " any terminators. "+
- "c) ID_IPV4_ADDR: A single four (4) octet IPv4 address. "+
- "d) ID_IPV6_ADDR: A single sixteen (16) octet IPv6 address. "+
- "e) DN_X509: Distinguished name in the X.509 tradition.";
- choice identity {
- description
- "Choice of identity.";
- leaf ipv4-address {
- type inet:ipv4-address;
- description
- "Specifies the identity as a single four (4)
- octet IPv4 address.
- An example is, 10.10.10.10. ";
- }
- leaf ipv6-address {
- type inet:ipv6-address;
- description
- "Specifies the identity as a single sixteen (16) "+
- "octet IPv6 address. "+
- "An example is, "+
- "FF01::101, 2001:DB8:0:0:8:800:200C:417A .";
- }
- leaf fqdn-string {
-/* type inet:domain-name; */
- type string {
- pattern
- '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
- + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
- + '|\.';
- length "1..253";
- }
- description
- "Specifies the identity as a Fully-Qualified
- Domain Name (FQDN) string.
- An example is: example.com.
- The string MUST not contain any terminators
- (e.g., NULL, CR, etc.).";
- }
- leaf rfc822-address-string {
- type string;
- description
- "Specifies the identity as a fully-qualified RFC822
- email address string.
- An example is, jsmith@example.com.
- The string MUST not contain any terminators
- (e.g., NULL, CR, etc.).";
- }
- leaf dnX509 {
- type string;
- description
- "Specifies the identity as a distinguished name
- in the X.509 tradition.";
- }
- }
- } /* grouping identity-grouping */
-
- grouping ike-general-policy-profile-grouping {
- description
- "IKE policy.";
- leaf connection-type {
- type connection-type-t;
- mandatory true;
- description
- "Specify the IKE connection type";
- }
- leaf pre-shared-key {
- type union {
- type string {
- length "16";
- }
- type string {
- length "40";
- pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
- }
-/* type yang:hex-string {
- length "40";
- }*/
- }
- description
- "Specify IKE pre-shared-key value";
- }
- leaf validate-certificate-identity {
- type empty;
- description
- "Validate Remote-ID payload against the
- ID's available in the certificate";
- }
- list seq {
- key seq-id;
- description
- "list of sequence of policy.";
- leaf seq-id {
- type uint32 {
- range "1..429496729";
- }
- description
- "Sequence Number";
- }
- leaf proposal {
- type leafref {
- path "/eipsec:ikev1/eipsec:proposal"+
- "/eipsec:name";
- }
- description
- "IKE Proposal reference.";
- }
- }
- container identity {
- description
- "Specify IKE identity value";
- container local {
- description
- "Specify the identity of the local IP Security (IPSec)
- tunnel endpoint in an Internet Key Exchange (IKE)
- policy to use when negotiating IKE request with a
- remote peer.";
- uses identity-grouping;
- }
- container remote {
- description
- "Specify the identity of the remote IP Security (IPSec)
- tunnel endpoint in an
- Internet Key Exchange (IKE) policy to use when
- negotiating IKE request with a remote peer.";
- uses identity-grouping;
- }
- }
- }
-
- grouping ike-policy-mode-grouping {
- description
- "IKE Policy Mode";
- container mode {
- description
- "Specify IKE mode configuration";
- leaf aggressive {
- type empty;
- description
- "Set IKE Aggressive mode";
- }
- leaf main {
- type empty;
- description
- "Set IKE Main mode";
- }
- }
- }
-
- grouping ike-policy-profile-grouping {
- description
- "Configure IKE policy";
- leaf name {
- type string;
- mandatory true;
- description
- "Specify an IKE policy name";
- }
- uses ike-policy-mode-grouping;
- uses ike-general-policy-profile-grouping;
- }
-
- grouping ikev2-policy-profile-grouping {
- description
- "Common information for multiple IKE sessions
- to be instantiated on a managed element.;
- One or more Ikev2Session instances might refer
- to this instance.";
- leaf name {
- type string;
- mandatory true;
- description
- "Value component of the RDN.";
- }
- container authentication {
- description
- "Specify IKE Proposal authentication configuration";
- leaf preshared-key {
- type empty;
- description
- "Use pre-shared key based authentication";
- }
- leaf rsa-signature {
- type empty;
- description
- "Use signature based authentication by using
- PKI certificates";
- }
- }
- leaf lifetime {
- type uint32;
- mandatory true;
- description
- "Configure lifetime for IKE SAs
- 0: for no timeout.
- 300 .. 99999999: IKE SA lifetime in seconds.";
- }
-
- container address-allocation {
- must "../connection-type = 'responder-only'" {
- description
- "address-allocation can be configured only with
- responder-only in ike2 policy";
- }
- leaf aaa {
- type empty;
- description
- "IRAC address allocation by AAA";
- }
- description
- "Specify IKE IRAS address allocation option";
- }
- uses ike-general-policy-profile-grouping;
-
- leaf description {
- type string;
- description
- "Specify the description.";
- }
- }
-
- grouping ipsec-policy-grouping {
- description
- "Holds configuration information for IPSec policies.";
- leaf name {
- type string;
- mandatory true;
- description
- "IPSec Policy Identification";
- }
- leaf description {
- type string;
- description
- "Specify the description.";
- }
-
- leaf anti-replay-window {
- type uint32 {
- range "0..1024";
- }
- description
- "Configure replay window size
- 0: to disable anti-replay-window
- 32..1024: IPSec anti-replay-window size in multiple of 32";
- }
- container perfect-forward-secrecy {
- description
- "Configure Perfect Forward Secrecy (PFS) for IPSec Policy";
- leaf dh-group {
- type diffie-hellman-group-t;
- description
- "Configure Diffie-Hellman group for
- perfect-forward-secrecy";
- }
- }
- list seq {
- key seq-id;
- description
- "Specify IPSEC proposal sequence number";
- leaf seq-id {
- type uint32;
- description
- "Sequence ID";
- }
- leaf description {
- type string;
- description
- "Specify the description.";
- }
-
- leaf proposal {
- type leafref {
- path "/eipsec:ipsec/"+
- "eipsec:proposal/eipsec:ipsec-proposal/eipsec:name";
- }
- description
- "IKE proposal reference.";
- }
- }
- }
-
- grouping key-string-grouping {
- description
- "Configure key for authentication algorithm";
- leaf key-str {
- type union {
- type string {
- length "16";
- }
- type string {
- length "40";
- pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
- }
-/* type yang:hex-string {
- length "40";
- }*/
- }
- description
- "Key string input is either string value (length of 16)
- or hexadecimal (length of 40)";
- }
- }
-
- grouping ipsec-sa-ah-grouping {
- description
- "Configure Authentication Header (AH) for
- Security Association (SA)";
- container ah {
- description
- "Configure Authentication Header (AH) for SA";
- choice authentication-algorithm {
- description
- "choice for authentication algorithm to set for AH";
- case hmac-aes-xcbc {
- container hmac-aes-xcbc {
- description
- "Set the authentication algorithm to hmac-aes-xcbc";
- uses key-string-grouping;
- }
- }
- case hmac-md5-96 {
- container hmac-md5-96 {
- description
- "Set the authentication algorithm to hmac-md5-96";
- uses key-string-grouping;
- }
- }
- case hmac-sha1-96 {
- container hmac-sha1-96 {
- description
- "Set the authentication algorithm to hmac-sha1-96";
- uses key-string-grouping;
- }
- }
- case key-string {
- container key-string {
- description
- "Configure key for authentication algorithm";
- uses key-string-grouping;
- }
- }
- }
- }
- }
-
- grouping ipsec-sa-esp-grouping {
- description
- "Configure IPSec Encapsulation Security Payload (ESP)";
- container esp {
- description
- "Set IPSec Encapsulation Security Payloer (ESP)";
- container authentication {
- description
- "Configure authentication for IPSec
- Encapsulation Secutiry Payload (ESP)";
- choice authentication-algorithm {
- description
- "choice for authentication algorithm to set";
- case hmac-aes-xcbc {
- container hmac-aes-xcbc {
- description
- "Set the authentication algorithm to hmac-aes-xcbc";
- uses key-string-grouping;
- }
- }
- case hmac-md5-96 {
- container hmac-md5-96 {
- description
- "Set the authentication algorithm to hmac-md5-96";
- uses key-string-grouping;
- }
- }
- case hmac-sha1-96 {
- container hmac-sha1-96 {
- description
- "Set the authentication algorithm to hmac-sha1-96";
- uses key-string-grouping;
- }
- }
- case key-string {
- container key-string {
- description
- "Configure key for authentication algorithm";
- uses key-string-grouping;
- }
- }
- }
- }
- container encryption {
- description
- "Configure encryption for IPSec
- Encapsulation Secutiry Payload (ESP)";
- choice encryption-algorithm {
- description
- "type of encryption";
- case des3-cbc {
- container des3-cbd {
- description
- "Set the encryption algorithm to des3-cbc";
- uses key-string-grouping;
- }
- }
- case aes-128-cbc {
- container aes-128-cbc {
- description
- "Set the encryption algorithm to aes-128-cbc";
- uses key-string-grouping;
- }
- }
- case aes-192-cbc {
- container aes-192-cbc {
- description
- "Set the encryption algorithm to aes-192-cbc";
- uses key-string-grouping;
- }
- }
- case aes-256-cbc {
- container aes-256-cbc {
- description
- "Set the encryption algorithm to aes-256-cbc";
- uses key-string-grouping;
- }
- }
- case des-cbc {
- container des-cbc {
- description
- "Set the encryption algorithm to des-cbc";
- uses key-string-grouping;
- }
- }
- case key-string {
- container key-string {
- description
- "Configure key for encryption algorithm";
- uses key-string-grouping;
- }
- }
- }
- }
- }
- }
-
- grouping ipsec-acl-dest-grouping {
- description
- "IPSEC ACL destination.";
- /* For destination */
- choice dest-address {
- description
- "destination address.";
- case dest-ipv4-address {
- leaf destination-ipv4-address {
- type inet:ipv4-address;
- description
- "Destination IPv4 Address A.B.C.D/0..32.";
- }
- }
- case dest-any {
- leaf dest-any {
- type empty;
- description
- "Match Any Destination IPv4 Address.";
- }
- }
- }
- }
-
- grouping ipsec-acl-seq-protocol-number-grouping {
- description
- "IPSec ACL Sequence protocol number.";
- leaf number {
- type uint16 {
- range "0..255";
- }
- description
- "Specify protocol number.";
- }
- choice argument {
- description
- "Source IPv4 address.";
- case source-ipv4-address {
- leaf source-ipv4-address {
- type inet:ipv4-address;
- description
- "Source IPv4 Address A.B.C.D/0..32.";
- }
- }
- case any {
- /* For source */
- leaf source-any {
- type empty;
- description
- "Match Any Source IPv4 Address.";
- }
- }
- }
- }
-
- grouping ipsec-acl-seq-ip-address-grouping {
- description
- "IPSec ACL Sequence IP Address.";
- leaf source-ipv4-address {
- type inet:ipv4-address;
- description
- "Source is IPv4 Address A.B.C.D/0..32.";
- }
- }
-
- grouping ipsec-acl-seq-any-grouping {
- description
- "IPSec ACL Sequence Any.";
- leaf any {
- type empty;
- description
- "Source is Any.";
- }
- }
-
- grouping ipsec-acl-seq-tcp-grouping {
- description
- "IPSec ACL Sequence TCP.";
- leaf tcp {
- type empty;
- description
- "Source is TCP protocol.";
- }
- }
-
- grouping ipsec-acl-seq-udp-grouping {
- description
- "IPSec ACL Sequence for UDP.";
- leaf udp {
- type empty;
- description
- "Source is UDP protocol.";
- }
- }
-
- grouping ipsec-acl-grouping {
- description
- "IPSec ACL";
- list access-list {
- if-feature ipsec-acl;
- key "name sequence-number";
- uses name-grouping;
- uses sequence-number-grouping;
- description
- "Configure the IPSec access-list.";
- choice protocol {
- description
- "IPSec ACL protocol.";
- case number {
- uses ipsec-acl-seq-protocol-number-grouping;
- }
- case source-ipv4-address {
- uses ipsec-acl-seq-ip-address-grouping;
- }
- case any {
- uses ipsec-acl-seq-any-grouping;
- }
- case tcp {
- uses ipsec-acl-seq-tcp-grouping;
- }
- case udp {
- uses ipsec-acl-seq-udp-grouping;
- }
- }
- uses ipsec-acl-dest-grouping;
- }
- }
-
- grouping ipsec-df-bit-grouping {
- description
- "IPSec Dont Fragment (DF) bit for IP header.";
- container df-bit {
- description
- "Configure Don't Fragment (DF) bit for IP Header.";
- leaf clear {
- type empty;
- description
- "Clear DF bit for outer IP header.";
- }
- leaf propagate {
- type empty;
- description
- "Propagate DF bit for outer IP header.";
- }
- leaf set {
- type empty;
- description
- "Set DF bit for outer IP header.";
- }
- }
- }
-
- grouping ipsec-profile-grouping {
- description
- "IPSec profile.";
- list profile {
- key "name";
- uses name-grouping;
- uses ipsec-df-bit-grouping;
- description
- "Configure the IPSec Profile.";
- leaf mtu {
- type uint32 {
- range "256..1600";
- }
- description
- "Set the MTU.";
- }
- list seq {
- key "sequence-number";
- uses sequence-number-grouping;
- description
- "IPSec Access List sequence number.";
- leaf policy {
- type leafref {
- path "/eipsec:ipsec/eipsec:policy"+
- "/eipsec:ipsec-policy/eipsec:name";
- }
- description
- "Specify IPSec policy name.";
- }
- }
- }
- }
-
-
- grouping ip-address-grouping {
- description
- "IP Address grouping";
-
- choice ip-address {
- description
- "Choice of IPv4 or IPv6.";
- leaf ipv4-address {
- type inet:ipv4-address;
- description
- "Specifies the identity as a single four (4)
- octet IPv4 address.
- An example is, 10.10.10.10. ";
- }
- leaf ipv6-address {
- type inet:ipv6-address;
- description
- "Specifies the identity as a single sixteen (16) "+
- "octet IPv6 address. "+
- "An example is, "+
- "FF01::101, 2001:DB8:0:0:8:800:200C:417A .";
- }
- }
- }
-
- grouping ipsec-sa-grouping {
- description
- "Configure Security Association (SA)";
- leaf spi {
- type uint32;
- description
- "Specify Security Parameter Index";
- }
- leaf anti-replay-window {
- type uint16 {
- range "0..1024";
- }
- description
- "Specify replay window size";
- }
- leaf ip-comp {
- type empty;
- description
- "Enables IPCOMP, which uses the IP payload compression
- protocol to compress IP security (IPsec) packets
- before encryption";
- }
-
- container local-peer {
- description
- "Specify the local peer IP address";
- uses ip-address-grouping;
- }
- container remote-peer {
- description
- "Specify the remote peer IP address";
- uses ip-address-grouping;
- }
- leaf sa-mode {
- type ipsec-mode;
- description
- "SA Mode: tunnel or transport mode";
- }
- leaf security-protocol {
- type ipsec-protocol;
- description
- "Security protocol of IPsec SA: Either AH or ESP.";
- }
- leaf sequence-number {
- type uint64;
- description
- "Current sequence number of IPsec packet.";
- }
- leaf sequence-number-overflow-flag {
- type boolean;
- description
- "The flag indicating whether overflow of the sequence
- number counter should prevent transmission of additional
- packets on the SA, or whether rollover is permitted.";
- }
- leaf path-mtu {
- type uint16;
- description
- "maximum size of an IPsec packet that can be transmitted
- without fragmentation";
- }
- container life-time {
- leaf life-time-in-seconds {
- type uint32;
- description
- "SA life time in seconds";
- }
- leaf remain-life-time-in-seconds {
- type uint32;
- description
- "Remain SA life time in seconds";
- }
- leaf life-time-in-byte {
- type uint32;
- description
- "SA life time in bytes";
- }
- leaf remain-life-time-in-byte {
- type uint32;
- description
- "Remain SA life time in bytes";
- }
- description
- "SA life time information";
- }
- leaf upper-protocol {
- type string;
- description
- "Upper-layer protocol to be used";
- }
- leaf direction {
- type ipsec-traffic-direction;
- description
- "It indicates whether the SA is inbound SA or
- out bound SA.";
- }
- container source-address {
- description
- "Specify the source IP address and
- port of protected traffic";
- uses ip-address-grouping;
- leaf port-number {
- type uint32;
- description
- "port of protected traffic";
- }
- }
- container destination-address {
- description
- "Specify the destination IP address and
- port of protected traffic";
- uses ip-address-grouping;
- leaf port-number {
- type uint32;
- description
- "port of protected traffic";
- }
- }
- leaf nat-traversal-flag {
- type boolean;
- description
- "Whether the SA is used to protect traffic that needs
- nat traversal";
- }
- uses ipsec-sa-ah-grouping;
- uses ipsec-sa-esp-grouping;
- }
-
-
- /* draft-wang-ipsecme-ike-yang-00 */
- grouping ipsec-common-configuration {
- choice df-flag {
- default copy;
- case set {
- leaf set {
- type empty;
- description
- "Set the df bit when encapsulate IPsec tunnel.";
- }
- }
- case clear {
- leaf clear {
- type empty;
- description
- "Clear the df bit when encapsulate IPsec tunnel.";
- }
- }
- case copy {
- leaf copy {
- type empty;
- description
- "Copy the inner IP header df bit.";
- }
- }
- description
- "It indicates how to process the df bit when encapsulate
- IPsec tunnel.";
- }
- leaf stateful-frag-check {
- type boolean;
- default false;
- description "Whether stateful fragment checking applies.";
- }
- leaf life-time-kb {
- type uint32;
- units "KB";
- default 2000000;
- description "IPsec SA Life time in KB.";
- }
- leaf life-time-second {
- type uint32;
- units "Second";
- default 18400;
- description "IPsec SA Life time in Seconds";
- }
- choice anti-replay {
- default enable;
- case enable {
- leaf enable {
- type empty;
- description "Enable Anti-replay";
- }
- choice anti-replay-windows-size {
- case size-32;
- case size-64;
- case size-128;
- case size-256;
- case size-512;
- case size-1024;
- default size-1024;
- description "It indicate the size of anti-replay window";
- }
- }
- case disable {
- leaf disable {
- type empty;
- description "Disable Anti-replay";
- }
- }
- description "Whether enable or disable anti-replay";
- }
- leaf inbound-dscp {
- type uint16 {
- range "0..63";
- }
- default 0;
- description "Inbound DSCP value";
- }
- leaf outbound-dscp {
- type uint16 {
- range "0..63";
- }
- default 0;
- description "Outbound DSCP value";
- }
- description "Common IPsec configurations";
- }
-
- /*--------------------*/
- /* Configuration Data */
- /*--------------------*/
- container ikev1 {
- if-feature ikev1;
- description
- "Configuration IPSec IKEv1";
- /* The following is for */
- list proposal {
- key "name";
- uses ike-proposal-grouping;
- description
- "Configure IKE proposal";
- }
- leaf keepalive {
- type empty;
- description
- "Enables sending Dead Peer Detection (DPD) messages "+
- "to Internet Key Exchange (IKE) peers.";
- }
- list policy {
- key "name";
- uses ike-policy-profile-grouping;
- description
- "Configure IKE Policy Profile.";
- }
- }
-
- container ikev2 {
- if-feature ikev2;
- description
- "Configuration IPSec IKEv2";
- /* The following is for */
- /* draft-wang-ipsecme-ike-yang-00 */
- container ike-global-configuration {
- if-feature ikev2-global;
- description "Global IKE configurations";
- uses ipsec-common-configuration;
- leaf local-name {
- type string;
- description
- "Global local name configuration, if it is not configed,
- ip address will be used as default. If configing special
- local name for special peer, it will overwrite the global
- name configuration when negotion with that peer.";
- }
- leaf nat-keepalive-interval {
- type uint16 {
- range "5..300";
- }
- units "Seconds";
- default 20;
- description "Global nat keepalive interval";
- }
- leaf dpd-interval {
- type uint16 {
- range "10..3600";
- }
- units "Seconds";
- default 30;
- description "Global DPD interval";
- }
- }
- container ike-peer {
- if-feature ikev2-peer;
- description "IKE peer information";
- list ike-peer-entries {
- key "peer-name";
- description "IKE peer information";
- leaf peer-name {
- type string;
- mandatory true;
- description "Name of IKE peer";
- }
- leaf ike-proposal-number {
- type ike-proposal-number-ref;
- description "IKE proposal number referenced by IKE peer";
- }
- leaf PresharedKey {
- type string;
- description "Preshare key";
- }
- leaf nat-traversal {
- type boolean;
- default false;
- description "Enable/Disable nat traversal";
- }
- choice local-id-type {
- default ip;
- case ip {
- leaf ip {
- type empty;
- description "IP address";
- }
- }
- case fqdn {
- leaf fqdn {
- type empty;
- description "Fully Qualifed Domain name ";
- }
- }
- case dn {
- leaf dn {
- type empty;
- description "Domain name";
- }
- }
- case user_fqdn {
- leaf user_fqdn {
- type empty;
- description "User FQDN";
- }
- }
- description "Local ID type";
- }
- leaf local-id {
- type string;
- description
- "Local ID Name. When IP is used as local ID type,
- it is ignored. If it is not configurated,
- global local name will be used.";
- }
- leaf remote-id {
- type "string";
- description "ID of IKE peer";
- }
- leaf low-remote-address {
- type inet:ip-address;
- description "Low range of remote address";
- }
- leaf high-remote-address {
- type inet:ip-address;
- description "High range of remote address";
- }
- leaf certificate {
- type string;
- description "Certificate file name";
- }
- leaf auth-address-begin {
- type inet:ip-address;
- description
- "The begin range of authenticated peer address";
- }
- leaf auth-address-end {
- type inet:ip-address;
- description
- "The end range of authenticated peer address";
- }
- }
- }//End of IKEPeerEntries
-
-
- list proposal {
- if-feature ikev2-proposal;
- key "name";
- uses ikev2-proposal-grouping;
- description
- "Configure IKEv2 proposal";
- }
- list policy {
- if-feature ikev2-policy;
- key "name";
- uses ikev2-policy-profile-grouping;
- description
- "IKEv2 Policy Profile";
- }
- }
-
- container ipsec {
- if-feature ipsec;
- description
- "Configuration IPsec";
- container sad {
- if-feature ipsec-sad;
- description
- "Configure the IPSec Security Association Database (SAD)";
- list sad-entries {
- key "spi direction";
- description
- "Configure IPsec Security Association Database(SAD)";
- uses ipsec-sa-grouping;
- }
- }
- container proposal {
- if-feature ipsec-proposal;
- description
- "IPSec Proposal Profile";
- list ipsec-proposal {
- key "name";
- uses ipsec-proposal-grouping;
- description
- "Configure the IP Security (IPSec) proposal";
- }
- }
- container spd {
- if-feature ipsec-spd;
- description
- "Configure the Security Policy Database (SPD)";
- list spd-entries {
- key "name";
- ordered-by user;
- uses ipsec-policy-grouping;
- description
- "Specify an IPSec policy name";
- }
- }
- container pad {
- description
- "Configure Peer Authorization Database (PAD)";
- list pad-entries {
- key "pad-type pad-id";
- ordered-by user;
- uses identity-grouping;
- description
- "Peer Authorization Database (PAD)";
- leaf pad-id {
- type uint32;
- description
- "PAD identity";
- }
- leaf pad-type {
- type pad-type-t;
- description
- " PAD type";
- }
- leaf ike-peer-name {
- type string;
- description
- "IKE Peer Name";
- }
- container peer-authentication {
- description
- "Specify IKE peer authentication configuration";
- leaf algorithm {
- type ike-integrity-algorithm-t;
- description
- "Specify the authentication algorithm";
- }
- leaf preshared-key {
- type empty;
- description
- "Use pre-shared key based authentication";
- }
- leaf rsa-signature {
- type empty;
- description
- "Use signature based authentication by using
- PKI certificates";
- }
- }
- }
- }
- }
-
-
-
- /*--------------------------*/
- /* Operational State Data */
- /*--------------------------*/
- grouping ike-proposal-state-components {
- description
- "IKE Proposal operational state";
- list proposal {
- if-feature ike-proposal-state;
- description
- "Operational data for IKE Proposal";
- leaf name {
- type string {
- length "1..50";
- }
- description
- "Name of the IKE proposal.";
- }
- leaf lifetime {
- type uint32;
- units "seconds";
- description
- "lifetime";
- }
- leaf encryption {
- type ike-encryption-algorithm-t;
- description
- "Encryption algorithm";
- }
- leaf dh-group {
- type diffie-hellman-group-t;
- description
- "Diffie-Hellman group.";
- }
- leaf authentication {
- type ike-integrity-algorithm-t;
- description
- "authentication";
- }
- }
- }
-
- grouping ike-policy-state-grouping {
- description
- "IKE Policy State.";
- list policy {
- if-feature ike-policy-state;
- description
- "Operational data for IKE policy";
- leaf name {
- type string {
- length "1..50";
- }
- description
- "Name of the IKE Policy.";
- }
- leaf description {
- type string;
- description
- "Description for IKE Policy.";
- }
- leaf mode {
- type enumeration {
- enum aggressive {
- description
- "Aggressive mode.";
- }
- enum main {
- description
- "Main mode.";
- }
- }
- description
- "IKE policy mode.";
- }
- leaf connection-type {
- type connection-type-t;
- description
- "IKE policy connection type.";
- }
- leaf local-identity {
- type inet:ipv4-address-no-zone;
- description
- "IP address of the local identity.";
- }
- leaf remote-identity {
- type inet:ipv4-address-no-zone;
- description
- "IP address of the remote identity.";
- }
- leaf pre-shared-key {
- type string;
- description
- "Pre-shared key";
- }
- leaf seq {
- type uint32;
- description
- "sequence number";
- }
- leaf proposal {
- type string;
- description
- "proposal name";
- }
- }
- }
-
- grouping ikev2-proposal-state-components {
- description
- "IKEv2 Operational state";
- list proposal {
- if-feature ikev2-proposal-state;
- description
- "IKEv2 proposal operational data";
- leaf name {
- type string;
- description
- "Name of IKEv2 Proposal.";
- }
- leaf pseudo-random-function {
- type pseudo-random-function-t;
- description
- "Pseudo Random Function for IKEv2.";
- }
- leaf authentication {
- type ike-integrity-algorithm-t;
- description
- "authentication";
- }
- leaf encryption {
- type ike-encryption-algorithm-t;
- description
- "Encryption algorithm";
- }
- leaf dh-group {
- type diffie-hellman-group-t;
- mandatory true;
- description
- "Diffie-Hellman group.";
- }
- }
- }
-
- grouping ipsec-policy-state-grouping {
- description
- "IPSec operational state";
- list policy {
- if-feature ipsec-policy-state;
- description
- "IPSec policy operational data";
- leaf name {
- type string;
- description
- "IPSec Policy name.";
- }
- leaf anti-replay-window {
- type uint32;
- description
- "replay window size";
- }
- leaf perfect-forward-secrecy {
- type diffie-hellman-group-t;
- description
- "Diffie-Hellman group for perfect-forward-secrecy";
- }
- list seq {
- description
- "Sequence number";
- leaf seq-id {
- type uint32;
- description
- "Sequence number";
- }
- leaf proposal-name {
- type string;
- description
- "IPSec proposal name";
- }
- }
- }
- }
- grouping ipsec-proposal-state-grouping {
- description
- "IPSec proposal operational data";
- list proposal {
- if-feature ipsec-proposal-state;
- description
- "IPSec proposal operational data";
- leaf name {
- type string;
- description
- "IPSec Proposal name";
- }
- leaf ah {
- type ike-integrity-algorithm-t;
- description
- "Authentication Header (AH).";
- }
- container esp {
- description
- "Encapsulating Security Payload (ESP).";
- leaf authentication {
- type ike-integrity-algorithm-t;
- description
- "ESP authentication";
- }
- leaf encryption {
- type ike-encryption-algorithm-t;
- description
- "ESP encryption";
- }
- }
- leaf ip-comp{
- type empty;
- description
- "IPSec proposal IP-COMP which uses the IP Payload "+
- "compression protocol to compress IP Security (IPSec) "+
- "packets before encryption";
- }
- container lifetime {
- description
- "lifetime for IPSEC SAs";
- leaf kbytes {
- type uint32;
- description
- "lifetime kbytes for IPSEC SAs";
-
- }
- leaf seconds {
- type uint32;
- description
- "lifetime seconds for IPSEC SAs";
- }
- }
- }
- }
-
- grouping ipsec-alarms-state-grouping {
- description
- "IPSec alarms operational data";
- leaf hold-down {
- if-feature ipsec-alarms-state;
- type uint32;
- description
- "Hold-down value";
- }
- }
-
- grouping ipsec-sa-ah-state-grouping {
- description
- "IPSec SA's AH operational data";
-
- leaf spi {
- if-feature ipsec-sa-ah-state;
- type uint32;
- description
- "Security Parameter Index (SPI) value";
- }
- leaf description {
- if-feature ipsec-sa-ah-state;
- type string;
- description
- "the description.";
- }
- leaf authentication-algorithm {
- if-feature ipsec-sa-ah-state;
- type ike-integrity-algorithm-t;
- description
- "Authentication algorithm";
- }
- leaf encryption-algorithm {
- if-feature ipsec-sa-ah-state;
- type ike-encryption-algorithm-t;
- description
- "Encryption algorithm";
- }
- }
-
- grouping ipsec-sa-state-grouping {
- description
- "IPSec Security Association Operational data";
- list sa {
- if-feature ipsec-sa-state;
- description
- "IPSec SA operational data";
- leaf name {
- type string;
- description
- "Specify IPSec Security Association (SA) name";
- }
- leaf anti-replay-window {
- type uint16;
- description
- "replay window size";
- }
- leaf ip-comp {
- type empty;
- description
- "Enables IPCOMP, which uses the IP payload compression
- protocol to compress IP security (IPsec) packets before
- encryption";
- }
- uses ipsec-sa-ah-state-grouping;
- }
- }
-
- /* draft-wang-ipsecme-ipsec-yang-00 */
- grouping ipsec-tunnel-mode-info {
- description
- "common infomations when using IPsec tunnel mode";
- leaf local-address {
- if-feature ipsec-tunnel;
- type string;
- description
- "Local address of IPsec tunnel mode";
- }
- leaf remote-address {
- if-feature ipsec-tunnel;
- type string;
- description
- "Remote address of IPsec tunnel mode";
- }
- leaf bypass-df {
- if-feature ipsec-tunnel;
- type enumeration {
- enum "set" {
- description
- "Set the df bit";
- }
- enum "clear" {
- description
- "Clear the df bit";
- }
- enum "copy" {
- description
- "Copy the df bit from inner header";
- }
- }
- description
- "This flag indicates how to process tunnel mode df flag";
- }
- leaf dscp-flag {
- if-feature ipsec-tunnel;
- type boolean;
- description
- "This flag indicate whether bypass DSCP or map to
- unprotected DSCP values (array) if needed to
- restrict bypass of DSCP values.";
- }
- leaf stateful-frag-check-flag {
- if-feature ipsec-tunnel;
- type boolean;
- description
- "This flag indicates whether stateful fragment checking
- will be used.";
- }
- }
- grouping traffic-selector {
- description
- "IPsec traffic selector information";
- leaf local-address-low {
- if-feature ipsec-local-address-range;
- type inet:ip-address;
- description
- "Low range of local address";
- }
- leaf local-address-high {
- if-feature ipsec-local-address-range;
- type inet:ip-address;
- description
- "High range of local address";
- }
- leaf remote-address-low {
- if-feature ipsec-remote-address-range;
- type inet:ip-address;
- description
- "Low range of remote address";
- }
- leaf remote-address-high {
- if-feature ipsec-remote-address-range;
- type inet:ip-address;
- description
- "High range of remote address";
- }
- leaf next-protocol-low {
- if-feature ipsec-next-protocol-range;
- type uint16;
- description
- "Low range of next protocol";
- }
- leaf next-protocol-high {
- if-feature ipsec-next-protocol-range;
- type uint16;
- description
- "High range of next protocol";
- }
- leaf local-port-low {
- if-feature ipsec-local-port-range;
- type inet:port-number;
- description
- "Low range of local port";
- }
- leaf local-port-high {
- if-feature ipsec-local-port-range;
- type inet:port-number;
- description
- "High range of local port";
- }
- leaf remote-port-high {
- if-feature ipsec-remote-port-range;
- type inet:port-number;
- description
- "Low range of remote port";
- }
- leaf remote-port-low {
- if-feature ipsec-remote-port-range;
- type inet:port-number;
- description
- "High range of remote port";
- }
- }
- grouping ipsec-algorithm-info {
- description
- "IPsec algorithm information used by SPD and SAD";
- leaf ah-auth-algorithm {
- if-feature ipsec-ah-authentication;
- type ipsec-authentication-algorithm;
- description
- "Authentication algorithm used by AH";
- }
- leaf esp-integrity-algorithm {
- if-feature ipsec-esp-integrity;
- type ipsec-authentication-algorithm;
- description
- "Integrity algorithm used by ESP";
- }
- leaf esp-encrypt-algorithm {
- if-feature ipsec-esp-encrypt;
- type ipsec-encryption-algorithm;
- description
- "Encryption algorithm used by ESP";
- }
- }
- grouping ipsec-stat {
- leaf inbound-packets {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Inbound Packet count";
- }
- leaf outbound-packets {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Outbound Packet count";
- }
- leaf inbound-bytes {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Inbound Packet bytes";
- }
- leaf outbound-bytes {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Outbound Packet bytes";
- }
- leaf inbound-drop-packets {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Inbound dropped packets count";
- }
- leaf outbound-drop-packets {
- if-feature ipsec-stat;
- type uint64;
- config false;
- description "Outbound dropped packets count";
- }
- container dropped-packet-detail {
- if-feature ipsec-stat;
- description "The detail information of dropped packets";
- leaf sa-non-exist {
- type uint64;
- config false;
- description
- "The dropped packets counts caused by SA non-exist.";
- }
- leaf queue-full {
- type uint64;
- config false;
- description
- "The dropped packets counts caused by full processing
- queue";
- }
- leaf auth-failure {
- type uint64;
- config false;
- description
- "The dropped packets counts caused by authentication
- failure";
- }
- leaf malform {
- type uint64;
- config false;
- description "The dropped packets counts of malform";
- }
- leaf replay {
- type uint64;
- config false;
- description "The dropped packets counts of replay";
- }
- leaf large-packet {
- type uint64;
- config false;
- description "The dropped packets counts of too large";
- }
- leaf invalid-sa {
- type uint64;
- config false;
- description "The dropped packets counts of invalid SA";
- }
- leaf policy-deny {
- type uint64;
- config false;
- description
- "The dropped packets counts of denyed by policy";
- }
- leaf other-reason {
- type uint64;
- config false;
- description
- "The dropped packets counts of other reason";
- }
- }
- description "IPsec statistics information";
- }
-
-
- container ike-state {
- if-feature ikev1-state;
- config "false";
- uses ike-proposal-state-components;
- uses ike-policy-state-grouping;
- description
- "Contain the operational data for IKE.";
- }
- container ikev2-state {
- if-feature ikev2-state;
- config "false";
- uses ikev2-proposal-state-components;
- uses ike-policy-state-grouping;
- description
- "Contain the operational data for IKEv2.";
- }
- container ipsec-state {
- if-feature ipsec-state;
- config "false";
- uses ipsec-policy-state-grouping;
- uses ipsec-proposal-state-grouping;
- uses ipsec-alarms-state-grouping;
- uses ipsec-sa-state-grouping;
- container redundancy {
- if-feature ipsec-redundancy;
- description
- "Configure redundancy for IPSec";
- leaf inter-chassis {
- type empty;
- description
- "Set redundancy at chassis level";
- }
- }
-
- description
- "Contain the operational data for IPSec.";
- }
-
- /* draft-wang-ipsecme-ipsec-yang-00 */
- container sad {
- if-feature sad;
- config false;
- description
- "The IPsec SA database";
- list sad-entries {
- key "spi security-protocol direction";
- description
- "The SA entries information";
- leaf spi {
- type ipsec-spi;
- description
- "Security parameter index of SA entry.";
- }
- leaf security-protocol {
- type ipsec-protocol;
- description
- "Security protocol of IPsec SA.";
- }
- leaf direction {
- type ipsec-traffic-direction;
- description
- "It indicates whether the SA is inbound SA or
- out bound SA.";
- }
- leaf sa-type {
- type enumeration {
- enum "manual" {
- description
- "Manual IPsec SA";
- }
- enum "isakmp" {
- description
- "ISAKMP IPsec SA";
- }
- }
- description
- "It indicates whether the SA is created by manual
- or by dynamic protocol.";
- }
- leaf sequence-number {
- type uint64;
- description
- "Current sequence number of IPsec packet.";
- }
- leaf sequence-number-overflow-flag {
- type boolean;
- description
- "The flag indicating whether overflow of the sequence
- number counter should prevent transmission of additional
- packets on the SA, or whether rollover is permitted.";
- }
- leaf anti-replay-enable-flag {
- type boolean;
- description
- "It indicates whether anti-replay is enable or disable.";
- }
- leaf anti-replay-window-size {
- type uint64;
- description
- "The size of anti-replay window.";
- }
- uses ipsec-algorithm-info;
- container life-time {
- leaf life-time-in-seconds {
- type uint32;
- description
- "SA life time in seconds";
- }
- leaf remain-life-time-in-seconds {
- type uint32;
- description
- "Remain SA life time in seconds";
- }
- leaf life-time-in-byte {
- type uint32;
- description
- "SA life time in bytes";
- }
- leaf remain-life-time-in-byte {
- type uint32;
- description
- "Remain SA life time in bytes";
- }
- description
- "SA life time information";
- }
- leaf protocol-mode {
- type ipsec-mode;
- description
- "It indicates whether tunnel mode or transport mode
- will be used.";
- }
- container tunnel-mode-process-info {
- when "../protocol-mode = 'tunnel'" {
- description
- "External information of SA when SA works in
- tunnel mode.";
- }
- uses ipsec-tunnel-mode-info;
- description
- "External information of SA when SA works in
- tunnel mode.";
- }
- leaf-list dscp {
- type uint8 {
- range "0..63";
- }
- description
- "When traffic matchs SPD, the DSCP values used to
- filter traffic";
- }
- leaf path-mtu {
- type uint16;
- description
- "Path MTU valie";
- }
- leaf nat-traversal-flag {
- type boolean;
- description
- "Whether the SA is used to protect traffic that needs
- nat traversal";
- }
- }
- }
- container spd {
- if-feature spd;
- config false;
- description
- "IPsec security policy database information";
- list spd-entries {
- description
- "IPsec SPD entry information";
- list name {
- description
- "SPD name information.";
- leaf name-type {
- type ipsec-spd-name;
- description
- "SPD name type.";
- }
- leaf name-string {
- when "../name-type = 'id_rfc_822_addr' or ../name-type =
- 'id_fqdn'" {
- description
- "when name type is id_rfc_822_addr or id_fqdn, the
- name are saved in string";
- }
- type string;
- description
- "SPD name content";
- }
- leaf name-binary {
- when "../name-type = 'id_der_asn1_dn' or ../name-type =
- 'id_key'" {
- description
- "when name type is id_der_asn1_dn or id_key, the name
- are saved in binary";
- }
- type binary;
- description
- "SPD name content";
- }
- }
- leaf pfp-flag {
- type boolean;
- description
- "populate from packet flag";
- }
- list traffic-selector {
- min-elements 1;
- uses traffic-selector;
- description
- "Traffic selectors of SAD entry";
- }
- leaf operation {
- type ipsec-spd-operation;
- description
- "It indicates how to process the traffic when it matches
- the security policy.";
- }
- container protect-operation {
- when "../operation = 'protect'" {
- description
- "How to protect the traffic when the SPD operation
- is protect";
- }
- leaf spd-ipsec-mode {
- type ipsec-mode;
- description
- "It indicates which mode is chosen when the traffic need
- be protected by IPsec.";
- }
- leaf esn-flag {
- type boolean;
- description
- "It indicates whether ESN is used.";
- }
- leaf spd-ipsec-protocol {
- type ipsec-protocol;
- description
- "It indicates which protocol (AH or ESP) is chosen.";
- }
- container tunnel-mode-additional {
- when "../spd-ipsec-mode = 'tunnel'" {
- description
- "Additional informations when choose tunnel mode";
- }
- uses ipsec-tunnel-mode-info;
- description
- "When use tunnel mode, the additional information of
- SPD.";
- }
- list spd-algorithm {
- min-elements 1;
- uses ipsec-algorithm-info;
- description
- "Algorithms defined in SPD, ordered by decreasing
- priority.";
- }
- description
- "How to protect the traffic when the SPD operation is
- protect";
- }
- }
- }
-
- container ipsec-global-statistics {
- if-feature ipsec-global-stats;
- config false;
- description "IPsec global statistics";
- container ipv4 {
- description "IPsec statistics of IPv4";
- uses ipsec-stat;
- }
- container ipv6 {
- description "IPsec statistics of IPv6";
- uses ipsec-stat;
- }
- container global {
- description "IPsec statistics of global";
- uses ipsec-stat;
- }
- }
-
-
- /*--------------------*/
- /* RPC */
- /*--------------------*/
- rpc clear-ipsec-group {
- if-feature clear-ipsec-group;
- description
- "RPC for clear ipsec states";
- input {
- leaf alarm-hold-down {
- type uint8;
- description
- "IPSec alarm hold-down";
- }
- leaf ipsec-policy-name {
- type leafref {
- path "/eipsec:ipsec/eipsec:spd/"+
- "eipsec:spd-entries/eipsec:name";
- }
- description
- "IPSec Policy name.";
- }
- }
- }
-
- rpc clear-ike-group {
- if-feature clear-ike-group;
- description
- "RPC for clear IKE states";
- input {
- leaf proposal {
- type leafref {
- path "/eipsec:ikev1/eipsec:proposal/"+
- "eipsec:name";
- }
- description
- "IPSec IKE Proposal name.";
- }
- }
- }
-
- rpc clear-ikev2-group {
- if-feature clear-ikev2-group;
- description
- "RPC for clear IKEv2 states";
- input {
- leaf proposal {
- type leafref {
- path "/eipsec:ikev2/eipsec:proposal/"+
- "eipsec:name";
- }
- description
- "IPSec IKEv2 Proposal name.";
- }
- }
- }
-
- /* draft-wang-ipsecme-ipsec-yang-00 */
- rpc reset-ipv4 {
- if-feature reset-ipv4;
- description "Reset IPsec IPv4 statistics";
- input {
- leaf ipv4 {
- type empty;
- description "Reset IPsec IPv4 statistics";
- }
- }
- output {
- leaf status {
- type string;
- description "Operation status";
- }
- }
- }
- rpc reset-ipv6 {
- if-feature reset-ipv6;
- description "Reset IPsec IPv6 statistics";
- input {
- leaf ipv6 {
- type empty;
- description "Reset IPsec IPv6 statistics";
- }
- }
- output {
- leaf status {
- type string;
- description "Operation status";
- }
- }
- }
- rpc reset-global {
- if-feature reset-global;
- description "Reset IPsec global statistics";
- input {
- leaf ipv6 {
- type empty;
- description "Reset IPsec global statistics";
- }
- }
- output {
- leaf status {
- type string;
- description "Operation status";
- }
- }
- }
-/* notification dpd-failure {
- description "IKE peer DPD detect failure";
- leaf peer-id {
- type string;
- description "Peer ID";
- }
- }
- notification peer-authentication-failure {
- if-feature peer-authentication-failure;
- description "Peer authentication fail when negotication";
- leaf peer-id {
- type string;
- description "The ID of remote peer";
- }
- }
- notification ike-reauth-failure {
- if-feature ike-reauth-failure;
- description "IKE peer reauthentication fail";
- leaf peer-id {
- type string;
- description "The ID of remote peer";
- }
- }
- notification ike-rekey-failure {
- if-feature ike-rekey-failure;
- description "IKE SA rekey failure";
- leaf peer-id {
- type string;
- description "The ID of remote peer";
- }
- leaf old-i-spi {
- type uint64;
- description "old SPI";
- }
- leaf old-r-spi {
- type uint64;
- description "old SPI";
- }
- }
- notification ipsec-rekey-failure {
- if-feature ipsec-rekey-failure;
- description "IPsec SA rekey failure";
- leaf peer-id {
- type string;
- description "The ID of remote peer";
- }
- leaf old-inbound-spi {
- type ipsec-spi;
- description "old inbound SPI";
- }
- leaf old-outbound-spi {
- type ipsec-spi;
- description "old outbound SPI";
- }
- } */
- } /* module ericsson-ipsec */
diff --git a/example/ietf-ipv4-unicast-routing@2014-10-26.yang b/example/ietf-ipv4-unicast-routing@2014-10-26.yang
deleted file mode 100644
index 5076bc60..00000000
--- a/example/ietf-ipv4-unicast-routing@2014-10-26.yang
+++ /dev/null
@@ -1,232 +0,0 @@
- module ietf-ipv4-unicast-routing {
-
- namespace "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing";
-
- prefix "v4ur";
-
- import ietf-routing {
- prefix "rt";
- revision-date "2014-10-26";
- }
-
- import ietf-inet-types {
- prefix "inet";
- revision-date "2013-07-15";
- }
-
- organization
- "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
-
- contact
- "WG Web:
- WG List:
-
- WG Chair: Thomas Nadeau
-
-
- WG Chair: Juergen Schoenwaelder
-
-
- Editor: Ladislav Lhotka
- ";
-
- description
- "This YANG module augments the 'ietf-routing' module with basic
- configuration and operational state data for IPv4 unicast
- routing.
-
- Copyright (c) 2014 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 XXXX; see the
- RFC itself for full legal notices.";
-
- revision 2014-10-26 {
- description
- "Initial revision.";
- reference
- "RFC XXXX: A YANG Data Model for Routing Management";
- }
-
- /* Identities */
-
- identity ipv4-unicast {
- base rt:ipv4;
- description
- "This identity represents the IPv4 unicast address family.";
- }
-
- /* Operational state data */
-
- augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" {
- when "../../rt:address-family = 'v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments an IPv4 unicast route.";
- leaf destination-prefix {
- type inet:ipv4-prefix;
- description
- "IPv4 destination prefix.";
- }
- }
-
- augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
- + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
- when "../../../rt:address-family = 'v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments the 'simple-next-hop' case of IPv4 unicast
- routes.";
- leaf next-hop-address {
- type inet:ipv4-address;
- description
- "IPv4 address of the next-hop.";
- }
- }
-
- augment "/rt:routing-state/rt:next-hop-lists/rt:next-hop-list/"
- + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
- when "../rt:address-family = 'v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments next-hop list with IPv4 next-hop address.
- routes.";
- leaf next-hop-address {
- type inet:ipv4-address;
- description
- "IPv4 address of the next-hop.";
- }
- }
-
- /* Configuration data */
-
- augment "/rt:routing/rt:routing-instance/rt:routing-protocols/"
- + "rt:routing-protocol/rt:static-routes" {
- description
- "This augment defines the configuration of the 'static'
- pseudo-protocol with data specific to IPv4 unicast.";
- container ipv4 {
- description
- "Configuration of a 'static' pseudo-protocol instance
- consists of a list of routes.";
- list route {
- key "destination-prefix";
- ordered-by "user";
- description
- "A user-ordered list of static routes.";
- leaf destination-prefix {
- type inet:ipv4-prefix;
- mandatory "true";
- description
- "IPv4 destination prefix.";
- }
- leaf description {
- type string;
- description
- "Textual description of the route.";
- }
- container next-hop {
- description
- "Configuration of next-hop.";
- grouping next-hop-content {
- description
- "Next-hop content for IPv4 unicast static routes.";
- uses rt:next-hop-content {
- augment "next-hop-options" {
- description
- "Add next-hop address case.";
- leaf next-hop-address {
- type inet:ipv4-address;
- description
- "IPv4 address of the next-hop.";
- }
- }
- }
- }
- choice simple-or-list {
- description
- "Options for next-hops.";
- list multipath-entry {
- if-feature rt:multipath-routes;
- key "name";
- description
- "List of alternative next-hops.";
- leaf name {
- type string;
- description
- "A unique identifier of the next-hop entry.";
- }
- uses next-hop-content;
- uses rt:next-hop-classifiers;
- }
- case simple-next-hop {
- uses next-hop-content;
- }
- }
- }
- }
- }
- }
-
- /* RPC methods */
-
- augment "/rt:fib-route/rt:input/rt:destination-address" {
- when "rt:address-family='v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments the 'rt:destination-address' parameter of
- the 'rt:fib-route' operation.";
- leaf address {
- type inet:ipv4-address;
- description
- "IPv4 destination address.";
- }
- }
-
- augment "/rt:fib-route/rt:output/rt:route" {
- when "rt:address-family='v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments the reply to the 'rt:fib-route'
- operation.";
- leaf destination-prefix {
- type inet:ipv4-prefix;
- description
- "IPv4 destination prefix.";
- }
- }
-
- augment "/rt:fib-route/rt:output/rt:route/rt:next-hop/"
- + "rt:next-hop-options/rt:simple-next-hop" {
- when "../rt:address-family='v4ur:ipv4-unicast'" {
- description
- "This augment is valid only for IPv4 unicast.";
- }
- description
- "This leaf augments the 'simple-next-hop' case in the reply to
- the 'rt:fib-route' operation.";
- leaf next-hop-address {
- type inet:ipv4-address;
- description
- "IPv4 address of the next-hop.";
- }
- }
- }
-
diff --git a/example/ietf-ipv6-unicast-routing@2014-10-26.yang b/example/ietf-ipv6-unicast-routing@2014-10-26.yang
deleted file mode 100644
index c5377da6..00000000
--- a/example/ietf-ipv6-unicast-routing@2014-10-26.yang
+++ /dev/null
@@ -1,636 +0,0 @@
-
- module ietf-ipv6-unicast-routing {
-
- namespace "urn:ietf:params:xml:ns:yang:ietf-ipv6-unicast-routing";
-
- prefix "v6ur";
-
- import ietf-routing {
- prefix "rt";
- revision-date "2014-10-26";
- }
-
- import ietf-inet-types {
- prefix "inet";
- revision-date "2013-07-15";
- }
-
- import ietf-interfaces {
- prefix "if";
- revision-date "2013-07-15";
- }
-
- import ietf-ip {
- prefix "ip";
- revision-date "2014-06-16";
- }
-
- organization
- "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
-
- contact
- "WG Web:
- WG List:
-
- WG Chair: Thomas Nadeau
-
-
- WG Chair: Juergen Schoenwaelder
-
-
- Editor: Ladislav Lhotka
- ";
-
- description
- "This YANG module augments the 'ietf-routing' module with basic
- configuration and operational state data for IPv6 unicast
- routing.
-
- Copyright (c) 2014 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 XXXX; see the
- RFC itself for full legal notices.";
-
- revision 2014-10-26 {
- description
- "Initial revision.";
- reference
- "RFC XXXX: A YANG Data Model for Routing Management";
- }
-
- /* Identities */
-
- identity ipv6-unicast {
- base rt:ipv6;
- description
- "This identity represents the IPv6 unicast address family.";
- }
-
- /* Operational state data */
-
- augment "/rt:routing-state/rt:routing-instance/rt:interfaces/"
- + "rt:interface" {
- description
- "IPv6-specific parameters of router interfaces.";
- container ipv6-router-advertisements {
- description
- "Parameters of IPv6 Router Advertisements.";
- leaf send-advertisements {
- type boolean;
- description
- "A flag indicating whether or not the router sends periodic
- Router Advertisements and responds to Router
- Solicitations.";
- }
- leaf max-rtr-adv-interval {
- type uint16 {
- range "4..1800";
- }
- units "seconds";
- description
- "The maximum time allowed between sending unsolicited
- multicast Router Advertisements from the interface.";
- }
- leaf min-rtr-adv-interval {
- type uint16 {
- range "3..1350";
- }
- units "seconds";
- description
- "The minimum time allowed between sending unsolicited
- multicast Router Advertisements from the interface.";
- }
- leaf managed-flag {
- type boolean;
- description
- "The value that is placed in the 'Managed address
- configuration' flag field in the Router Advertisement.";
- }
- leaf other-config-flag {
- type boolean;
- description
- "The value that is placed in the 'Other configuration' flag
- field in the Router Advertisement.";
- }
- leaf link-mtu {
- type uint32;
- description
- "The value that is placed in MTU options sent by the
- router. A value of zero indicates that no MTU options are
- sent.";
- }
- leaf reachable-time {
- type uint32 {
- range "0..3600000";
- }
- units "milliseconds";
- description
- "The value that is placed in the Reachable Time field in
- the Router Advertisement messages sent by the router. A
- value of zero means unspecified (by this router).";
- }
- leaf retrans-timer {
- type uint32;
- units "milliseconds";
- description
- "The value that is placed in the Retrans Timer field in the
- Router Advertisement messages sent by the router. A value
- of zero means unspecified (by this router).";
- }
- leaf cur-hop-limit {
- type uint8;
- description
- "The value that is placed in the Cur Hop Limit field in the
- Router Advertisement messages sent by the router. A value
- of zero means unspecified (by this router).";
- }
- leaf default-lifetime {
- type uint16 {
- range "0..9000";
- }
- units "seconds";
- description
- "The value that is placed in the Router Lifetime field of
- Router Advertisements sent from the interface, in seconds.
- A value of zero indicates that the router is not to be
- used as a default router.";
- }
- container prefix-list {
- description
- "A list of prefixes that are placed in Prefix Information
- options in Router Advertisement messages sent from the
- interface.
-
- By default, these are all prefixes that the router
- advertises via routing protocols as being on-link for the
- interface from which the advertisement is sent.";
- list prefix {
- key "prefix-spec";
- description
- "Advertised prefix entry and its parameters.";
- leaf prefix-spec {
- type inet:ipv6-prefix;
- description
- "IPv6 address prefix.";
- }
- leaf valid-lifetime {
- type uint32;
- units "seconds";
- description
- "The value that is placed in the Valid Lifetime in the
- Prefix Information option. The designated value of all
- 1's (0xffffffff) represents infinity.";
- }
- leaf on-link-flag {
- type boolean;
- description
- "The value that is placed in the on-link flag ('L-bit')
- field in the Prefix Information option.";
- }
- leaf preferred-lifetime {
- type uint32;
- units "seconds";
- description
- "The value that is placed in the Preferred Lifetime in
- the Prefix Information option, in seconds. The
- designated value of all 1's (0xffffffff) represents
- infinity.";
- }
- leaf autonomous-flag {
- type boolean;
- description
- "The value that is placed in the Autonomous Flag field
- in the Prefix Information option.";
- }
- }
- }
- }
- }
-
- augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" {
- when "../../rt:address-family = 'v6ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments an IPv6 unicast route.";
- leaf destination-prefix {
- type inet:ipv6-prefix;
- description
- "IPv6 destination prefix.";
- }
- }
-
- augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/"
- + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
- when "../../../rt:address-family = 'v6ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments the 'simple-next-hop' case of IPv6 unicast
- routes.";
- leaf next-hop {
- type inet:ipv6-address;
- description
- "IPv6 address of the next-hop.";
- }
- }
-
- augment "/rt:routing-state/rt:next-hop-lists/rt:next-hop-list/"
- + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" {
- when "../rt:address-family = 'v6ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments next-hop list with IPv6 next-hop address.
- routes.";
- leaf next-hop-address {
- type inet:ipv6-address;
- description
- "IPv6 address of the next-hop.";
- }
- }
-
- /* Configuration data */
-
- augment
- "/rt:routing/rt:routing-instance/rt:interfaces/rt:interface" {
- when "/if:interfaces/if:interface[if:name=current()/rt:name]/"
- + "ip:ipv6/ip:enabled='true'" {
- description
- "This augment is only valid for router interfaces with
- enabled IPv6.";
- }
- description
- "Configuration of IPv6-specific parameters of router
- interfaces.";
- container ipv6-router-advertisements {
- description
- "Configuration of IPv6 Router Advertisements.";
- leaf send-advertisements {
- type boolean;
- default "false";
- description
- "A flag indicating whether or not the router sends periodic
- Router Advertisements and responds to Router
- Solicitations.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvSendAdvertisements.";
- }
- leaf max-rtr-adv-interval {
- type uint16 {
- range "4..1800";
- }
- units "seconds";
- default "600";
- description
- "The maximum time allowed between sending unsolicited
- multicast Router Advertisements from the interface.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- MaxRtrAdvInterval.";
- }
- leaf min-rtr-adv-interval {
- type uint16 {
- range "3..1350";
- }
- units "seconds";
- must ". <= 0.75 * ../max-rtr-adv-interval" {
- description
- "The value MUST NOT be greater than 75 % of
- 'max-rtr-adv-interval'.";
- }
- description
- "The minimum time allowed between sending unsolicited
- multicast Router Advertisements from the interface.
-
- The default value to be used operationally if this leaf is
- not configured is determined as follows:
-
- - if max-rtr-adv-interval >= 9 seconds, the default value
- is 0.33 * max-rtr-adv-interval;
-
- - otherwise it is 0.75 * max-rtr-adv-interval.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- MinRtrAdvInterval.";
- }
- leaf managed-flag {
- type boolean;
- default "false";
- description
- "The value to be placed in the 'Managed address
- configuration' flag field in the Router Advertisement.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvManagedFlag.";
- }
- leaf other-config-flag {
- type boolean;
- default "false";
- description
- "The value to be placed in the 'Other configuration' flag
- field in the Router Advertisement.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvOtherConfigFlag.";
- }
- leaf link-mtu {
- type uint32;
- default "0";
- description
- "The value to be placed in MTU options sent by the router.
- A value of zero indicates that no MTU options are sent.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvLinkMTU.";
- }
- leaf reachable-time {
- type uint32 {
- range "0..3600000";
- }
- units "milliseconds";
- default "0";
- description
- "The value to be placed in the Reachable Time field in the
- Router Advertisement messages sent by the router. A value
- of zero means unspecified (by this router).";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvReachableTime.";
- }
- leaf retrans-timer {
- type uint32;
- units "milliseconds";
- default "0";
- description
- "The value to be placed in the Retrans Timer field in the
- Router Advertisement messages sent by the router. A value
- of zero means unspecified (by this router).";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvRetransTimer.";
- }
- leaf cur-hop-limit {
- type uint8;
- description
- "The value to be placed in the Cur Hop Limit field in the
- Router Advertisement messages sent by the router. A value
- of zero means unspecified (by this router).
-
- If this parameter is not configured, the device SHOULD use
- the value specified in IANA Assigned Numbers that was in
- effect at the time of implementation.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvCurHopLimit.
-
- IANA: IP Parameters,
- http://www.iana.org/assignments/ip-parameters";
- }
- leaf default-lifetime {
- type uint16 {
- range "0..9000";
- }
- units "seconds";
- description
- "The value to be placed in the Router Lifetime field of
- Router Advertisements sent from the interface, in seconds.
- It MUST be either zero or between max-rtr-adv-interval and
- 9000 seconds. A value of zero indicates that the router is
- not to be used as a default router. These limits may be
- overridden by specific documents that describe how IPv6
- operates over different link layers.
-
- If this parameter is not configured, the device SHOULD use
- a value of 3 * max-rtr-adv-interval.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvDefaultLifeTime.";
- }
- container prefix-list {
- description
- "Configuration of prefixes to be placed in Prefix
- Information options in Router Advertisement messages sent
- from the interface.
-
- Prefixes that are advertised by default but do not have
- their entries in the child 'prefix' list are advertised
- with the default values of all parameters.
-
- The link-local prefix SHOULD NOT be included in the list
- of advertised prefixes.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) -
- AdvPrefixList.";
- list prefix {
- key "prefix-spec";
- description
- "Configuration of an advertised prefix entry.";
- leaf prefix-spec {
- type inet:ipv6-prefix;
- description
- "IPv6 address prefix.";
- }
- choice control-adv-prefixes {
- default "advertise";
- description
- "The prefix either may be explicitly removed from the
- set of advertised prefixes, or parameters with which
- it is advertised may be specified (default case).";
- leaf no-advertise {
- type empty;
- description
- "The prefix will not be advertised.
-
- This can be used for removing the prefix from the
- default set of advertised prefixes.";
- }
- case advertise {
- leaf valid-lifetime {
- type uint32;
- units "seconds";
- default "2592000";
- description
- "The value to be placed in the Valid Lifetime in
- the Prefix Information option. The designated
- value of all 1's (0xffffffff) represents
- infinity.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6
- (IPv6) - AdvValidLifetime.";
- }
- leaf on-link-flag {
- type boolean;
- default "true";
- description
- "The value to be placed in the on-link flag
- ('L-bit') field in the Prefix Information
- option.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6
- (IPv6) - AdvOnLinkFlag.";
- }
- leaf preferred-lifetime {
- type uint32;
- units "seconds";
- must ". <= ../valid-lifetime" {
- description
- "This value MUST NOT be greater than
- valid-lifetime.";
- }
- default "604800";
- description
- "The value to be placed in the Preferred Lifetime
- in the Prefix Information option. The designated
- value of all 1's (0xffffffff) represents
- infinity.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6
- (IPv6) - AdvPreferredLifetime.";
- }
- leaf autonomous-flag {
- type boolean;
- default "true";
- description
- "The value to be placed in the Autonomous Flag
- field in the Prefix Information option.";
- reference
- "RFC 4861: Neighbor Discovery for IP version 6
- (IPv6) - AdvAutonomousFlag.";
- }
- }
- }
- }
- }
- }
- }
-
- augment "/rt:routing/rt:routing-instance/rt:routing-protocols/"
- + "rt:routing-protocol/rt:static-routes" {
- description
- "This augment defines the configuration of the 'static'
- pseudo-protocol with data specific to IPv6 unicast.";
- container ipv6 {
- description
- "Configuration of a 'static' pseudo-protocol instance
- consists of a list of routes.";
- list route {
- key "destination-prefix";
- ordered-by "user";
- description
- "A user-ordered list of static routes.";
- leaf destination-prefix {
- type inet:ipv6-prefix;
- mandatory "true";
- description
- "IPv6 destination prefix.";
- }
- leaf description {
- type string;
- description
- "Textual description of the route.";
- }
- container next-hop {
- description
- "Configuration of next-hop.";
- grouping next-hop-content {
- description
- "Next-hop content for IPv6 unicast static routes.";
- uses rt:next-hop-content {
- augment "next-hop-options" {
- description
- "Add next-hop address case.";
- leaf next-hop-address {
- type inet:ipv6-address;
- description
- "IPv6 address of the next-hop.";
- }
- }
- }
- }
- choice simple-or-list {
- description
- "Options for next-hops.";
- list multipath-entry {
- if-feature rt:multipath-routes;
- key "name";
- description
- "List of alternative next-hops.";
- leaf name {
- type string;
- description
- "A unique identifier of the next-hop entry.";
- }
- uses next-hop-content;
- uses rt:next-hop-classifiers;
- }
- case simple-next-hop {
- uses next-hop-content;
- }
- }
- }
- }
- }
- }
-
- /* RPC methods */
-
- augment "/rt:fib-route/rt:input/rt:destination-address" {
- when "rt:address-family='v6ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments the 'rt:destination-address' parameter of
- the 'rt:fib-route' operation.";
- leaf address {
- type inet:ipv6-address;
- description
- "IPv6 destination address.";
- }
- }
-
- augment "/rt:fib-route/rt:output/rt:route" {
- when "rt:address-family='v6ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments the reply to the 'rt:fib-route'
- operation.";
- leaf destination-prefix {
- type inet:ipv6-prefix;
- description
- "IPv6 destination prefix.";
- }
- }
-
- augment "/rt:fib-route/rt:output/rt:route/rt:next-hop/"
- + "rt:next-hop-options/rt:simple-next-hop" {
- when "../rt:address-family='v4ur:ipv6-unicast'" {
- description
- "This augment is valid only for IPv6 unicast.";
- }
- description
- "This leaf augments the 'simple-next-hop' case in the reply to
- the 'rt:fib-route' operation.";
- leaf next-hop-address {
- type inet:ipv6-address;
- description
- "IPv6 address of the next-hop.";
- }
- }
- }
-
diff --git a/example/ietf-routing@2014-10-26.yang b/example/ietf-routing@2014-10-26.yang
deleted file mode 100644
index 9c870419..00000000
--- a/example/ietf-routing@2014-10-26.yang
+++ /dev/null
@@ -1,1112 +0,0 @@
- module ietf-routing {
-
- namespace "urn:ietf:params:xml:ns:yang:ietf-routing";
-
- prefix "rt";
-
- import ietf-yang-types {
- prefix "yang";
- revision-date "2013-07-15";
- }
-
- import ietf-interfaces {
- prefix "if";
- revision-date "2014-05-08";
- }
-
- import ietf-inet-types {
- prefix "inet";
- revision-date "2013-07-15";
- }
-
- organization
- "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
-
- contact
- "WG Web:
- WG List:
-
- WG Chair: Thomas Nadeau
-
-
- WG Chair: Juergen Schoenwaelder
-
-
- Editor: Ladislav Lhotka
- ";
-
- description
- "This YANG module defines essential components for the management
- of a routing subsystem.
-
- Copyright (c) 2014 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 XXXX; see the
- RFC itself for full legal notices.";
-
- revision 2014-10-26 {
- description
- "Initial revision.";
- reference
- "RFC XXXX: A YANG Data Model for Routing Management";
- }
-
- /* Features */
-
- feature multiple-ribs {
- description
- "This feature indicates that the server supports user-defined
- RIBS and the framework for passing routes between RIBs.
-
- Servers that do not advertize this feature MUST provide
- exactly one system-controlled RIB per supported address family
- and make them also the default RIBs. These RIBs then appear as
- entries of the list /routing-state/ribs/rib.";
- }
-
- feature multipath-routes {
- description
-
- "This feature indicates that the server supports multipath
- routes that have a list of next-hops.";
- }
-
- feature router-id {
- description
- "This feature indicates that the server supports configuration
- of an explicit 32-bit router ID that is used by some routing
- protocols.
-
- Servers that do not advertize this feature set a router ID
- algorithmically, usually to one of configured IPv4 addresses.
- However, this algorithm is implementation-specific.";
- }
-
- /* Identities */
-
- identity address-family {
- description
- "Base identity from which identities describing address
- families are derived.";
- }
-
- identity ipv4 {
- base address-family;
- description
- "This identity represents IPv4 address family.";
- }
-
- identity ipv6 {
- base address-family;
- description
- "This identity represents IPv6 address family.";
- }
-
- identity routing-instance {
- description
- "Base identity from which identities describing routing
- instance types are derived.";
- }
-
- identity default-routing-instance {
- base routing-instance;
- description
- "This identity represents either a default routing instance, or
- the only routing instance on systems that do not support
- multiple instances.";
- }
-
- identity routing-protocol {
- description
- "Base identity from which routing protocol identities are
- derived.";
- }
-
- identity direct {
- base routing-protocol;
- description
- "Routing pseudo-protocol which provides routes to directly
- connected networks.";
- }
-
- identity static {
- base routing-protocol;
- description
- "Static routing pseudo-protocol.";
- }
-
- identity route-filter {
- description
- "Base identity from which all route filters are derived.";
- }
-
- identity deny-all-route-filter {
- base route-filter;
- description
- "Route filter that blocks all routes.";
- }
-
- identity allow-all-route-filter {
- base route-filter;
- description
- "Route filter that permits all routes.";
- }
-
- /* Type Definitions */
-
- typedef routing-instance-ref {
- type leafref {
- path "/rt:routing/rt:routing-instance/rt:name";
- }
- description
- "This type is used for leafs that reference a routing instance
- configuration.";
- }
-
- typedef routing-instance-state-ref {
-
- type leafref {
- path "/rt:routing-state/rt:routing-instance/rt:name";
- }
- description
- "This type is used for leafs that reference state data of a
- routing instance.";
- }
-
- typedef rib-ref {
- type leafref {
- path "/rt:routing/rt:ribs/rt:rib/rt:name";
- }
- description
- "This type is used for leafs that reference a RIB
- configuration.";
- }
-
- typedef rib-state-ref {
- type leafref {
- path "/rt:routing-state/rt:ribs/rt:rib/rt:name";
- }
- description
- "This type is used for leafs that reference a RIB in state
- data.";
- }
-
- typedef next-hop-list-ref {
- type leafref {
- path "/rt:routing-state/rt:next-hop-lists/rt:next-hop-list/"
- + "rt:id";
- }
- description
- "This type is used for leafs that reference a next-hop list (in
- state data).";
- }
-
- typedef route-filter-ref {
- type leafref {
- path "/rt:routing/rt:route-filters/rt:route-filter/rt:name";
- }
- description
- "This type is used for leafs that reference a route filter
- configuration.";
- }
-
- typedef route-filter-state-ref {
- type leafref {
- path "/rt:routing-state/rt:route-filters/rt:route-filter/"
- + "rt:name";
- }
- description
- "This type is used for leafs that reference state data of a
- route filter.";
- }
-
- typedef route-preference {
- type uint32;
- description
- "This type is used for route preferences.";
- }
-
- /* Groupings */
-
- grouping address-family {
- description
- "This grouping provides a leaf identifying an address
- family.";
- leaf address-family {
- type identityref {
- base address-family;
- }
- mandatory "true";
- description
- "Address family.";
- }
- }
-
- grouping state-entry-id {
- description
- "This grouping provides a unique identifier for entries in
- several operational state lists.";
- leaf id {
- type uint64;
- description
- "Unique numerical identifier of a list entry in operational
- state. It may be used by protocols or tools that inspect
- and/or manipulate operational state data and prefer
- fixed-size integers as list entry handles.
-
- These identifiers are always ephemeral, i.e., they may
- change after a reboot.";
- }
- }
-
- grouping router-id {
- description
- "This grouping provides router ID.";
- leaf router-id {
- type yang:dotted-quad;
- description
- "A 32-bit number in the form of a dotted quad that is used by
- some routing protocols identifying a router.";
- reference
- "RFC 2328: OSPF Version 2.";
- }
- }
-
- grouping next-hop-classifiers {
- description
- "This grouping provides two next-hop classifiers.";
- leaf priority {
- type enumeration {
- enum primary {
- value "1";
- description
- "Primary next-hop.";
- }
- enum backup {
- value "2";
- description
- "Backup next-hop.";
- }
- }
- description
- "Simple priority for distinguishing between primary and
- backup next-hops.
-
- Backup next-hops are used if and only if no primary
- next-hops are reachable.";
- }
- leaf weight {
- type uint8;
- must ". = 0 or not(../../next-hop/weight = 0)" {
- error-message "Illegal combination of zero and non-zero "
- + "next-hop weights.";
- description
- "Next-hop weights must be either all zero (equal
- load-balancing) or all non-zero.";
- }
- description
- "This parameter specifies the weight of the next-hop for load
- balancing. The number specifies the relative fraction of the
- traffic that will use the corresponding next-hop.
-
- A value of 0 represents equal load-balancing.
-
- If both primary and backup next-hops are present, then the
- weights for each priority level are used separately.";
- }
- }
-
- grouping special-next-hop {
- description
- "This grouping provides a leaf with enumeration of special
- next-hops.";
- leaf special-next-hop {
- type enumeration {
- enum blackhole {
- description
- "Silently discard the packet.";
- }
- enum unreachable {
- description
- "Discard the packet and notify the sender with an error
- message indicating that the destination host is
- unreachable.";
- }
- enum prohibit {
- description
- "Discard the packet and notify the sender with an error
- message indicating that the communication is
- administratively prohibited.";
- }
- enum receive {
- description
- "The packet will be received by the local system.";
- }
- }
- description
- "Special next-hop options.";
- }
- }
-
- grouping next-hop-content {
- description
- "Generic parameters of next-hops in static routes.";
- choice next-hop-options {
- mandatory "true";
- description
- "Options for next-hops in static routes.";
- case simple-next-hop {
- description
-
- "Simple next-hop is specified as an outgoing interface,
- next-hop address or both.
-
- Address-family-specific modules are expected to provide
- 'next-hop-address' leaf via augmentation.";
- leaf outgoing-interface {
- type leafref {
- path "/rt:routing/rt:routing-instance/rt:interfaces/"
- + "rt:interface/rt:name";
- }
- description
- "Name of the outgoing interface.";
- }
- }
- case special-next-hop {
- uses special-next-hop;
- }
- }
- }
-
- grouping next-hop-state-content {
- description
- "Generic parameters of next-hops in state data.";
- choice next-hop-options {
- mandatory "true";
- description
- "Options for next-hops in state data.";
- leaf next-hop-list {
- type next-hop-list-ref;
- description
- "Reference to a next-hop list.";
- }
- leaf use-rib {
- type rib-state-ref;
- description
- "Reference to a RIB in which a new look-up is to be
- performed.";
- }
- case simple-next-hop {
- description
- "Simple next-hop is specified as an outgoing interface,
- next-hop address or both.
-
- Address-family-specific modules are expected to provide
- 'next-hop-address' leaf via augmentation.";
- leaf outgoing-interface {
- type leafref {
- path "/rt:routing-state/rt:routing-instance/"
- + "rt:interfaces/rt:interface/rt:name";
- }
- description
- "Name of the outgoing interface.";
- }
- }
- case special-next-hop {
- uses special-next-hop;
- }
- }
- }
-
- grouping route-metadata {
- description
- "Route metadata.";
- leaf source-protocol {
- type identityref {
- base routing-protocol;
- }
- mandatory "true";
- description
- "Type of the routing protocol from which the route
- originated.";
- }
- leaf active {
- type empty;
- description
- "Presence of this leaf indicates that the route is preferred
- among all routes in the same RIB that have the same
- destination prefix.";
- }
- leaf last-updated {
- type yang:date-and-time;
- description
- "Time stamp of the last modification of the route. If the
- route was never modified, it is the time when the route was
- inserted into the RIB.";
- }
- }
-
- /* Operational state data */
-
- container routing-state {
- config "false";
- description
- "Operational state of the routing subsystem.";
- list routing-instance {
- key "name";
- unique "id";
- min-elements "1";
- description
- "Each list entry is a container for operational state data of
- a routing instance.
-
- An implementation MAY create one or more system-controlled
- instances, other user-controlled instances MAY be created by
- configuration.";
- leaf name {
- type string;
- description
- "The name of the routing instance.
-
- For system-controlled instances the name is persistent,
- i.e., it SHOULD NOT change across reboots.";
- }
- uses state-entry-id {
- refine "id" {
- mandatory "true";
- }
- }
- leaf type {
- type identityref {
- base routing-instance;
- }
- description
- "The routing instance type.";
- }
- container default-ribs {
- description
- "Default RIBs used by the routing instance.";
- list default-rib {
- key "address-family";
- description
- "Each list entry specifies the default RIB for one
- address family.
-
- The default RIB is operationally connected to all
- routing protocols for which a connected RIB has not been
- explicitly configured.
-
- The 'direct' pseudo-protocol is always connected to the
- default RIBs.";
- uses address-family;
- leaf rib-name {
- type rib-state-ref;
- mandatory "true";
- description
- "Name of an existing RIB to be used as the default RIB
- for the given routing instance and address family.";
- }
- }
- }
- container interfaces {
- description
- "Network layer interfaces belonging to the routing
- instance.";
- list interface {
- key "name";
- description
- "List of network layer interfaces assigned to the routing
- instance.";
- leaf name {
- type if:interface-state-ref;
- description
- "A reference to the name of a configured network layer
- interface.";
- }
- }
- }
- container routing-protocols {
- description
- "Container for the list of routing protocol instances.";
- list routing-protocol {
- key "type name";
- description
- "Operational state of a routing protocol instance.
-
- An implementation MUST provide exactly one
- system-controlled instance of the type 'direct'. Other
- instances MAY be created by configuration.";
- leaf type {
- type identityref {
- base routing-protocol;
- }
- description
- "Type of the routing protocol.";
- }
- leaf name {
- type string;
- description
- "The name of the routing protocol instance.
-
- For system-controlled instances this name is
- persistent, i.e., it SHOULD NOT change across
- reboots.";
- }
- leaf route-preference {
- type route-preference;
- mandatory "true";
- description
- "The value of route preference (administrative
- distance) assigned to all routes generated by the
- routing protocol instance. A lower value means a more
- preferred route.";
- }
- container connected-ribs {
- description
- "Container for connected RIBs.";
- list connected-rib {
- key "rib-name";
- description
- "List of RIBs to which the routing protocol instance
- is connected.
-
- By default, routes learned by the routing protocol
- instance are installed in all connected RIBs of the
- matching address family, and, conversely, all routes
- from connected RIBs are installed in the routing
- protocol instance. However, routing protocols may
- specify other rules.";
- leaf rib-name {
- type rib-state-ref;
- description
- "Name of an existing RIB.";
- }
- leaf import-filter {
- type route-filter-state-ref;
- description
- "Reference to a route filter that is used for
- filtering routes passed from this routing protocol
- instance to the RIB specified by the 'rib-name'
- sibling node.
-
- If this leaf is not present, the behavior is
- protocol-specific, but typically it means that all
- routes are accepted.";
- }
- leaf export-filter {
- type route-filter-state-ref;
- description
- "Reference to a route filter that is used for
- filtering routes passed from the RIB specified by
- the 'rib-name' sibling node to this routing
- protocol instance.
-
- If this leaf is not present, the behavior is
- protocol-specific - typically it means that all
- routes are accepted.
-
- The 'direct' and 'static' pseudo-protocols accept
- no routes from any RIB.";
- }
- }
- }
- }
- }
- }
- container next-hop-lists {
- description
- "Container for next-hop lists.";
- list next-hop-list {
- key "id";
- description
- "Next-hop list.";
- uses state-entry-id;
- uses address-family;
- list next-hop {
- description
- "Entry in a next-hop list.";
- uses next-hop-state-content;
- uses next-hop-classifiers;
- }
- }
- }
- container ribs {
- description
- "Container for RIBs.";
- list rib {
- key "name";
- unique "id";
- description
- "Each entry represents a RIB identified by the 'name' key.
- All routes in a RIB MUST belong to the same address
- family.
-
- The server MUST provide a system-controlled default RIB
- for each address family, and MAY provide other
- system-controlled RIBs. Additional RIBs MAY be created in
- the configuration.";
- leaf name {
- type string;
- description
- "The name of the RIB.";
- }
- uses state-entry-id {
- refine "id" {
- mandatory "true";
- }
- }
- uses address-family;
- container routes {
- description
- "Current content of the RIB.";
- list route {
- description
- "A RIB route entry. This data node MUST be augmented
- with information specific for routes of each address
- family.";
- leaf route-preference {
- type route-preference;
- description
- "This route attribute, also known as administrative
- distance, allows for selecting the preferred route
- among routes with the same destination prefix. A
- smaller value means a more preferred route.";
- }
- container next-hop {
- description
- "Route's next-hop attribute.";
- uses next-hop-state-content;
- }
- uses route-metadata;
- }
- }
- container recipient-ribs {
- description
- "Container for recipient RIBs.";
- list recipient-rib {
- key "rib-name";
- description
- "List of RIBs that receive routes from this RIB.";
- leaf rib-name {
- type rib-state-ref;
- description
- "The name of the recipient RIB.";
- }
- leaf filter {
- type route-filter-state-ref;
- description
- "A route filter which is applied to the routes passed
- to the recipient RIB.";
- }
- }
- }
- }
- }
- container route-filters {
- description
- "Container for route filters.";
- list route-filter {
- key "name";
- description
- "Route filters are used for filtering and/or manipulating
- routes that are passed between a routing protocol and a
- RIB and vice versa, or between two RIBs.
-
- It is expected that other modules augment this list with
- contents specific for a particular route filter type.";
- leaf name {
- type string;
- description
- "The name of the route filter.";
- }
- leaf type {
- type identityref {
- base route-filter;
- }
- mandatory "true";
- description
- "Type of the route-filter - an identity derived from the
- 'route-filter' base identity.";
- }
- }
- }
- }
-
- /* Configuration Data */
- container routing {
- description
- "Configuration parameters for the routing subsystem.";
- list routing-instance {
- key "name";
- description
- "Configuration of a routing instance.";
- leaf name {
- type string;
- description
- "The name of the routing instance.
-
- For system-controlled entries, the value of this leaf must
- be the same as the name of the corresponding entry in
- state data.
-
- For user-controlled entries, an arbitrary name can be
- used.";
- }
- leaf type {
- type identityref {
- base routing-instance;
- }
- default "rt:default-routing-instance";
- description
- "The type of the routing instance.";
- }
- leaf enabled {
- type boolean;
- default "true";
- description
- "Enable/disable the routing instance.
-
- If this parameter is false, the parent routing instance is
- disabled and does not appear in operational state data,
- despite any other configuration that might be present.";
- }
- uses router-id {
- if-feature router-id;
- description
- "Configuration of the global router ID. Routing protocols
- that use router ID can use this parameter or override it
- with another value.";
- }
- leaf description {
- type string;
- description
- "Textual description of the routing instance.";
- }
- container default-ribs {
- if-feature multiple-ribs;
- description
- "Configuration of the default RIBs used by the routing
- instance.
-
- The default RIB for an addressed family if by default
- connected to all routing protocol instances supporting
- that address family, and always receives direct routes.";
- list default-rib {
- must "address-family=/routing/ribs/rib[name=current()/"
- + "rib-name]/address-family" {
- error-message "Address family mismatch.";
- description
- "The entry's address family MUST match that of the
- referenced RIB.";
- }
- key "address-family";
- description
- "Each list entry configures the default RIB for one
- address family.";
- uses address-family;
- leaf rib-name {
- type string;
- mandatory "true";
- description
- "Name of an existing RIB to be used as the default RIB
- for the given routing instance and address family.";
- }
- }
- }
- container interfaces {
- description
- "Configuration of the routing instance's interfaces.";
- list interface {
- key "name";
- description
- "List of network layer interfaces assigned to the routing
- instance.";
- leaf name {
- type if:interface-ref;
- description
- "A reference to the name of a configured network layer
- interface.";
- }
- }
- }
- container routing-protocols {
- description
- "Configuration of routing protocol instances.";
- list routing-protocol {
- key "type name";
- description
- "Each entry contains configuration of a routing protocol
- instance.";
- leaf type {
- type identityref {
- base routing-protocol;
- }
- description
- "Type of the routing protocol - an identity derived
- from the 'routing-protocol' base identity.";
- }
- leaf name {
- type string;
- description
- "An arbitrary name of the routing protocol instance.";
- }
- leaf description {
- type string;
- description
- "Textual description of the routing protocol
- instance.";
- }
- leaf enabled {
- type boolean;
- default "true";
- description
- "Enable/disable the routing protocol instance.
-
- If this parameter is false, the parent routing
- protocol instance is disabled and does not appear in
- operational state data, despite any other
- configuration that might be present.";
- }
- leaf route-preference {
- type route-preference;
- description
- "The value of route preference (administrative
- distance).
-
- The default value depends on the routing protocol
- type, and may also be implementation-dependent.";
- }
- container connected-ribs {
- description
- "Configuration of connected RIBs.";
- list connected-rib {
- key "rib-name";
- description
- "Each entry configures a RIB to which the routing
- protocol instance is connected.
- If no connected RIB is configured for an address
- family, the routing protocol is connected to the
- default RIB for that address family.";
- leaf rib-name {
- type rib-ref;
- must "../../../type != 'rt:direct' or "
- + "../../../../../default-ribs/ "
- + "default-rib/rib-name=." {
- error-message "The 'direct' protocol can be "
- + "connected only to a default RIB.";
- description
- "For the 'direct' pseudo-protocol, the connected
- RIB must always be a default RIB.";
- }
- description
- "Name of an existing RIB.";
- }
- leaf import-filter {
- type route-filter-ref;
- description
- "Configuration of import filter.";
- }
- leaf export-filter {
- type route-filter-ref;
- description
- "Configuration of export filter.";
- }
- }
- }
- container static-routes {
- when "../type='rt:static'" {
- description
- "This container is only valid for the 'static'
- routing protocol.";
- }
- description
- "Configuration of the 'static' pseudo-protocol.
-
- Address-family-specific modules augment this node with
- their lists of routes.";
- }
- }
- }
- }
- container ribs {
- description
- "Configuration of RIBs.";
- list rib {
- key "name";
- description
- "Each entry represents a configured RIB identified by the
- 'name' key.
-
- Entries having the same key as a system-controlled entry
- of the list /routing-state/ribs/rib are used for
- configuring parameters of that entry. Other entries define
- additional user-controlled RIBs.";
- leaf name {
- type string;
- description
- "The name of the RIB.
-
- For system-controlled entries, the value of this leaf
- must be the same as the name of the corresponding entry
- in state data.
-
- For user-controlled entries, an arbitrary name can be
- used.";
- }
- uses address-family;
- leaf description {
- type string;
- description
- "Textual description of the RIB.";
- }
- container recipient-ribs {
- if-feature multiple-ribs;
- description
- "Configuration of recipient RIBs.";
- list recipient-rib {
- must "rib-name != ../../name" {
- error-message
- "Source and recipient RIBs are identical.";
- description
- "A RIB MUST NOT appear among its recipient RIBs.";
- }
- must "/routing/ribs/rib[name=current()/rib-name]/"
- + "address-family=../../address-family" {
- error-message "Address family mismatch.";
- description
- "Address family of the recipient RIB MUST match that
- of the source RIB.";
- }
- key "rib-name";
- description
- "Each entry configures a recipient RIB.";
- leaf rib-name {
- type rib-ref;
- description
- "The name of the recipient RIB.";
- }
- leaf filter {
- type route-filter-ref;
- description
- "A route filter which is applied to the routes passed
- to the recipient RIB.";
- }
- }
- }
- }
- }
- container route-filters {
- description
- "Configuration of route filters.";
- list route-filter {
- key "name";
- description
- "Each entry configures a named route filter.";
- leaf name {
- type string;
- description
- "The name of the route filter.";
- }
- leaf description {
- type string;
- description
- "Textual description of the route filter.";
- }
- leaf type {
- type identityref {
- base route-filter;
- }
- mandatory "true";
- description
- "Type of the route filter..";
- }
- }
- }
- }
-
- /* RPC methods */
-
- rpc fib-route {
- description
- "Return the active FIB route that a routing-instance uses for
- sending packets to a destination address.";
- input {
- leaf routing-instance-name {
- type routing-instance-state-ref;
- mandatory "true";
- description
- "Name of the routing instance whose forwarding information
- base is being queried.
-
- If the routing instance with name equal to the value of
- this parameter doesn't exist, then this operation SHALL
- fail with error-tag 'data-missing' and error-app-tag
- 'routing-instance-not-found'.";
- }
- container destination-address {
- description
- "Network layer destination address.
-
- Address family specific modules MUST augment this
- container with a leaf named 'address'.";
- uses address-family;
- }
- }
- output {
- container route {
- description
- "The active route for the specified destination.
-
- If the routing instance has no active route for the
- destination address, no output is returned - the server
- SHALL send an containing a single element
- .
-
- Address family specific modules MUST augment this list
- with appropriate route contents.";
- uses address-family;
- container next-hop {
- description
- "Route's next-hop attribute.";
- uses next-hop-state-content;
- }
- uses route-metadata;
- }
- }
- }
-
- rpc route-count {
- description
- "Return the current number of routes in a RIB.";
- input {
- leaf rib-name {
- type rib-state-ref;
- mandatory "true";
- description
- "Name of the RIB.
-
- If the RIB with name equal to the value of this parameter
- doesn't exist, then this operation SHALL fail with
- error-tag 'data-missing' and error-app-tag
- 'rib-not-found'.";
- }
- }
- output {
- leaf number-of-routes {
- type uint64;
- mandatory "true";
- description
- "Number of routes in the RIB.";
- }
- }
- }
- }
-
diff --git a/extras/rpm/clixon.spec b/extras/rpm/clixon.spec
index c6ee5f70..20e77c04 100644
--- a/extras/rpm/clixon.spec
+++ b/extras/rpm/clixon.spec
@@ -36,7 +36,7 @@ This package contains header files for CLIXON.
%setup
%build
-%configure --with-cligen=%{cligen_prefix} --without-keyvalue
+%configure --with-cligen=%{cligen_prefix}
make
%install
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_config.h.in b/include/clixon_config.h.in
index 1cc881df..f8678e33 100644
--- a/include/clixon_config.h.in
+++ b/include/clixon_config.h.in
@@ -1,8 +1,5 @@
/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */
-/* Clixon data dir for system yang files etc */
-#undef CLIXON_DATADIR
-
/* Location for apps to find default config file */
#undef CLIXON_DEFAULT_CONFIG
@@ -42,6 +39,9 @@
/* Define to 1 if you have the `crypt' library (-lcrypt). */
#undef HAVE_LIBCRYPT
+/* Define to 1 if you have the `curl' library (-lcurl). */
+#undef HAVE_LIBCURL
+
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
@@ -133,4 +133,4 @@
`char[]'. */
#undef YYTEXT_POINTER
-#include "clixon_custom.h"
+#include
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index 8bad0f92..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,3 +51,10 @@ int strverscmp (__const char *__s1, __const char *__s2);
*/
#define XMLNS_YANG_ONLY 1
+/* 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).
+ * All new references should use @datamodel (or CLICON_CLI_MODEL_TREENAME).
+ * whereas older code used @datamodel:tree.
+ */
+#define CLICON_CLI_MODEL_TREENAME_PATCH
diff --git a/lib/Makefile.in b/lib/Makefile.in
index eba8991d..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
#
@@ -80,5 +80,5 @@ distclean: clean
do (cd $$i; $(MAKE) $(MFLAGS) distclean); done; \
(cd clixon; $(MAKE) $(MFLAGS) $@)
-tags:
+TAGS:
find $(srcdir) -name '*.[chyl]' -print | etags -
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 c454835f..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.
@@ -87,6 +87,7 @@
#include
#include
#include
+#include
/*
* Global variables generated by Makefile
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/datastore/keyvalue/clixon_qdb.h b/lib/clixon/clixon_nacm.h
similarity index 55%
rename from datastore/keyvalue/clixon_qdb.h
rename to lib/clixon/clixon_nacm.h
index d2d38785..4b016dd5 100644
--- a/datastore/keyvalue/clixon_qdb.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.
@@ -31,43 +31,43 @@
***** END LICENSE BLOCK *****
+ * XML sort and earch functions when used with YANG
*/
-
-#ifndef _CLIXON_QDB_H_
-#define _CLIXON_QDB_H_
-
+#ifndef _CLIXON_NACM_H
+#define _CLIXON_NACM_H
/*
- * Low level API
+ * Constants
*/
+/* RFC8341 defines a "recovery session" as outside the scope.
+ * Clixon defines this user as having special admin rights to expemt from
+ * all access control enforcements
+ */
+#define NACM_RECOVERY_USER "_nacm_recovery"
-struct db_pair {
- char *dp_key; /* database key */
- char *dp_matched; /* Matched component of key */
- char *dp_val; /* pointer to vector of lvalues */
- int dp_vlen; /* length of vector of lvalues */
+/*
+ * Types
+ */
+/* NACM access rights,
+ * Note that these are not the same as netconf operations
+ * @see rfc8341 3.2.2
+ * @see enum operation_type Netconf operations
+ */
+enum nacm_access{
+ NACM_CREATE,
+ NACM_READ,
+ NACM_UPDATE,
+ NACM_DELETE,
+ NACM_EXEC
};
-
/*
* Prototypes
- */
-int db_init(char *file);
+ */
+int nacm_rpc(char *rpc, char *module, char *username, cxobj *xnacm, cbuf *cbret);
+int nacm_datanode_read(cxobj *xt, cxobj **xvec, size_t xlen, char *username, cxobj *nacm_xtree);
+int nacm_datanode_write(cxobj *xt, cxobj *xr, enum nacm_access access,
+ char *username, cxobj *xnacm, cbuf *cbret);
+int nacm_access_pre(clicon_handle h, char *username, cxobj **xnacmp);
+int nacm_access(char *mode, cxobj *xnacmin, char *username);
-int db_delete(char *file);
-
-int db_set(char *file, char *key, void *data, size_t datalen);
-
-int db_get(char *file, char *key, void *data, size_t *datalen);
-
-int db_get_alloc(char *file, char *key, void **data, size_t *datalen);
-
-int db_del(char *file, char *key);
-
-int db_exists(char *file, char *key);
-
-int db_regexp(char *file, char *regexp, const char *label,
- struct db_pair **pairs, int noval);
-
-char *db_sanitize(char *rx, const char *label);
-
-#endif /* _CLIXON_QDB_H_ */
+#endif /* _CLIXON_NACM_H */
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 f06660f0..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);
@@ -105,8 +109,11 @@ int clicon_option_del(clicon_handle h, const char *name);
static inline char *clicon_configfile(clicon_handle h){
return clicon_option_str(h, "CLICON_CONFIGFILE");
}
-static inline char *clicon_yang_dir(clicon_handle h){
- return clicon_option_str(h, "CLICON_YANG_DIR");
+static inline char *clicon_yang_main_file(clicon_handle h){
+ return clicon_option_str(h, "CLICON_YANG_MAIN_FILE");
+}
+static inline char *clicon_yang_main_dir(clicon_handle h){
+ return clicon_option_str(h, "CLICON_YANG_MAIN_DIR");
}
static inline char *clicon_yang_module_main(clicon_handle h){
return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN");
@@ -132,6 +139,9 @@ static inline char *clicon_clispec_dir(clicon_handle h){
static inline char *clicon_cli_mode(clicon_handle h){
return clicon_option_str(h, "CLICON_CLI_MODE");
}
+static inline char *clicon_cli_model_treename(clicon_handle h){
+ return clicon_option_str(h, "CLICON_CLI_MODEL_TREENAME");
+}
static inline char *clicon_sock(clicon_handle h){
return clicon_option_str(h, "CLICON_SOCK");
}
@@ -165,10 +175,11 @@ int clicon_quiet_mode_set(clicon_handle h, int val);
yang_spec * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
-#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
+cxobj * clicon_nacm_ext(clicon_handle h);
+int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
+
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 59e25913..da732586 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.
@@ -41,6 +41,12 @@
/*
* Constants
*/
+/* If rpc call does not have a namespace (eg w xmlns) then use the default NETCONF
+ * 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
@@ -80,11 +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 */
-
-/* Sort and binary search of XML children
- * Experimental
+/* Iterate through modules to find the matching datanode
+ * or rpc if no xmlns attribute specifies namespace.
+ * 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_STRICT clixon configure option
*/
-extern int xml_child_sort;
+extern int _CLICON_XML_NS_STRICT;
/*
* Prototypes
@@ -92,8 +100,10 @@ extern int xml_child_sort;
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);
@@ -109,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);
@@ -118,6 +130,8 @@ int xml_childvec_set(cxobj *x, int len);
cxobj *xml_new(char *name, cxobj *xn_parent, yang_stmt *spec);
yang_stmt *xml_spec(cxobj *x);
int xml_spec_set(cxobj *x, yang_stmt *spec);
+cg_var *xml_cv(cxobj *x);
+int xml_cv_set(cxobj *x, cg_var *cv);
cxobj *xml_find(cxobj *xn_parent, char *name);
int xml_addsub(cxobj *xp, cxobj *xc);
@@ -126,9 +140,13 @@ int xml_purge(cxobj *xc);
int xml_child_rm(cxobj *xp, int i);
int xml_rm(cxobj *xc);
int xml_rootchild(cxobj *xp, int i, cxobj **xcp);
+int xml_rootchild_node(cxobj *xp, cxobj *xc);
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..042d414f 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.
@@ -78,7 +78,7 @@ typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop);
/* Type of xmldb put function */
-typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
+typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
/* Type of xmldb copy function */
typedef int (xmldb_copy_t)(xmldb_handle xh, const char *from, const char *to);
@@ -139,7 +139,7 @@ int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop);
-int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, cbuf *cbret);
+int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, const char *db, int pid);
int xmldb_unlock(clicon_handle h, const char *db);
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index 6a5a32fe..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,8 +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_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,
@@ -57,12 +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..d38b91cf 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,16 +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_cmp(const void* arg1, const void* arg2);
+int xml_child_spec(cxobj *x, cxobj *xp, yang_spec *yspec, yang_stmt **yp);
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);
int xml_insert_pos(cxobj *x0, char *name, int yangi, enum rfc_6020 keyword,
@@ -53,6 +47,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 3399d4fa..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.
@@ -49,15 +49,18 @@
/*
* Types
*/
+struct xml;
/*! YANG keywords from RFC6020.
* See also keywords generated by yacc/bison in clicon_yang_parse.tab.h, but they start with K_
* instead of Y_
* Wanted to unify these (K_ and Y_) but gave up for several reasons:
* - Dont want to expose a generated yacc file to the API
* - Cant use the symbols in this file because yacc needs token definitions
+ * - Use 0 as no keyword --> therefore start enumeration with 1.
*/
enum rfc_6020{
- Y_ACTION = 0,
+ Y_ACTION = 1,
+ Y_ANYDATA,
Y_ANYXML,
Y_ARGUMENT,
Y_AUGMENT,
@@ -93,6 +96,7 @@ enum rfc_6020{
Y_MANDATORY,
Y_MAX_ELEMENTS,
Y_MIN_ELEMENTS,
+ Y_MODIFIER,
Y_MODULE,
Y_MUST,
Y_NAMESPACE,
@@ -156,7 +160,6 @@ typedef enum yang_class yang_class;
*/
#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES)
-
/* Yang schema node .
* See RFC 7950 Sec 3:
* o schema node: A node in the schema tree. One of action, container,
@@ -173,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 */
@@ -191,6 +193,8 @@ struct yang_stmt{
char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */
+ /*--------------here common for all -------*/
+ char *ys_extra; /* For unknown */
cg_var *ys_cv; /* cligen variable. See ys_populate()
Following stmts have cv:s:
leaf: for default value
@@ -198,7 +202,7 @@ struct yang_stmt{
config: boolean true or false
mandatory: boolean true or false
fraction-digits for fraction-digits
- unkown-stmt (argument)
+ unknown-stmt (argument)
*/
cvec *ys_cvec; /* List of stmt-specific variables
Y_RANGE: range_min, range_max
@@ -208,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 */
@@ -247,26 +250,28 @@ 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(yang_node *yn, int keyword, char *argument);
+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 *dir,
- const char *revision, yang_spec *ysp, yang_stmt **ymodp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg);
-int yang_abs_schema_nodeid(yang_spec *yspec, char *schema_nodeid,
+int yang_abs_schema_nodeid(yang_spec *yspec, yang_stmt *ys,
+ char *schema_nodeid,
enum rfc_6020 keyword, yang_stmt **yres);
int yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid,
enum rfc_6020 keyword, yang_stmt **yres);
@@ -274,8 +279,10 @@ 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 *dir, char *revision, yang_spec *yspec, yang_stmt **ymodp);
-int yang_spec_parse_file(clicon_handle h, char *filename, char *dir, yang_spec *yspec, yang_stmt **ymodp);
+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 787c19c8..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
#
@@ -70,10 +70,11 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_handle.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
+ clixon_yang_cardinality.c \
clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
- clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c
+ clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
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 794b35f2..4b412e20 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"
@@ -91,21 +93,30 @@ enum childtype{
};
/*! 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_notype(x, CX_ATTR);
if (xml_type(x) != CX_ELMNT)
return -1; /* n/a */
- if (xml_child_nr(x) == 0)
+ if (clen == 0)
return NULL_CHILD;
- if (xml_child_nr(x) > 1)
+ if (clen > 1)
return ANY_CHILD;
- xc1 = xml_child_i(x, 0); /* From here exactly one child */
- if (xml_child_nr(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;
@@ -154,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,
@@ -163,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;
@@ -171,12 +188,20 @@ 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)
+ || (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)
+ || (nsx && ns2 && strcmp(nsx,ns2)==0))
+ eqprev++;
+ }
if (eqprev && eqnext)
array = MIDDLE_ARRAY;
else if (eqprev)
@@ -191,42 +216,58 @@ array_eval(cxobj *xprev,
return array;
}
-/*! Escape a json string
+/*! Escape a json string as well as decode xml cdata
+ * And a
*/
-static char *
-json_str_escape(char *str)
+static int
+json_str_escape_cdata(cbuf *cb,
+ char *str)
{
- int i, j;
- char *snew;
+ int retval = -1;
+ char *snew = NULL;
+ int i;
+ int esc = 0; /* cdata escape */
- j = 0;
for (i=0;i", strlen("]]>")) == 0){
+ esc=0;
+ i += strlen("]]>")-1;
+ }
+ else
+ cprintf(cb, "%c", str[i]);
+ break;
default: /* fall thru */
- snew[j++]=str[i];
+ cprintf(cb, "%c", str[i]);
+ break;
}
- snew[j++]='\0';
- return snew;
+ if ((snew = strdup(cbuf_get(cb))) ==NULL){
+ clicon_err(OE_XML, errno, "strdup");
+ goto done;
+ }
+ retval = 0;
+ done:
+ return retval;
}
/*! Do the actual work of translating XML to JSON
@@ -267,13 +308,13 @@ json_str_escape(char *str)
+----------+--------------+--------------+--------------+
*/
static int
-xml2json1_cbuf(cbuf *cb,
- cxobj *x,
+xml2json1_cbuf(cbuf *cb,
+ cxobj *x,
enum array_element_type arraytype,
- int level,
- int pretty,
- int flat,
- int bodystr)
+ int level,
+ int pretty,
+ int flat,
+ int bodystr)
{
int retval = -1;
int i;
@@ -281,10 +322,30 @@ xml2json1_cbuf(cbuf *cb,
enum childtype childt;
enum array_element_type xc_arraytype;
yang_stmt *ys;
+ yang_stmt *ymod; /* yang module */
+ yang_spec *yspec = NULL; /* yang spec */
int bodystr0=1;
+ char *prefix=NULL; /* prefix / local namespace name */
+ char *namespace=NULL; /* namespace uri */
+ char *modname=NULL; /* Module name */
+ int commas;
- childt = childtype(x);
- ys = xml_spec(x);
+ /* If x is labelled with a default namespace, it should be translated
+ * to a module name.
+ * Harder if x has a prefix, then that should also be translated to associated
+ * module name
+ */
+ 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 */
+ if (namespace && yspec &&
+ (ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
+ modname = ymod->ys_argument;
+ }
+ childt = child_type(x);
if (pretty==2)
cprintf(cb, "#%s_array, %s_child ",
arraytype2str(arraytype),
@@ -292,22 +353,21 @@ xml2json1_cbuf(cbuf *cb,
switch(arraytype){
case BODY_ARRAY:{
if (bodystr){
- char *str;
- if ((str = json_str_escape(xml_value(x))) == NULL)
+ /* XXX String if right type */
+ cprintf(cb, "\"");
+ if (json_str_escape_cdata(cb, xml_value(x)) < 0)
goto done;
- cprintf(cb, "\"%s\"", str);
- free(str);
+ cprintf(cb, "\"");
}
else
cprintf(cb, "%s", xml_value(x));
-
break;
}
case NO_ARRAY:
if (!flat){
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
- if (xml_namespace(x))
- cprintf(cb, "%s:", xml_namespace(x));
+ if (modname) /* XXX should remove this? */
+ cprintf(cb, "%s:", modname);
cprintf(cb, "%s\": ", xml_name(x));
}
switch (childt){
@@ -326,8 +386,8 @@ xml2json1_cbuf(cbuf *cb,
case FIRST_ARRAY:
case SINGLE_ARRAY:
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
- if (xml_namespace(x))
- cprintf(cb, "%s:", xml_namespace(x));
+ if (modname)
+ cprintf(cb, "%s:", modname);
cprintf(cb, "%s\": ", xml_name(x));
level++;
cprintf(cb, "[%s%*s",
@@ -368,7 +428,7 @@ xml2json1_cbuf(cbuf *cb,
break;
}
/* Check for typed sub-body if:
- * arracytype=* but chilt-type is BODY_CHILD
+ * arraytype=* but child-type is BODY_CHILD
* This is code for writing 42 as "a":42 and not "a":"42"
*/
if (childt == BODY_CHILD && ys!=NULL &&
@@ -391,8 +451,11 @@ xml2json1_cbuf(cbuf *cb,
break;
}
+ commas = xml_child_nr_notype(x, CX_ATTR) - 1;
for (i=0; i 0) {
cprintf(cb, ",%s", pretty?"\n":"");
+ --commas;
+ }
}
switch (arraytype){
case BODY_ARRAY:
@@ -491,10 +556,24 @@ xml2json_cbuf(cbuf *cb,
{
int retval = 1;
int level = 0;
+ char *prefix;
+ char *namespace;
cprintf(cb, "%*s{%s",
pretty?level*JSON_INDENT:0,"",
pretty?"\n":"");
+ /* If x is labelled with a default namespace, it should be translated
+ * to a module name.
+ * Harder if x has a prefix, then that should also be translated to associated
+ * module name
+ */
+ prefix = xml_prefix(x);
+ if (xml2ns(x, prefix, &namespace) < 0)
+ goto done;
+ /* Some complexities in grafting namespace in existing trees to new */
+ if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL && namespace)
+ if (xmlns_set(x, prefix, namespace) < 0)
+ goto done;
if (xml2json1_cbuf(cb,
x,
NO_ARRAY,
@@ -510,7 +589,7 @@ xml2json_cbuf(cbuf *cb,
return retval;
}
-/*! Translate a vector of xml objects to JSON CLigen buffer.
+/*! Translate a vector of xml objects to JSON Cligen buffer.
* This is done by adding a top pseudo-object, and add the vector as subs,
* and then not printing the top pseudo-object using the 'flat' option.
* @param[out] cb Cligen buffer to write to
@@ -534,12 +613,21 @@ xml2json_cbuf_vec(cbuf *cb,
int i;
cxobj *xp = NULL;
cxobj *xc;
+ char *prefix;
+ char *namespace;
- if ((xp = xml_new("", NULL, NULL)) == NULL)
+ if ((xp = xml_new("xml2json", NULL, NULL)) == NULL)
goto done;
+ /* Some complexities in grafting namespace in existing trees to new */
for (i=0; 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
new file mode 100644
index 00000000..60fa115b
--- /dev/null
+++ b/lib/src/clixon_nacm.c
@@ -0,0 +1,910 @@
+/*
+ *
+ ***** 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 *****
+
+ * NACM code according to RFC8341 Network Configuration Access Control Model
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "clixon_config.h" /* generated by config & autoconf */
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* cligen */
+#include
+
+/* clixon */
+#include "clixon_queue.h"
+#include "clixon_hash.h"
+#include "clixon_err.h"
+#include "clixon_log.h"
+#include "clixon_string.h"
+#include "clixon_handle.h"
+#include "clixon_yang.h"
+#include "clixon_xml.h"
+#include "clixon_options.h"
+#include "clixon_netconf_lib.h"
+#include "clixon_xpath_ctx.h"
+#include "clixon_xpath.h"
+#include "clixon_xml_db.h"
+#include "clixon_nacm.h"
+
+/*! Match nacm access operations according to RFC8341 3.4.4.
+ * Incoming RPC Message Validation Step 7 (c)
+ * The rule's "access-operations" leaf has the "exec" bit set or
+ * has the special value "*".
+ * @param[in] mode Primary mode, eg read, create, update, delete, exec
+ * @param[in] mode2 Secondary mode, eg "write"
+ * @retval 0 No match
+ * @retval 1 Match
+ * @note access_operations is bit-fields
+ */
+static int
+match_access(char *access_operations,
+ char *mode,
+ char *mode2)
+{
+ if (access_operations==NULL)
+ return 0;
+ if (strcmp(access_operations,"*")==0)
+ return 1;
+ if (strstr(access_operations, mode)!=NULL)
+ return 1;
+ if (mode2 && strstr(access_operations, mode2)!=NULL)
+ return 1;
+ return 0;
+}
+
+/*! Match nacm single rule. Either match with access or deny. Or not match.
+ * @param[in] rpc rpc name
+ * @param[in] module Yang module name
+ * @param[in] xrule NACM rule XML tree
+ * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
+ * @retval -1 Error
+ * @retval 0 Matching rule AND Not access and cbret set
+ * @retval 1 Matching rule AND Access
+ * @retval 2 No matching rule Goto step 10
+ * @see RFC8341 3.4.4. Incoming RPC Message Validation
+ 7.(cont) A rule matches if all of the following criteria are met:
+ * The rule's "module-name" leaf is "*" or equals the name of
+ the YANG module where the protocol operation is defined.
+
+ * Either (1) the rule does not have a "rule-type" defined or
+ (2) the "rule-type" is "protocol-operation" and the
+ "rpc-name" is "*" or equals the name of the requested
+ protocol operation.
+
+ * The rule's "access-operations" leaf has the "exec" bit set or
+ has the special value "*".
+ */
+static int
+nacm_rule_rpc(char *rpc,
+ char *module,
+ cxobj *xrule)
+{
+ int retval = -1;
+ char *module_rule; /* rule module name */
+ char *rpc_rule;
+ char *access_operations;
+
+ /* 7a) The rule's "module-name" leaf is "*" or equals the name of
+ the YANG module where the protocol operation is defined. */
+ if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
+ goto nomatch;
+ if (strcmp(module_rule,"*") && strcmp(module_rule,module))
+ goto nomatch;
+ /* 7b) Either (1) the rule does not have a "rule-type" defined or
+ (2) the "rule-type" is "protocol-operation" and the
+ "rpc-name" is "*" or equals the name of the requested
+ protocol operation. */
+ if ((rpc_rule = xml_find_body(xrule, "rpc-name")) == NULL){
+ if (xml_find_body(xrule, "path") || xml_find_body(xrule, "notification-name"))
+ goto nomatch;
+ }
+ if (rpc_rule && (strcmp(rpc_rule, "*") && strcmp(rpc_rule, rpc)))
+ goto nomatch;
+ /* 7c) The rule's "access-operations" leaf has the "exec" bit set or
+ has the special value "*". */
+ access_operations = xml_find_body(xrule, "access-operations");
+ if (!match_access(access_operations, "exec", NULL))
+ goto nomatch;
+ retval = 1;
+ done:
+ return retval;
+ nomatch:
+ retval = 0;
+ goto done;
+}
+
+/*! Process nacm incoming RPC message validation steps
+ * @param[in] module Yang module name
+ * @param[in] rpc rpc name
+ * @param[in] username User name of requestor
+ * @param[in] xnacm NACM xml tree
+ * @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
+ * @retval -1 Error
+ * @retval 0 Not access and cbret set
+ * @retval 1 Access
+ * @see RFC8341 3.4.4. Incoming RPC Message Validation
+ * @see nacm_datanode_write
+ * @see nacm_datanode_read
+ */
+int
+nacm_rpc(char *rpc,
+ char *module,
+ char *username,
+ cxobj *xnacm,
+ cbuf *cbret)
+{
+ int retval = -1;
+ cxobj *xrule;
+ cxobj **gvec = NULL; /* groups */
+ size_t glen;
+ cxobj *rlist;
+ cxobj **rlistvec = NULL; /* rule-list */
+ size_t rlistlen;
+ cxobj **rvec = NULL; /* rules */
+ size_t rlen;
+ int i, j;
+ char *exec_default = NULL;
+ char *gname;
+ char *action;
+ int match= 0;
+
+ /* 3. If the requested operation is the NETCONF
+ protocol operation, then the protocol operation is permitted.
+ */
+ if (strcmp(rpc, "close-session") == 0)
+ goto permit;
+ /* 4. Check all the "group" entries to see if any of them contain a
+ "user-name" entry that equals the username for the session
+ making the request. (If the "enable-external-groups" leaf is
+ "true", add to these groups the set of groups provided by the
+ transport layer.) */
+ if (username == NULL)
+ goto step10;
+ /* User's group */
+ if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
+ goto done;
+ /* 5. If no groups are found, continue with step 10. */
+ if (glen == 0)
+ goto step10;
+ /* 6. Process all rule-list entries, in the order they appear in the
+ configuration. If a rule-list's "group" leaf-list does not
+ match any of the user's groups, proceed to the next rule-list
+ entry. */
+ if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
+ goto done;
+ for (i=0; i or , then the protocol operation
+ is denied. */
+ if (strcmp(rpc, "kill-session")==0 || strcmp(rpc, "delete-config")==0){
+ if (netconf_access_denied(cbret, "application", "default deny") < 0)
+ goto done;
+ goto deny;
+ }
+ /* 12. If the "exec-default" leaf is set to "permit", then permit the
+ protocol operation; otherwise, deny the request. */
+ exec_default = xml_find_body(xnacm, "exec-default");
+ if (exec_default ==NULL || strcmp(exec_default, "permit")==0)
+ goto permit;
+ if (netconf_access_denied(cbret, "application", "default deny") < 0)
+ goto done;
+ goto deny;
+ permit:
+ retval = 1;
+ done:
+ clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
+ if (gvec)
+ free(gvec);
+ if (rlistvec)
+ free(rlistvec);
+ if (rvec)
+ free(rvec);
+ return retval;
+ deny: /* Here, cbret must contain a netconf error msg */
+ assert(cbuf_len(cbret));
+ retval = 0;
+ goto done;
+}
+
+/*---------------------------------------------------------------
+ * Datanode/module read and write
+ */
+
+/*! We have a rule matching user group. Now match proper write operation and module
+ * @retval -1 Error
+ * @retval 0 No Match
+ * @retval 1 Match
+ * @see RFC8341 3.4.5. Data Node Access Validation point (6)
+ */
+static int
+nacm_rule_datanode(cxobj *xt,
+ cxobj *xr,
+ cxobj *xrule,
+ enum nacm_access access)
+{
+ int retval = -1;
+ char *path;
+ char *access_operations;
+ char *module_rule; /* rule module name */
+ yang_stmt *ys;
+ yang_stmt *ymod;
+ char *module;
+ cxobj *xpath; /* xpath match */
+ cxobj *xp; /* parent */
+
+ /* 6a) The rule's "module-name" leaf is "*" or equals the name of
+ * the YANG module where the requested data node is defined. */
+ if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
+ goto nomatch;
+ if (strcmp(module_rule,"*")!=0){
+ if ((ys = xml_spec(xr)) == NULL)
+ goto nomatch;
+ ymod = ys_module(ys);
+ module = ymod->ys_argument;
+ if (strcmp(module, module_rule) != 0)
+ goto nomatch;
+ }
+
+ /* 6b) Either (1) the rule does not have a "rule-type" defined or
+ (2) the "rule-type" is "data-node" and the "path" matches the
+ requested data node, action node, or notification node. A
+ path is considered to match if the requested node is the node
+ specified by the path or is a descendant node of the path.*/
+ if ((path = xml_find_body(xrule, "path")) == NULL){
+ if (xml_find_body(xrule, "rpc-name") ||xml_find_body(xrule, "notification-name"))
+ goto nomatch;
+ }
+ access_operations = xml_find_body(xrule, "access-operations");
+ switch (access){
+ case NACM_READ:
+ /* 6c) For a "read" access operation, the rule's "access-operations"
+ leaf has the "read" bit set or has the special value "*" */
+ if (!match_access(access_operations, "read", NULL))
+ goto nomatch;
+ break;
+ case NACM_CREATE:
+ /* 6d) For a "create" access operation, the rule's "access-operations"
+ leaf has the "create" bit set or has the special value "*". */
+ if (!match_access(access_operations, "create", "write"))
+ goto nomatch;
+ break;
+ case NACM_DELETE:
+ /* 6e) For a "delete" access operation, the rule's "access-operations"
+ leaf has the "delete" bit set or has the special value "*". */
+ if (!match_access(access_operations, "delete", "write"))
+ goto nomatch;
+ break;
+ case NACM_UPDATE:
+ /* 6f) For an "update" access operation, the rule's "access-operations"
+ leaf has the "update" bit set or has the special value "*". */
+ if (!match_access(access_operations, "update", "write"))
+ goto nomatch;
+ break;
+ default:
+ break;
+ }
+ /* Here module is matched, now check for path if any NYI */
+ if (path){
+ if ((xpath = xpath_first(xt, "%s", path)) == NULL)
+ goto nomatch;
+ /* The requested node xr is the node specified by the path or is a
+ * descendant node of the path:
+ * xmatch is one of xvec[] or an ancestor of the xvec[] nodes.
+ */
+ xp = xr;
+ do {
+ if (xpath == xp)
+ goto match;
+ } while ((xp = xml_parent(xp)) != NULL);
+ }
+ match:
+ retval = 1;
+ done:
+ return retval;
+ nomatch:
+ retval = 0;
+ goto done;
+}
+
+/*! Go through all rules for a requested node
+ * @param[in] xt XML root tree with "config" label
+ * @param[in] xr Requested node (node in xt)
+ * @param[in] gvec NACM groups where user is member
+ * @param[in] glen Length of gvec
+ * @param[in] rlistvec NACM rule-list entries
+ * @param[in] rlistlen Length of rlistvec
+ * @param[out] xrulep If set, then points to matching rule
+ */
+static int
+nacm_data_read_xr(cxobj *xt,
+ cxobj *xr,
+ cxobj **gvec,
+ size_t glen,
+ cxobj **rlistvec,
+ size_t rlistlen,
+ cxobj **xrulep)
+{
+ int retval = -1;
+ int i, j;
+ cxobj *rlist;
+ char *gname;
+ cxobj **rvec = NULL; /* rules */
+ size_t rlen;
+ cxobj *xrule = NULL;
+ int match = 0;
+
+ for (i=0; i and Operations
+ * Data nodes to which the client does not have read access are silently
+ * omitted, along with any descendants, from the message.
+ * For NETCONF filtering purposes, the selection criteria are applied to the
+ * subset of nodes that the user is authorized to read, not the entire datastore.
+ * @note assume mode is internal or external, not disabled
+ * @node There is unclarity on what "a data node" means wrt a read operation.
+ * Suppose a tree is accessed. Is "the data node" just the top of the tree?
+ * (1) Or is it all nodes, recursively, in the data-tree?
+ * (2) Or is the datanode only the requested tree, NOT the whole datatree?
+ * Example:
+ * - r0 default permit/deny *
+ * - rule r1 to permit/deny /a
+ * - rule r2 to permit/deny /a/b
+ * - rule r3 to permit/deny /a/b/c
+ * - rule r4 to permit/deny /d
+
+ * - read access on /a/b which returns ?
+ * permit - t; deny - f
+ * r1 | r2 | r3 | result (r0 and r4 are dont cares - dont match)
+ * ------+------+------+---------
+ * t | t | t |
+ * t | t | f |
+ * t | f | t |
+ * t | f | f |
+ * f | t | t |
+ * f | t | f |
+ * f | f | t |
+ * f | f | f |
+ *
+ * - read access on / which returns ?
+ * permit - t; deny - f
+ * r0 | r4 | result
+ * ------+------+---------
+ * t | t |
+ * t | f |
+ * f | t |
+ * f | f |
+ * Algorithm 1, based on an xml tree XT:
+ * The special variable ACTION can have values:
+ * permit/deny/default
+ * 1. Traverse through all nodes x in xt. Set ACTION to default
+ * - Find first exact matching rule r of non-default rules r1-rn on x
+ * - if found set ACTION to r->action (permit/deny).
+ * - if ACTION is deny purge x and all descendants
+ * - if ACTION is permit, mark x as PERMIT
+ * - continue traverse
+ * 2. If default action is
+ * 2. Traverse through all nodes x in xt. Set ACTION to default r0->action
+ * - if x is marked as PERMIT
+ * - if ACTION is deny deny purge x and all descendants
+ *
+ * Algorithm 2 (based on requested node set XRS which are sub-trees in XT).
+ * For each XR in XRS, check match with rule r1-rn, r0.
+ * 1. XR is PERMIT
+ * - Recursively match subtree to find reject sub-trees and purge.
+ * 2. XR is REJECT. Purge XR.
+ * Module-rule w no path is implicit rule on top node.
+ *
+ * A module rule has the "module-name" leaf set but no nodes from the
+ * "rule-type" choice set.
+ * @see RFC8341 3.4.5. Data Node Access Validation
+ * @see nacm_datanode_write
+ * @see nacm_rpc
+ */
+int
+nacm_datanode_read(cxobj *xt,
+ cxobj **xrvec,
+ size_t xrlen,
+ char *username,
+ cxobj *xnacm)
+{
+ int retval = -1;
+ cxobj **gvec = NULL; /* groups */
+ size_t glen;
+ cxobj *xr;
+ cxobj **rlistvec = NULL; /* rule-list */
+ size_t rlistlen;
+ int i;
+ char *read_default = NULL;
+ cxobj *xrule;
+ char *action;
+
+ /* 3. Check all the "group" entries to see if any of them contain a
+ "user-name" entry that equals the username for the session
+ making the request. (If the "enable-external-groups" leaf is
+ "true", add to these groups the set of groups provided by the
+ transport layer.) */
+ if (username == NULL)
+ goto step9;
+ /* User's group */
+ if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
+ goto done;
+ /* 4. If no groups are found, continue with step 9. */
+ if (glen == 0)
+ goto step9;
+ /* 5. Process all rule-list entries, in the order they appear in the
+ configuration. If a rule-list's "group" leaf-list does not
+ match any of the user's groups, proceed to the next rule-list
+ entry. */
+ if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
+ goto done;
+ for (i=0; i"
- "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)
@@ -983,15 +1012,9 @@ netconf_module_load(clicon_handle h)
{
int retval = -1;
cxobj *xc;
- // cxobj *x;
yang_spec *yspec;
yspec = clicon_dbspec_yang(h);
- /* Load yang spec */
- if (yang_spec_parse_module(h, "ietf-netconf", CLIXON_DATADIR, NULL, yspec, NULL)< 0)
- goto done;
- if (yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec, NULL)< 0)
- goto done;
if ((xc = clicon_conf_xml(h)) == NULL){
clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
goto done;
@@ -1005,6 +1028,12 @@ netconf_module_load(clicon_handle h)
goto done;
if (xml_parse_string("ietf-netconf:xpath ", yspec, &xc) < 0)
goto done;
+
+ /* Load yang spec */
+ if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
+ goto done;
+ if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
+ goto done;
retval = 0;
done:
return retval;
diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c
index 62202def..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.
@@ -33,8 +33,6 @@
*
* CLICON options
- * See clicon_tutorial appendix and clicon.conf.cpp.cpp on documentation of
- * options
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@@ -65,6 +63,7 @@
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
+#include "clixon_xml_sort.h"
#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
@@ -112,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.
@@ -137,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");
@@ -168,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);
@@ -178,8 +186,16 @@ parse_configfile(clicon_handle h,
__FUNCTION__, name, body);
continue;
}
+ /* hard-coded exceptions for configure options that are leaf-lists (not leaf)
+ * They must be accessed directly by looping over clicon_conf_xml(h)
+ */
if (strcmp(name,"CLICON_FEATURE")==0)
continue;
+ if (strcmp(name,"CLICON_YANG_DIR")==0)
+ continue;
+ /* Used as an arg to this fn */
+ if (strcmp(name,"CLICON_CONFIGFILE")==0)
+ continue;
if (hash_add(copt,
name,
body,
@@ -190,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)
@@ -197,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.
@@ -236,20 +290,30 @@ 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;
}
- /* Parse clixon yang spec */
- if (yang_parse(h, NULL, "clixon-config", CLIXON_DATADIR, NULL, yspec, NULL) < 0)
- goto done;
- /* Read configfile */
+ /* 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;
if (xml_rootchild(xconfig, 0, &xconfig) < 0)
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;
+ /* Parse clixon yang spec */
+ if (yang_spec_parse_module(h, "clixon-config", NULL, yspec) < 0)
+ goto done;
+ clicon_conf_xml_set(h, NULL);
+ if (xconfig)
+ xml_free(xconfig);
+ /* Read configfile second time now with check yang spec */
+ if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
+ goto done;
+ if (xml_rootchild(xconfig, 0, &xconfig) < 0)
+ goto done;
+ /* Set clixon_conf pointer to handle */
+ clicon_conf_xml_set(h, xconfig);
+ /* 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;
@@ -588,6 +652,48 @@ clicon_dbspec_yang_set(clicon_handle h,
return 0;
}
+/*! Get NACM (rfc 8341) XML parse tree if external not in std xml config
+ * @param[in] h Clicon handle
+ * @retval xn XML NACM tree, or NULL
+ * @note only used if config option CLICON_NACM_MODE is external
+ * @see clicon_nacm_ext_set
+ */
+cxobj *
+clicon_nacm_ext(clicon_handle h)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+ size_t len;
+ void *p;
+
+ if ((p = hash_value(cdat, "nacm_xml", &len)) != NULL)
+ return *(cxobj **)p;
+ return NULL;
+}
+
+/*! Set NACM (rfc 8341) external XML parse tree, free old if any
+ * @param[in] h Clicon handle
+ * @param[in] xn XML Nacm tree
+ * @note only used if config option CLICON_NACM_MODE is external
+ * @see clicon_nacm_ext
+ */
+int
+clicon_nacm_ext_set(clicon_handle h,
+ cxobj *xn)
+{
+ clicon_hash_t *cdat = clicon_data(h);
+ cxobj *xo;
+
+ if ((xo = clicon_nacm_ext(h)) != NULL)
+ xml_free(xo);
+ /* It is the pointer to xn that should be copied by hash,
+ so we send a ptr to the ptr to indicate what to copy.
+ */
+ if (hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL)
+ return -1;
+ return 0;
+}
+
+
#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
/*! Get YANG specification for clixon config
* Must use hash functions directly since they are not strings.
@@ -638,7 +744,7 @@ clicon_conf_xml(clicon_handle h)
return NULL;
}
-/*! Set YANG specification for Clixon system options and features
+ /*! Set YANG specification for Clixon system options and features
* ys must be a malloced pointer
*/
int
diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c
index 7710ae80..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.
@@ -269,8 +269,7 @@ clixon_plugins_load(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
/* Get plugin objects names from plugin directory */
- if((ndp = clicon_file_dirent(dir, &dp,
- regexp?regexp:"(.so)$", S_IFREG))<0)
+ if((ndp = clicon_file_dirent(dir, &dp, regexp?regexp:"(.so)$", S_IFREG)) < 0)
goto done;
/* Load all plugins */
for (i = 0; i < ndp; i++) {
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 9de878c0..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 "
" "
" ",
@@ -826,7 +826,8 @@ clicon_rpc_debug(clicon_handle h,
char *username;
username = clicon_username_get(h);
- if ((msg = clicon_msg_encode("%d ", username?username:"", level)) == NULL)
+ /* XXX: hardcoded example yang, should be clixon-config!!! */
+ 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..5a8a2851 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,147 @@ 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])) /* trim from front */
+ s++;
+ for (i=strlen(s)-1; i>=0; i--){ /* trim from rear */
+ if (isblank(s[i]))
+ s[i] = '\0';
+ else
+ break;
+ }
+ return s;
+}
+
+/*! Transform from XSD regex to posix ERE
+ * The usecase is that Yang (RFC7950) supports XSD regexpressions but CLIgen supports
+ * Current translations:
+ * \d --> [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 https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs
+ * @see https://www.regular-expressions.info/posixbrackets.html#class translation
+ * Translation is not complete but covers some character sequences:
+ * \d decimal digit
+ * \w alphanum + underscore
+ */
+int
+regexp_xsd2posix(char *xsd,
+ char **posix)
+{
+ int retval = -1;
+ cbuf *cb = NULL;
+ char x;
+ int i;
+ int esc;
+
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ esc=0;
+ for (i=0; ia
* b
*
+ * From https://www.w3.org/TR/2009/REC-xml-names-20091208
+ * Definitions:
+ * - XML namespace: is identified by a URI reference [RFC3986]; element and
+ * attribute names may be placed in an XML namespace using the mechanisms
+ * described in this specification.
+ * - Expanded name: is a pair consisting of a namespace name and a local name.
+ * - Namespace name: For a name N in a namespace identified by a URI I, the
+ * "namespace name" is I.
+ * For a name N that is not in a namespace, the "namespace name" has no value.
+ * - Local name: In either case the "local name" is N (also "prefix")
+ * It is this combination of the universally managed URI namespace with the
+ * vocabulary's local names that is effective in avoiding name clashes.
*/
struct xml{
char *x_name; /* name of node */
- char *x_namespace; /* namespace, if any */
+ char *x_prefix; /* namespace localname N, called prefix */
struct xml *x_up; /* parent node in hierarchy if any */
struct xml **x_childvec; /* vector of children nodes */
int x_childvec_len;/* length of vector */
@@ -112,8 +126,21 @@ struct xml{
int x_flags; /* Flags according to XML_FLAG_* */
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */
+ cg_var *x_cv; /* Cached value as cligen variable
+ (eg xml_cmp) */
};
+/*
+ * Variables
+ */
+/* Iterate 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."
+ * @see CLICON_XML_NS_STRICT clixon configure option
+ */
+int _CLICON_XML_NS_STRICT = 1;
+
/* Mapping between xml type <--> string */
static const map_str2int xsmap[] = {
{"error", CX_ERROR},
@@ -175,27 +202,27 @@ xml_name_set(cxobj *xn,
* @retval namespace of xml node
*/
char*
-xml_namespace(cxobj *xn)
+xml_prefix(cxobj *xn)
{
- return xn->x_namespace;
+ return xn->x_prefix;
}
/*! Set name space of xnode, namespace is copied
* @param[in] xn xml node
- * @param[in] namespace new namespace, null-terminated string, copied by function
+ * @param[in] localname new namespace, null-terminated string, copied by function
* @retval -1 on error with clicon-err set
* @retval 0 OK
*/
int
-xml_namespace_set(cxobj *xn,
- char *namespace)
+xml_prefix_set(cxobj *xn,
+ char *localname)
{
- if (xn->x_namespace){
- free(xn->x_namespace);
- xn->x_namespace = NULL;
+ if (xn->x_prefix){
+ free(xn->x_prefix);
+ xn->x_prefix = NULL;
}
- if (namespace){
- if ((xn->x_namespace = strdup(namespace)) == NULL){
+ if (localname){
+ if ((xn->x_prefix = strdup(localname)) == NULL){
clicon_err(OE_XML, errno, "strdup");
return -1;
}
@@ -203,12 +230,90 @@ xml_namespace_set(cxobj *xn,
return 0;
}
-/*! See if xmlns:= exists, if so return
+/*! 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
+ * @param[in] prefix prefix/ns localname. If NULL then return default.
+ * @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
+ * @retval 0 OK
+ * @retval -1 Error
+ * @see xmlns_check XXX can these be merged?
+ */
+int
+xml2ns(cxobj *x,
+ char *prefix,
+ char **namespace)
+{
+ int retval = -1;
+ char *ns;
+ cxobj *xp;
+
+ if (prefix != NULL) /* xmlns:="" */
+ ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
+ else /* xmlns="" */
+ ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
+
+ /* namespace not found, try parent */
+ if (ns == NULL){
+ if ((xp = xml_parent(x)) != NULL){
+ if (xml2ns(xp, prefix, &ns) < 0)
+ goto done;
+ }
+ /* If no parent, return default namespace if defined */
+#if defined(DEFAULT_XML_RPC_NAMESPACE)
+ else
+ ns = DEFAULT_XML_RPC_NAMESPACE;
+#endif
+ }
+ if (namespace)
+ *namespace = ns;
+ retval = 0;
+ done:
+ 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:[=]