Merge branch 'develop' into nacm
This commit is contained in:
commit
9a7ce8e06d
194 changed files with 5941 additions and 3452 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -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
|
||||
118
CHANGELOG.md
118
CHANGELOG.md
|
|
@ -1,47 +1,105 @@
|
|||
# Clixon Changelog
|
||||
|
||||
## 3.9.0 (Preliminary Target: 31 December 2018)
|
||||
## 3.9.0 (Preliminary Target: Mid-January 2019)
|
||||
|
||||
### Planned new features
|
||||
* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
|
||||
* [Roadmap](ROADMAP.md)
|
||||
|
||||
### Major New features
|
||||
|
||||
* NACM extension (RFC8341)
|
||||
* NACM module support (RFC8341 A1+A2)
|
||||
* Recovery user "_nacm_recovery" added.
|
||||
* Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
|
||||
* Example user changed adm1 to andy to comply with RFC8341 example
|
||||
* Correct XML namespace handling
|
||||
* XML multiple modules was based on non-strict semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208) as well as strict Netconf and Restconf namespace handling, which causes problems with overlapping names and false positives, and most importantly, with standard conformance.
|
||||
* There are still the following non-strict namespace handling:
|
||||
* Everything in ietf-netconf base syntax with namespace `urn:ietf:params:xml:ns:netconf:base:1.0` is default and need not be explicitly given
|
||||
* edit-config xpath select statement does not support namespaces
|
||||
* notifications do not support namespaces.
|
||||
* Below see netconf old (but wrong) netconf RPC:
|
||||
```
|
||||
<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,7 +125,14 @@
|
|||
* <!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>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
CLIXON is dual license.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -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
|
||||
|
|
|
|||
35
ROADMAP.md
35
ROADMAP.md
|
|
@ -1,27 +1,42 @@
|
|||
# Clixon roadmap
|
||||
|
||||
Not in prio order (yet)
|
||||
## High prio
|
||||
- NACM (RFC 8341)
|
||||
- Module rules (done)
|
||||
- Data node rules (read/create/delete/update/execute)
|
||||
- Special handling of the initial startup transaction to avoid exit at startup
|
||||
- Possibly - draft-wu-netconf-restconf-factory-restore-03
|
||||
- Handle revisions to data model.
|
||||
- Possibly draft-wang-netmod-module-revision-management-01
|
||||
- XML [Namespace handling](https://github.com/clicon/clixon/issues/49)
|
||||
|
||||
- XML
|
||||
- [Namespace handling](https://github.com/clicon/clixon/issues/49)
|
||||
## Medium prio:
|
||||
- Input validation on custom RPCs/ (done)
|
||||
- [Sanity checks](https://github.com/clicon/clixon/issues/47)
|
||||
- Support for XML regex's.
|
||||
- Currently Posix extended regular expressions
|
||||
- Support a plugin callback that is invoked when copy-config is called.
|
||||
- Preserve CLI command history across sessions. The up/down arrows
|
||||
|
||||
## Low prio:
|
||||
- Provide a client library to access netconf APIs provided by system services.
|
||||
- Netconf backend (Clixon acts as netconf controller)
|
||||
- Support for restconf call-home (RFC 8071)
|
||||
|
||||
Not prioritized:
|
||||
- Support for restconf PATCH method
|
||||
- NETCONF
|
||||
- Support for additional Netconf [edit-config modes](https://github.com/clicon/clixon/issues/53)
|
||||
- Netconf [framing](https://github.com/clicon/clixon/issues/50)
|
||||
- [Sanity checks](https://github.com/clicon/clixon/issues/47)
|
||||
- [Child ordering](https://github.com/clicon/clixon/issues/22)
|
||||
- Netconf backend (Clixon acts as netconf controller)
|
||||
- Restconf
|
||||
- Query parameters
|
||||
- NACM (RFC 8341) is somewhat limited
|
||||
- Extend with data node access (read/create/delete/update/execute)
|
||||
- Streams (netconf and restconf)
|
||||
- Extend native stream mode with external persistent timeseries database, eg influxdb.
|
||||
- Jenkins CI/CD and webhooks
|
||||
- YANG
|
||||
- [Cardinality](https://github.com/clicon/clixon/issues/48)
|
||||
- RFC 6022 [NETCONF monitoring](https://github.com/clicon/clixon/issues/39)
|
||||
- Factory default Setting - draft-wu-netconf-restconf-factory-restore-03
|
||||
- Deviation, belongs-to, min/max-elements, action, unique
|
||||
- Deviation, min/max-elements, action, unique
|
||||
- Containers
|
||||
- [Docker improvements](https://github.com/clicon/clixon/issues/44)
|
||||
- Kubernetes Helm chart definition
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,14 +309,14 @@ 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)
|
||||
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") &&
|
||||
(retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
|
||||
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") &&
|
||||
(retval = yang_modules_state_get(h, yspec, xret)) != 0)
|
||||
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;
|
||||
|
|
@ -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,8 +378,12 @@ 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 (ret == 1){ /* Error from callback (error in xret) */
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply>"); /* OK */
|
||||
if (xret==NULL)
|
||||
cprintf(cbret, "<data/>");
|
||||
else{
|
||||
|
|
@ -390,22 +393,9 @@ from_client_get(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
cprintf(cbret, "</rpc-reply>");
|
||||
}
|
||||
else { /* 1 Error from callback */
|
||||
if ((cbx = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
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));
|
||||
}
|
||||
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");
|
||||
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;
|
||||
}
|
||||
ok:
|
||||
if (!cbuf_len(cbret))
|
||||
assert(cbuf_len(cbret) == 0);
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
ok:
|
||||
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)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
|
||||
if ((ret = xml_yang_validate_rpc(x, cbret)) < 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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));
|
||||
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
|
||||
* @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;
|
||||
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 (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)
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,11 +177,15 @@ 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;
|
||||
|
|
@ -189,9 +194,7 @@ db_merge(clicon_handle h,
|
|||
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);
|
||||
|
|
@ -386,7 +391,12 @@ startup_mode_running(clicon_handle h,
|
|||
char *extraxml_file)
|
||||
{
|
||||
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,35 +409,47 @@ 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){
|
||||
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", 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
|
||||
*/
|
||||
clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting.", __FUNCTION__);
|
||||
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;
|
||||
}
|
||||
/* Merge user reset state and extra xml file (no commit) */
|
||||
if (db_merge(h, "tmp", "running") < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xmldb_delete(h, "tmp") < 0)
|
||||
goto done;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Clixon startup startup mode: Commit startup configuration into running state
|
||||
|
||||
|
|
@ -455,7 +477,13 @@ startup_mode_startup(clicon_handle h,
|
|||
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,8 +843,7 @@ 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)
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,7 +236,7 @@ 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)
|
||||
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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -503,7 +503,7 @@ cli_start_shell(clicon_handle h,
|
|||
{
|
||||
char *cmd;
|
||||
struct passwd *pw;
|
||||
int retval;
|
||||
int retval = -1;
|
||||
char bcmd[128];
|
||||
cg_var *cv1 = cvec_i(vars, 1);
|
||||
|
||||
|
|
@ -512,42 +512,43 @@ cli_start_shell(clicon_handle h,
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
/* 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");
|
||||
if (mincv){
|
||||
if ((r = cv2str_dup(mincv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||
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,"]");
|
||||
}
|
||||
}
|
||||
}
|
||||
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, "%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;
|
||||
}
|
||||
}
|
||||
}
|
||||
cprintf(cb, "%s]", r); /* range */
|
||||
free(r);
|
||||
r = NULL;
|
||||
}
|
||||
if (options & YANG_OPTIONS_PATTERN)
|
||||
cprintf(cb, " regexp:\"%s\"", pattern);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,9 +80,10 @@
|
|||
* @param[in] cb Packet buffer
|
||||
*/
|
||||
static int
|
||||
process_incoming_packet(clicon_handle h,
|
||||
netconf_input_packet(clicon_handle h,
|
||||
cbuf *cb)
|
||||
{
|
||||
int retval = -1;
|
||||
char *str;
|
||||
char *str0;
|
||||
cxobj *xreq = NULL; /* Request (in) */
|
||||
|
|
@ -92,9 +93,12 @@ process_incoming_packet(clicon_handle h,
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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>"
|
||||
|
|
@ -365,66 +318,6 @@ netconf_edit_config(clicon_handle h,
|
|||
"</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:
|
||||
|
|
@ -433,99 +326,6 @@ netconf_copy_config(clicon_handle h,
|
|||
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:
|
||||
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,133 +389,6 @@ 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;
|
||||
|
|
@ -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:
|
||||
|
|
@ -882,6 +556,10 @@ netconf_application_rpc(clicon_handle h,
|
|||
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)
|
||||
goto done;
|
||||
if (xml_yang_validate_add(xn, NULL) < 0)
|
||||
if ((ret = xml_yang_validate_all_top(xn, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
netconf_output_encap(1, cbret, "rpc-error");
|
||||
goto ok;
|
||||
}
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||
if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
|
||||
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,46 +702,13 @@ 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)
|
||||
cc_closed++;
|
||||
if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* RFC 5277 :notification */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
[
|
||||
```
|
||||
Get the type of a specific interface:
|
||||
```
|
||||
> curl -G http://127.0.0.1/restconf/data/interfaces/interface=eth9/type
|
||||
{
|
||||
"type": "eth"
|
||||
"ietf-interfaces:type": "eth"
|
||||
}
|
||||
]
|
||||
|
||||
curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
|
||||
```
|
||||
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:
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,10 +71,11 @@ 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", 401}, /* or 403 */
|
||||
{"access-denied", 403},
|
||||
{"lock-denied", 409},
|
||||
{"resource-denied", 409},
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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;
|
||||
}
|
||||
/* Second pass: modify tree */
|
||||
#endif
|
||||
x0vec[i++] = x0c; /* != NULL if x0c is matching x1c */
|
||||
}
|
||||
/* 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] 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[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,15 +957,12 @@ text_put(xmldb_handle xh,
|
|||
yang_spec *yspec;
|
||||
cxobj *x0 = NULL;
|
||||
struct db_element *de = NULL;
|
||||
int cbretlocal = 0; /* Set if cbret is NULL on entry */
|
||||
int ret;
|
||||
|
||||
if (cbret == NULL){
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
clicon_err(OE_XML, EINVAL, "cbret is NULL");
|
||||
goto done;
|
||||
}
|
||||
cbretlocal++;
|
||||
}
|
||||
if ((yspec = th->th_yangspec) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
144
doc/FAQ.md
144
doc/FAQ.md
|
|
@ -42,9 +42,9 @@ The example:
|
|||
sudo make install
|
||||
```
|
||||
|
||||
## How do you run Clixon example commands?
|
||||
## How do I run Clixon example commands?
|
||||
|
||||
- Start a backend server: `clixon_backend -Ff /usr/local/etc/example.xml`
|
||||
- Start a backend server: `clixon_backend -F -s init -f /usr/local/etc/example.xml`
|
||||
- Start a cli session: `clixon_cli -f /usr/local/etc/example.xml`
|
||||
- Start a netconf session: `clixon_netconf -f /usr/local/etc/example.xml`
|
||||
- Start a restconf daemon: `sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data`
|
||||
|
|
@ -72,6 +72,82 @@ grep clicon /etc/group
|
|||
clicon:x:1001:<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,7 +265,7 @@ 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?
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
```
|
||||
clixon_cli -f /usr/local/etc/example.xml
|
||||
cli> notify
|
||||
cli> Routing notification
|
||||
Routing notification
|
||||
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`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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,7 +158,7 @@ route_count(clicon_handle h,
|
|||
* in [RFC6241].
|
||||
*/
|
||||
static int
|
||||
empty(clicon_handle h, /* Clicon handle */
|
||||
empty_rpc(clicon_handle h, /* Clicon handle */
|
||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||
void *arg, /* client_entry */
|
||||
|
|
@ -167,6 +168,39 @@ empty(clicon_handle h, /* Clicon handle */
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
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
Loading…
Add table
Add a link
Reference in a new issue