Merge branch 'develop' into nacm

This commit is contained in:
Olof hagsand 2019-01-12 12:29:34 +01:00
commit 9a7ce8e06d
194 changed files with 5941 additions and 3452 deletions

5
.gitignore vendored
View file

@ -14,10 +14,9 @@ lib/Makefile
lib/*/Makefile
autom4te.cache/
clixon.conf.cpp
clixon.mk
config.log
config.status
TAGS
apps/backend/clixon_backend
apps/backend/test
@ -45,5 +44,5 @@ build-root/*.tar.xz
build-root/*.rpm
build-root/rpmbuild
test/public
test/site.sh
doc/html

View file

@ -1,47 +1,105 @@
# Clixon Changelog
## 3.9.0 (Preliminary Target: 31 December 2018)
## 3.9.0 (Preliminary Target: Mid-January 2019)
### Planned new features
* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
* [Roadmap](ROADMAP.md)
### Major New features
* NACM extension (RFC8341)
* NACM module support (RFC8341 A1+A2)
* Recovery user "_nacm_recovery" added.
* Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
* Example user changed adm1 to andy to comply with RFC8341 example
* Correct XML namespace handling
* XML multiple modules was based on non-strict semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208) as well as strict Netconf and Restconf namespace handling, which causes problems with overlapping names and false positives, and most importantly, with standard conformance.
* There are still the following non-strict namespace handling:
* Everything in ietf-netconf base syntax with namespace `urn:ietf:params:xml:ns:netconf:base:1.0` is default and need not be explicitly given
* edit-config xpath select statement does not support namespaces
* notifications do not support namespaces.
* Below see netconf old (but wrong) netconf RPC:
```
<rpc>
<my-own-method/>
</rpc>
<rpc-reply>
<route>
<address-family>ipv4</address-family>
</route>
</rpc-reply>
```
This is the currently correct Netconf RPC:
```
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> # xmlns may be ommitted
<my-own-method xmlns="urn:example:my-own">
</rpc>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing">
<address-family>ipv4</address-family>
</route>
</rpc-reply>
```
* Another example for restconf rpc with new correct syntax. Note that while Netconf uses xmlns attribute syntax, Restconf uses module name prefix. First the request:
```
POST http://localhost/restconf/operations/example:example)
Content-Type: application/yang-data+json
{
"example:input":{
"x":0
}
}
```
then the reply:
```
HTTP/1.1 200 OK
{
"example:output": {
"x": "0",
"y": "42"
}
}
```
* To keep previous non-strict namespace handling (backwards compatible), set CLICON_XML_NS_STRICT to false.
* See https://github.com/clicon/clixon/issues/49
* Yang code upgrade (RFC7950)
* YANG parser cardinality checked (https://github.com/clicon/clixon/issues/48)
* See https://github.com/clicon/clixon/issues/84
* RPC method input parameters validated
* see https://github.com/clicon/clixon/issues/47
* Support of submodule, include and belongs-to.
* Openconfig yang specs parsed: https://github.com/openconfig/public
* Parsing of standard yang files supported, such as:
* https://github.com/openconfig/public - except [https://github.com/clicon/clixon/issues/60].
* See [test/test_openconfig.sh]
* https://github.com/YangModels/yang - except vendor-specific specs
* See [test/test_yangmodels.sh]
* Improved "unknown" handling
* More precise Yang validation and better error messages
* Example: adding bad-, missing-, or unknown-element error messages, instead of operation-failed.
* Validation of mandatory choice and recursive mandatory containers
* Yang load file configure options changed
* `CLICON_YANG_DIR` is changed from a single directory to a path of directories
* Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
* CLICON_YANG_MAIN_FILE Provides a filename with a single module filename.
* CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
* Correct XML namespace handling
* XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration:
```
<rpc><my-own-method></rpc> # Wrong but accepted
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> # Correct
<my-own-method xmlns="http://example.net/me/my-own/1.0">
</rpc>
```
* To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default)
* XML to JSON translator support for mapping xmlns attribute to module name prefix.
* Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0"
* See https://github.com/clicon/clixon/issues/49
* NACM extension (RFC8341)
* NACM module support (RFC8341 A1+A2)
* Recovery user "_nacm_recovery" added.
* Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
* Example user changed adm1 to andy to comply with RFC8341 example
* Added -o "<option>=<value>" command-line option to all programs: backend, cli, netconf, restconf.
* Any config option from file can be overrided by giving them on command-line.
### API changes on existing features (you may need to change your code)
* Date-and-time type now properly uses ISO 8601 UTC timezone designators.
* Eg 2008-09-21T18:57:21.003456 is changed to 2008-09-21T18:57:21.003456Z
* Renamed yang file `ietf-netconf-notification@2008-07-01.yang` to `clixon-rfc5277`.
* Fixed validation problems, see [https://github.com/clicon/clixon/issues/62]
* Name confusion, the file is manually constructed from the rfc.
* Changed prefix to `ncevent`
* Stricter YANG choice validation leads to enforcement of structures like: `choice c{ mandatory true; leaf x` statements. `x` was not previously enforced.
* Many hand-crafted validation messages have been removed and replaced with generic validations, which may lead to changed rpc-error messages.
* CLICON_XML_SORT option (in clixon-config.yang) has been removed and set to true permanently. Unsorted XML lists leads to slower performance and old obsolete code can be removed.
* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.
* Removed `delete-config` support for candidate db since it is not supported in RFC6241.
* Switched the order of `error-type` and `error-tag` in all netconf and restconf error messages to comply to RFC order.
* Yang parser is stricter (see above) which may break parsing of existing yang specs.
* XML namespace handling is corrected (see above)
* For backward compatibility set config option CLICON_XML_NS_ITERATE
* For backward compatibility set config option CLICON_XML_NS_LOOSE
* Yang parser functions have changed signatures. Please check the source if you call these functions.
* Add `<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>` to your configuration file, or corresponding CLICON_DATADIR directory for Clixon system yang files.
* Change all @datamodel:tree to @datamodel in all CLI specification files
@ -49,6 +107,13 @@
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
### Minor changes
* Cligen uses posix regex while yang uses XSD. It differs in some aspects. A translator function has been added for `\d` -> `[0-9]` translation, there may be more.
* Added new clixon-lib yang module for internal netconf protocol. Currently only extends the standard with a debug RPC.
* Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK.
* This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath`
* Added new xml functions for specific types: `xml_child_nr_notype`, `xml_child_nr_notype`, `xml_child_i_type`, `xml_find_type`.
* Added example_rpc RPC to example backend
* Renamed xml_namespace[_set]() to xml_prefix[_set]()
* Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3)
* Removed return value ymodp from yang parse functions (eg yang_parse()).
@ -60,8 +125,15 @@
* <!DOCTYPE (ie DTD) is not supported.
### Corrected Bugs
* [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)
### Known issues
* debug rpc added in example application (should be in clixon-config).
@ -265,7 +337,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:
```
<crypto xmlns:x="urn:example:des">x:des3</crypto>
```

View file

@ -97,6 +97,9 @@ EOF
## New release
What to think about when doing a new release.
* valgrind for memory leaks
* New clixon-config.yang revision?
Tagging:
* git merge --no-ff develop
* change CLIXON_VERSION in configure.ac
* git tag -a <version"

View file

@ -1,4 +1,4 @@
Copyright 2009-2018 Olof Hagsand and Benny Holmgren
Copyright 2009-2019 Olof Hagsand and Benny Holmgren
CLIXON is dual license.

View file

@ -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
#

View file

@ -105,6 +105,23 @@ The standards covered include:
Not supported:
- !DOCTYPE (ie DTD)
Historically, Clixon has not until 3.9 made strict namespace
enforcing. For example, the following non-strict netconf was
previously accepted:
```
<rpc><my-own-method/></rpc>
```
In 3.9, the same statement should be, for example:
```
<rpc><my-own-method xmlns="urn:example:my-own"/></rpc>
```
Note that base netconf syntax is still not enforced but recommended:
```
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<my-own-method xmlns="urn:example:my-own"/>
</rpc>
```
Yang
====
YANG and XML is the heart of Clixon. Yang modules are used as a
@ -125,8 +142,10 @@ However, the following YANG syntax modules are not implemented:
- belongs-to
Restrictions on Yang types are as follows:
- The range statement does not support multiple values (RFC7895 sec 9.2.4)
- The range statement for built-in integers does not support multiple values (RFC7950 9.2.4)
- The length statement for built-in strings does not support multiple values (RFC7950 9.4.4)
- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60)
- default values on leaf-lists (RFC7950 7.7.2)
Netconf
=======
@ -152,6 +171,9 @@ Clixon does not support the following netconf features:
- edit-config config-text
- edit-config operation
Some other deviations from the RFC:
- edit-config xpath select statement does not support namespaces
Restconf
========
Clixon Restconf is a daemon based on FastCGI C-API. Instructions are available to

View file

@ -1,27 +1,42 @@
# Clixon roadmap
Not in prio order (yet)
## High prio
- NACM (RFC 8341)
- Module rules (done)
- Data node rules (read/create/delete/update/execute)
- Special handling of the initial startup transaction to avoid exit at startup
- Possibly - draft-wu-netconf-restconf-factory-restore-03
- Handle revisions to data model.
- Possibly draft-wang-netmod-module-revision-management-01
- XML [Namespace handling](https://github.com/clicon/clixon/issues/49)
- XML
- [Namespace handling](https://github.com/clicon/clixon/issues/49)
## Medium prio:
- Input validation on custom RPCs/ (done)
- [Sanity checks](https://github.com/clicon/clixon/issues/47)
- Support for XML regex's.
- Currently Posix extended regular expressions
- Support a plugin callback that is invoked when copy-config is called.
- Preserve CLI command history across sessions. The up/down arrows
## Low prio:
- Provide a client library to access netconf APIs provided by system services.
- Netconf backend (Clixon acts as netconf controller)
- Support for restconf call-home (RFC 8071)
Not prioritized:
- Support for restconf PATCH method
- NETCONF
- Support for additional Netconf [edit-config modes](https://github.com/clicon/clixon/issues/53)
- Netconf [framing](https://github.com/clicon/clixon/issues/50)
- [Sanity checks](https://github.com/clicon/clixon/issues/47)
- [Child ordering](https://github.com/clicon/clixon/issues/22)
- Netconf backend (Clixon acts as netconf controller)
- Restconf
- Query parameters
- NACM (RFC 8341) is somewhat limited
- Extend with data node access (read/create/delete/update/execute)
- Streams (netconf and restconf)
- Extend native stream mode with external persistent timeseries database, eg influxdb.
- Jenkins CI/CD and webhooks
- YANG
- [Cardinality](https://github.com/clicon/clixon/issues/48)
- RFC 6022 [NETCONF monitoring](https://github.com/clicon/clixon/issues/39)
- Factory default Setting - draft-wu-netconf-restconf-factory-restore-03
- Deviation, belongs-to, min/max-elements, action, unique
- Deviation, min/max-elements, action, unique
- Containers
- [Docker improvements](https://github.com/clicon/clixon/issues/44)
- Kubernetes Helm chart definition

View file

@ -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
#

View file

@ -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
#

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -292,7 +292,7 @@ client_get_streams(clicon_handle h,
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 OK
* @retval 1 Statedata callback failed
* @retval 1 Statedata callback failed (clicon_err called)
*/
static int
client_statedata(clicon_handle h,
@ -309,15 +309,15 @@ client_statedata(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
(retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
(retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
(retval = yang_modules_state_get(h, yspec, xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
if ((retval = client_get_streams(h, yspec, xpath, "clixon-rfc5277", "netconf", xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040"))
if ((retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
goto done;
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895"))
if ((retval = yang_modules_state_get(h, yspec, xret)) != 0)
goto done;
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
goto done;
/* Code complex to filter out anything that is outside of xpath */
@ -339,7 +339,7 @@ client_statedata(clicon_handle h,
/* reset flag */
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
retval = 0;
retval = 0; /* OK */
done:
if (xvec)
free(xvec);
@ -363,7 +363,6 @@ from_client_get(clicon_handle h,
char *xpath = "/";
cxobj *xret = NULL;
int ret;
cbuf *cbx = NULL; /* Assist cbuf */
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
@ -379,33 +378,24 @@ from_client_get(clicon_handle h,
clicon_err_reset();
if ((ret = client_statedata(h, xpath, &xret)) < 0)
goto done;
if (ret == 0){ /* OK */
cprintf(cbret, "<rpc-reply>");
if (xret==NULL)
cprintf(cbret, "<data/>");
else{
if (xml_name_set(xret, "data") < 0)
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
}
else { /* 1 Error from callback */
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
if (ret == 1){ /* Error from callback (error in xret) */
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
goto done;
}
cprintf(cbx, "Internal error:%s", clicon_err_reason);
if (netconf_operation_failed(cbret, "rpc", cbuf_get(cbx))< 0)
goto done;
clicon_log(LOG_NOTICE, "%s Error in backend_statedata_call:%s", __FUNCTION__, xml_name(xe));
goto ok;
}
cprintf(cbret, "<rpc-reply>"); /* OK */
if (xret==NULL)
cprintf(cbret, "<data/>");
else{
if (xml_name_set(xret, "data") < 0)
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
goto done;
}
cprintf(cbret, "</rpc-reply>");
ok:
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
if (xret)
xml_free(xret);
return retval;
@ -433,14 +423,16 @@ from_client_edit_config(clicon_handle h,
int non_config = 0;
yang_spec *yspec;
cbuf *cbx = NULL; /* Assist cbuf */
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9");
goto done;
}
if ((target = netconf_db_find(xn, "target")) == NULL){
clicon_err(OE_XML, 0, "db not found");
goto done;
if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
@ -468,12 +460,12 @@ from_client_edit_config(clicon_handle h,
}
}
if ((xc = xpath_first(xn, "config")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>config</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
goto done;
goto ok;
}
else{
/* <config> yang spec may be set to anyxml by ingress yang check,...*/
/* <config> yang spec may be set to anyxmly by ingress yang check,...*/
if (xml_spec(xc) != NULL)
xml_spec_set(xc, NULL);
/* Populate XML with Yang spec (why not do this in parser?)
@ -491,24 +483,27 @@ from_client_edit_config(clicon_handle h,
/* Cant do this earlier since we dont have a yang spec to
* the upper part of the tree, until we get the "config" tree.
*/
if (xml_child_sort && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
if (xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
if (xmldb_put(h, target, operation, xc, cbret) < 0){
if ((ret = xmldb_put(h, target, operation, xc, cbret)) < 0){
clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
goto done;
goto ok;
}
if (ret == 0)
goto ok;
}
assert(cbuf_len(cbret) == 0);
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
if (!cbuf_len(cbret))
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
return retval;
} /* from_client_edit_config */
/*! Internal message: Lock database
@ -530,7 +525,7 @@ from_client_lock(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@ -589,7 +584,7 @@ from_client_unlock(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((db = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@ -651,7 +646,7 @@ from_client_kill_session(clicon_handle h,
if ((x = xml_find(xe, "session-id")) == NULL ||
(str = xml_find_value(x, "body")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>session-id</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "session-id", NULL) < 0)
goto done;
goto ok;
}
@ -709,7 +704,7 @@ from_client_copy_config(clicon_handle h,
cbuf *cbx = NULL; /* Assist cbuf */
if ((source = netconf_db_find(xe, "source")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done;
goto ok;
}
@ -724,7 +719,7 @@ from_client_copy_config(clicon_handle h,
goto ok;
}
if ((target = netconf_db_find(xe, "target")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@ -777,7 +772,7 @@ from_client_delete_config(clicon_handle h,
if ((target = netconf_db_find(xe, "target")) == NULL||
strcmp(target, "running")==0){
if (netconf_missing_element(cbret, "protocol", "<bad-element>target</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "target", NULL) < 0)
goto done;
goto ok;
}
@ -856,7 +851,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//stopTime")) != NULL){
if ((stoptime = xml_find_value(x, "body")) != NULL &&
str2time(stoptime, &stop) < 0){
if (netconf_bad_element(cbret, "application", "<bad-element>stopTime</bad-element>", "Expected timestamp") < 0)
if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
goto done;
goto ok;
}
@ -864,7 +859,7 @@ from_client_create_subscription(clicon_handle h,
if ((x = xpath_first(xe, "//startTime")) != NULL){
if ((starttime = xml_find_value(x, "body")) != NULL &&
str2time(starttime, &start) < 0){
if (netconf_bad_element(cbret, "application", "<bad-element>startTime</bad-element>", "Expected timestamp") < 0)
if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
goto done;
goto ok;
}
@ -925,7 +920,7 @@ from_client_debug(clicon_handle h,
char *valstr;
if ((valstr = xml_find_body(xe, "level")) == NULL){
if (netconf_missing_element(cbret, "application", "<bad-element>level</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "application", "level", NULL) < 0)
goto done;
goto ok;
}
@ -971,6 +966,7 @@ from_client_msg(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
pid = ce->ce_pid;
yspec = clicon_dbspec_yang(h);
/* Return netconf message. Should be filled in by the dispatch(sub) functions
* as wither rpc-error or by positive response.
*/
@ -978,28 +974,26 @@ from_client_msg(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_msg_decode(msg, &xt) < 0){
if (clicon_msg_decode(msg, yspec, &xt) < 0){
if (netconf_malformed_message(cbret, "XML parse error")< 0)
goto done;
goto reply;
}
/* Get yang spec */
yspec = clicon_dbspec_yang(h); /* XXX maybe move to clicon_msg_decode? */
if ((x = xpath_first(xt, "/rpc")) == NULL){
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
goto done;
goto reply;
}
/* Populate incoming XML tree with yang */
/* Populate incoming XML tree with yang -
* should really have been dealt with by decode above
* maybe not necessary since it should be */
if (xml_spec_populate_rpc(h, x, yspec) < 0)
goto done;
if ((ret = xml_yang_validate_rpc(x, cbret)) < 0)
goto done;
if ((ret = xml_yang_validate_rpc(x)) < 0)
goto done;
if (ret == 0){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
if (ret == 0)
goto reply;
}
xe = NULL;
username = xml_find_value(x, "username");
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
@ -1059,7 +1053,7 @@ from_client_msg(clicon_handle h,
}
else if (strcmp(rpc, "validate") == 0){
if ((db = netconf_db_find(xe, "source")) == NULL){
if (netconf_missing_element(cbret, "protocol", "<bad-element>source</bad-element>", NULL) < 0)
if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
goto done;
goto reply;
}

View file

@ -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.

View file

@ -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; i<td->td_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; i<td->td_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; i<td->td_alen; i++){
x2 = td->td_avec[i];
if (xml_apply0(x2, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_add, NULL) < 0)
if ((ret = xml_yang_validate_add(x2, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
retval = 0;
// ok:
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Common code of candidate_validate and candidate_commit
* @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state
* @retval 0 OK
* @retval -1 Fatal error or validation fail
* @note Need to differentiate between error and validation fail
* @retval -1 Error - or validation failed (but cbret not set)
* @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
* @note Need to differentiate between error and validation fail
* (only done for generic_validate)
*/
static int
validate_common(clicon_handle h,
char *candidate,
transaction_data_t *td)
transaction_data_t *td,
cbuf *cbret)
{
int retval = -1;
yang_spec *yspec;
int i;
cxobj *xn;
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
/* 2. Parse xml trees */
/* 2. Parse xml trees
* This is the state we are going from */
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
goto done;
/* This is the state we are going to */
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
goto done;
/* Validate the target state. It is not completely clear this should be done
* here. It is being made in generic_validate below.
* But xml_diff requires some basic validation, at least check that yang-specs
* have been assigned
*/
if ((ret = xml_yang_validate_all_top(td->td_target, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
@ -193,9 +223,12 @@ validate_common(clicon_handle h,
if (plugin_transaction_begin(h, td) < 0)
goto done;
/* 5. Make generic validation on all new or changed data. */
if (generic_validate(yspec, td) < 0)
/* 5. Make generic validation on all new or changed data.
Note this is only call that uses 3-values */
if ((ret = generic_validate(yspec, td, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
/* 6. Call plugin transaction validate callbacks */
if (plugin_transaction_validate(h, td) < 0)
@ -204,9 +237,12 @@ validate_common(clicon_handle h,
/* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0)
goto done;
retval = 0;
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Do a diff between candidate and running, then start a commit transaction
@ -216,57 +252,69 @@ validate_common(clicon_handle h,
* do something more drastic?
* @param[in] h Clicon handle
* @param[in] candidate A candidate database, not necessarily "candidate"
* @retval 0 OK
* @retval -1 Fatal error or validation fail
* @retval -1 Error - or validation failed
* @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
* @note Need to differentiate between error and validation fail
* (only done for validate_common)
*/
int
candidate_commit(clicon_handle h,
char *candidate)
char *candidate,
cbuf *cbret)
{
int retval = -1;
int retval = -1;
transaction_data_t *td = NULL;
int ret;
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with validate) */
if (validate_common(h, candidate, td) < 0)
/* Common steps (with validate). Load candidate and running and compute diffs
* Note this is only call that uses 3-values
*/
if ((ret = validate_common(h, candidate, td, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
/* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0)
goto done;
/* Optionally write (potentially modified) tree back to candidate */
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
if (xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL) < 0)
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
if ((ret = xmldb_put(h, candidate, OP_REPLACE, td->td_target, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
/* 8. Success: Copy candidate to running
*/
if (xmldb_copy(h, candidate, "running") < 0)
goto done;
/* 9. Call plugin transaction end callbacks */
plugin_transaction_end(h, td);
/* 8. Copy running back to candidate in case end functions updated running */
if (xmldb_copy(h, "running", candidate) < 0){
/* ignore errors or signal major setback ? */
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
goto done;
}
retval = 0;
retval = 1;
done:
/* In case of failure, call plugin transaction termination callbacks */
if (retval < 0 && td)
/* In case of failure (or error), call plugin transaction termination callbacks */
if (retval < 1 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
fail:
retval = 0;
goto done;
}
/*! Commit changes from candidate to running
@ -283,6 +331,7 @@ from_client_commit(clicon_handle h,
int retval = -1;
int piddb;
cbuf *cbx = NULL; /* Assist cbuf */
int ret;
/* Check if target locked by other client */
piddb = xmldb_islocked(h, "running");
@ -296,9 +345,10 @@ from_client_commit(clicon_handle h,
goto done;
goto ok;
}
if (candidate_commit(h, "candidate") < 0){ /* Assume validation fail, nofatal */
if ((ret = candidate_commit(h, "candidate", cbret)) < 0){ /* Assume validation fail, nofatal */
clicon_debug(1, "Commit candidate failed");
if (netconf_invalid_value(cbret, "protocol", clicon_err_reason)< 0)
if (ret < 0)
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
goto ok;
}
@ -367,6 +417,7 @@ from_client_validate(clicon_handle h,
{
int retval = -1;
transaction_data_t *td = NULL;
int ret;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
if (netconf_invalid_value(cbret, "protocol", "No such database")< 0)
@ -379,17 +430,20 @@ from_client_validate(clicon_handle h,
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with commit) */
if (validate_common(h, db, td) < 0){
if ((ret = validate_common(h, db, td, cbret)) < 1){
clicon_debug(1, "Validate %s failed", db);
/* XXX: candidate_validate should have proper error handling */
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
if (ret < 0){
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
}
goto ok;
}
/* Optionally write (potentially modified) tree back to candidate */
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
if (xmldb_put(h, "candidate", OP_REPLACE, td->td_target, NULL) < 0)
goto done;
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target, cbret)) < 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;

View file

@ -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_ */

View file

@ -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.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -73,7 +73,7 @@
#include "backend_handle.h"
/* Command line options to be passed to getopt(3) */
#define BACKEND_OPTS "hD:f:l:d:b:Fza:u:P:1s:c:g:y:x:" /* substitute s: for IRCc:r */
#define BACKEND_OPTS "hD:f:l:d:b:Fza:u:P:1s:c:g:y:x:o:"
#define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log"
@ -154,7 +154,8 @@ usage(clicon_handle h,
"\t-g <group>\tClient membership required to this group (default: %s)\n"
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-x <plugin>\tXMLDB plugin\n",
"\t-x <plugin>\tXMLDB plugin\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
plgdir ? plgdir : "none",
confsock ? confsock : "none",
@ -176,22 +177,24 @@ db_reset(clicon_handle h,
}
/*! Merge db1 into db2 without commit
* @retval -1 Error
* @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
*/
static int
db_merge(clicon_handle h,
const char *db1,
const char *db2)
const char *db2,
cbuf *cbret)
{
int retval = -1;
cxobj *xt = NULL;
int retval = -1;
cxobj *xt = NULL;
/* Get data as xml from db1 */
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
goto done;
/* Merge xml into db2. Without commit */
if (xmldb_put(h, (char*)db2, OP_MERGE, xt, NULL) < 0)
goto done;
retval = 0;
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, cbret);
done:
if (xt)
xml_free(xt);
@ -266,7 +269,7 @@ nacm_load_external(clicon_handle h)
}
if ((yspec = yspec_new()) == NULL)
goto done;
if (yang_parse(h, NULL, "ietf-netconf-acm", NULL, yspec) < 0)
if (yang_spec_parse_module(h, "ietf-netconf-acm", NULL, yspec) < 0)
goto done;
fd = fileno(f);
/* Read configfile */
@ -288,18 +291,22 @@ nacm_load_external(clicon_handle h)
}
/*! Merge xml in filename into database
* @retval -1 Error
* @retval 0 Validation failed (with cbret set)
* @retval 1 Validation OK
*/
static int
load_extraxml(clicon_handle h,
char *filename,
const char *db)
const char *db,
cbuf *cbret)
{
int retval = -1;
cxobj *xt = NULL;
int fd = -1;
if (filename == NULL)
return 0;
return 1;
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
@ -310,9 +317,7 @@ load_extraxml(clicon_handle h,
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
goto done;
retval = 0;
retval = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret);
done:
if (fd != -1)
close(fd);
@ -385,8 +390,13 @@ static int
startup_mode_running(clicon_handle h,
char *extraxml_file)
{
int retval = -1;
int retval = -1;
cbuf *cbret = NULL;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Stash original running to candidate for later commit */
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
@ -399,34 +409,46 @@ startup_mode_running(clicon_handle h,
/* Application may define extra xml in its reset function*/
if (clixon_plugin_reset(h, "tmp") < 0)
goto done;
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = 0;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done;
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
goto fail;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
/* Commit original running. Assume -1 is validate fail */
if (candidate_commit(h, "candidate") < 0){
/* (1) We cannot differentiate between fatal errors and validation
* failures
* (2) If fatal error, we should exit
* (3) If validation fails we cannot continue. How could we?
* (4) Need to restore the running db since we destroyed it above
*/
clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting.", __FUNCTION__);
/* Reinstate original */
if (xmldb_copy(h, "candidate", "running") < 0)
goto done;
goto done;
}
if (candidate_commit(h, "candidate", cbret) < 1)
goto fail;
/* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0)
goto done;
if (db_merge(h, "tmp", "running", cbret) < 1)
goto fail;
retval = 0;
done:
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
if (cbret)
cbuf_free(cbret);
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval;
fail:
/* (1) We cannot differentiate between fatal errors and validation
* failures
* (2) If fatal error, we should exit
* (3) If validation fails we cannot continue. How could we?
* (4) Need to restore the running db since we destroyed it above
*/
if (strlen(cbuf_get(cbret)))
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
__FUNCTION__, cbuf_get(cbret));
else
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
__FUNCTION__, clicon_err_reason);
/* Reinstate original */
if (xmldb_copy(h, "candidate", "running") < 0)
goto done;
goto done;
}
/*! Clixon startup startup mode: Commit startup configuration into running state
@ -452,10 +474,16 @@ startup -------------------------+--|
*/
static int
startup_mode_startup(clicon_handle h,
char *extraxml_file)
char *extraxml_file)
{
int retval = -1;
cbuf *cbret = NULL;
/* Create return buffer for netconf xml errors */
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Stash original running to backup */
if (xmldb_copy(h, "running", "backup") < 0)
goto done;
@ -472,33 +500,46 @@ startup_mode_startup(clicon_handle h,
/* Application may define extra xml in its reset function*/
if (clixon_plugin_reset(h, "tmp") < 0)
goto done;
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = 0;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done;
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
goto fail;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
/* Commit startup */
if (candidate_commit(h, "startup") < 0){ /* diff */
/* We cannot differentiate between fatal errors and validation
* failures
* In both cases we copy back the original running and quit
*/
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting.", __FUNCTION__);
if (xmldb_copy(h, "backup", "running") < 0)
goto done;
goto done;
}
if (candidate_commit(h, "startup", cbret) < 1) /* diff */
goto fail;
/* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0)
goto done;
if (db_merge(h, "tmp", "running", cbret) < 1)
goto fail;
retval = 0;
done:
/* XXX Kludge to low-level functions to search for xml in all yang modules */
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
if (cbret)
cbuf_free(cbret);
if (xmldb_delete(h, "backup") < 0)
goto done;
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval;
fail:
/* We cannot differentiate between fatal errors and validation
* failures
* In both cases we copy back the original running and quit
*/
if (strlen(cbuf_get(cbret)))
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
__FUNCTION__, cbuf_get(cbret));
else
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
__FUNCTION__, clicon_err_reason);
if (xmldb_copy(h, "backup", "running") < 0)
goto done;
goto done;
}
int
@ -523,7 +564,6 @@ main(int argc,
int sockfamily;
char *xmldb_plugin;
int xml_cache;
int xml_pretty;
char *xml_format;
char *nacm_mode;
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
@ -662,6 +702,15 @@ main(int argc,
clicon_option_str_set(h, "CLICON_XMLDB_PLUGIN", optarg);
break;
}
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
default:
usage(h, argv[0]);
break;
@ -766,6 +815,9 @@ main(int argc,
if ((str = clicon_yang_main_dir(h)) != NULL)
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@ -778,7 +830,7 @@ main(int argc,
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Set options: database dir and yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
@ -791,9 +843,8 @@ main(int argc,
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
goto done;
if ((xml_pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) >= 0)
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)xml_pretty) < 0)
goto done;
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
goto done;
/* Startup mode needs to be defined, */
startup_mode = clicon_startup_mode(h);
if (startup_mode == -1){

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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
#

View file

@ -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;

View file

@ -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.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -169,28 +169,6 @@ static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, cbuf *cb,
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
yang_stmt *ytype, cbuf *cb, char *helptext);
/*! Patched maxstring to account for DEC64 types
* @note kludge to fix overflow error -> Fix the original error in cvtype_max2str
* by adding a fraction_digits argument.
*/
static char *
cvtype_max2str_dup2(enum cv_type type,
int fraction_digits)
{
int len;
char *str;
if (type!=CGV_DEC64 || fraction_digits==0)
return cvtype_max2str_dup(type);
if ((len = cvtype_max2str(type, NULL, 0)) < 0)
return NULL;
if ((str = (char *)malloc(len+1)) == NULL)
return NULL;
memset(str, '\0', len+1);
len = snprintf(str, len+1, "%" PRId64 ".0", (INT64_MAX/((int)pow(10,fraction_digits))));
return str;
}
/*! Generate CLI code for Yang leaf statement to CLIgen variable of specific type
* Check for completion (of already existent values), ranges (eg range[min:max]) and
* patterns, (eg regexp:"[0.9]*").
@ -208,18 +186,17 @@ yang2cli_var_sub(clicon_handle h,
char *helptext,
enum cv_type cvtype,
int options,
cg_var *mincv,
cg_var *maxcv,
cvec *cvv,
char *pattern,
uint8_t fraction_digits
)
{
int retval = -1;
char *type;
char *r;
yang_stmt *yi = NULL;
int i = 0;
char *cvtypestr;
cg_var *cv;
if (cvtype == CGV_VOID){
retval = 0;
@ -276,47 +253,39 @@ yang2cli_var_sub(clicon_handle h,
if (options & YANG_OPTIONS_FRACTION_DIGITS)
cprintf(cb, " fraction-digits:%u", fraction_digits);
if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){
assert(mincv || maxcv);
cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length");
if (mincv){
if ((r = cv2str_dup(mincv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
cprintf(cb, "%s:", r);
free(r);
r = NULL;
}
if (maxcv != NULL){
if ((r = cv2str_dup(maxcv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
}
else{ /* Cligen does not have 'max' keyword in range so need to find actual
max value of type if yang range expression is 0..max
*/
if (cvtype==CGV_STRING){
if ((r = malloc(512)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
snprintf(r, 512, "%d", MAXPATHLEN);
}
else {
if ((r = cvtype_max2str_dup2(cvtype, fraction_digits)) == NULL){
clicon_err(OE_UNIX, errno, "cvtype_max2str");
goto done;
/* Loop through range_min and range_min..rang_max */
i = 0;
while (i<cvec_len(cvv)){
// if (i)
// clicon_log(LOG_NOTICE, "%s: Warning %s has more ranges, ignoring", __FUNCTION__, ys->ys_argument);
cv = cvec_i(cvv, i++);
if (strcmp(cv_name_get(cv),"range_min") == 0){
cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length");
cv2cbuf(cv, cb);
cprintf(cb,":");
/* probe next */
if (i<cvec_len(cvv) &&
(cv = cvec_i(cvv, i)) != NULL &&
strcmp(cv_name_get(cv),"range_max") == 0){
i++;
cv2cbuf(cv, cb);
}
else /* If not, it is a single number range [x:x]*/
cv2cbuf(cv, cb);
cprintf(cb,"]");
}
}
cprintf(cb, "%s]", r); /* range */
free(r);
r = NULL;
}
if (options & YANG_OPTIONS_PATTERN)
cprintf(cb, " regexp:\"%s\"", pattern);
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);
}
cprintf(cb, ">");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
@ -347,8 +316,7 @@ yang2cli_var_union_one(clicon_handle h,
{
int retval = -1;
int options = 0;
cg_var *mincv = NULL;
cg_var *maxcv = NULL;
cvec *cvv = NULL;
char *pattern = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
@ -356,9 +324,9 @@ yang2cli_var_union_one(clicon_handle h,
char *restype;
/* Resolve the sub-union type to a resolved type */
if (yang_type_resolve(ys, ytsub, /* in */
if (yang_type_resolve(ys, ys, ytsub, /* in */
&ytype, &options, /* resolved type */
&mincv, &maxcv, &pattern, &fraction_digits) < 0)
&cvv, &pattern, &fraction_digits) < 0)
goto done;
restype = ytype?ytype->ys_argument:NULL;
@ -367,10 +335,10 @@ yang2cli_var_union_one(clicon_handle h,
goto done;
}
else {
if (clicon_type2cv(origtype, restype, &cvtype) < 0)
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
if ((retval = yang2cli_var_sub(h, ys, ytype, cb, helptext, cvtype,
options, mincv, maxcv, pattern, fraction_digits)) < 0)
options, cvv, pattern, fraction_digits)) < 0)
goto done;
}
retval = 0;
@ -438,8 +406,7 @@ yang2cli_var(clicon_handle h,
char *origtype;
yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */
cg_var *mincv = NULL;
cg_var *maxcv = NULL;
cvec *cvv = NULL;
char *pattern = NULL;
uint8_t fraction_digits = 0;
enum cv_type cvtype;
@ -448,7 +415,7 @@ yang2cli_var(clicon_handle h,
char *type;
if (yang_type_get(ys, &origtype, &yrestype,
&options, &mincv, &maxcv, &pattern, &fraction_digits) < 0)
&options, &cvv, &pattern, &fraction_digits) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
@ -456,7 +423,7 @@ yang2cli_var(clicon_handle h,
retval = 0;
goto done;
}
if (clicon_type2cv(origtype, restype, &cvtype) < 0)
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
/* Note restype can be NULL here for example with unresolved hardcoded uuid */
if (restype && strcmp(restype, "union") == 0){
@ -485,7 +452,7 @@ yang2cli_var(clicon_handle h,
if (completionp)
cprintf(cb, "(");
if ((retval = yang2cli_var_sub(h, ys, yrestype, cb, helptext, cvtype,
options, mincv, maxcv, pattern, fraction_digits)) < 0)
options, cvv, pattern, fraction_digits)) < 0)
goto done;
if (completionp){
if (cli_expand_var_generate(h, ys, cvtype, cb,
@ -751,11 +718,6 @@ yang2cli_stmt(clicon_handle h,
if (yang_config(ys)){
switch (ys->ys_keyword){
case Y_GROUPING:
case Y_RPC:
case Y_AUGMENT:
return 0;
break;
case Y_CONTAINER:
if (yang2cli_container(h, ys, cbuf, gt, level) < 0)
goto done;
@ -773,19 +735,21 @@ yang2cli_stmt(clicon_handle h,
if (yang2cli_leaf(h, ys, cbuf, gt, level, 1) < 0)
goto done;
break;
default:
case Y_CASE:
case Y_SUBMODULE:
case Y_MODULE:
for (i=0; i<ys->ys_len; i++)
if ((yc = ys->ys_stmt[i]) != NULL)
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
goto done;
break;
default: /* skip */
break;
}
}
retval = 0;
done:
return retval;
}
/*! Generate CLI code for Yang specification
@ -814,13 +778,13 @@ yang2cli(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Traverse YANG specification: loop through statements */
/* Traverse YANG, loop through all modules and generate CLI */
for (i=0; i<yspec->yp_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;

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -71,7 +71,7 @@
#include "cli_handle.h"
/* Command line options to be passed to getopt(3) */
#define CLI_OPTS "hD:f:xl:F:1a:u:d:m:qpGLy:c:U:"
#define CLI_OPTS "hD:f:xl:F:1a:u:d:m:qpGLy:c:U:o:"
#define CLI_LOGFILE "/tmp/clixon_cli.log"
@ -227,7 +227,8 @@ usage(clicon_handle h,
"\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
"\t-c <file>\tSpecify cli spec file.\n"
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n",
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
plgdir ? plgdir : "none"
);
@ -403,6 +404,15 @@ main(int argc, char **argv)
if (clicon_username_set(h, optarg) < 0)
goto done;
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
default:
usage(h, argv[0]);
break;
@ -446,6 +456,9 @@ main(int argc, char **argv)
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)

View file

@ -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.

View file

@ -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.

View file

@ -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: <dbname>,<format>,<xpath>[,<attr>]", 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:

View file

@ -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.

View file

@ -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.

View file

@ -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
#

View file

@ -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,

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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;
}

View file

@ -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_ */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -71,7 +71,7 @@
#include "netconf_rpc.h"
/* Command line options to be passed to getopt(3) */
#define NETCONF_OPTS "hD:f:l:qa:u:d:y:U:t:"
#define NETCONF_OPTS "hD:f:l:qa:u:d:y:U:t:o:"
#define NETCONF_LOGFILE "/tmp/clixon_netconf.log"
@ -80,21 +80,25 @@
* @param[in] cb Packet buffer
*/
static int
process_incoming_packet(clicon_handle h,
netconf_input_packet(clicon_handle h,
cbuf *cb)
{
char *str;
char *str0;
cxobj *xreq = NULL; /* Request (in) */
int isrpc = 0; /* either hello or rpc */
cbuf *cbret = NULL;
cxobj *xret = NULL; /* Return (out) */
cxobj *xrpc;
cxobj *xc;
int retval = -1;
char *str;
char *str0;
cxobj *xreq = NULL; /* Request (in) */
int isrpc = 0; /* either hello or rpc */
cbuf *cbret = NULL;
cxobj *xret = NULL; /* Return (out) */
cxobj *xrpc;
cxobj *xc;
yang_spec *yspec;
int ret;
cxobj *xa;
cxobj *xa2;
clicon_debug(1, "RECV");
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(2, "%s: \"%s\"", __FUNCTION__, cbuf_get(cb));
if ((cbret = cbuf_new()) == NULL){
clicon_err(LOG_ERR, errno, "cbuf_new");
goto done;
@ -108,22 +112,21 @@ process_incoming_packet(clicon_handle h,
/* Parse incoming XML message */
if (xml_parse_string(str, yspec, &xreq) < 0){
free(str0);
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
free(str0);
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
int ret;
isrpc++;
if ((ret = xml_yang_validate_rpc(xrpc)) < 0)
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
goto done;
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
goto done;
if (ret == 0){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
netconf_output(1, cbret, "rpc-error");
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
}
else
@ -142,8 +145,13 @@ process_incoming_packet(clicon_handle h,
goto done;
}
else{ /* there is a return message in xret */
cxobj *xa, *xa2;
assert(xret);
if (xret == NULL){
if (netconf_operation_failed(cbret, "rpc", "Internal error: no xml return")< 0)
goto done;
netconf_output_encap(1, cbret, "rpc-error");
goto done;
}
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
@ -158,16 +166,15 @@ process_incoming_packet(clicon_handle h,
if (xml_addsub(xc, xa2) < 0)
goto done;
}
add_preamble(cbret);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
add_postamble(cbret);
if (netconf_output(1, cbret, "rpc-reply") < 0){
if (netconf_output_encap(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}
ok:
retval = 0;
done:
if (xreq)
xml_free(xreq);
@ -175,7 +182,7 @@ process_incoming_packet(clicon_handle h,
xml_free(xret);
if (cbret)
cbuf_free(cbret);
return 0;
return retval;
}
/*! Get netconf message: detect end-of-msg
@ -228,8 +235,8 @@ netconf_input_cb(int s,
/* OK, we have an xml string from a client */
/* Remove trailer */
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
if (process_incoming_packet(h, cb) < 0)
goto done;
if (netconf_input_packet(h, cb) < 0)
; //goto done; // ignore errors
if (cc_closed)
break;
cbuf_reset(cb);
@ -326,7 +333,8 @@ usage(clicon_handle h,
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
"\t-t <sec>\tTimeout in seconds. Quit after this time.\n",
"\t-t <sec>\tTimeout in seconds. Quit after this time.\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
clicon_netconf_dir(h)
);
@ -439,7 +447,15 @@ main(int argc,
case 't': /* timeout in seconds */
tv.tv_sec = atoi(optarg);
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
default:
usage(h, argv[0]);
break;
@ -468,7 +484,9 @@ main(int argc,
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -56,6 +56,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>
@ -134,20 +135,10 @@ netconf_get_config(clicon_handle h,
{
cxobj *xfilter; /* filter */
int retval = -1;
char *source;
char *ftype = NULL;
cxobj *xfilterconf;
cxobj *xconf;
if ((source = netconf_get_target(xn, "source")) == NULL){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>source</bad-element></error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
/* ie <filter>...</filter> */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
ftype = xml_find_value(xfilter, "type");
@ -186,7 +177,6 @@ netconf_get_config(clicon_handle h,
"<error-info>type</error-info>"
"</rpc-error></rpc-reply>");
}
ok: /* netconf error is not fatal */
retval = 0;
done:
return retval;
@ -222,11 +212,9 @@ get_edit_opts(cxobj *xn,
if ((optstr = xml_body(x)) != NULL){
if (strcmp(optstr, "test-then-set") == 0)
*testopt = TEST_THEN_SET;
else
if (strcmp(optstr, "set") == 0)
else if (strcmp(optstr, "set") == 0)
*testopt = SET;
else
if (strcmp(optstr, "test-only") == 0)
else if (strcmp(optstr, "test-only") == 0)
*testopt = TEST_ONLY;
else
goto parerr;
@ -310,53 +298,18 @@ netconf_edit_config(clicon_handle h,
{
int retval = -1;
int optret;
enum operation_type operation = OP_MERGE;
enum test_option testopt = TEST_THEN_SET;/* only supports this */
enum error_option erropt = STOP_ON_ERROR; /* only supports this */
cxobj *xc; /* config */
cxobj *x;
cxobj *xfilter;
char *ftype = NULL;
char *target; /* db */
/* must have target, and it should be candidate */
if ((target = netconf_get_target(xn, "target")) == NULL ||
strcmp(target, "candidate")){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
/* CLICON addition, eg <filter type="restconf" select=<api-path> /> */
if ((xfilter = xpath_first(xn, "filter")) != NULL) {
if ((ftype = xml_find_value(xfilter, "type")) != NULL)
if (strcmp(ftype,"restconf")){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
goto ok;
}
}
if ((x = xpath_first(xn, "default-operation")) != NULL){
if (xml_operation(xml_body(x), &operation) < 0){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
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, "<rpc-reply><rpc-error>"
"<error-tag>operation-not-supported</error-tag>"
@ -364,168 +317,15 @@ netconf_edit_config(clicon_handle h,
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
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
<copy-config>
<target>
<candidate/>
</target>
<source>
<url>
<!- - location specifier for file containing the new configuration - ->
</url>
</source>
<copy-config>
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>source</bad-element></error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
if ((target = netconf_get_target(xn, "target")) == NULL){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
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-config>
<target>
<candidate/>
</target>
</delete-config>
Delete a configuration datastore. The <running>
configuration datastore cannot be deleted.
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
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
<lock>
<target>
<candidate/>
</target>
</lock>
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
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
<unlock>
<target>
<candidate/>
</target>
</unlock>
XXX
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
* @param[out] xret Return XML, error or OK
*/
static int
netconf_unlock(clicon_handle h,
cxobj *xn,
cxobj **xret)
{
return netconf_lock(h, xn, xret);
}
/*! Get running configuration and device state information
*
*
@ -589,138 +389,11 @@ netconf_get(clicon_handle h,
"<error-info>type</error-info>"
"</rpc-error></rpc-reply>");
}
// ok: /* netconf error is not fatal */
retval = 0;
done:
return retval;
}
/*! Close a (user) session
<close-session/>
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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
<kill-session>
<session-id>PID</session-id>
</kill-session>
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>session-id</bad-element></error-info>"
"</rpc-error></rpc-reply>");
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/>
:validate
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-info><bad-element>target</bad-element></error-info>"
"</rpc-error></rpc-reply>");
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
<commit/>
:candidate
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> 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
<discard-changes/>
:candidate
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
* @param[out] xret Return XML, error or OK
*/
static int
netconf_discard_changes(clicon_handle h,
cxobj *xn,
cxobj **xret)
{
int retval = -1;
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Called when a notification has happened on backend
* and this session has registered for that event.
* Filter it and forward it.
@ -753,6 +426,8 @@ netconf_notification_cb(int s,
cbuf *cb;
cxobj *xn = NULL; /* event xml */
cxobj *xt = NULL; /* top xml */
clicon_handle h = (clicon_handle)arg;
yang_spec *yspec = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* get msg (this is the reason this function is called) */
@ -766,7 +441,8 @@ netconf_notification_cb(int s,
event_unreg_fd(s, netconf_notification_cb);
goto done;
}
if (clicon_msg_decode(reply, &xt) < 0)
yspec = clicon_dbspec_yang(h);
if (clicon_msg_decode(reply, yspec, &xt) < 0)
goto done;
if ((xn = xpath_first(xt, "notification")) == NULL)
goto ok;
@ -775,12 +451,10 @@ netconf_notification_cb(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
add_preamble(cb); /* Make it well-formed netconf xml */
if (clicon_xml2cbuf(cb, xn, 0, 0) < 0)
goto done;
add_postamble(cb);
/* Send it to listening client on stdout */
if (netconf_output(1, cb, "notification") < 0){
if (netconf_output_encap(1, cb, "notification") < 0){
cbuf_free(cb);
goto done;
}
@ -840,7 +514,7 @@ netconf_create_subscription(clicon_handle h,
goto ok;
if (event_reg_fd(s,
netconf_notification_cb,
NULL,
h,
"notification socket") < 0)
goto done;
ok:
@ -876,12 +550,16 @@ netconf_application_rpc(clicon_handle h,
cbuf *cb = NULL;
cbuf *cbret = NULL;
int ret;
/* First check system / netconf RPC:s */
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
}
/* Find yang rpc statement, return yang rpc statement if found
Check application RPC */
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -902,13 +580,9 @@ netconf_application_rpc(clicon_handle h,
goto ok;
}
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
if ((yrpc==NULL) && _CLICON_XML_NS_ITERATE){
int i;
for (i=0; i<yspec->yp_len; i++){
ymod = yspec->yp_stmt[i];
if ((yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn))) != NULL)
break;
}
if ((yrpc==NULL) && !_CLICON_XML_NS_STRICT){
if (xml_yang_find_non_strict(xn, yspec, &yrpc) < 0) /* Y_RPC */
goto done;
}
/* Check if found */
if (yrpc != NULL){
@ -917,15 +591,18 @@ netconf_application_rpc(clicon_handle h,
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xn, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
goto done;
if (xml_yang_validate_add(xn, NULL) < 0)
if (ret == 0){
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
goto done;
}
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
if (ret == 0){
netconf_output_encap(1, cbret, "rpc-error");
goto ok;
}
}
/* Look for local (client-side) netconf plugins. */
if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0)
@ -943,11 +620,18 @@ netconf_application_rpc(clicon_handle h,
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply(xoutput, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
if ((ret = xml_yang_validate_all_top(xoutput, cbret)) < 0)
goto done;
if (xml_yang_validate_add(xoutput, NULL) < 0)
if (ret == 0){
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
goto ok;
}
if ((ret = xml_yang_validate_add(xoutput, cbret)) < 0)
goto done;
if (ret == 0){
clicon_log(LOG_WARNING, "Errors in output netconf %s", cbuf_get(cbret));
goto ok;
}
}
retval = 1; /* handled by callback */
goto done;
@ -992,9 +676,25 @@ netconf_rpc_dispatch(clicon_handle h,
if (xml_value_set(xa, username) < 0)
goto done;
}
/* Many of these calls are now calling generic clicon_rpc_netconf_xml
* directly, since the validation is generic and done before this place
* in the call. Some call however need extra validation, such as the
* filter parameter to get/get-config and tes- err-opts of edit-config.
*/
xe = NULL;
while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xe), "get-config") == 0){
if (strcmp(xml_name(xe), "copy-config") == 0 ||
strcmp(xml_name(xe), "delete-config") == 0 ||
strcmp(xml_name(xe), "lock") == 0 ||
strcmp(xml_name(xe), "unlock") == 0 ||
strcmp(xml_name(xe), "kill-session") == 0 ||
strcmp(xml_name(xe), "validate") == 0 || /* :validate */
strcmp(xml_name(xe), "commit") == 0 || /* :candidate */
strcmp(xml_name(xe), "discard-changes") == 0){
if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "get-config") == 0){
if (netconf_get_config(h, xe, xret) < 0)
goto done;
}
@ -1002,47 +702,14 @@ netconf_rpc_dispatch(clicon_handle h,
if (netconf_edit_config(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "copy-config") == 0){
if (netconf_copy_config(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "delete-config") == 0){
if (netconf_delete_config(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "lock") == 0) {
if (netconf_lock(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "unlock") == 0){
if (netconf_unlock(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "get") == 0){
if (netconf_get(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "close-session") == 0){
if (netconf_close_session(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "kill-session") == 0) {
if (netconf_kill_session(h, xe, xret) < 0)
goto done;
}
/* Validate capability :validate */
else if (strcmp(xml_name(xe), "validate") == 0){
if (netconf_validate(h, xe, xret) < 0)
goto done;
}
/* Candidate configuration capability :candidate */
else if (strcmp(xml_name(xe), "commit") == 0){
if (netconf_commit(h, xe, xret) < 0)
goto done;
}
else if (strcmp(xml_name(xe), "discard-changes") == 0){
if (netconf_discard_changes(h, xe, xret) < 0)
goto done;
cc_closed++;
if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
goto done;
}
/* RFC 5277 :notification */
else if (strcmp(xml_name(xe), "create-subscription") == 0){

View file

@ -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.

View file

@ -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
#

View file

@ -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: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2018-11-04T14:47:11.373124</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>
data: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2018-11-04T14:47:16.375265</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>
@ -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:
```

View file

@ -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.

View file

@ -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, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n", cbuf_get(cb));

View file

@ -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.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -81,7 +81,7 @@
#include "restconf_stream.h"
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:y:a:u:"
#define RESTCONF_OPTS "hD:f:l:p:y:a:u:o:"
/* RESTCONF enables deployments to specify where the RESTCONF API is
located. The client discovers this by getting the "/.well-known/host-meta"
@ -495,7 +495,8 @@ usage(clicon_handle h,
"\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n"
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n"
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n",
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0,
clicon_restconf_dir(h)
);
@ -611,6 +612,15 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
default:
usage(h, argv[0]);
break;
@ -645,7 +655,9 @@ main(int argc,
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
@ -654,7 +666,7 @@ main(int argc,
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0)
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Call start function in all plugins before we go interactive
Pass all args after the standard options to plugin_start

File diff suppressed because it is too large Load diff

View file

@ -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.

View file

@ -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, "<rpc><create-subscription><stream>%s</stream>", name);
cprintf(cb, "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>%s</stream>", name);
/* Print all fields */
for (i=0; i<cvec_len(qvec); i++){
cv = cvec_i(qvec, i);

View file

@ -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.

View file

@ -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
#

View file

@ -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
#

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -106,7 +106,7 @@ main(int argc, char **argv)
char *plugin = NULL;
char *cmd = NULL;
yang_spec *yspec = NULL;
char *yangmodule = NULL;
char *yangfilename = NULL;
char *dbdir = NULL;
int ret;
int pid;
@ -151,7 +151,7 @@ main(int argc, char **argv)
case 'y': /* Yang file */
if (!optarg)
usage(argv0);
yangmodule = optarg;
yangfilename = optarg;
break;
}
/*
@ -173,8 +173,8 @@ main(int argc, char **argv)
clicon_err(OE_DB, 0, "Missing dbdir -b 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 */
@ -187,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, yangmodule, NULL, NULL, yspec) < 0)
if (yang_spec_parse_file(h, yangfilename, yspec) < 0)
goto done;
/* Set database directory option */
if (xmldb_setopt(h, "dbdir", dbdir) < 0)
@ -239,15 +239,18 @@ main(int argc, char **argv)
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
usage(argv0);
}
_CLICON_XML_NS_ITERATE = 1;
_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, cbret) < 1)
goto done;
}
else if (strcmp(cmd, "copy")==0){
if (argc != 2)

View file

@ -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
#

View file

@ -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.
@ -358,6 +358,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
@ -470,6 +480,10 @@ 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: <config>...</config> */
@ -530,12 +544,9 @@ text_get(xmldb_handle xh,
/* Add default values (if not set) */
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* Order XML children according to YANG */
if (!xml_child_sort && xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
#if 0 /* debug */
if (xml_child_sort && xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #2", __FUNCTION__);
#if 1 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
#endif
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
@ -555,17 +566,22 @@ text_get(xmldb_handle xh,
}
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
* @param[in] th text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
* @param[in] x0p Parent of x0
* @param[in] x1 xml tree which modifies base
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[out] cbret Initialized cligen buffer. Contains return XML or "".
* @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
* @retval -1 Error
* @retval 0 Failed (cbret set)
* @retval 1 OK
* Assume x0 and x1 are same on entry and that y is the spec
* @see put in clixon_keyvalue.c
* @see text_modify_top
*/
static int
text_modify(cxobj *x0,
text_modify(struct text_handle *th,
cxobj *x0,
yang_node *y0,
cxobj *x0p,
cxobj *x1,
@ -576,6 +592,8 @@ text_modify(cxobj *x0,
char *opstr;
char *x1name;
char *x1cname; /* child name */
cxobj *x0a; /* attribute */
cxobj *x1a; /* attribute */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
@ -583,6 +601,7 @@ text_modify(cxobj *x0,
yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
int ret;
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
@ -598,7 +617,7 @@ text_modify(cxobj *x0,
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto ok;
goto fail;
}
case OP_NONE: /* fall thru */
case OP_MERGE:
@ -607,6 +626,13 @@ text_modify(cxobj *x0,
// int iamkey=0;
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
/* Copy xmlns attributes */
if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != 0){
if ((x0a = xml_dup(x1a)) == NULL)
goto done;
if (xml_addsub(x0, x0a) < 0)
goto done;
}
#if 0
/* If it is key I dont want to mark it */
if ((iamkey=yang_key_match(y0->yn_parent, x1name)) < 0)
@ -636,7 +662,7 @@ text_modify(cxobj *x0,
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0){
@ -653,7 +679,7 @@ text_modify(cxobj *x0,
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto ok;
goto fail;
}
case OP_REPLACE: /* fall thru */
if (x0){
@ -679,11 +705,19 @@ text_modify(cxobj *x0,
if (x0==NULL){
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
/* Copy xmlns attributes */
if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != 0){
if ((x0a = xml_dup(x1a)) == NULL)
goto done;
if (xml_addsub(x0, x0a) < 0)
goto done;
}
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
}
/* First pass: mark existing children in base */
/* Loop through children of the modification tree */
/* First pass: Loop through children of the x1 modification tree
* collect matching nodes from x0 in x0vec (no changes to x0 children)
*/
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
@ -699,28 +733,40 @@ text_modify(cxobj *x0,
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
if (match_base_child(x0, x1c, &x0c, yc) < 0)
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
x0vec[i++] = x0c;
#if 1
if (x0c && (yc != xml_spec(x0c))){
/* There is a match but is should be replaced (choice)*/
if (xml_purge(x0c) < 0)
goto done;
x0c = NULL;
}
#endif
x0vec[i++] = x0c; /* != NULL if x0c is matching x1c */
}
/* Second pass: modify tree */
/* Second pass: Loop through children of the x1 modification tree again
* Now potentially modify x0:s children
* Here x0vec contains one-to-one matching nodes of x1:s children.
*/
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
x0c = x0vec[i++];
yc = yang_find_datanode(y0, x1cname);
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret) < 0)
if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
if (ret == 0)
goto fail;
}
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0)
@ -731,20 +777,26 @@ text_modify(cxobj *x0,
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
xml_sort(x0p, NULL);
ok:
retval = 0;
retval = 1;
done:
if (x0vec)
free(x0vec);
return retval;
fail: /* cbret set */
retval = 0;
goto done;
} /* text_modify */
/*! Modify a top-level base tree x0 with modification tree x1
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] x1 xml tree which modifies base
* @param[in] th text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] x1 xml tree which modifies base
* @param[in] yspec Top-level yang spec (if y is NULL)
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[out] cbret Initialized cligen buffer. Contains return XML or "".
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
* @retval -1 Error
* @retval 0 Failed (cbret set)
* @retval 1 OK
* @see text_modify
*/
static int
@ -762,6 +814,7 @@ text_modify_top(struct text_handle *th,
yang_stmt *yc; /* yang child */
yang_stmt *ymod;/* yang module */
char *opstr;
int ret;
/* Assure top-levels are 'config' */
assert(x0 && strcmp(xml_name(x0),"config")==0);
@ -790,7 +843,7 @@ text_modify_top(struct text_handle *th,
case OP_DELETE:
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto ok;
goto fail;
break;
default:
break;
@ -812,35 +865,42 @@ text_modify_top(struct text_handle *th,
goto done;
if (ymod != NULL)
yc = yang_find_datanode((yang_node*)ymod, x1cname);
if (yc == NULL && _CLICON_XML_NS_ITERATE){
int i;
for (i=0; i<yspec->yp_len; i++){
ymod = yspec->yp_stmt[i];
if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) != NULL)
break;
}
if (yc == NULL && !_CLICON_XML_NS_STRICT){
if (xml_yang_find_non_strict(x1c, yspec, &yc) < 0)
goto done;
}
if (yc == NULL){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
if (netconf_unknown_element(cbret, "application", x1cname, "Unassigned yang spec") < 0)
goto done;
goto ok;
goto fail;
}
/* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, &x0c, yc) < 0)
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op, cbret) < 0)
#if 1
if (x0c && (yc != xml_spec(x0c))){
/* There is a match but is should be replaced (choice)*/
if (xml_purge(x0c) < 0)
goto done;
x0c = NULL;
}
#endif
if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
if (ret == 0)
goto fail;
}
ok:
retval = 0;
// ok:
retval = 1;
done:
return retval;
fail: /* cbret set */
retval = 0;
goto done;
} /* text_modify_top */
/*! For containers without presence and no children, remove
/*! For containers without presence and no children(except attrs), remove
* @param[in] x XML tree node
* See section 7.5.1 in rfc6020bis-02.txt:
* No presence:
@ -869,7 +929,7 @@ xml_container_presence(cxobj *x,
}
/* Mark node that is: container, have no children, dont have presence */
if (y->ys_keyword == Y_CONTAINER &&
xml_child_nr(x)==0 &&
xml_child_nr_notype(x, CX_ATTR)==0 &&
yang_find((yang_node*)y, Y_PRESENCE, NULL) == NULL)
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
retval = 0;
@ -897,14 +957,11 @@ text_put(xmldb_handle xh,
yang_spec *yspec;
cxobj *x0 = NULL;
struct db_element *de = NULL;
int cbretlocal = 0; /* Set if cbret is NULL on entry */
int ret;
if (cbret == NULL){
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cbretlocal++;
clicon_err(OE_XML, EINVAL, "cbret is NULL");
goto done;
}
if ((yspec = th->th_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -957,25 +1014,19 @@ text_put(xmldb_handle xh,
xml_name(x0));
goto done;
}
#if 0
/* Add yang specification backpointer to all XML nodes
* This is already done in from_client_edit_config() */
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
#endif
#if 0 /* debug */
if (xml_child_sort && xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
if (xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
#endif
/*
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
if (text_modify_top(th, x0, x1, yspec, op, cbret) < 0)
if ((ret = text_modify_top(th, x0, x1, yspec, op, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))
goto ok;
if (ret == 0)
goto fail;
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
@ -990,7 +1041,7 @@ text_put(xmldb_handle xh,
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
goto done;
#if 0 /* debug */
if (xml_child_sort && xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
if (xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
#endif
/* Write back to datastore cache if first time */
@ -1025,11 +1076,8 @@ text_put(xmldb_handle xh,
}
else if (clicon_xml2file(f, x0, 0, th->th_pretty) < 0)
goto done;
ok:
retval = 0;
retval = 1;
done:
if (cbretlocal && cbret)
cbuf_free(cbret);
if (f != NULL)
fclose(f);
if (dbfile)
@ -1041,6 +1089,9 @@ text_put(xmldb_handle xh,
if (!th->th_cache && x0)
xml_free(x0);
return retval;
fail:
retval = 0;
goto done;
}
/*! Copy database from db1 to db2
@ -1386,6 +1437,8 @@ main(int argc,
char *yangmod; /* yang file */
yang_spec *yspec = NULL;
clicon_handle h;
cbuf *cbret = NULL;
int ret;
if ((h = clicon_handle_init()) == NULL)
goto done;
@ -1400,7 +1453,7 @@ main(int argc,
db_init(db);
if ((yspec = yspec_new()) == NULL)
goto done
if (yang_parse(h, NULL, yangmod, NULL, yspec) < 0)
if (yang_spec_parse_module(h, yangmod, NULL, yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){
if (argc < 5)
@ -1434,13 +1487,21 @@ main(int argc,
op = OP_REMOVE;
else
usage(argv[0]);
if (xmldb_put(h, db, op, NULL, xn, NULL) < 0)
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if ((ret = xmldb_put(h, db, op, NULL, xn, cbret)) < 0)
goto done;
if (ret == 0)
fprintf(stderr, "%s\n", cbuf_get(cbret));
}
else
usage(argv[0]);
printf("\n");
done:
if (cbret)
cbuf_free(cbret);
return 0;
}

View file

@ -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.

View file

@ -42,9 +42,9 @@ The example:
sudo make install
```
## How do you run Clixon example commands?
## How do I run Clixon example commands?
- Start a backend server: `clixon_backend -Ff /usr/local/etc/example.xml`
- Start a backend server: `clixon_backend -F -s init -f /usr/local/etc/example.xml`
- Start a cli session: `clixon_cli -f /usr/local/etc/example.xml`
- Start a netconf session: `clixon_netconf -f /usr/local/etc/example.xml`
- Start a restconf daemon: `sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data`
@ -72,6 +72,82 @@ grep clicon /etc/group
clicon:x:1001:<user>,www-data
```
## How do I use the CLI?
The easiest way to use Clixon is via the CLI. Once the backend is started
Example:
```
clixon_cli -f /usr/local/etc/example.xml
cli> set interfaces interface eth9 ?
description enabled ipv4
ipv6 link-up-down-trap-enable type
cli> set interfaces interface eth9 type ex:eth
cli> validate
cli> commit
cli> show configuration xml
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth9</name>
<type>ex:eth</type>
<enabled>true</enabled>
</interface>
</interfaces>
cli> delete interfaces interface eth9
```
## How do I use netconf?
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
Example:
```
clixon_netconf -qf /usr/local/etc/example.xml
<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>
<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth9</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>
```
However, more useful is to run clixon_netconf as an SSH
subsystem. Register the subsystem in /etc/sshd_config:
```
Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/example.xml
```
and then invoke it from a client using
```
ssh -s <host> netconf
```
## How do I use restconf?
You can access clixon via REST API using restconf, such as using
curl. GET, PUT, POST are supported.
You need a web-server, such as nginx, and start a restconf fcgi
daemon, clixon_restconf.
For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default:
```
server {
...
location /restconf {
fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
include fastcgi_params;
}
}
```
Start nginx daemon
```
sudo /etc/init.d/nginx start
```
Example:
```
curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type
[
{
"ietf-interfaces:type": "ex:eth"
}
]
```
Read more in the (restconf)[../apps/restconf] docs.
## What about reference documentation?
Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
You need to install doxygen and graphviz on your system.
@ -121,7 +197,7 @@ are included.
The following configuration file options control the loading of Yang files:
- `CLICON_YANG_DIR` - A list of directories (yang dir path) where Clixon searches for module and submodules.
- `CLICON_YANG_MAIN_FILE` - Load a specific Yang module fiven by a file.
- `CLICON_YANG_MAIN_FILE` - Load a specific Yang module given by a file.
- `CLICON_YANG_MODULE_MAIN` - Specifies a single module to load. The module is searched for in the yang dir path.
- `CLICON_YANG_MODULE_REVISION` : Specifies a revision to the main module.
- `CLICON_YANG_MAIN_DIR` - Load all yang modules in this directory.
@ -161,59 +237,6 @@ sudo docker run -td olofhagsand/clixon_example
```
Look in the example documentation for more info.
## How do I use netconf?
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
Example:
```
echo "<rpc><get-config><source><candidate/></source><configuration/></get-config></rpc>]]>]]>" | clixon_netconf -f /usr/local/etc/example.xml
```
However, more useful is to run clixon_netconf as an SSH
subsystem. Register the subsystem in /etc/sshd_config:
```
Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/example.xml
```
and then invoke it from a client using
```
ssh -s <host> netconf
```
## How do I use restconf?
You can access clixon via REST API using restconf, such as using
curl. GET, PUT, POST are supported.
You need a web-server, such as nginx, and start a restconf fcgi
daemon, clixon_restconf.
For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default:
```
server {
...
location /restconf {
fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
include fastcgi_params;
}
}
```
Start nginx daemon
```
sudo /etc/init.d/nginx start
```
Example:
```
curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type
[
{
"type": "eth"
}
]
```
Read more in the (restconf)[../apps/restconf] docs.
## Does Clixon support event streams?
Yes, Clixon supports event notification streams in the CLI, Netconf and Restconf API:s.
@ -233,7 +256,7 @@ severity major;
or via NETCONF:
```
clixon_netconf -qf /usr/local/etc/example.xml
<rpc><create-subscription><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>
<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2018-09-30T12:44:59.657276</eventTime><event xmlns="http://example.com/event/1.0"><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>]]>]]>
...
@ -242,11 +265,11 @@ or via restconf:
```
curl -H "Accept: text/event-stream" -s -X GET http://localhost/streams/EXAMPLE
```
Consult (../apps/restconf/README.md) on more information on how to setup a reverse proxy for restconf streams. It is also possible to configure a pub/sub system such as (Nginx Nchan)[https://nchan.io].
Consult [clixon restconf](../apps/restconf/README.md) on more information on how to setup a reverse proxy for restconf streams. It is also possible to configure a pub/sub system such as [Nginx Nchan](https://nchan.io).
## How should I start the backend daemon?
There are four different backend startup modes. There is differences in running state treatment, ie what state the machine is when you startthe daemon and how loading the configuration affects it:
There are four different backend startup modes. There is differences in running state treatment, ie what state the machine is when you start the daemon and how loading the configuration affects it:
- none - Do not touch running state. Typically after crash when running state and db are synched.
- init - Initialize running state. Start with a completely clean running state.
- running - Commit running db configuration into running state. Typically after reboot if a persistent running db exists.
@ -277,7 +300,7 @@ There are two ways to add extra XML to running database after start. Note that
The first way is via a file. Assume you want to add this xml (the config tag is a necessary top-level tag):
```
<config>
<x>extra</x>
<x xmlns="urn:example:clixon">extra</x>
</config>
```
You add this via the -c option:
@ -289,12 +312,13 @@ The second way is by programming the plugin_reset() in the backend
plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c).
## I want to program. How do I extend the example?
See [../apps/example]
See [../apps/example](../apps/example)
- example.xml - Change the configuration file
- The yang specifications - This is the central part. It changes the XML, database and the config cli.
- example_cli.cli - Change the fixed part of the CLI commands
- example_cli.c - Cli C-commands are placed here.
- example_backend.c - Commit and validate functions.
- example_backend_nacm.c - Secondary example plugin (for authorization)
- example_netconf.c - Netconf plugin
- example_restconf.c - Add restconf authentication, etc.

View file

@ -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
#

View file

@ -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
#

View file

@ -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
#

View file

@ -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
#

View file

@ -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
#

View file

@ -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
#

View file

@ -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
#

View file

@ -1,19 +1,33 @@
# Clixon example
* [Content](#content)
* [Compile and run](#compile)
* [Using the CLI](#using-the-cli)
* [Using netconf](#using-netconf)
* [Streams](#streams)
* [RPC Operations](#rpc-operations)
* [State data](#state-data)
* [Authentication and NACM](#authentication-and-nacm)
* [Systemd](#systemd)
* [Docker](#docker)
* [Plugins](#plugins)
## Content
This directory contains a Clixon example which includes a simple example. It contains the following files:
* example.xml The configuration file. See yang/clixon-config@<date>.yang for all available fields.
* example.yang The yang spec of the example. It mainly includes ietf routing and IP modules.
* example_cli.cli CLIgen specification.
* example_cli.c CLI callback plugin containing functions called in the cli file above: a generic callback (`mycallback`) and an RPC (`fib_route_rpc`).
* example_backend.c Backend callback plugin including example of:
* `example.xml` The configuration file. See (yang/clixon-config@<date>.yang)[../yang/clixon-config@2018-10-21.yang] for the documentation of all available fields.
* `example.yang` The yang spec of the example. It mainly includes ietf routing and IP modules.
* `example_cli.cli` CLIgen specification.
* `example_cli.c` CLI callback plugin containing functions called in the cli file above: a generic callback (`mycallback`) and an RPC (`fib_route_rpc`).
* `example_backend.c` Backend callback plugin including example of:
* transaction callbacks (validate/commit),
* notification,
* rpc handler
* state-data handler, ie non-config data
* example_backend_nacm.c Secondary backend plugin. Plugins are loaded alphabetically.
* example_restconf.c Restconf callback plugin containing an HTTP basic authentication callback
* example_netconf.c Netconf callback plugin
* Makefile.in Example makefile where plugins are built and installed
* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically.
* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback
* `example_netconf.c` Netconf callback plugin
* `Makefile.in` Example makefile where plugins are built and installed
## Compile and run
@ -47,10 +61,37 @@ Send restconf command
curl -G http://127.0.0.1/restconf/data
```
## Setting data example using netconf
## Using the CLI
The example CLI allows you to modify and view the data model using `set`, `delete` and `show` via generated code.
There are also many other commands available as examples. View the source file (example_cli.cli)[example_cli.cli] for more details.
The following example shows how to add an interface in candidate, validate and commit it to running, then look at it (as xml) and finally delete it.
```
clixon_cli -f /usr/local/etc/example.xml
cli> set interfaces interface eth9 ?
description enabled ipv4
ipv6 link-up-down-trap-enable type
cli> set interfaces interface eth9 type ex:eth
cli> validate
cli> commit
cli> show configuration xml
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth9</name>
<type>ex:eth</type>
<enabled>true</enabled>
</interface>
</interfaces>
cli> delete interfaces interface eth9
```
## Using Netconf
The following example shows how to set data using netconf:
```
<rpc><edit-config><target><candidate/></target><config>
<interfaces>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth1</name>
<enabled>true</enabled>
@ -65,13 +106,13 @@ Send restconf command
</config></edit-config></rpc>]]>]]>
```
## Getting data using netconf
### Getting data using netconf
```
<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter/></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter type="xpath"/></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter type="subtree"><configuration><interfaces><interface><ipv4/></interface></interfaces></configuration></filter></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface/ipv4"/></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter type="subtree"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth9</name><type>ex:eth</type></interface></interfaces></data></filter></get-config></rpc>]]>]]>
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface"/></get-config></rpc>]]>]]>
<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>
```
@ -80,23 +121,134 @@ Send restconf command
The example has an EXAMPLE stream notification triggering every 5s. To start a notification
stream in the session using netconf, create a subscription:
```
<rpc><create-subscription><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>
<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]>
<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2019-01-02T10:20:05.929272</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>]]>]]>
...
```
This can also be triggered via the CLI:
```
cli> notify
cli> Routing notification
Routing notification
clixon_cli -f /usr/local/etc/example.xml
cli> notify
cli> event-class fault;
reportingEntity {
card Ethernet0;
}
severity major;
...
cli> no notify
cli>
```
Restconf support is also supported, see [../apps/restconf/README.md].
Restconf support is also supported, see (restc)[../apps/restconf/README.md].
## Initializing a plugin
## RPC Operations
Clixon implements Yang RPC operations by an extension mechanism. The
extension mechanism enables you to add application-specific
operations. It works by adding user-defined callbacks for added
netconf operations. It is possible to use the extension mechanism
independent of the yang rpc construct, but it is recommended. The example includes an example:
Example using CLI:
```
cli> rpc ipv4
rpc-reply {
route {
address-family ipv4;
next-hop {
next-hop-list 2.3.4.5;
}
source-protocol static;
}
}
```
Netconf:
```
<rpc><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance-name>ipv4</routing-instance-name></fib-route></rpc>]]>]]>
<rpc-reply><route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop><source-protocol>static</source-protocol></route></rpc-reply>]]>]]>
```
Restconf:
```
curl -X POST http://localhost/restconf/operations/ietf-routing:fib-route -d '{"ietf-routing:input":{"routing-instance-name":"ipv4"}}'
```
### Details
The example works by creating a netconf rpc call and sending it to the backend: (see the fib_route_rpc() function in [example_cli.c](example_cli.c)).
In the (example_backend.c)[example_backend.c], a callback is registered (fib_route()) which handles the RPC (this is just dummy data):
```
static int
fib_route(clicon_handle h,
cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* Client session */
void *regarg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><route xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\">"
"<address-family>ipv4</address-family>"
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
"<source-protocol>static</source-protocol>"
"</route></rpc-reply>");
return 0;
}
int
clixon_plugin_init(clicon_handle h)
{
...
rpc_callback_register(h, fib_route, NULL, "fib-route");
...
}
```
## State data
Netconf <get> and restconf GET also returns state data(not only configuration data).
In YANG state data is specified with `config false;`. In the example,
`state` is state data, see (example.yang)[example.yang]
To return state data, you need to write a backend state data callback
with the name "plugin_statedata" where you return an XML tree with
state. This is then merged with config data by the system.
A static example of returning state data is in the example. Note that
a real example would poll or get the interface counters via a system
call, as well as use the "xpath" argument to identify the requested
state data.
## Authentication and NACM
The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341):
* A basic auth HTTP callback, see: example_restconf_credentials() containing three example users: andy, wilma, and guest, according to the examples in Appendix A in [RFC8341](https://tools.ietf.org/html/rfc8341).
* A NACM backend plugin reporting the mandatory NACM state variables.
## Systemd
Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example.
## Docker
Run the example as a docker container and access it from a host CLI as follows:
```
ID=$(sudo docker run -td olofhagsand/clixon_example)
IP=$(sudo docker inspect -f '{{.NetworkSettings.IPAddress }}' $ID)
clixon_cli -a IPv4 -u $IP -f ./example.xml
```
Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push:
```
make docker
make push
sudo docker run -ti --rm you/clixon_example
```
Note that the configuration database is internal in the container, so
it is deleted if the container is restarted. To make the configuration
database persistent, you need to mount running_db using `-v`
## Plugins
The example includes a restonf, netconf, CLI and two backend plugins.
Each plugin is initiated with an API struct followed by a plugin init function.
@ -127,96 +279,3 @@ clixon_plugin_init(clicon_handle h)
return &api; /* Return NULL on error */
}
```
## Operation data
Clixon implements Yang RPC operations by an extension mechanism. The
extension mechanism enables you to add application-specific
operations. It works by adding user-defined callbacks for added
netconf operations. It is possible to use the extension mechanism
independent of the yang rpc construct, but it is recommended. The example includes an example:
Example:
```
cli> rpc ipv4
<rpc-reply>
<ok/>
</rpc-reply>
```
The example works by creating a netconf rpc call and sending it to the backend: (see the fib_route_rpc() function).
```
<rpc>
<fib-route>
<routing-instance-name>ipv4</routing-instance-name>
</fib-route>
</rpc>
```
In the backend, a callback is registered (fib_route()) which handles the RPC.
```
static int
fib_route(clicon_handle h,
cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* Client session */
void *regarg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
return 0;
}
int
clixon_plugin_init(clicon_handle h)
{
...
rpc_callback_register(h, fib_route, NULL, "fib-route");
...
}
```
## State data
Netconf <get> and restconf GET also returns state data, in contrast to
config data.
p
In YANG state data is specified with "config false;". In the example, interface-state is state data.
To return state data, you need to write a backend state data callback
with the name "plugin_statedata" where you return an XML tree with
state. This is then merged with config data by the system.
A static example of returning state data is in the example. Note that
a real example would poll or get the interface counters via a system
call, as well as use the "xpath" argument to identify the requested
state data.
## Authentication and NACM
The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341):
* A basic auth HTTP callback, see: example_restconf_credentials() containing three example users: andy, wilma, and guest, according to the examples in Appendix A in [RFC8341](https://tools.ietf.org/html/rfc8341).
* A NACM backend plugin reporting the mandatory NACM state variables.
## Systemd files
Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example.
## Docker
Run the example as a docker container and access it from a host CLI as follows:
```
ID=$(sudo docker run -td olofhagsand/clixon_example)
IP=$(sudo docker inspect -f '{{.NetworkSettings.IPAddress }}' $ID)
clixon_cli -a IPv4 -u $IP -f ./example.xml
```
Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push:
```
make docker
make push
sudo docker run -ti --rm you/clixon_example
```
Note that the configuration database is internal in the container, so
it is deleted if the container is restarted. To make the configuration
database persistent, you need to mount running_db using `-v`

View file

@ -26,6 +26,10 @@ module example {
}
/* Translation function example - See also example_cli */
list translate{
key k;
leaf k{
type string;
}
leaf value{
type string;
}
@ -73,13 +77,55 @@ module example {
}
}
}
rpc debug {
description "Set debug level of backend. XXX should be in clixon-config";
rpc empty {
description "Smallest possible RPC with no input or output";
}
rpc example {
description "Some example input/output for testing RFC7950 7.14.
RPC simply echoes the input for debugging.";
input {
leaf level {
type uint32;
leaf x {
description
"If a leaf in the input tree has a 'mandatory' statement with
the value 'true', the leaf MUST be present in an RPC invocation.";
type string;
mandatory true;
}
leaf y {
description
"If a leaf in the input tree has a 'mandatory' statement with the
value 'true', the leaf MUST be present in an RPC invocation.";
type string;
default "42";
}
leaf-list z {
description
"If a leaf-list in the input tree has one or more default
values, the server MUST use these values (XXX not supported)";
type string;
}
leaf w {
description
"If any node has a 'when' statement that would evaluate to
'false',then this node MUST NOT be present in the input tree.
(XXX not supported)";
type string;
when "/translate/k=5/value='w'";
}
}
output {
leaf x {
type string;
}
leaf y {
type string;
}
leaf z {
type string;
}
leaf w {
type string;
}
}
}
}

View file

@ -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", "<event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event>") < 0)
goto done;
if (example_stream_timer_setup(h) < 0)
@ -129,9 +129,10 @@ fib_route(clicon_handle h, /* Clicon handle */
void *arg, /* Client session */
void *regarg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><route>"
cprintf(cbret, "<rpc-reply><route xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\">"
"<address-family>ipv4</address-family>"
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
"<source-protocol>static</source-protocol>"
"</route></rpc-reply>");
return 0;
}
@ -146,7 +147,7 @@ route_count(clicon_handle h,
void *arg,
void *regarg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><number-of-routes>42</number-of-routes></rpc-reply>");
cprintf(cbret, "<rpc-reply><number-of-routes xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\">42</number-of-routes></rpc-reply>");
return 0;
}
@ -157,16 +158,49 @@ route_count(clicon_handle h,
* in [RFC6241].
*/
static int
empty(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* client_entry */
void *regarg) /* Argument given at register */
empty_rpc(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg, /* client_entry */
void *regarg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
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: <rpc><xn></rpc> */
cbuf *cbret, /* Reply eg <rpc-reply>... */
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, "<rpc-reply>");
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, "</rpc-reply>");
retval = 0;
done:
return retval;
}
/*! Called to get state data from plugin
* @param[in] h Clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all
@ -196,7 +230,7 @@ example_statedata(clicon_handle h,
* Note this state needs to be accomanied by yang snippet
* above
*/
if (xml_parse_string("<state>"
if (xml_parse_string("<state xmlns=\"urn:example:clixon\">"
"<op>42</op>"
"</state>", NULL, &xstate) < 0)
goto done;
@ -225,19 +259,32 @@ example_reset(clicon_handle h,
{
int retval = -1;
cxobj *xt = NULL;
int ret;
cbuf *cbret = NULL;
if (xml_parse_string("<config><interfaces><interface>"
if (xml_parse_string("<config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface>"
"<name>lo</name><type>ex:loopback</type>"
"</interface></interfaces></config>", NULL, &xt) < 0)
goto done;
/* Replace parent w fiorst child */
/* Replace parent w first child */
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* Merge user reset state */
if ((ret = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret)) < 0)
goto done;
if (ret == 0){
clicon_err(OE_XML, 0, "Error when writing to XML database: %s",
cbuf_get(cbret));
goto done;
}
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
if (xt != NULL)
xml_free(xt);
return retval;
@ -315,22 +362,31 @@ clixon_plugin_init(clicon_handle h)
if (example_stream_timer_setup(h) < 0)
goto done;
/* Register callback for routing rpc calls */
/* Register callback for routing rpc calls
*/
if (rpc_callback_register(h, fib_route,
NULL,
"fib-route"/* Xml tag when callback is made */
) < 0)
goto done;
/* From ietf-routing.yang */
if (rpc_callback_register(h, route_count,
NULL,
"route-count"/* Xml tag when callback is made */
) < 0)
goto done;
if (rpc_callback_register(h, empty,
/* From example.yang (clicon) */
if (rpc_callback_register(h, empty_rpc,
NULL,
"empty"/* Xml tag when callback is made */
) < 0)
goto done;
if (rpc_callback_register(h, example_rpc,
NULL,
"example"/* Xml tag when callback is made */
) < 0)
goto done;
/* Return plugin API */
return &api;

View file

@ -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.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -96,7 +96,7 @@ fib_route_rpc(clicon_handle h,
/* User supplied variable in CLI command */
instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
/* Create XML for fib-route netconf RPC */
if (xml_parse_va(&xtop, NULL, "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" username=\"%s\"><fib-route xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\"><routing-instance-name>%s</routing-instance-name></fib-route></rpc>",
if (xml_parse_va(&xtop, NULL, "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" username=\"%s\"><fib-route xmlns=\"urn:ietf:params:xml:ns:yang:ietf-routing\"><routing-instance-name>%s</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>",
clicon_username_get(h),
cv_string_get(instance)) < 0)
goto done;
@ -110,7 +110,7 @@ fib_route_rpc(clicon_handle h,
goto done;
}
/* Print result */
xml_print(stdout, xml_child_i(xret, 0));
xml2txt(stdout, xml_child_i(xret, 0), 0);
retval = 0;
done:
if (xret)

View file

@ -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:string expand_dbvar("candidate","/interfaces/interface=%s/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
<name:string expand_dbvar("candidate","/ietf-interfaces:interfaces/interface=%s/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
}
}
discard("Discard edits (rollback 0)"), discard_changes();

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -71,7 +71,7 @@ int netconf_client_rpc(clicon_handle h,
void *regarg)
{
clicon_debug(1, "%s restconf", __FUNCTION__);
cprintf(cbret, "<rpc-reply><result>ok</result></rpc-reply>");
cprintf(cbret, "<rpc-reply><result xmlns=\"urn:example:clixon\">ok</result></rpc-reply>");
return 0;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -275,7 +275,7 @@ restconf_client_rpc(clicon_handle h,
{
// FCGX_Request *r = (FCGX_Request *)arg;
clicon_debug(1, "%s", __FUNCTION__);
cprintf(cbret, "<rpc-reply><result>ok</result></rpc-reply>");
cprintf(cbret, "<rpc-reply><result xmlns=\"urn:example:clixon\">ok</result></rpc-reply>");
return 0;
}

View file

@ -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
#

View file

@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -51,11 +51,6 @@ int strverscmp (__const char *__s1, __const char *__s2);
*/
#define XMLNS_YANG_ONLY 1
/* Set for full XML namespace code in XML, NETCONF and YANG
* Experimental
*/
#undef ENABLE_XMLNS
/* If set, patch all CLI spec calls to @datamodel:tree to @datamodel.
* This is a backward compatible fix for 3.9 for CLIgen specification files
* using model generation (CLIXON_CLI_GENMODEL).

View file

@ -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
#

View file

@ -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
#

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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);

View file

@ -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.

View file

@ -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.

View file

@ -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);

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
@ -80,6 +80,10 @@ enum startup_mode_t{
/* Print registry on file. For debugging. */
void clicon_option_dump(clicon_handle h, int dblevel);
/* Add a clicon options overriding file setting */
int clicon_option_add(clicon_handle h, char *name, char *value);
/* Initialize options: set defaults, read config-file, etc */
int clicon_options_main(clicon_handle h, yang_spec *yspec);
@ -174,10 +178,8 @@ int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
yang_spec * clicon_config_yang(clicon_handle h);
int clicon_config_yang_set(clicon_handle h, struct yang_spec *ys);
#endif
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);

View file

@ -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.

View file

@ -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);

View file

@ -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.

View file

@ -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.

View file

@ -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.

Some files were not shown because too many files have changed in this diff Show more