* 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.
* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily. * 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`.
This commit is contained in:
parent
861300d6c0
commit
0baebc93fd
71 changed files with 2679 additions and 1573 deletions
75
CHANGELOG.md
75
CHANGELOG.md
|
|
@ -1,11 +1,61 @@
|
||||||
# Clixon Changelog
|
# Clixon Changelog
|
||||||
|
|
||||||
## 3.9.0 (Preliminary Target: 31 December 2018)
|
## 3.9.0 (Preliminary Target: Mid-January 2019)
|
||||||
|
|
||||||
### Planned new features
|
### Planned new features
|
||||||
* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
|
* [Roadmap](ROADMAP.md) (Uncommitted and unprioritized)
|
||||||
|
|
||||||
### Major New features
|
### Major New features
|
||||||
|
* 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
|
||||||
* NACM extension (RFC8341)
|
* NACM extension (RFC8341)
|
||||||
* NACM module support (RFC8341 A1+A2)
|
* NACM module support (RFC8341 A1+A2)
|
||||||
* Recovery user "_nacm_recovery" added.
|
* Recovery user "_nacm_recovery" added.
|
||||||
|
|
@ -27,25 +77,15 @@
|
||||||
* Note CLIXON_DATADIR (=/usr/local/share/clixon) need to be in the list
|
* 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_FILE Provides a filename with a single module filename.
|
||||||
* CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
|
* 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
|
|
||||||
|
|
||||||
### API changes on existing features (you may need to change your code)
|
### API changes on existing features (you may need to change your code)
|
||||||
* Removed delete-config support for candidate db since it is not supported in RFC6241.
|
* 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.
|
||||||
|
* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily.
|
||||||
|
* 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.
|
* 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.
|
* Yang parser is stricter (see above) which may break parsing of existing yang specs.
|
||||||
* XML namespace handling is corrected (see above)
|
* 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.
|
* 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.
|
* 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
|
* Change all @datamodel:tree to @datamodel in all CLI specification files
|
||||||
|
|
@ -53,6 +93,9 @@
|
||||||
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
|
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
* 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
|
* Added example_rpc RPC to example backend
|
||||||
* Renamed xml_namespace[_set]() to xml_prefix[_set]()
|
* Renamed xml_namespace[_set]() to xml_prefix[_set]()
|
||||||
* Changed all make tags --> make TAGS
|
* Changed all make tags --> make TAGS
|
||||||
|
|
@ -273,7 +316,7 @@ translate {
|
||||||
|
|
||||||
### Known issues
|
### Known issues
|
||||||
* Namespace name relabeling is not supported.
|
* 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>
|
<crypto xmlns:x="urn:example:des">x:des3</crypto>
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,9 @@ EOF
|
||||||
|
|
||||||
## New release
|
## New release
|
||||||
What to think about when doing a 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
|
* git merge --no-ff develop
|
||||||
* change CLIXON_VERSION in configure.ac
|
* change CLIXON_VERSION in configure.ac
|
||||||
* git tag -a <version"
|
* git tag -a <version"
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -105,6 +105,23 @@ The standards covered include:
|
||||||
Not supported:
|
Not supported:
|
||||||
- !DOCTYPE (ie DTD)
|
- !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
|
||||||
====
|
====
|
||||||
YANG and XML is the heart of Clixon. Yang modules are used as a
|
YANG and XML is the heart of Clixon. Yang modules are used as a
|
||||||
|
|
@ -153,6 +170,9 @@ Clixon does not support the following netconf features:
|
||||||
- edit-config config-text
|
- edit-config config-text
|
||||||
- edit-config operation
|
- edit-config operation
|
||||||
|
|
||||||
|
Some other deviations from the RFC:
|
||||||
|
- edit-config xpath select statement does not support namespaces
|
||||||
|
|
||||||
Restconf
|
Restconf
|
||||||
========
|
========
|
||||||
Clixon Restconf is a daemon based on FastCGI C-API. Instructions are available to
|
Clixon Restconf is a daemon based on FastCGI C-API. Instructions are available to
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ client_get_streams(clicon_handle h,
|
||||||
* @param[in,out] xret Existing XML tree, merge x into this
|
* @param[in,out] xret Existing XML tree, merge x into this
|
||||||
* @retval -1 Error (fatal)
|
* @retval -1 Error (fatal)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval 1 Statedata callback failed
|
* @retval 1 Statedata callback failed (clicon_err called)
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
client_statedata(clicon_handle h,
|
client_statedata(clicon_handle h,
|
||||||
|
|
@ -309,14 +309,14 @@ client_statedata(clicon_handle h,
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
|
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
|
||||||
(retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
|
if ((retval = client_get_streams(h, yspec, xpath, "ietf-netconf-notification", "netconf", xret)) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
|
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040"))
|
||||||
(retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
|
if ((retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") &&
|
if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895"))
|
||||||
(retval = yang_modules_state_get(h, yspec, xret)) != 0)
|
if ((retval = yang_modules_state_get(h, yspec, xret)) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
|
if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -339,7 +339,7 @@ client_statedata(clicon_handle h,
|
||||||
/* reset flag */
|
/* reset flag */
|
||||||
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
if (xml_apply(*xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0; /* OK */
|
||||||
done:
|
done:
|
||||||
if (xvec)
|
if (xvec)
|
||||||
free(xvec);
|
free(xvec);
|
||||||
|
|
@ -363,7 +363,6 @@ from_client_get(clicon_handle h,
|
||||||
char *xpath = "/";
|
char *xpath = "/";
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
|
||||||
|
|
||||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||||
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
if ((xpath = xml_find_value(xfilter, "select"))==NULL)
|
||||||
|
|
@ -379,8 +378,12 @@ from_client_get(clicon_handle h,
|
||||||
clicon_err_reset();
|
clicon_err_reset();
|
||||||
if ((ret = client_statedata(h, xpath, &xret)) < 0)
|
if ((ret = client_statedata(h, xpath, &xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* OK */
|
if (ret == 1){ /* Error from callback (error in xret) */
|
||||||
cprintf(cbret, "<rpc-reply>");
|
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
cprintf(cbret, "<rpc-reply>"); /* OK */
|
||||||
if (xret==NULL)
|
if (xret==NULL)
|
||||||
cprintf(cbret, "<data/>");
|
cprintf(cbret, "<data/>");
|
||||||
else{
|
else{
|
||||||
|
|
@ -390,22 +393,9 @@ from_client_get(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cbret, "</rpc-reply>");
|
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:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cbx)
|
|
||||||
cbuf_free(cbx);
|
|
||||||
if (xret)
|
if (xret)
|
||||||
xml_free(xret);
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -433,6 +423,7 @@ from_client_edit_config(clicon_handle h,
|
||||||
int non_config = 0;
|
int non_config = 0;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
cbuf *cbx = NULL; /* Assist cbuf */
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||||
|
|
@ -491,24 +482,27 @@ from_client_edit_config(clicon_handle h,
|
||||||
/* Cant do this earlier since we dont have a yang spec to
|
/* Cant do this earlier since we dont have a yang spec to
|
||||||
* the upper part of the tree, until we get the "config" tree.
|
* 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 (clicon_xml_sort(h) && xml_apply0(xc, CX_ELMNT, xml_sort, NULL) < 0)
|
||||||
goto done;
|
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__);
|
clicon_debug(1, "%s ERROR PUT", __FUNCTION__);
|
||||||
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
|
if (netconf_operation_failed(cbret, "protocol", clicon_err_reason)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
if (ret == 0)
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
ok:
|
assert(cbuf_len(cbret) == 0);
|
||||||
if (!cbuf_len(cbret))
|
|
||||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cbx)
|
if (cbx)
|
||||||
cbuf_free(cbx);
|
cbuf_free(cbx);
|
||||||
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
|
clicon_debug(1, "%s done cbret:%s", __FUNCTION__, cbuf_get(cbret));
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
} /* from_client_edit_config */
|
} /* from_client_edit_config */
|
||||||
|
|
||||||
/*! Internal message: Lock database
|
/*! Internal message: Lock database
|
||||||
|
|
|
||||||
|
|
@ -165,12 +165,24 @@ validate_common(clicon_handle h,
|
||||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||||
goto done;
|
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)
|
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* This is the state we are going to */
|
||||||
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
|
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
|
||||||
goto done;
|
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 */
|
/* 3. Compute differences */
|
||||||
if (xml_diff(yspec,
|
if (xml_diff(yspec,
|
||||||
td->td_src,
|
td->td_src,
|
||||||
|
|
@ -240,7 +252,7 @@ validate_common(clicon_handle h,
|
||||||
* do something more drastic?
|
* do something more drastic?
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] candidate A candidate database, not necessarily "candidate"
|
* @param[in] candidate A candidate database, not necessarily "candidate"
|
||||||
* @retval -1 Error - or validation failed (but cbret not set)
|
* @retval -1 Error - or validation failed
|
||||||
* @retval 0 Validation failed (with cbret set)
|
* @retval 0 Validation failed (with cbret set)
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @note Need to differentiate between error and validation fail
|
* @note Need to differentiate between error and validation fail
|
||||||
|
|
@ -259,7 +271,9 @@ candidate_commit(clicon_handle h,
|
||||||
if ((td = transaction_new()) == NULL)
|
if ((td = transaction_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Common steps (with validate). Note this is only call that uses 3-values */
|
/* 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)
|
if ((ret = validate_common(h, candidate, td, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
@ -270,12 +284,14 @@ candidate_commit(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Optionally write (potentially modified) tree back to candidate */
|
/* Optionally write (potentially modified) tree back to candidate */
|
||||||
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
|
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
|
||||||
if (xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL) < 0)
|
if ((ret = xmldb_put(h, candidate, OP_REPLACE, td->td_target, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
/* 8. Success: Copy candidate to running
|
/* 8. Success: Copy candidate to running
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (xmldb_copy(h, candidate, "running") < 0)
|
if (xmldb_copy(h, candidate, "running") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
@ -423,9 +439,11 @@ from_client_validate(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Optionally write (potentially modified) tree back to candidate */
|
/* Optionally write (potentially modified) tree back to candidate */
|
||||||
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD"))
|
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
|
||||||
if (xmldb_put(h, "candidate", OP_REPLACE, td->td_target, NULL) < 0)
|
if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -176,11 +176,15 @@ db_reset(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Merge db1 into db2 without commit
|
/*! Merge db1 into db2 without commit
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 Validation failed (with cbret set)
|
||||||
|
* @retval 1 Validation OK
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
db_merge(clicon_handle h,
|
db_merge(clicon_handle h,
|
||||||
const char *db1,
|
const char *db1,
|
||||||
const char *db2)
|
const char *db2,
|
||||||
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
|
|
@ -189,9 +193,7 @@ db_merge(clicon_handle h,
|
||||||
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
|
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Merge xml into db2. Without commit */
|
/* Merge xml into db2. Without commit */
|
||||||
if (xmldb_put(h, (char*)db2, OP_MERGE, xt, NULL) < 0)
|
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, cbret);
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
done:
|
||||||
if (xt)
|
if (xt)
|
||||||
xml_free(xt);
|
xml_free(xt);
|
||||||
|
|
@ -288,18 +290,22 @@ nacm_load_external(clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Merge xml in filename into database
|
/*! Merge xml in filename into database
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 Validation failed (with cbret set)
|
||||||
|
* @retval 1 Validation OK
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
load_extraxml(clicon_handle h,
|
load_extraxml(clicon_handle h,
|
||||||
char *filename,
|
char *filename,
|
||||||
const char *db)
|
const char *db,
|
||||||
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
|
||||||
if (filename == NULL)
|
if (filename == NULL)
|
||||||
return 0;
|
return 1;
|
||||||
if ((fd = open(filename, O_RDONLY)) < 0){
|
if ((fd = open(filename, O_RDONLY)) < 0){
|
||||||
clicon_err(OE_UNIX, errno, "open(%s)", filename);
|
clicon_err(OE_UNIX, errno, "open(%s)", filename);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -310,9 +316,7 @@ load_extraxml(clicon_handle h,
|
||||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Merge user reset state */
|
/* Merge user reset state */
|
||||||
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
|
retval = xmldb_put(h, (char*)db, OP_MERGE, xt, cbret);
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
done:
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
@ -388,6 +392,10 @@ startup_mode_running(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cbret = NULL;
|
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 */
|
/* Stash original running to candidate for later commit */
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -400,42 +408,47 @@ startup_mode_running(clicon_handle h,
|
||||||
/* Application may define extra xml in its reset function*/
|
/* Application may define extra xml in its reset function*/
|
||||||
if (clixon_plugin_reset(h, "tmp") < 0)
|
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||||
goto done;
|
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 */
|
/* Get application extra xml from file */
|
||||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
|
||||||
goto done;
|
goto fail;
|
||||||
/* Clear running db */
|
/* Clear running db */
|
||||||
if (db_reset(h, "running") < 0)
|
if (db_reset(h, "running") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((cbret = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Commit original running. Assume -1 is validate fail */
|
/* Commit original running. Assume -1 is validate fail */
|
||||||
if (candidate_commit(h, "candidate", cbret) < 1){
|
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
|
/* (1) We cannot differentiate between fatal errors and validation
|
||||||
* failures
|
* failures
|
||||||
* (2) If fatal error, we should exit
|
* (2) If fatal error, we should exit
|
||||||
* (3) If validation fails we cannot continue. How could we?
|
* (3) If validation fails we cannot continue. How could we?
|
||||||
* (4) Need to restore the running db since we destroyed it above
|
* (4) Need to restore the running db since we destroyed it above
|
||||||
*/
|
*/
|
||||||
clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting: %s.",
|
if (strlen(cbuf_get(cbret)))
|
||||||
|
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
|
||||||
__FUNCTION__, cbuf_get(cbret));
|
__FUNCTION__, cbuf_get(cbret));
|
||||||
|
else
|
||||||
|
clicon_log(LOG_NOTICE, "%s: Commit of running failed, exiting: %s.",
|
||||||
|
__FUNCTION__, clicon_err_reason);
|
||||||
/* Reinstate original */
|
/* Reinstate original */
|
||||||
if (xmldb_copy(h, "candidate", "running") < 0)
|
if (xmldb_copy(h, "candidate", "running") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
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 (cbret)
|
|
||||||
cbuf_free(cbret);
|
|
||||||
if (xmldb_delete(h, "tmp") < 0)
|
|
||||||
goto done;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Clixon startup startup mode: Commit startup configuration into running state
|
/*! Clixon startup startup mode: Commit startup configuration into running state
|
||||||
|
|
||||||
|
|
@ -465,6 +478,11 @@ startup_mode_startup(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cbret = NULL;
|
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 */
|
/* Stash original running to backup */
|
||||||
if (xmldb_copy(h, "running", "backup") < 0)
|
if (xmldb_copy(h, "running", "backup") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -481,34 +499,25 @@ startup_mode_startup(clicon_handle h,
|
||||||
/* Application may define extra xml in its reset function*/
|
/* Application may define extra xml in its reset function*/
|
||||||
if (clixon_plugin_reset(h, "tmp") < 0)
|
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||||
goto done;
|
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 */
|
/* Get application extra xml from file */
|
||||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
if (load_extraxml(h, extraxml_file, "tmp", cbret) < 1)
|
||||||
goto done;
|
goto fail;
|
||||||
/* Clear running db */
|
/* Clear running db */
|
||||||
if (db_reset(h, "running") < 0)
|
if (db_reset(h, "running") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Create return buffer (not used) */
|
|
||||||
if ((cbret = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Commit startup */
|
/* Commit startup */
|
||||||
if (candidate_commit(h, "startup", cbret) < 1){ /* diff */
|
if (candidate_commit(h, "startup", cbret) < 1) /* diff */
|
||||||
/* We cannot differentiate between fatal errors and validation
|
goto fail;
|
||||||
* failures
|
|
||||||
* In both cases we copy back the original running and quit
|
|
||||||
*/
|
|
||||||
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting: %s.",
|
|
||||||
__FUNCTION__, cbuf_get(cbret));
|
|
||||||
if (xmldb_copy(h, "backup", "running") < 0)
|
|
||||||
goto done;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Merge user reset state and extra xml file (no commit) */
|
/* Merge user reset state and extra xml file (no commit) */
|
||||||
if (db_merge(h, "tmp", "running") < 0)
|
if (db_merge(h, "tmp", "running", cbret) < 1)
|
||||||
goto done;
|
goto fail;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
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)
|
if (cbret)
|
||||||
cbuf_free(cbret);
|
cbuf_free(cbret);
|
||||||
if (xmldb_delete(h, "backup") < 0)
|
if (xmldb_delete(h, "backup") < 0)
|
||||||
|
|
@ -516,6 +525,20 @@ startup_mode_startup(clicon_handle h,
|
||||||
if (xmldb_delete(h, "tmp") < 0)
|
if (xmldb_delete(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
return retval;
|
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
|
int
|
||||||
|
|
@ -540,7 +563,6 @@ main(int argc,
|
||||||
int sockfamily;
|
int sockfamily;
|
||||||
char *xmldb_plugin;
|
char *xmldb_plugin;
|
||||||
int xml_cache;
|
int xml_cache;
|
||||||
int xml_pretty;
|
|
||||||
char *xml_format;
|
char *xml_format;
|
||||||
char *nacm_mode;
|
char *nacm_mode;
|
||||||
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
|
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
|
||||||
|
|
@ -808,8 +830,9 @@ main(int argc,
|
||||||
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
|
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
|
||||||
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
|
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xml_pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) >= 0)
|
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
|
||||||
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)xml_pretty) < 0)
|
goto done;
|
||||||
|
if (xmldb_setopt(h, "sort", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XML_SORT")) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Startup mode needs to be defined, */
|
/* Startup mode needs to be defined, */
|
||||||
startup_mode = clicon_startup_mode(h);
|
startup_mode = clicon_startup_mode(h);
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ clixon_plugin_reset(clicon_handle h,
|
||||||
* @param[in,out] xtop State XML tree is merged with existing tree.
|
* @param[in,out] xtop State XML tree is merged with existing tree.
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval 1 Statedata callback failed
|
* @retval 1 Statedata callback failed (xret set with netconf-error)
|
||||||
* @note xtop can be replaced
|
* @note xtop can be replaced
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ cli_dbxml(clicon_handle h,
|
||||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
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;
|
goto done;
|
||||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -293,7 +293,7 @@ cli_set(clicon_handle h,
|
||||||
cvec *cvv,
|
cvec *cvv,
|
||||||
cvec *argv)
|
cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = 1;
|
int retval = -1;
|
||||||
|
|
||||||
if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0)
|
if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -503,7 +503,7 @@ cli_start_shell(clicon_handle h,
|
||||||
{
|
{
|
||||||
char *cmd;
|
char *cmd;
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
int retval;
|
int retval = -1;
|
||||||
char bcmd[128];
|
char bcmd[128];
|
||||||
cg_var *cv1 = cvec_i(vars, 1);
|
cg_var *cv1 = cvec_i(vars, 1);
|
||||||
|
|
||||||
|
|
@ -512,42 +512,43 @@ cli_start_shell(clicon_handle h,
|
||||||
if ((pw = getpwuid(getuid())) == NULL){
|
if ((pw = getpwuid(getuid())) == NULL){
|
||||||
fprintf(stderr, "%s: getpwuid: %s\n",
|
fprintf(stderr, "%s: getpwuid: %s\n",
|
||||||
__FUNCTION__, strerror(errno));
|
__FUNCTION__, strerror(errno));
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
if (chdir(pw->pw_dir) < 0){
|
if (chdir(pw->pw_dir) < 0){
|
||||||
fprintf(stderr, "%s: chdir(%s): %s\n",
|
fprintf(stderr, "%s: chdir(%s): %s\n",
|
||||||
__FUNCTION__, pw->pw_dir, strerror(errno));
|
__FUNCTION__, pw->pw_dir, strerror(errno));
|
||||||
endpwent();
|
endpwent();
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
endpwent();
|
endpwent();
|
||||||
cli_signal_flush(h);
|
cli_signal_flush(h);
|
||||||
cli_signal_unblock(h);
|
cli_signal_unblock(h);
|
||||||
if (cmd){
|
if (cmd){
|
||||||
snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd);
|
snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd);
|
||||||
if ((retval = system(bcmd)) < 0){
|
if (system(bcmd) < 0){
|
||||||
cli_signal_block(h);
|
cli_signal_block(h);
|
||||||
fprintf(stderr, "%s: system(bash -c): %s\n",
|
fprintf(stderr, "%s: system(bash -c): %s\n",
|
||||||
__FUNCTION__, strerror(errno));
|
__FUNCTION__, strerror(errno));
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if ((retval = system("bash -l")) < 0){
|
if (system("bash -l") < 0){
|
||||||
cli_signal_block(h);
|
cli_signal_block(h);
|
||||||
fprintf(stderr, "%s: system(bash): %s\n",
|
fprintf(stderr, "%s: system(bash): %s\n",
|
||||||
__FUNCTION__, strerror(errno));
|
__FUNCTION__, strerror(errno));
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
cli_signal_block(h);
|
cli_signal_block(h);
|
||||||
#if 0 /* Allow errcodes from bash */
|
#if 0 /* Allow errcodes from bash */
|
||||||
if (retval != 0){
|
if (retval != 0){
|
||||||
fprintf(stderr, "%s: system(%s) code=%d\n", __FUNCTION__, cmd, retval);
|
fprintf(stderr, "%s: system(%s) code=%d\n", __FUNCTION__, cmd, retval);
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
retval = 0;
|
||||||
return 0;
|
done:
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generic quit callback
|
/*! Generic quit callback
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ expand_dbvar(void *h,
|
||||||
/* This is primarily to get "y",
|
/* This is primarily to get "y",
|
||||||
* xpath2xml would have worked!!
|
* 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;
|
goto done;
|
||||||
if (y==NULL)
|
if (y==NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
@ -443,6 +443,7 @@ cli_show_config(clicon_handle h,
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
enum genmodel_type gt;
|
enum genmodel_type gt;
|
||||||
|
yang_spec *yspec;
|
||||||
|
|
||||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
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));
|
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);
|
clicon_rpc_generate_error("Get configuration", xerr);
|
||||||
goto done;
|
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 */
|
/* Print configuration according to format */
|
||||||
switch (format){
|
switch (format){
|
||||||
case FORMAT_XML:
|
case FORMAT_XML:
|
||||||
|
|
@ -516,7 +524,7 @@ cli_show_config(clicon_handle h,
|
||||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||||
goto done;
|
goto done;
|
||||||
xc = NULL; /* Dont print xt itself */
|
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 */
|
xml2cli(stdout, xc, NULL, gt); /* cli syntax */
|
||||||
break;
|
break;
|
||||||
case FORMAT_NETCONF:
|
case FORMAT_NETCONF:
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,8 @@ process_incoming_packet(clicon_handle h,
|
||||||
free(str0);
|
free(str0);
|
||||||
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
|
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
|
||||||
isrpc++;
|
isrpc++;
|
||||||
|
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
|
||||||
|
goto done;
|
||||||
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
|
if ((ret = xml_yang_validate_rpc(xrpc, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
|
|
|
||||||
|
|
@ -905,13 +905,9 @@ netconf_application_rpc(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
|
yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn));
|
||||||
if ((yrpc==NULL) && _CLICON_XML_NS_ITERATE){
|
if ((yrpc==NULL) && !_CLICON_XML_NS_STRICT){
|
||||||
int i;
|
if (xml_yang_find_non_strict(xn, yspec, &yrpc) < 0) /* Y_RPC */
|
||||||
for (i=0; i<yspec->yp_len; i++){
|
goto done;
|
||||||
ymod = yspec->yp_stmt[i];
|
|
||||||
if ((yrpc = yang_find((yang_node*)ymod, Y_RPC, xml_name(xn))) != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* Check if found */
|
/* Check if found */
|
||||||
if (yrpc != NULL){
|
if (yrpc != NULL){
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
# Clixon Restconf
|
# Clixon Restconf
|
||||||
|
|
||||||
* [Installation](#Installation)
|
* [Installation](#installation)
|
||||||
* [Streams](Streams)
|
* [Streams](#streams)
|
||||||
* [Nchan Streams](Nchan)
|
* [Nchan Streams](#nchan-streams)
|
||||||
* [Debugging](Debugging)
|
* [Debugging](#debugging)
|
||||||
|
|
||||||
### 1. Installation
|
## 1. Installation
|
||||||
|
|
||||||
The examples are based on Nginx. Other reverse proxies should work but are not verified.
|
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
|
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
|
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":[
|
"interface":[
|
||||||
{
|
{
|
||||||
"name": "eth0",
|
|
||||||
"type": "eth",
|
|
||||||
"enabled": "true",
|
|
||||||
"name": "eth9",
|
"name": "eth9",
|
||||||
"type": "eth",
|
"type": "ex:eth",
|
||||||
"enabled": "true"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
```
|
||||||
|
Example of writing a new interfaces specification:
|
||||||
curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
|
```
|
||||||
|
curl -sX PUT http://localhost/restconf/data -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth1","type":"ex:eth","enabled":true}}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Streams
|
## 2. Streams
|
||||||
|
|
||||||
Clixon have two experimental restconf event stream implementations following
|
Clixon have two experimental restconf event stream implementations following
|
||||||
RFC8040 Section 6 using SSE. One native and one using Nginx
|
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:
|
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: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>
|
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.
|
See (stream tests)[../test/test_streams.sh] for more examples.
|
||||||
|
|
||||||
### 3. Nchan
|
## 3. Nchan
|
||||||
|
|
||||||
As an alternative streams implementation, Nginx/Nchan can be used.
|
As an alternative streams implementation, Nginx/Nchan can be used.
|
||||||
Nginx uses pub/sub channels and can be configured in a variety of
|
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.
|
See (https://nchan.io/#eventsource) on more info on how to access an SSE sub endpoint.
|
||||||
|
|
||||||
### 4. Debugging
|
## 4. Debugging
|
||||||
|
|
||||||
Start the restconf fastcgi program with debug flag:
|
Start the restconf fastcgi program with debug flag:
|
||||||
```
|
```
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -249,7 +249,7 @@ restconf_stream(clicon_handle h,
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
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 */
|
/* Print all fields */
|
||||||
for (i=0; i<cvec_len(qvec); i++){
|
for (i=0; i<cvec_len(qvec); i++){
|
||||||
cv = cvec_i(qvec, i);
|
cv = cvec_i(qvec, i);
|
||||||
|
|
|
||||||
|
|
@ -239,15 +239,18 @@ main(int argc, char **argv)
|
||||||
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
|
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
|
||||||
usage(argv0);
|
usage(argv0);
|
||||||
}
|
}
|
||||||
_CLICON_XML_NS_ITERATE = 1;
|
_CLICON_XML_NS_STRICT = 0;
|
||||||
if (xml_parse_string(argv[2], NULL, &xt) < 0)
|
if (xml_parse_string(argv[2], NULL, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((cbret = cbuf_new()) == NULL)
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_put(h, db, op, xt, cbret) < 0)
|
}
|
||||||
|
if (xmldb_put(h, db, op, xt, cbret) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (strcmp(cmd, "copy")==0){
|
else if (strcmp(cmd, "copy")==0){
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ struct text_handle {
|
||||||
Assumes single backend*/
|
Assumes single backend*/
|
||||||
char *th_format; /* Datastroe format: xml / json */
|
char *th_format; /* Datastroe format: xml / json */
|
||||||
int th_pretty; /* Store xml/json pretty-printed. */
|
int th_pretty; /* Store xml/json pretty-printed. */
|
||||||
|
int th_sort; /* Sort XML lists and leaf-lists alphabetically
|
||||||
|
(unless ordered-by user) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Struct per database in hash */
|
/* Struct per database in hash */
|
||||||
|
|
@ -239,6 +241,8 @@ text_getopt(xmldb_handle xh,
|
||||||
*value = th->th_format;
|
*value = th->th_format;
|
||||||
else if (strcmp(optname, "pretty") == 0)
|
else if (strcmp(optname, "pretty") == 0)
|
||||||
*value = &th->th_pretty;
|
*value = &th->th_pretty;
|
||||||
|
else if (strcmp(optname, "sort") == 0)
|
||||||
|
*value = &th->th_sort;
|
||||||
else{
|
else{
|
||||||
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
|
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -288,6 +292,9 @@ text_setopt(xmldb_handle xh,
|
||||||
else if (strcmp(optname, "pretty") == 0){
|
else if (strcmp(optname, "pretty") == 0){
|
||||||
th->th_pretty = (intptr_t)value;
|
th->th_pretty = (intptr_t)value;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(optname, "sort") == 0){
|
||||||
|
th->th_sort = (intptr_t)value;
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
|
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -358,6 +365,16 @@ xml_copy_marked(cxobj *x0,
|
||||||
|
|
||||||
assert(x0 && x1);
|
assert(x0 && x1);
|
||||||
yt = xml_spec(x0); /* can be null */
|
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:
|
/* Go through children to detect any marked nodes:
|
||||||
* (3) Special case: key nodes in lists are copied if any
|
* (3) Special case: key nodes in lists are copied if any
|
||||||
* node in list is marked
|
* node in list is marked
|
||||||
|
|
@ -470,6 +487,10 @@ text_get(xmldb_handle xh,
|
||||||
if (singleconfigroot(xt, &xt) < 0)
|
if (singleconfigroot(xt, &xt) < 0)
|
||||||
goto done;
|
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 */
|
} /* xt == NULL */
|
||||||
/* Here xt looks like: <config>...</config> */
|
/* Here xt looks like: <config>...</config> */
|
||||||
|
|
||||||
|
|
@ -530,12 +551,15 @@ text_get(xmldb_handle xh,
|
||||||
/* Add default values (if not set) */
|
/* Add default values (if not set) */
|
||||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Order XML children according to YANG */
|
/* Order XML children according to YANG.
|
||||||
if (!xml_child_sort && xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
* XXX: should this be !th->th_sort?
|
||||||
|
*/
|
||||||
|
if (!th->th_sort)
|
||||||
|
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#if 0 /* debug */
|
#if 1 /* debug */
|
||||||
if (xml_child_sort && xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
|
if (th->th_sort && xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
|
||||||
clicon_log(LOG_NOTICE, "%s: verify failed #2", __FUNCTION__);
|
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
|
||||||
#endif
|
#endif
|
||||||
if (debug>1)
|
if (debug>1)
|
||||||
clicon_xml2file(stderr, xt, 0, 1);
|
clicon_xml2file(stderr, xt, 0, 1);
|
||||||
|
|
@ -555,6 +579,7 @@ text_get(xmldb_handle xh,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
/*! 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] 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] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||||
* @param[in] x0p Parent of x0
|
* @param[in] x0p Parent of x0
|
||||||
|
|
@ -562,10 +587,11 @@ text_get(xmldb_handle xh,
|
||||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
* @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 or "".
|
||||||
* Assume x0 and x1 are same on entry and that y is the spec
|
* 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
|
static int
|
||||||
text_modify(cxobj *x0,
|
text_modify(struct text_handle *th,
|
||||||
|
cxobj *x0,
|
||||||
yang_node *y0,
|
yang_node *y0,
|
||||||
cxobj *x0p,
|
cxobj *x0p,
|
||||||
cxobj *x1,
|
cxobj *x1,
|
||||||
|
|
@ -576,6 +602,8 @@ text_modify(cxobj *x0,
|
||||||
char *opstr;
|
char *opstr;
|
||||||
char *x1name;
|
char *x1name;
|
||||||
char *x1cname; /* child name */
|
char *x1cname; /* child name */
|
||||||
|
cxobj *x0a; /* attribute */
|
||||||
|
cxobj *x1a; /* attribute */
|
||||||
cxobj *x0c; /* base child */
|
cxobj *x0c; /* base child */
|
||||||
cxobj *x0b; /* base body */
|
cxobj *x0b; /* base body */
|
||||||
cxobj *x1c; /* mod child */
|
cxobj *x1c; /* mod child */
|
||||||
|
|
@ -607,6 +635,13 @@ text_modify(cxobj *x0,
|
||||||
// int iamkey=0;
|
// int iamkey=0;
|
||||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||||
goto done;
|
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 0
|
||||||
/* If it is key I dont want to mark it */
|
/* If it is key I dont want to mark it */
|
||||||
if ((iamkey=yang_key_match(y0->yn_parent, x1name)) < 0)
|
if ((iamkey=yang_key_match(y0->yn_parent, x1name)) < 0)
|
||||||
|
|
@ -679,6 +714,13 @@ text_modify(cxobj *x0,
|
||||||
if (x0==NULL){
|
if (x0==NULL){
|
||||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||||
goto done;
|
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)
|
if (op==OP_NONE)
|
||||||
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
|
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
|
||||||
}
|
}
|
||||||
|
|
@ -699,7 +741,7 @@ text_modify(cxobj *x0,
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (match_base_child(x0, x1c, &x0c, th->th_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
x0vec[i++] = x0c;
|
x0vec[i++] = x0c;
|
||||||
}
|
}
|
||||||
|
|
@ -709,7 +751,7 @@ text_modify(cxobj *x0,
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
yc = yang_find_datanode(y0, x1cname);
|
yc = yang_find_datanode(y0, x1cname);
|
||||||
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret) < 0)
|
if (text_modify(th, x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
if (cbuf_len(cbret))
|
if (cbuf_len(cbret))
|
||||||
|
|
@ -740,6 +782,7 @@ text_modify(cxobj *x0,
|
||||||
} /* text_modify */
|
} /* text_modify */
|
||||||
|
|
||||||
/*! Modify a top-level base tree x0 with modification tree x1
|
/*! 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] x0 Base xml tree (can be NULL in add scenarios)
|
||||||
* @param[in] x1 xml tree which modifies base
|
* @param[in] x1 xml tree which modifies base
|
||||||
* @param[in] yspec Top-level yang spec (if y is NULL)
|
* @param[in] yspec Top-level yang spec (if y is NULL)
|
||||||
|
|
@ -812,23 +855,19 @@ text_modify_top(struct text_handle *th,
|
||||||
goto done;
|
goto done;
|
||||||
if (ymod != NULL)
|
if (ymod != NULL)
|
||||||
yc = yang_find_datanode((yang_node*)ymod, x1cname);
|
yc = yang_find_datanode((yang_node*)ymod, x1cname);
|
||||||
if (yc == NULL && _CLICON_XML_NS_ITERATE){
|
if (yc == NULL && !_CLICON_XML_NS_STRICT){
|
||||||
int i;
|
if (xml_yang_find_non_strict(x1c, yspec, &yc) < 0)
|
||||||
for (i=0; i<yspec->yp_len; i++){
|
goto done;
|
||||||
ymod = yspec->yp_stmt[i];
|
|
||||||
if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (yc == NULL){
|
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 done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* 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, &x0c, th->th_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op, cbret) < 0)
|
if (text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
if (cbuf_len(cbret))
|
if (cbuf_len(cbret))
|
||||||
|
|
@ -840,7 +879,7 @@ text_modify_top(struct text_handle *th,
|
||||||
return retval;
|
return retval;
|
||||||
} /* text_modify_top */
|
} /* 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
|
* @param[in] x XML tree node
|
||||||
* See section 7.5.1 in rfc6020bis-02.txt:
|
* See section 7.5.1 in rfc6020bis-02.txt:
|
||||||
* No presence:
|
* No presence:
|
||||||
|
|
@ -869,7 +908,7 @@ xml_container_presence(cxobj *x,
|
||||||
}
|
}
|
||||||
/* Mark node that is: container, have no children, dont have presence */
|
/* Mark node that is: container, have no children, dont have presence */
|
||||||
if (y->ys_keyword == Y_CONTAINER &&
|
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)
|
yang_find((yang_node*)y, Y_PRESENCE, NULL) == NULL)
|
||||||
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
|
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -964,7 +1003,7 @@ text_put(xmldb_handle xh,
|
||||||
goto done;
|
goto done;
|
||||||
#endif
|
#endif
|
||||||
#if 0 /* debug */
|
#if 0 /* debug */
|
||||||
if (xml_child_sort && xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
|
if (th->th_sort && xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
|
||||||
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
|
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
|
|
@ -975,7 +1014,7 @@ text_put(xmldb_handle xh,
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
if (cbuf_len(cbret))
|
if (cbuf_len(cbret))
|
||||||
goto ok;
|
goto fail;
|
||||||
|
|
||||||
/* Remove NONE nodes if all subs recursively are also NONE */
|
/* Remove NONE nodes if all subs recursively are also NONE */
|
||||||
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
|
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
|
||||||
|
|
@ -990,7 +1029,7 @@ text_put(xmldb_handle xh,
|
||||||
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
|
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#if 0 /* debug */
|
#if 0 /* debug */
|
||||||
if (xml_child_sort && xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
|
if (th->th_sort && xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
|
||||||
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
|
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
|
||||||
#endif
|
#endif
|
||||||
/* Write back to datastore cache if first time */
|
/* Write back to datastore cache if first time */
|
||||||
|
|
@ -1025,8 +1064,7 @@ text_put(xmldb_handle xh,
|
||||||
}
|
}
|
||||||
else if (clicon_xml2file(f, x0, 0, th->th_pretty) < 0)
|
else if (clicon_xml2file(f, x0, 0, th->th_pretty) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
retval = 1;
|
||||||
retval = 0;
|
|
||||||
done:
|
done:
|
||||||
if (cbretlocal && cbret)
|
if (cbretlocal && cbret)
|
||||||
cbuf_free(cbret);
|
cbuf_free(cbret);
|
||||||
|
|
@ -1041,6 +1079,9 @@ text_put(xmldb_handle xh,
|
||||||
if (!th->th_cache && x0)
|
if (!th->th_cache && x0)
|
||||||
xml_free(x0);
|
xml_free(x0);
|
||||||
return retval;
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Copy database from db1 to db2
|
/*! Copy database from db1 to db2
|
||||||
|
|
@ -1434,7 +1475,7 @@ main(int argc,
|
||||||
op = OP_REMOVE;
|
op = OP_REMOVE;
|
||||||
else
|
else
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
if (xmldb_put(h, db, op, NULL, xn, NULL) < 0)
|
if (xmldb_put(h, db, op, NULL, xn, NULL) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
142
doc/FAQ.md
142
doc/FAQ.md
|
|
@ -42,9 +42,9 @@ The example:
|
||||||
sudo make install
|
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 cli session: `clixon_cli -f /usr/local/etc/example.xml`
|
||||||
- Start a netconf session: `clixon_netconf -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`
|
- 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
|
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?
|
## What about reference documentation?
|
||||||
Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
|
Clixon uses [Doxygen](http://www.doxygen.nl/index.html) for reference documentation.
|
||||||
You need to install doxygen and graphviz on your system.
|
You need to install doxygen and graphviz on your system.
|
||||||
|
|
@ -161,59 +237,6 @@ sudo docker run -td olofhagsand/clixon_example
|
||||||
```
|
```
|
||||||
Look in the example documentation for more info.
|
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?
|
## Does Clixon support event streams?
|
||||||
|
|
||||||
Yes, Clixon supports event notification streams in the CLI, Netconf and Restconf API:s.
|
Yes, Clixon supports event notification streams in the CLI, Netconf and Restconf API:s.
|
||||||
|
|
@ -233,7 +256,7 @@ severity major;
|
||||||
or via NETCONF:
|
or via NETCONF:
|
||||||
```
|
```
|
||||||
clixon_netconf -qf /usr/local/etc/example.xml
|
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>]]>]]>
|
<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>]]>]]>
|
<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
|
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?
|
## 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):
|
The first way is via a file. Assume you want to add this xml (the config tag is a necessary top-level tag):
|
||||||
```
|
```
|
||||||
<config>
|
<config>
|
||||||
<x>extra</x>
|
<x xmlns="urn:example:clixon">extra</x>
|
||||||
</config>
|
</config>
|
||||||
```
|
```
|
||||||
You add this via the -c option:
|
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).
|
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?
|
## I want to program. How do I extend the example?
|
||||||
See [../apps/example]
|
See [../apps/example](../apps/example)
|
||||||
- example.xml - Change the configuration file
|
- example.xml - Change the configuration file
|
||||||
- The yang specifications - This is the central part. It changes the XML, database and the config cli.
|
- 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.cli - Change the fixed part of the CLI commands
|
||||||
- example_cli.c - Cli C-commands are placed here.
|
- example_cli.c - Cli C-commands are placed here.
|
||||||
- example_backend.c - Commit and validate functions.
|
- example_backend.c - Commit and validate functions.
|
||||||
|
- example_backend_nacm.c - Secondary example plugin (for authorization)
|
||||||
- example_netconf.c - Netconf plugin
|
- example_netconf.c - Netconf plugin
|
||||||
- example_restconf.c - Add restconf authentication, etc.
|
- example_restconf.c - Add restconf authentication, etc.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,33 @@
|
||||||
# Clixon example
|
# 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:
|
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.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.yang` The yang spec of the example. It mainly includes ietf routing and IP modules.
|
||||||
* example_cli.cli CLIgen specification.
|
* `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_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_backend.c` Backend callback plugin including example of:
|
||||||
* transaction callbacks (validate/commit),
|
* transaction callbacks (validate/commit),
|
||||||
* notification,
|
* notification,
|
||||||
* rpc handler
|
* rpc handler
|
||||||
* state-data handler, ie non-config data
|
* state-data handler, ie non-config data
|
||||||
* example_backend_nacm.c Secondary backend plugin. Plugins are loaded alphabetically.
|
* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically.
|
||||||
* example_restconf.c Restconf callback plugin containing an HTTP basic authentication callback
|
* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback
|
||||||
* example_netconf.c Netconf callback plugin
|
* `example_netconf.c` Netconf callback plugin
|
||||||
* Makefile.in Example makefile where plugins are built and installed
|
* `Makefile.in` Example makefile where plugins are built and installed
|
||||||
|
|
||||||
|
|
||||||
## Compile and run
|
## Compile and run
|
||||||
|
|
@ -47,10 +61,37 @@ Send restconf command
|
||||||
curl -G http://127.0.0.1/restconf/data
|
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>
|
<rpc><edit-config><target><candidate/></target><config>
|
||||||
<interfaces>
|
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
<interface>
|
<interface>
|
||||||
<name>eth1</name>
|
<name>eth1</name>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
|
|
@ -65,13 +106,13 @@ Send restconf command
|
||||||
</config></edit-config></rpc>]]>]]>
|
</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></get-config></rpc>]]>]]>
|
||||||
<rpc><get-config><source><candidate/></source><filter/></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="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="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/ipv4"/></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>]]>]]>
|
<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
|
The example has an EXAMPLE stream notification triggering every 5s. To start a notification
|
||||||
stream in the session using netconf, create a subscription:
|
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>]]>]]>
|
<rpc-reply><ok/></rpc-reply>]]>]]>
|
||||||
<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>]]>]]>
|
||||||
<notification><event>Routing notification</event></notification>]]>]]>
|
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
This can also be triggered via the CLI:
|
This can also be triggered via the CLI:
|
||||||
```
|
```
|
||||||
|
clixon_cli -f /usr/local/etc/example.xml
|
||||||
cli> notify
|
cli> notify
|
||||||
cli> Routing notification
|
cli> event-class fault;
|
||||||
Routing notification
|
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.
|
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.
|
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 */
|
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`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ example_stream_timer(int fd,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
clicon_handle h = (clicon_handle)arg;
|
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)
|
if (stream_notify(h, "EXAMPLE", "<event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event>") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (example_stream_timer_setup(h) < 0)
|
if (example_stream_timer_setup(h) < 0)
|
||||||
|
|
@ -129,7 +129,7 @@ fib_route(clicon_handle h, /* Clicon handle */
|
||||||
void *arg, /* Client session */
|
void *arg, /* Client session */
|
||||||
void *regarg) /* Argument given at register */
|
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>"
|
"<address-family>ipv4</address-family>"
|
||||||
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
|
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
|
||||||
"<source-protocol>static</source-protocol>"
|
"<source-protocol>static</source-protocol>"
|
||||||
|
|
@ -147,7 +147,7 @@ route_count(clicon_handle h,
|
||||||
void *arg,
|
void *arg,
|
||||||
void *regarg) /* Argument given at register */
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,9 +180,17 @@ example_rpc(clicon_handle h, /* Clicon handle */
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x = NULL;
|
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>");
|
cprintf(cbret, "<rpc-reply>");
|
||||||
while ((x = xml_child_each(xe, x, CX_ELMNT)) != NULL) {
|
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)
|
if (clicon_xml2cbuf(cbret, x, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -222,7 +230,7 @@ example_statedata(clicon_handle h,
|
||||||
* Note this state needs to be accomanied by yang snippet
|
* Note this state needs to be accomanied by yang snippet
|
||||||
* above
|
* above
|
||||||
*/
|
*/
|
||||||
if (xml_parse_string("<state>"
|
if (xml_parse_string("<state xmlns=\"urn:example:clixon\">"
|
||||||
"<op>42</op>"
|
"<op>42</op>"
|
||||||
"</state>", NULL, &xstate) < 0)
|
"</state>", NULL, &xstate) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -251,17 +259,22 @@ example_reset(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
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>"
|
"<name>lo</name><type>ex:loopback</type>"
|
||||||
"</interface></interfaces></config>", NULL, &xt) < 0)
|
"</interface></interfaces></config>", NULL, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Replace parent w fiorst child */
|
/* Replace parent w first child */
|
||||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Merge user reset state */
|
/* Merge user reset state */
|
||||||
if (xmldb_put(h, (char*)db, OP_MERGE, xt, NULL) < 0)
|
if ((ret = xmldb_put(h, (char*)db, OP_MERGE, xt, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
clicon_err(OE_XML, 0, "Error when writing to XML database");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xt != NULL)
|
if (xt != NULL)
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ fib_route_rpc(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Print result */
|
/* Print result */
|
||||||
xml_print(stdout, xml_child_i(xret, 0));
|
xml2txt(stdout, xml_child_i(xret, 0), 1);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xret)
|
if (xret)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ debug("Debugging parts of the system"), cli_debug_cli((int32)1);{
|
||||||
}
|
}
|
||||||
copy("Copy and create a new object") {
|
copy("Copy and create a new object") {
|
||||||
interface("Copy interface"){
|
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();
|
discard("Discard edits (rollback 0)"), discard_changes();
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ int netconf_client_rpc(clicon_handle h,
|
||||||
void *regarg)
|
void *regarg)
|
||||||
{
|
{
|
||||||
clicon_debug(1, "%s restconf", __FUNCTION__);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ restconf_client_rpc(clicon_handle h,
|
||||||
{
|
{
|
||||||
// FCGX_Request *r = (FCGX_Request *)arg;
|
// FCGX_Request *r = (FCGX_Request *)arg;
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,6 @@ int strverscmp (__const char *__s1, __const char *__s2);
|
||||||
*/
|
*/
|
||||||
#define XMLNS_YANG_ONLY 1
|
#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.
|
/* 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
|
* This is a backward compatible fix for 3.9 for CLIgen specification files
|
||||||
* using model generation (CLIXON_CLI_GENMODEL).
|
* using model generation (CLIXON_CLI_GENMODEL).
|
||||||
|
|
|
||||||
|
|
@ -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_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty);
|
||||||
int xml2json(FILE *f, cxobj *x, int pretty);
|
int xml2json(FILE *f, cxobj *x, int pretty);
|
||||||
int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, 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_str(char *str, cxobj **xt);
|
||||||
int json_parse_file(int fd, yang_spec *yspec, cxobj **xt);
|
int json_parse_file(int fd, yang_spec *yspec, cxobj **xt);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ 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_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(cbuf *cb, char *type, char *element, char *message);
|
||||||
int netconf_unknown_element_xml(cxobj **xret, 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 *info, 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(cbuf *cb, char *type, char *message);
|
||||||
int netconf_access_denied_xml(cxobj **xret, 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);
|
int netconf_lock_denied(cbuf *cb, char *info, char *message);
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,9 @@ static inline char *clicon_xmldb_dir(clicon_handle h){
|
||||||
static inline char *clicon_xmldb_plugin(clicon_handle h){
|
static inline char *clicon_xmldb_plugin(clicon_handle h){
|
||||||
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
|
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
|
||||||
}
|
}
|
||||||
|
static inline int clicon_xml_sort(clicon_handle h){
|
||||||
|
return clicon_option_bool(h, "CLICON_XML_SORT");
|
||||||
|
}
|
||||||
|
|
||||||
/*-- Specific option access functions for YANG options w type conversion--*/
|
/*-- Specific option access functions for YANG options w type conversion--*/
|
||||||
int clicon_cli_genmodel(clicon_handle h);
|
int clicon_cli_genmodel(clicon_handle h);
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ int xml_chardata_encode(char **escp, char *fmt, ...);
|
||||||
int uri_percent_decode(char *enc, char **str);
|
int uri_percent_decode(char *enc, char **str);
|
||||||
const char *clicon_int2str(const map_str2int *mstab, int i);
|
const char *clicon_int2str(const map_str2int *mstab, int i);
|
||||||
int clicon_str2int(const map_str2int *mstab, char *str);
|
int clicon_str2int(const map_str2int *mstab, char *str);
|
||||||
|
int nodeid_split(char *nodeid, char **prefix, char **id);
|
||||||
#ifndef HAVE_STRNDUP
|
#ifndef HAVE_STRNDUP
|
||||||
char *clicon_strndup (const char *, size_t);
|
char *clicon_strndup (const char *, size_t);
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#endif /* ! HAVE_STRNDUP */
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@
|
||||||
* namespace (rfc6241 3.1)
|
* namespace (rfc6241 3.1)
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_XML_RPC_NAMESPACE "urn:ietf:params:xml:ns:netconf:base:1.0"
|
#define DEFAULT_XML_RPC_NAMESPACE "urn:ietf:params:xml:ns:netconf:base:1.0"
|
||||||
|
/* default namespace statement, such as in <rpc xmlns="..."> */
|
||||||
|
#define DEFAULT_XMLNS "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\""
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types
|
* Types
|
||||||
|
|
@ -84,14 +86,13 @@ typedef int (xml_applyfn_t)(cxobj *x, void *arg);
|
||||||
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
|
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
|
||||||
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
|
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
|
||||||
|
|
||||||
|
|
||||||
/* Iterate through modules to find the matching datanode
|
/* Iterate through modules to find the matching datanode
|
||||||
* or rpc if no xmlns attribute specifies namespace.
|
* or rpc if no xmlns attribute specifies namespace.
|
||||||
* This is loose semantics of finding namespaces.
|
* This is lazy non-strict semantics of finding namespaces.
|
||||||
* And it is wrong, but is the way Clixon originally was written."
|
* And it is wrong, but is the way Clixon originally was written."
|
||||||
* @see CLICON_XML_NS_ITERATE clixon configure option
|
* @see CLICON_XML_NS_STRICT clixon configure option
|
||||||
*/
|
*/
|
||||||
extern int _CLICON_XML_NS_ITERATE;
|
extern int _CLICON_XML_NS_STRICT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
|
|
@ -102,6 +103,7 @@ int xml_name_set(cxobj *xn, char *name);
|
||||||
char *xml_prefix(cxobj *xn);
|
char *xml_prefix(cxobj *xn);
|
||||||
int xml_prefix_set(cxobj *xn, char *name);
|
int xml_prefix_set(cxobj *xn, char *name);
|
||||||
int xml2ns(cxobj *x, char *localname, char **namespace);
|
int xml2ns(cxobj *x, char *localname, char **namespace);
|
||||||
|
int xmlns_set(cxobj *x, char *prefix, char *namespace);
|
||||||
cxobj *xml_parent(cxobj *xn);
|
cxobj *xml_parent(cxobj *xn);
|
||||||
int xml_parent_set(cxobj *xn, cxobj *parent);
|
int xml_parent_set(cxobj *xn, cxobj *parent);
|
||||||
|
|
||||||
|
|
@ -117,7 +119,9 @@ int xml_type_set(cxobj *xn, enum cxobj_type type);
|
||||||
|
|
||||||
int xml_child_nr(cxobj *xn);
|
int xml_child_nr(cxobj *xn);
|
||||||
int xml_child_nr_type(cxobj *xn, enum cxobj_type type);
|
int xml_child_nr_type(cxobj *xn, enum cxobj_type type);
|
||||||
|
int xml_child_nr_notype(cxobj *xn, enum cxobj_type type);
|
||||||
cxobj *xml_child_i(cxobj *xn, int i);
|
cxobj *xml_child_i(cxobj *xn, int i);
|
||||||
|
cxobj *xml_child_i_type(cxobj *xn, int i, enum cxobj_type type);
|
||||||
cxobj *xml_child_i_set(cxobj *xt, int i, cxobj *xc);
|
cxobj *xml_child_i_set(cxobj *xt, int i, cxobj *xc);
|
||||||
cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
|
cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
|
||||||
|
|
||||||
|
|
@ -139,6 +143,7 @@ char *xml_body(cxobj *xn);
|
||||||
cxobj *xml_body_get(cxobj *xn);
|
cxobj *xml_body_get(cxobj *xn);
|
||||||
char *xml_find_type_value(cxobj *xn_parent, char *prefix,
|
char *xml_find_type_value(cxobj *xn_parent, char *prefix,
|
||||||
char *name, enum cxobj_type type);
|
char *name, enum cxobj_type type);
|
||||||
|
cxobj *xml_find_type(cxobj *xn_parent, char *prefix, char *name, enum cxobj_type type);
|
||||||
char *xml_find_value(cxobj *xn_parent, char *name);
|
char *xml_find_value(cxobj *xn_parent, char *name);
|
||||||
char *xml_find_body(cxobj *xn, char *name);
|
char *xml_find_body(cxobj *xn, char *name);
|
||||||
cxobj *xml_find_body_obj(cxobj *xt, char *name, char *val);
|
cxobj *xml_find_body_obj(cxobj *xt, char *name, char *val);
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,13 @@
|
||||||
*/
|
*/
|
||||||
int xml2txt(FILE *f, cxobj *x, int level);
|
int xml2txt(FILE *f, cxobj *x, int level);
|
||||||
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
|
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
|
||||||
|
int xml_yang_root(cxobj *x, cxobj **xr);
|
||||||
|
int xmlns_assign(cxobj *x);
|
||||||
int xml_yang_validate_rpc(cxobj *xrpc, cbuf *cbret);
|
int xml_yang_validate_rpc(cxobj *xrpc, cbuf *cbret);
|
||||||
int xml_yang_validate_add(cxobj *xt, cbuf *cbret);
|
int xml_yang_validate_add(cxobj *xt, cbuf *cbret);
|
||||||
int xml_yang_validate_all(cxobj *xt, cbuf *cbret);
|
int xml_yang_validate_all(cxobj *xt, cbuf *cbret);
|
||||||
int xml_yang_validate_all_top(cxobj *xt, cbuf *cbret);
|
int xml_yang_validate_all_top(cxobj *xt, cbuf *cbret);
|
||||||
|
int xml_yang_find_non_strict(cxobj *x, yang_spec *yspec, yang_stmt **y);
|
||||||
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
||||||
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
||||||
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
|
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
|
||||||
|
|
@ -64,8 +67,7 @@ int xml_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, void *arg);
|
int xml_non_config_data(cxobj *xt, void *arg);
|
||||||
int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_spec *yspec);
|
int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_spec *yspec);
|
||||||
int xml_spec_populate(cxobj *x, void *arg);
|
int xml_spec_populate(cxobj *x, void *arg);
|
||||||
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
int api_path2xpath(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||||
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
|
||||||
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
||||||
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
|
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
|
||||||
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec, char **reason);
|
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec, char **reason);
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,16 @@
|
||||||
#define _CLIXON_XML_SORT_H
|
#define _CLIXON_XML_SORT_H
|
||||||
|
|
||||||
/* Sort and binary search of XML children
|
/* Sort and binary search of XML children
|
||||||
* Experimental
|
* XXX This variable is a kludge since low-level functions xml_merge/xml_diff calls
|
||||||
|
* match_base_child without handle
|
||||||
|
* @see clicon_xml_sort
|
||||||
*/
|
*/
|
||||||
extern int xml_child_sort;
|
extern int xml_child_sort;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int xml_child_spec(char *name, cxobj *xp, yang_spec *yspec, yang_stmt **yp);
|
int xml_child_spec(cxobj *x, cxobj *xp, yang_spec *yspec, yang_stmt **yp);
|
||||||
int xml_cmp(const void* arg1, const void* arg2);
|
int xml_cmp(const void* arg1, const void* arg2);
|
||||||
int xml_sort(cxobj *x0, void *arg);
|
int xml_sort(cxobj *x0, void *arg);
|
||||||
cxobj *xml_search(cxobj *x, char *name, int yangi, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
cxobj *xml_search(cxobj *x, char *name, int yangi, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
||||||
|
|
@ -53,6 +55,6 @@ int xml_insert_pos(cxobj *x0, char *name, int yangi, enum rfc_6020 keyword,
|
||||||
int upper);
|
int upper);
|
||||||
cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
||||||
int xml_sort_verify(cxobj *x, void *arg);
|
int xml_sort_verify(cxobj *x, void *arg);
|
||||||
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
|
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, int xml_sort, yang_stmt *yc);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_SORT_H */
|
#endif /* _CLIXON_XML_SORT_H */
|
||||||
|
|
|
||||||
|
|
@ -252,17 +252,16 @@ yang_stmt *yn_each(yang_node *yn, yang_stmt *ys);
|
||||||
char *yang_key2str(int keyword);
|
char *yang_key2str(int keyword);
|
||||||
char *yarg_prefix(yang_stmt *ys);
|
char *yarg_prefix(yang_stmt *ys);
|
||||||
char *yarg_id(yang_stmt *ys);
|
char *yarg_id(yang_stmt *ys);
|
||||||
int yang_nodeid_split(char *nodeid, char **prefix, char **id);
|
|
||||||
int ys_module_by_xml(yang_spec *ysp, struct xml *xt, yang_stmt **ymodp);
|
int ys_module_by_xml(yang_spec *ysp, struct xml *xt, yang_stmt **ymodp);
|
||||||
yang_stmt *ys_module(yang_stmt *ys);
|
yang_stmt *ys_module(yang_stmt *ys);
|
||||||
yang_spec *ys_spec(yang_stmt *ys);
|
yang_spec *ys_spec(yang_stmt *ys);
|
||||||
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
||||||
yang_stmt *yang_find_module_by_namespace(yang_spec *yspec, char *namespace);
|
yang_stmt *yang_find_module_by_namespace(yang_spec *yspec, char *namespace);
|
||||||
|
yang_stmt *yang_find_module_by_name(yang_spec *yspec, char *name);
|
||||||
yang_stmt *yang_find(yang_node *yn, int keyword, const char *argument);
|
yang_stmt *yang_find(yang_node *yn, int keyword, const char *argument);
|
||||||
int yang_match(yang_node *yn, int keyword, char *argument);
|
int yang_match(yang_node *yn, int keyword, char *argument);
|
||||||
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
|
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, yang_class class);
|
|
||||||
char *yang_find_myprefix(yang_stmt *ys);
|
char *yang_find_myprefix(yang_stmt *ys);
|
||||||
char *yang_find_mynamespace(yang_stmt *ys);
|
char *yang_find_mynamespace(yang_stmt *ys);
|
||||||
int yang_order(yang_stmt *y);
|
int yang_order(yang_stmt *y);
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ clicon_err_reset(void)
|
||||||
* @param[in] err Error number, typically errno
|
* @param[in] err Error number, typically errno
|
||||||
* @param[in] suberr Sub-error number
|
* @param[in] suberr Sub-error number
|
||||||
* @param[in] reason Error string, format with argv
|
* @param[in] reason Error string, format with argv
|
||||||
|
* @see clicon_err_reser Resetting the global error variables.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_err_fn(const char *fn,
|
clicon_err_fn(const char *fn,
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,12 @@
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_netconf_lib.h"
|
||||||
#include "clixon_json.h"
|
#include "clixon_json.h"
|
||||||
#include "clixon_json_parse.h"
|
#include "clixon_json_parse.h"
|
||||||
|
|
||||||
|
|
@ -90,43 +92,31 @@ enum childtype{
|
||||||
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
|
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Number of children EXCEPT attributes
|
|
||||||
* @param[in] xn xml node
|
|
||||||
* @retval number of children in XML tree (except children of type CX_ATTR)
|
|
||||||
* @see xml_child_nr
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
xml_child_nr_noattr(cxobj *xn)
|
|
||||||
{
|
|
||||||
cxobj *x = NULL;
|
|
||||||
int nr = 0;
|
|
||||||
|
|
||||||
while ((x = xml_child_each(xn, x, -1)) != NULL) {
|
|
||||||
if (xml_type(x) != CX_ATTR)
|
|
||||||
nr++;
|
|
||||||
}
|
|
||||||
return nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! x is element and has exactly one child which in turn has none
|
/*! x is element and has exactly one child which in turn has none
|
||||||
* remove attributes from x
|
* remove attributes from x
|
||||||
* Clone from clixon_xml_map.c
|
* Clone from clixon_xml_map.c
|
||||||
*/
|
*/
|
||||||
static enum childtype
|
static enum childtype
|
||||||
childtype(cxobj *x)
|
child_type(cxobj *x)
|
||||||
{
|
{
|
||||||
cxobj *xc1; /* the only child of x */
|
cxobj *xc; /* the only child of x */
|
||||||
int clen; /* nr of children */
|
int clen; /* nr of children */
|
||||||
|
|
||||||
clen = xml_child_nr_noattr(x);
|
clen = xml_child_nr_notype(x, CX_ATTR);
|
||||||
if (xml_type(x) != CX_ELMNT)
|
if (xml_type(x) != CX_ELMNT)
|
||||||
return -1; /* n/a */
|
return -1; /* n/a */
|
||||||
if (clen == 0)
|
if (clen == 0)
|
||||||
return NULL_CHILD;
|
return NULL_CHILD;
|
||||||
if (clen > 1)
|
if (clen > 1)
|
||||||
return ANY_CHILD;
|
return ANY_CHILD;
|
||||||
xc1 = xml_child_i(x, 0); /* From here exactly one child */
|
/* From here exactly one noattr child, get it */
|
||||||
if (xml_child_nr_noattr(xc1) == 0 && xml_type(xc1)==CX_BODY)
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(x, xc, -1)) != NULL)
|
||||||
|
if (xml_type(xc) != CX_ATTR)
|
||||||
|
break;
|
||||||
|
if (xc == NULL)
|
||||||
|
return -2; /* n/a */
|
||||||
|
if (xml_child_nr_notype(xc, CX_ATTR) == 0 && xml_type(xc)==CX_BODY)
|
||||||
return BODY_CHILD;
|
return BODY_CHILD;
|
||||||
else
|
else
|
||||||
return ANY_CHILD;
|
return ANY_CHILD;
|
||||||
|
|
@ -175,6 +165,9 @@ arraytype2str(enum array_element_type lt)
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Check typeof x in array
|
||||||
|
* Some complexity when x is in different namespaces
|
||||||
|
*/
|
||||||
static enum array_element_type
|
static enum array_element_type
|
||||||
array_eval(cxobj *xprev,
|
array_eval(cxobj *xprev,
|
||||||
cxobj *x,
|
cxobj *x,
|
||||||
|
|
@ -184,7 +177,10 @@ array_eval(cxobj *xprev,
|
||||||
int eqprev=0;
|
int eqprev=0;
|
||||||
int eqnext=0;
|
int eqnext=0;
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
|
char *nsx; /* namespace of x */
|
||||||
|
char *ns2;
|
||||||
|
|
||||||
|
nsx = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
|
||||||
if (xml_type(x)!=CX_ELMNT){
|
if (xml_type(x)!=CX_ELMNT){
|
||||||
array=BODY_ARRAY;
|
array=BODY_ARRAY;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -192,12 +188,18 @@ array_eval(cxobj *xprev,
|
||||||
ys = xml_spec(x);
|
ys = xml_spec(x);
|
||||||
if (xnext &&
|
if (xnext &&
|
||||||
xml_type(xnext)==CX_ELMNT &&
|
xml_type(xnext)==CX_ELMNT &&
|
||||||
strcmp(xml_name(x),xml_name(xnext))==0)
|
strcmp(xml_name(x),xml_name(xnext))==0){
|
||||||
|
ns2 = xml_find_type_value(xnext, NULL, "xmlns", CX_ATTR);
|
||||||
|
if (nsx && ns2 && strcmp(nsx,ns2)==0)
|
||||||
eqnext++;
|
eqnext++;
|
||||||
|
}
|
||||||
if (xprev &&
|
if (xprev &&
|
||||||
xml_type(xprev)==CX_ELMNT &&
|
xml_type(xprev)==CX_ELMNT &&
|
||||||
strcmp(xml_name(x),xml_name(xprev))==0)
|
strcmp(xml_name(x),xml_name(xprev))==0){
|
||||||
|
ns2 = xml_find_type_value(xprev, NULL, "xmlns", CX_ATTR);
|
||||||
|
if (nsx && ns2 && strcmp(nsx,ns2)==0)
|
||||||
eqprev++;
|
eqprev++;
|
||||||
|
}
|
||||||
if (eqprev && eqnext)
|
if (eqprev && eqnext)
|
||||||
array = MIDDLE_ARRAY;
|
array = MIDDLE_ARRAY;
|
||||||
else if (eqprev)
|
else if (eqprev)
|
||||||
|
|
@ -316,8 +318,8 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
* module name
|
* module name
|
||||||
*/
|
*/
|
||||||
prefix = xml_prefix(x);
|
prefix = xml_prefix(x);
|
||||||
if (xml2ns(x, prefix, &namespace) < 0)
|
namespace = xml_find_type_value(x, prefix, "xmlns", CX_ATTR);
|
||||||
goto done;
|
|
||||||
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
|
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
|
||||||
yspec = ys_spec(ys);
|
yspec = ys_spec(ys);
|
||||||
/* Find module name associated with namspace URI */
|
/* Find module name associated with namspace URI */
|
||||||
|
|
@ -325,7 +327,7 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
|
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
|
||||||
modname = ymod->ys_argument;
|
modname = ymod->ys_argument;
|
||||||
}
|
}
|
||||||
childt = childtype(x);
|
childt = child_type(x);
|
||||||
if (pretty==2)
|
if (pretty==2)
|
||||||
cprintf(cb, "#%s_array, %s_child ",
|
cprintf(cb, "#%s_array, %s_child ",
|
||||||
arraytype2str(arraytype),
|
arraytype2str(arraytype),
|
||||||
|
|
@ -442,7 +444,7 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
xc_arraytype,
|
xc_arraytype,
|
||||||
level+1, pretty, 0, bodystr0) < 0)
|
level+1, pretty, 0, bodystr0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (i<xml_child_nr_noattr(x)-1)
|
if (i<xml_child_nr_notype(x, CX_ATTR)-1)
|
||||||
cprintf(cb, ",%s", pretty?"\n":"");
|
cprintf(cb, ",%s", pretty?"\n":"");
|
||||||
}
|
}
|
||||||
switch (arraytype){
|
switch (arraytype){
|
||||||
|
|
@ -532,10 +534,24 @@ xml2json_cbuf(cbuf *cb,
|
||||||
{
|
{
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
int level = 0;
|
int level = 0;
|
||||||
|
char *prefix;
|
||||||
|
char *namespace;
|
||||||
|
|
||||||
cprintf(cb, "%*s{%s",
|
cprintf(cb, "%*s{%s",
|
||||||
pretty?level*JSON_INDENT:0,"",
|
pretty?level*JSON_INDENT:0,"",
|
||||||
pretty?"\n":"");
|
pretty?"\n":"");
|
||||||
|
/* If x is labelled with a default namespace, it should be translated
|
||||||
|
* to a module name.
|
||||||
|
* Harder if x has a prefix, then that should also be translated to associated
|
||||||
|
* module name
|
||||||
|
*/
|
||||||
|
prefix = xml_prefix(x);
|
||||||
|
if (xml2ns(x, prefix, &namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Some complexities in grafting namespace in existing trees to new */
|
||||||
|
if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL && namespace)
|
||||||
|
if (xmlns_set(x, prefix, namespace) < 0)
|
||||||
|
goto done;
|
||||||
if (xml2json1_cbuf(cb,
|
if (xml2json1_cbuf(cb,
|
||||||
x,
|
x,
|
||||||
NO_ARRAY,
|
NO_ARRAY,
|
||||||
|
|
@ -551,7 +567,7 @@ xml2json_cbuf(cbuf *cb,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate a vector of xml objects to JSON CLigen buffer.
|
/*! Translate a vector of xml objects to JSON Cligen buffer.
|
||||||
* This is done by adding a top pseudo-object, and add the vector as subs,
|
* This is done by adding a top pseudo-object, and add the vector as subs,
|
||||||
* and then not printing the top pseudo-object using the 'flat' option.
|
* and then not printing the top pseudo-object using the 'flat' option.
|
||||||
* @param[out] cb Cligen buffer to write to
|
* @param[out] cb Cligen buffer to write to
|
||||||
|
|
@ -575,12 +591,21 @@ xml2json_cbuf_vec(cbuf *cb,
|
||||||
int i;
|
int i;
|
||||||
cxobj *xp = NULL;
|
cxobj *xp = NULL;
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
char *prefix;
|
||||||
|
char *namespace;
|
||||||
|
|
||||||
if ((xp = xml_new("", NULL, NULL)) == NULL)
|
if ((xp = xml_new("xml2json", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Some complexities in grafting namespace in existing trees to new */
|
||||||
for (i=0; i<veclen; i++){
|
for (i=0; i<veclen; i++){
|
||||||
|
prefix = xml_prefix(vec[i]);
|
||||||
|
if (xml2ns(vec[i], prefix, &namespace) < 0)
|
||||||
|
goto done;
|
||||||
xc = xml_dup(vec[i]);
|
xc = xml_dup(vec[i]);
|
||||||
xml_addsub(xp, xc);
|
xml_addsub(xp, xc);
|
||||||
|
if (xml_find_type_value(xc, prefix, "xmlns", CX_ATTR) == NULL && namespace)
|
||||||
|
if (xmlns_set(xc, prefix, namespace) < 0)
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
if (0){
|
if (0){
|
||||||
cprintf(cb, "[%s", pretty?"\n":" ");
|
cprintf(cb, "[%s", pretty?"\n":" ");
|
||||||
|
|
@ -677,6 +702,66 @@ xml2json_vec(FILE *f,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Translate from JSON module:name to XML name xmlns="uri" recursively
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[in,out] x XML tree. Translate it in-line
|
||||||
|
* @param[out] xerr If namespace not set, create xml error tree
|
||||||
|
* @retval 0 OK (if xerr set see above)
|
||||||
|
* @retval -1 Error
|
||||||
|
* @note the opposite - xml2ns is made inline in xml2json1_cbuf
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
json2xml_ns(yang_spec *yspec,
|
||||||
|
cxobj *x,
|
||||||
|
cxobj **xerr)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *ymod;
|
||||||
|
char *namespace0;
|
||||||
|
char *namespace;
|
||||||
|
char *name = NULL;
|
||||||
|
char *prefix = NULL;
|
||||||
|
cxobj *xc;
|
||||||
|
|
||||||
|
if (nodeid_split(xml_name(x), &prefix, &name) < 0)
|
||||||
|
goto done;
|
||||||
|
if (prefix != NULL){
|
||||||
|
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
|
||||||
|
if (netconf_unknown_namespace_xml(xerr, "application",
|
||||||
|
prefix,
|
||||||
|
"No yang module found corresponding to prefix") < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
namespace = yang_find_mynamespace(ymod);
|
||||||
|
/* Get existing default namespace in tree */
|
||||||
|
if (xml2ns(x, NULL, &namespace0) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Set xmlns="" default namespace attribute (if diff from default) */
|
||||||
|
if (namespace0==NULL || strcmp(namespace0, namespace))
|
||||||
|
if (xmlns_set(x, NULL, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Remove prefix from name */
|
||||||
|
if (xml_name_set(x, name) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL){
|
||||||
|
if (json2xml_ns(yspec, xc, xerr) < 0)
|
||||||
|
goto done;
|
||||||
|
if (*xerr != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (prefix)
|
||||||
|
free(prefix);
|
||||||
|
if (name)
|
||||||
|
free(name);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Parse a string containing JSON and return an XML tree
|
/*! Parse a string containing JSON and return an XML tree
|
||||||
* @param[in] str Input string containing JSON
|
* @param[in] str Input string containing JSON
|
||||||
* @param[in] name Log string, typically filename
|
* @param[in] name Log string, typically filename
|
||||||
|
|
|
||||||
|
|
@ -315,9 +315,10 @@ netconf_unknown_attribute(cbuf *cb,
|
||||||
* @param[in] message Error message
|
* @param[in] message Error message
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
netconf_element_xml_common(cxobj **xret,
|
netconf_common_xml(cxobj **xret,
|
||||||
char *type,
|
char *type,
|
||||||
char *tag,
|
char *tag,
|
||||||
|
char *infotag,
|
||||||
char *element,
|
char *element,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
|
|
@ -334,9 +335,9 @@ netconf_element_xml_common(cxobj **xret,
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
|
if (xml_parse_va(&xerr, NULL, "<error-type>%s</error-type>"
|
||||||
"<error-tag>%s</error-tag>"
|
"<error-tag>%s</error-tag>"
|
||||||
"<error-info><bad-element>%s</bad-element></error-info>"
|
"<error-info><%s>%s</%s></error-info>"
|
||||||
"<error-severity>error</error-severity>",
|
"<error-severity>error</error-severity>",
|
||||||
type, tag, element) < 0)
|
type, tag, infotag, element, infotag) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
if (message && xml_parse_va(&xerr, NULL, "<error-message>%s</error-message>",
|
||||||
message) < 0)
|
message) < 0)
|
||||||
|
|
@ -363,7 +364,8 @@ netconf_missing_element(cbuf *cb,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if (netconf_element_xml_common(&xret, type, "missing-element", element, message) < 0)
|
if (netconf_common_xml(&xret, type, "missing-element",
|
||||||
|
"bad-element", element, message) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -387,7 +389,8 @@ netconf_missing_element_xml(cxobj **xret,
|
||||||
char *element,
|
char *element,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
return netconf_element_xml_common(xret, type, "missing-element", element, message);
|
return netconf_common_xml(xret, type, "missing-element",
|
||||||
|
"bad-element", element, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
|
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
|
||||||
|
|
@ -408,7 +411,8 @@ netconf_bad_element(cbuf *cb,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if (netconf_element_xml_common(&xret, type, "bad-element", element, message) < 0)
|
if (netconf_common_xml(&xret, type, "bad-element",
|
||||||
|
"bad-element",element, message) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -424,7 +428,7 @@ netconf_bad_element_xml(cxobj **xret,
|
||||||
char *element,
|
char *element,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
return netconf_element_xml_common(xret, type, "bad-element", element, message);
|
return netconf_common_xml(xret, type, "bad-element", "bad-element", element, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
||||||
|
|
@ -444,7 +448,8 @@ netconf_unknown_element(cbuf *cb,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if (netconf_element_xml_common(&xret, type, "unknown-element", element, message) < 0)
|
if (netconf_common_xml(&xret, type, "unknown-element",
|
||||||
|
"bad-element", element, message) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -454,13 +459,23 @@ netconf_unknown_element(cbuf *cb,
|
||||||
xml_free(xret);
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
||||||
|
*
|
||||||
|
* An unexpected element is present.
|
||||||
|
* @param[out] xret XML buffer
|
||||||
|
* @param[in] type Error type: "application" or "protocol"
|
||||||
|
* @param[in] element Bad element name
|
||||||
|
* @param[in] message Error message
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
netconf_unknown_element_xml(cxobj **xret,
|
netconf_unknown_element_xml(cxobj **xret,
|
||||||
char *type,
|
char *type,
|
||||||
char *element,
|
char *element,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
return netconf_element_xml_common(xret, type, "unknown-element", element, message);
|
return netconf_common_xml(xret, type, "unknown-element",
|
||||||
|
"bad-element", element, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
|
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
|
||||||
|
|
@ -474,35 +489,32 @@ netconf_unknown_element_xml(cxobj **xret,
|
||||||
int
|
int
|
||||||
netconf_unknown_namespace(cbuf *cb,
|
netconf_unknown_namespace(cbuf *cb,
|
||||||
char *type,
|
char *type,
|
||||||
char *info,
|
char *namespace,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *encstr = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
if (netconf_common_xml(&xret, type, "unknown-namespace",
|
||||||
"<error-type>%s</error-type>"
|
"bad-namespace", namespace, message) < 0)
|
||||||
"<error-tag>unknown-namespace</error-tag>"
|
goto done;
|
||||||
"<error-info>%s</error-info>"
|
if (clicon_xml2cbuf(cb, xret, 0, 0) < 0)
|
||||||
"<error-severity>error</error-severity>",
|
|
||||||
type, info) <0)
|
|
||||||
goto err;
|
|
||||||
if (message){
|
|
||||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
||||||
goto err;
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (encstr)
|
if (xret)
|
||||||
free(encstr);
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
err:
|
}
|
||||||
clicon_err(OE_XML, errno, "cprintf");
|
|
||||||
goto done;
|
int
|
||||||
|
netconf_unknown_namespace_xml(cxobj **xret,
|
||||||
|
char *type,
|
||||||
|
char *namespace,
|
||||||
|
char *message)
|
||||||
|
{
|
||||||
|
return netconf_common_xml(xret, type, "unknown-namespace",
|
||||||
|
"bad-namespace", namespace, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
|
/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
|
||||||
|
|
@ -989,7 +1001,7 @@ netconf_trymerge(cxobj *x,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Load ietf netconf yang module and set enabled features
|
/*! Load ietf netconf yang module and set enabled features
|
||||||
* The features added are:
|
* The features added are (in order):
|
||||||
* candidate (8.3)
|
* candidate (8.3)
|
||||||
* validate (8.6)
|
* validate (8.6)
|
||||||
* startup (8.7)
|
* startup (8.7)
|
||||||
|
|
|
||||||
|
|
@ -253,9 +253,8 @@ clicon_options_main(clicon_handle h,
|
||||||
clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix);
|
clicon_err(OE_CFG, 0, "%s: suffix %s not recognized (Run ./configure --with-config-compat?)", configfile, suffix);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
|
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||||
_CLICON_XML_NS_ITERATE = 1;
|
_CLICON_XML_NS_STRICT = 0;
|
||||||
#endif
|
|
||||||
/* Read configfile first without yangspec, for bootstrapping */
|
/* Read configfile first without yangspec, for bootstrapping */
|
||||||
if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
|
if (parse_configfile(h, configfile, yspec, &xconfig) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -281,9 +280,8 @@ clicon_options_main(clicon_handle h,
|
||||||
xml_child_sort = 1;
|
xml_child_sort = 1;
|
||||||
else
|
else
|
||||||
xml_child_sort = 0;
|
xml_child_sort = 0;
|
||||||
#if 1 /* XXX Kludge to low-level functions to iterate over namspaces or not */
|
/* XXX Kludge to low-level functions to search for xml in all yang modules */
|
||||||
_CLICON_XML_NS_ITERATE = clicon_option_bool(h, "CLICON_XML_NS_ITERATE");
|
_CLICON_XML_NS_STRICT = clicon_option_bool(h, "CLICON_XML_NS_STRICT");
|
||||||
#endif
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,7 @@ clicon_rpc_edit_config(clicon_handle h,
|
||||||
|
|
||||||
if ((cb = cbuf_new()) == NULL)
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, "<rpc");
|
cprintf(cb, "<rpc %s", DEFAULT_XMLNS);
|
||||||
if ((username = clicon_username_get(h)) != NULL)
|
if ((username = clicon_username_get(h)) != NULL)
|
||||||
cprintf(cb, " username=\"%s\"", username);
|
cprintf(cb, " username=\"%s\"", username);
|
||||||
cprintf(cb, "><edit-config><target><%s/></target>", db);
|
cprintf(cb, "><edit-config><target><%s/></target>", db);
|
||||||
|
|
@ -787,7 +787,7 @@ clicon_rpc_create_subscription(clicon_handle h,
|
||||||
char *username;
|
char *username;
|
||||||
|
|
||||||
username = clicon_username_get(h);
|
username = clicon_username_get(h);
|
||||||
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription>"
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">"
|
||||||
"<stream>%s</stream>"
|
"<stream>%s</stream>"
|
||||||
"<filter type=\"xpath\" select=\"%s\" />"
|
"<filter type=\"xpath\" select=\"%s\" />"
|
||||||
"</create-subscription></rpc>",
|
"</create-subscription></rpc>",
|
||||||
|
|
|
||||||
|
|
@ -562,6 +562,54 @@ clicon_str2int(const map_str2int *mstab,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Split colon-separated node identifier into prefix and name
|
||||||
|
* @param[in] node-id
|
||||||
|
* @param[out] prefix Malloced string. May be NULL.
|
||||||
|
* @param[out] id Malloced identifier.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @code
|
||||||
|
* char *prefix = NULL;
|
||||||
|
* char *id = NULL;
|
||||||
|
* if (nodeid_split(nodeid, &prefix, &id) < 0)
|
||||||
|
* goto done;
|
||||||
|
* if (prefix)
|
||||||
|
* free(prefix);
|
||||||
|
* if (id)
|
||||||
|
* free(id);
|
||||||
|
* @note caller need to free id and prefix after use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
nodeid_split(char *nodeid,
|
||||||
|
char **prefix,
|
||||||
|
char **id)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
if ((str = strchr(nodeid, ':')) == NULL){
|
||||||
|
if ((*id = strdup(nodeid)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if ((*prefix = strdup(nodeid)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
(*prefix)[str-nodeid] = '\0';
|
||||||
|
str++;
|
||||||
|
if ((*id = strdup(str)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! strndup() for systems without it, such as xBSD
|
/*! strndup() for systems without it, such as xBSD
|
||||||
*/
|
*/
|
||||||
#ifndef HAVE_STRNDUP
|
#ifndef HAVE_STRNDUP
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_options.h" /* xml_spec_populate */
|
||||||
|
#include "clixon_xml_map.h" /* xml_spec_populate */
|
||||||
#include "clixon_xml_sort.h"
|
#include "clixon_xml_sort.h"
|
||||||
#include "clixon_xml_parse.h"
|
#include "clixon_xml_parse.h"
|
||||||
|
|
||||||
|
|
@ -133,9 +135,9 @@ struct xml{
|
||||||
* or rpc if no xmlns attribute specifies namespace.
|
* or rpc if no xmlns attribute specifies namespace.
|
||||||
* This is loose semantics of finding namespaces.
|
* This is loose semantics of finding namespaces.
|
||||||
* And it is wrong, but is the way Clixon originally was written."
|
* And it is wrong, but is the way Clixon originally was written."
|
||||||
* @see CLICON_XML_NS_ITERATE clixon configure option
|
* @see CLICON_XML_NS_STRICT clixon configure option
|
||||||
*/
|
*/
|
||||||
int _CLICON_XML_NS_ITERATE = 0;
|
int _CLICON_XML_NS_STRICT = 1;
|
||||||
|
|
||||||
/* Mapping between xml type <--> string */
|
/* Mapping between xml type <--> string */
|
||||||
static const map_str2int xsmap[] = {
|
static const map_str2int xsmap[] = {
|
||||||
|
|
@ -226,8 +228,7 @@ xml_prefix_set(cxobj *xn,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Given an xml tree return URI namespace recursively : default or localname given
|
||||||
/*! Given an xml tree return URI namespace: default or localname given
|
|
||||||
*
|
*
|
||||||
* Given an XML tree and a prefix (or NULL) return URI namespace.
|
* Given an XML tree and a prefix (or NULL) return URI namespace.
|
||||||
* @param[in] x XML tree
|
* @param[in] x XML tree
|
||||||
|
|
@ -235,7 +236,7 @@ xml_prefix_set(cxobj *xn,
|
||||||
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
|
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see xmlns_check XXX coordinate
|
* @see xmlns_check XXX can these be merged?
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml2ns(cxobj *x,
|
xml2ns(cxobj *x,
|
||||||
|
|
@ -246,9 +247,9 @@ xml2ns(cxobj *x,
|
||||||
char *ns;
|
char *ns;
|
||||||
cxobj *xp;
|
cxobj *xp;
|
||||||
|
|
||||||
if (prefix != NULL) /* xmlns:<prefix> */
|
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
|
||||||
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
|
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
|
||||||
else /* default ns */
|
else /* xmlns="<uri>" */
|
||||||
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
|
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
|
||||||
|
|
||||||
/* namespace not found, try parent */
|
/* namespace not found, try parent */
|
||||||
|
|
@ -270,6 +271,40 @@ xml2ns(cxobj *x,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Add a namespace attribute to an XML node, either default or specific prefix
|
||||||
|
* @param[in] x XML tree
|
||||||
|
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
|
||||||
|
* @param[out] namespace URI namespace (or NULL). Will be copied
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see xml2ns
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xmlns_set(cxobj *x,
|
||||||
|
char *prefix,
|
||||||
|
char *namespace)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xa;
|
||||||
|
|
||||||
|
if (prefix != NULL){ /* xmlns:<prefix>="<uri>" */
|
||||||
|
if ((xa = xml_new(prefix, x, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_prefix_set(xa, "xmlns") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{ /* xmlns="<uri>" */
|
||||||
|
if ((xa = xml_new("xmlns", x, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
xml_type_set(xa, CX_ATTR);
|
||||||
|
}
|
||||||
|
if (xml_value_set(xa, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! See if xmlns:[<localname>=]<uri> exists, if so return <uri>
|
/*! See if xmlns:[<localname>=]<uri> exists, if so return <uri>
|
||||||
*
|
*
|
||||||
* @param[in] xn XML node
|
* @param[in] xn XML node
|
||||||
|
|
@ -483,6 +518,8 @@ xml_type_set(cxobj *xn,
|
||||||
/*! Get number of children
|
/*! Get number of children
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @retval number of children in XML tree
|
* @retval number of children in XML tree
|
||||||
|
* @see xml_child_nr_type
|
||||||
|
* @see xml_child_nr_notype
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_child_nr(cxobj *xn)
|
xml_child_nr(cxobj *xn)
|
||||||
|
|
@ -490,10 +527,33 @@ xml_child_nr(cxobj *xn)
|
||||||
return xn->x_childvec_len;
|
return xn->x_childvec_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get number of children of EXCEPT specific type
|
||||||
|
* @param[in] xn xml node
|
||||||
|
* @param[in] type XML type or -1 for all
|
||||||
|
* @retval number of typed children in XML tree (except type)
|
||||||
|
* @see xml_child_nr
|
||||||
|
* @see xml_child_nr_type
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_child_nr_notype(cxobj *xn,
|
||||||
|
enum cxobj_type type)
|
||||||
|
{
|
||||||
|
cxobj *x = NULL;
|
||||||
|
int nr = 0;
|
||||||
|
|
||||||
|
while ((x = xml_child_each(xn, x, -1)) != NULL) {
|
||||||
|
if (xml_type(x) != type)
|
||||||
|
nr++;
|
||||||
|
}
|
||||||
|
return nr;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Get number of children of specific type
|
/*! Get number of children of specific type
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @param[in] type XML type or -1 for all
|
* @param[in] type XML type or -1 for all
|
||||||
* @retval number of typed children in XML tree
|
* @retval number of typed children in XML tree
|
||||||
|
* @see xml_child_nr
|
||||||
|
* @see xml_child_nr_notype
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_child_nr_type(cxobj *xn,
|
xml_child_nr_type(cxobj *xn,
|
||||||
|
|
@ -521,6 +581,28 @@ xml_child_i(cxobj *xn,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get a specific child of a specific type
|
||||||
|
* @param[in] xn xml node
|
||||||
|
* @param[in] i the number of the child of specific type
|
||||||
|
* @param[in] type Child type
|
||||||
|
* @retval child in XML tree, or NULL if no such child, or empty child
|
||||||
|
* @see xml_child_i
|
||||||
|
*/
|
||||||
|
cxobj *
|
||||||
|
xml_child_i_type(cxobj *xn,
|
||||||
|
int i,
|
||||||
|
enum cxobj_type type)
|
||||||
|
{
|
||||||
|
cxobj *x = NULL;
|
||||||
|
int it = 0;
|
||||||
|
|
||||||
|
while ((x = xml_child_each(xn, x, type)) != NULL) {
|
||||||
|
if (x->x_type == type && (i == it++))
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Set specific child
|
/*! Set specific child
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @param[in] i the number of the child, eg order in children vector
|
* @param[in] i the number of the child, eg order in children vector
|
||||||
|
|
@ -938,7 +1020,7 @@ xml_body_get(cxobj *xt)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Find and return the value of an xml child of specific type
|
/*! Find and return the value of an xml child of specific type given prefix and name
|
||||||
*
|
*
|
||||||
* The value can be of an attribute only
|
* The value can be of an attribute only
|
||||||
* @param[in] xt xml tree node
|
* @param[in] xt xml tree node
|
||||||
|
|
@ -950,6 +1032,7 @@ xml_body_get(cxobj *xt)
|
||||||
* char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
|
* char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note, make a copy of the return value to use it properly
|
* @note, make a copy of the return value to use it properly
|
||||||
|
* @see xml_find_type return the xml object
|
||||||
* @see xml_find_value where a body can be found as well
|
* @see xml_find_value where a body can be found as well
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
|
|
@ -957,6 +1040,32 @@ xml_find_type_value(cxobj *xt,
|
||||||
char *prefix,
|
char *prefix,
|
||||||
char *name,
|
char *name,
|
||||||
enum cxobj_type type)
|
enum cxobj_type type)
|
||||||
|
{
|
||||||
|
cxobj *x;
|
||||||
|
|
||||||
|
if ((x = xml_find_type(xt, prefix, name, type)) != NULL)
|
||||||
|
return xml_value(x);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Find and return the xml child of specific type given prefix and name
|
||||||
|
*
|
||||||
|
* The value can be of an attribute only
|
||||||
|
* @param[in] xt xml tree node
|
||||||
|
* @param[in] prefix Prefix (namespace local name) or NULL
|
||||||
|
* @param[in] name name of xml tree node (eg attr name or "body")
|
||||||
|
* @retval val Pointer to the name string
|
||||||
|
* @retval NULL No such node or no value in node
|
||||||
|
* @code
|
||||||
|
* cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
|
||||||
|
* @endcode
|
||||||
|
* @see xml_find_value where a body can be found as well
|
||||||
|
*/
|
||||||
|
cxobj *
|
||||||
|
xml_find_type(cxobj *xt,
|
||||||
|
char *prefix,
|
||||||
|
char *name,
|
||||||
|
enum cxobj_type type)
|
||||||
{
|
{
|
||||||
cxobj *x = NULL;
|
cxobj *x = NULL;
|
||||||
int pmatch; /* prefix match */
|
int pmatch; /* prefix match */
|
||||||
|
|
@ -969,7 +1078,7 @@ xml_find_type_value(cxobj *xt,
|
||||||
else
|
else
|
||||||
pmatch = 1;
|
pmatch = 1;
|
||||||
if (pmatch && strcmp(name, xml_name(x)) == 0)
|
if (pmatch && strcmp(name, xml_name(x)) == 0)
|
||||||
return xml_value(x);
|
return x;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -1397,6 +1506,8 @@ _xml_parse(const char *str,
|
||||||
goto done;
|
goto done;
|
||||||
/* Sort the complete tree after parsing */
|
/* Sort the complete tree after parsing */
|
||||||
if (yspec){
|
if (yspec){
|
||||||
|
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||||
|
goto done;
|
||||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
|
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
|
||||||
|
|
@ -1592,7 +1703,7 @@ xml_parse_va(cxobj **xtop,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Copy single xml node without copying children
|
/*! Copy single xml node frm x0 to x1 without copying children
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_copy_one(cxobj *x0,
|
xml_copy_one(cxobj *x0,
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,8 @@ xmldb_get(clicon_handle h,
|
||||||
* @param[in] op Top-level operation, can be superceded by other op in tree
|
* @param[in] op Top-level operation, can be superceded by other op in tree
|
||||||
* @param[in] xt xml-tree. Top-level symbol is dummy
|
* @param[in] xt xml-tree. Top-level symbol is dummy
|
||||||
* @param[out] cbret Initialized cligen buffer or NULL. On exit contains XML or "".
|
* @param[out] cbret Initialized cligen buffer or NULL. On exit contains XML or "".
|
||||||
* @retval 0 OK
|
* @retval 1 OK
|
||||||
|
* @retval 0 Failed, cbret contains error xml message
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* The xml may contain the "operation" attribute which defines the operation.
|
* The xml may contain the "operation" attribute which defines the operation.
|
||||||
* @code
|
* @code
|
||||||
|
|
@ -388,8 +389,10 @@ xmldb_get(clicon_handle h,
|
||||||
* cxobj *xret = NULL;
|
* cxobj *xret = NULL;
|
||||||
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
|
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
|
||||||
* err;
|
* err;
|
||||||
* if (xmldb_put(xh, "running", OP_MERGE, xt, cbret) < 0)
|
* if ((ret = xmldb_put(xh, "running", OP_MERGE, xt, cbret)) < 0)
|
||||||
* err;
|
* err;
|
||||||
|
* if (ret==0)
|
||||||
|
* cbret contains netconf error message
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note that you can add both config data and state data. In comparison,
|
* @note that you can add both config data and state data. In comparison,
|
||||||
* xmldb_get has a parameter to get config data only.
|
* xmldb_get has a parameter to get config data only.
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
* +---------+
|
* +---------+
|
||||||
* | file |
|
* | file |
|
||||||
* +---------+
|
* +---------+
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "clixon_config.h" /* generated by config & autoconf */
|
#include "clixon_config.h" /* generated by config & autoconf */
|
||||||
|
|
@ -95,14 +96,20 @@
|
||||||
static int
|
static int
|
||||||
tleaf(cxobj *x)
|
tleaf(cxobj *x)
|
||||||
{
|
{
|
||||||
cxobj *c;
|
cxobj *xc;
|
||||||
|
|
||||||
if (xml_type(x) != CX_ELMNT)
|
if (xml_type(x) != CX_ELMNT)
|
||||||
return 0;
|
return 0;
|
||||||
if (xml_child_nr(x) != 1)
|
if (xml_child_nr_notype(x, CX_ATTR) != 1)
|
||||||
return 0;
|
return 0;
|
||||||
c = xml_child_i(x, 0);
|
/* From here exactly one noattr child, get it */
|
||||||
return (xml_child_nr(c) == 0);
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(x, xc, -1)) != NULL)
|
||||||
|
if (xml_type(xc) != CX_ATTR)
|
||||||
|
break;
|
||||||
|
if (xc == NULL)
|
||||||
|
return -1; /* n/a */
|
||||||
|
return (xml_child_nr_notype(xc, CX_ATTR) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate XML -> TEXT
|
/*! Translate XML -> TEXT
|
||||||
|
|
@ -114,39 +121,42 @@ xml2txt(FILE *f,
|
||||||
cxobj *x,
|
cxobj *x,
|
||||||
int level)
|
int level)
|
||||||
{
|
{
|
||||||
cxobj *xe = NULL;
|
cxobj *xc = NULL;
|
||||||
int children=0;
|
int children=0;
|
||||||
char *term = NULL;
|
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
xe = NULL; /* count children */
|
xc = NULL; /* count children (elements and bodies, not attributes) */
|
||||||
while ((xe = xml_child_each(x, xe, -1)) != NULL)
|
while ((xc = xml_child_each(x, xc, -1)) != NULL)
|
||||||
|
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
|
||||||
children++;
|
children++;
|
||||||
if (!children){
|
if (!children){ /* If no children print line */
|
||||||
if (xml_type(x) == CX_BODY){
|
switch (xml_type(x)){
|
||||||
term = xml_value(x);
|
case CX_BODY:
|
||||||
|
fprintf(f, "%s;\n", xml_value(x));
|
||||||
|
break;
|
||||||
|
case CX_ELMNT:
|
||||||
|
fprintf(f, "%*s;\n", 4*level, xml_name(x));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else{
|
goto ok;
|
||||||
fprintf(f, "%*s", 4*level, "");
|
|
||||||
term = xml_name(x);
|
|
||||||
}
|
|
||||||
fprintf(f, "%s;\n", term);
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
fprintf(f, "%*s", 4*level, "");
|
fprintf(f, "%*s", 4*level, "");
|
||||||
fprintf(f, "%s ", xml_name(x));
|
fprintf(f, "%s ", xml_name(x));
|
||||||
if (!tleaf(x))
|
if (!tleaf(x))
|
||||||
fprintf(f, "{\n");
|
fprintf(f, "{\n");
|
||||||
xe = NULL;
|
xc = NULL;
|
||||||
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
while ((xc = xml_child_each(x, xc, -1)) != NULL){
|
||||||
if (xml2txt(f, xe, level+1) < 0)
|
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
|
||||||
|
if (xml2txt(f, xc, level+1) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!tleaf(x))
|
if (!tleaf(x))
|
||||||
fprintf(f, "%*s}\n", 4*level, "");
|
fprintf(f, "%*s}\n", 4*level, "");
|
||||||
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
// done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,7 +182,10 @@ xml2cli(FILE *f,
|
||||||
int match;
|
int match;
|
||||||
char *body;
|
char *body;
|
||||||
|
|
||||||
ys = xml_spec(x);
|
if (xml_type(x)==CX_ATTR)
|
||||||
|
goto ok;
|
||||||
|
if ((ys = xml_spec(x)) == NULL)
|
||||||
|
goto ok;
|
||||||
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
|
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
|
||||||
if (prepend0)
|
if (prepend0)
|
||||||
fprintf(f, "%s", prepend0);
|
fprintf(f, "%s", prepend0);
|
||||||
|
|
@ -361,6 +374,37 @@ validate_identityref(cxobj *xt,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Given an XML node, return root node
|
||||||
|
* A root node is an ancestor xr of x with one or both of the following properties
|
||||||
|
* - its XML parent is NULL parent,
|
||||||
|
* - its associated yang specification's parent is a yang module.
|
||||||
|
* @param[in] x XML node
|
||||||
|
* @param[out] xr XML root
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_yang_root(cxobj *x,
|
||||||
|
cxobj **xr)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xp;
|
||||||
|
yang_stmt *y;
|
||||||
|
yang_stmt *yp;
|
||||||
|
|
||||||
|
while ((xp = xml_parent(x)) != NULL){
|
||||||
|
if ((y = xml_spec(x)) != NULL &&
|
||||||
|
(yp = (yang_stmt*)y->ys_parent) != NULL)
|
||||||
|
/* Actually, maybe only the Y_MODULE clause is relevant */
|
||||||
|
if (yp==NULL ||
|
||||||
|
yp->ys_keyword == Y_MODULE ||
|
||||||
|
yp->ys_keyword == Y_SUBMODULE)
|
||||||
|
break; /* x is the root */
|
||||||
|
x = xp;
|
||||||
|
}
|
||||||
|
*xr = x;
|
||||||
|
retval = 0;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Validate an RPC node
|
/*! Validate an RPC node
|
||||||
* @param[in] xt XML node to be validated
|
* @param[in] xt XML node to be validated
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
|
|
@ -408,7 +452,6 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *yn=NULL; /* rpc name */
|
yang_stmt *yn=NULL; /* rpc name */
|
||||||
cxobj *xn; /* rpc name */
|
cxobj *xn; /* rpc name */
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (strcmp(xml_name(xrpc), "rpc")){
|
if (strcmp(xml_name(xrpc), "rpc")){
|
||||||
clicon_err(OE_XML, EINVAL, "Expected RPC");
|
clicon_err(OE_XML, EINVAL, "Expected RPC");
|
||||||
|
|
@ -417,16 +460,15 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
||||||
xn = NULL;
|
xn = NULL;
|
||||||
/* xn is name of rpc, ie <rcp><xn/></rpc> */
|
/* xn is name of rpc, ie <rcp><xn/></rpc> */
|
||||||
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
|
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
|
||||||
if ((yn = xml_spec(xn)) == NULL)
|
if ((yn = xml_spec(xn)) == NULL){
|
||||||
goto fail;
|
if (netconf_unknown_element(cbret, "application", xml_name(xn), NULL) < 0)
|
||||||
if ((ret = xml_yang_validate_all(xn, cbret)) < 0)
|
goto done;
|
||||||
goto fail;
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
|
||||||
if ((ret = xml_yang_validate_add(xn, cbret)) < 0)
|
|
||||||
goto fail;
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
if ((retval = xml_yang_validate_all(xn, cbret)) < 1)
|
||||||
|
goto done; /* error or validation fail */
|
||||||
|
if ((retval = xml_yang_validate_add(xn, cbret)) < 1)
|
||||||
|
goto done; /* error or validation fail */
|
||||||
if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
|
if (xml_apply0(xn, CX_ELMNT, xml_default, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -443,9 +485,9 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
||||||
* 1. Check if mandatory leafs present as subs.
|
* 1. Check if mandatory leafs present as subs.
|
||||||
* 2. Check leaf values, eg int ranges and string regexps.
|
* 2. Check leaf values, eg int ranges and string regexps.
|
||||||
* @param[in] xt XML node to be validated
|
* @param[in] xt XML node to be validated
|
||||||
* @param[out] cbret Error buffer
|
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed
|
* @retval 0 Validation failed (cbret set)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* cxobj *x;
|
* cxobj *x;
|
||||||
|
|
@ -456,6 +498,7 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
||||||
* fail;
|
* fail;
|
||||||
* @endcode
|
* @endcode
|
||||||
* @see xml_yang_validate_all
|
* @see xml_yang_validate_all
|
||||||
|
* @see xml_yang_validate_rpc
|
||||||
* @note Should need a variant accepting cxobj **xret
|
* @note Should need a variant accepting cxobj **xret
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -544,9 +587,9 @@ xml_yang_validate_add(cxobj *xt,
|
||||||
/*! Validate a single XML node with yang specification for all (not only added) entries
|
/*! Validate a single XML node with yang specification for all (not only added) entries
|
||||||
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
|
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
|
||||||
* @param[in] xt XML node to be validated
|
* @param[in] xt XML node to be validated
|
||||||
* @param[out] cbret Error buffer
|
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed
|
* @retval 0 Validation failed (cbret set)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* cxobj *x;
|
* cxobj *x;
|
||||||
|
|
@ -680,6 +723,8 @@ xml_yang_validate_all(cxobj *xt,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
xml_yang_validate_all_top(cxobj *xt,
|
xml_yang_validate_all_top(cxobj *xt,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
|
|
@ -695,6 +740,47 @@ xml_yang_validate_all_top(cxobj *xt,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Given XML node x, find yang spec in _any_ module matching name
|
||||||
|
* This is non-struct namespace semantics (not correct) but necessary
|
||||||
|
* in historic Clixon code.
|
||||||
|
* Also, add a proper default namespaces statement (xmlns="uri") in x
|
||||||
|
* @param[in] x XML node (find yang statement on this one)
|
||||||
|
* @param[in] yspec Top-level yang spec
|
||||||
|
* @param[out] y Yang stmt associated to x. NULL i not found
|
||||||
|
* @retval 0 OK
|
||||||
|
* @see CLICON_XML_NS_STRICT clixon config option
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_yang_find_non_strict(cxobj *x,
|
||||||
|
yang_spec *yspec,
|
||||||
|
yang_stmt **yp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *name;
|
||||||
|
yang_stmt *ymod;
|
||||||
|
int i;
|
||||||
|
yang_stmt *y=NULL;
|
||||||
|
char *ns;
|
||||||
|
|
||||||
|
name = xml_name(x);
|
||||||
|
for (i=0; i<yspec->yp_len; i++){
|
||||||
|
ymod = yspec->yp_stmt[i];
|
||||||
|
if ((y = yang_find_schemanode((yang_node*)ymod, name)) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (y){
|
||||||
|
*yp = y;
|
||||||
|
if ((ns = yang_find_mynamespace(ymod)) != NULL){
|
||||||
|
if (xml_find_type_value(x, NULL, "xmlns", CX_ATTR) == NULL)
|
||||||
|
if (xmlns_set(x, NULL, ns) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
||||||
* @param[in] xt XML tree containing one top node
|
* @param[in] xt XML tree containing one top node
|
||||||
* @param[in] ys Yang spec containing type specification of top-node of xt
|
* @param[in] ys Yang spec containing type specification of top-node of xt
|
||||||
|
|
@ -861,28 +947,6 @@ cvec2xml_1(cvec *cvv,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Find next yang node, either start from yang_spec or some yang-node
|
|
||||||
* @param[in] y Node spec or sny yang-node
|
|
||||||
* @param[in] name Name of childnode to find
|
|
||||||
* @retval ys yang statement
|
|
||||||
* @retval NULL Error: no node found
|
|
||||||
*/
|
|
||||||
static yang_stmt *
|
|
||||||
yang_next(yang_node *y,
|
|
||||||
char *name)
|
|
||||||
{
|
|
||||||
yang_stmt *ys;
|
|
||||||
|
|
||||||
if (y->yn_keyword == Y_SPEC)
|
|
||||||
ys = yang_find_topnode((yang_spec*)y, name, YC_DATANODE);
|
|
||||||
else
|
|
||||||
ys = yang_find_datanode(y, name);
|
|
||||||
if (ys == NULL)
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
return ys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Recursive help function to compute differences between two xml trees
|
/*! Recursive help function to compute differences between two xml trees
|
||||||
* @param[in] x1 First XML tree
|
* @param[in] x1 First XML tree
|
||||||
* @param[in] x2 Second XML tree
|
* @param[in] x2 Second XML tree
|
||||||
|
|
@ -920,9 +984,12 @@ xml_diff1(yang_stmt *ys,
|
||||||
*/
|
*/
|
||||||
x1c = NULL;
|
x1c = NULL;
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
|
||||||
if ((yc = yang_next((yang_node*)ys, xml_name(x1c))) == NULL)
|
if ((yc = xml_spec(x1c)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
|
||||||
goto done;
|
goto done;
|
||||||
if (match_base_child(x2, x1c, &x2c, yc) < 0)
|
}
|
||||||
|
/* XXX xml_child_sort is global */
|
||||||
|
if (match_base_child(x2, x1c, &x2c, xml_child_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (x2c == NULL){
|
if (x2c == NULL){
|
||||||
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
|
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
|
||||||
|
|
@ -954,9 +1021,12 @@ xml_diff1(yang_stmt *ys,
|
||||||
*/
|
*/
|
||||||
x2c = NULL;
|
x2c = NULL;
|
||||||
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
|
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
|
||||||
if ((yc = yang_next((yang_node*)ys, xml_name(x2c))) == NULL)
|
if ((yc = xml_spec(x2c)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x2c));
|
||||||
goto done;
|
goto done;
|
||||||
if (match_base_child(x1, x2c, &x1c, yc) < 0)
|
}
|
||||||
|
/* XXX xml_child_sort is global */
|
||||||
|
if (match_base_child(x1, x2c, &x1c, xml_child_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (x1c == NULL)
|
if (x1c == NULL)
|
||||||
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
|
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
|
||||||
|
|
@ -1025,10 +1095,11 @@ xml_diff(yang_spec *yspec,
|
||||||
* Recursively construct it to the top.
|
* Recursively construct it to the top.
|
||||||
* Example:
|
* Example:
|
||||||
* yang: container a -> list b -> key c -> leaf d
|
* yang: container a -> list b -> key c -> leaf d
|
||||||
* xpath: /a/b/%s/d
|
* xpath: /modname:a/b/%s/d
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
|
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
|
||||||
* @param[out] cb api_path_fmt,
|
* @param[out] cb api_path_fmt,
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
yang2api_path_fmt_1(yang_stmt *ys,
|
yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
|
|
@ -1040,28 +1111,35 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
cvec *cvk = NULL; /* vector of index keys */
|
cvec *cvk = NULL; /* vector of index keys */
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
yp = ys->ys_parent;
|
if ((yp = ys->ys_parent) == NULL){
|
||||||
if (yp != NULL &&
|
clicon_err(OE_YANG, EINVAL, "yang expected parent %s", ys->ys_argument);
|
||||||
yp->yn_keyword != Y_MODULE &&
|
|
||||||
yp->yn_keyword != Y_SUBMODULE){
|
|
||||||
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (yp != NULL && /* XXX rm */
|
||||||
|
yp->yn_keyword != Y_MODULE &&
|
||||||
|
yp->yn_keyword != Y_SUBMODULE){
|
||||||
|
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
|
||||||
|
goto done;
|
||||||
|
cprintf(cb, "/");
|
||||||
|
}
|
||||||
|
else /* top symbol - mark with name prefix */
|
||||||
|
cprintf(cb, "/%s:", yp->yn_argument);
|
||||||
|
|
||||||
if (inclkey){
|
if (inclkey){
|
||||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||||
cprintf(cb, "/%s", ys->ys_argument);
|
cprintf(cb, "%s", ys->ys_argument);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
#if 1
|
#if 1
|
||||||
if (ys->ys_keyword == Y_LEAF && yp &&
|
if (ys->ys_keyword == Y_LEAF && yp &&
|
||||||
yp->yn_keyword == Y_LIST){
|
yp->yn_keyword == Y_LIST){
|
||||||
if (yang_key_match(yp, ys->ys_argument) == 0)
|
if (yang_key_match(yp, ys->ys_argument) == 0)
|
||||||
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
|
cprintf(cb, "%s", ys->ys_argument); /* Not if leaf and key */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||||
cprintf(cb, "/%s", ys->ys_argument);
|
cprintf(cb, "%s", ys->ys_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ys->ys_keyword){
|
switch (ys->ys_keyword){
|
||||||
|
|
@ -1095,6 +1173,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
|
* @param[in] inclkey If set include key leaf (eg last leaf d in ex)
|
||||||
* @param[out] api_path_fmt XML api path. Needs to be freed after use.
|
* @param[out] api_path_fmt XML api path. Needs to be freed after use.
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang2api_path_fmt(yang_stmt *ys,
|
yang2api_path_fmt(yang_stmt *ys,
|
||||||
|
|
@ -1148,6 +1227,8 @@ yang2api_path_fmt(yang_stmt *ys,
|
||||||
* api_path_fmt: /subif-entry=%s,%s/subid
|
* api_path_fmt: /subif-entry=%s,%s/subid
|
||||||
* cvv: foo
|
* cvv: foo
|
||||||
* api_path: /subif-entry=foo/subid
|
* api_path: /subif-entry=foo/subid
|
||||||
|
*
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_path_fmt2api_path(char *api_path_fmt,
|
api_path_fmt2api_path(char *api_path_fmt,
|
||||||
|
|
@ -1211,7 +1292,6 @@ api_path_fmt2api_path(char *api_path_fmt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Transform an xml key format and a vector of values to an XML path
|
/*! Transform an xml key format and a vector of values to an XML path
|
||||||
* Used to input xmldb_get() or xmldb_get_vec
|
* Used to input xmldb_get() or xmldb_get_vec
|
||||||
* @param[in] api_path_fmt XML key format
|
* @param[in] api_path_fmt XML key format
|
||||||
|
|
@ -1230,6 +1310,8 @@ api_path_fmt2api_path(char *api_path_fmt,
|
||||||
* api_path_fmt: /subif-entry=%s,%s/subid
|
* api_path_fmt: /subif-entry=%s,%s/subid
|
||||||
* cvv: foo
|
* cvv: foo
|
||||||
* xpath: /subif-entry[if-name=foo]/subid"
|
* xpath: /subif-entry[if-name=foo]/subid"
|
||||||
|
*
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_path_fmt2xpath(char *api_path_fmt,
|
api_path_fmt2xpath(char *api_path_fmt,
|
||||||
|
|
@ -1470,6 +1552,9 @@ xml_default(cxobj *xt,
|
||||||
|
|
||||||
/*! Order XML children according to YANG
|
/*! Order XML children according to YANG
|
||||||
* @param[in] xt XML top of tree
|
* @param[in] xt XML top of tree
|
||||||
|
* @param[in] arg Dummy (so it can be called from xml_apply)
|
||||||
|
* @see xml_sort XXX: how do they relate?
|
||||||
|
* Called from text_get *only*
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_order(cxobj *xt,
|
xml_order(cxobj *xt,
|
||||||
|
|
@ -1603,9 +1688,7 @@ xml_spec_populate_rpc(clicon_handle h,
|
||||||
yang_stmt *y=NULL; /* yang node */
|
yang_stmt *y=NULL; /* yang node */
|
||||||
yang_stmt *ymod=NULL; /* yang module */
|
yang_stmt *ymod=NULL; /* yang module */
|
||||||
yang_stmt *yi = NULL; /* input */
|
yang_stmt *yi = NULL; /* input */
|
||||||
// yang_stmt *ya = NULL; /* arg */
|
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
// cxobj *xi;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if ((strcmp(xml_name(xrpc), "rpc"))!=0){
|
if ((strcmp(xml_name(xrpc), "rpc"))!=0){
|
||||||
|
|
@ -1621,7 +1704,7 @@ xml_spec_populate_rpc(clicon_handle h,
|
||||||
/* Loose semantics: loop through all modules to find the node
|
/* Loose semantics: loop through all modules to find the node
|
||||||
*/
|
*/
|
||||||
if (y == NULL &&
|
if (y == NULL &&
|
||||||
clicon_option_bool(h, "CLICON_XML_NS_ITERATE")){
|
!clicon_option_bool(h, "CLICON_XML_NS_STRICT")){
|
||||||
for (i=0; i<yspec->yp_len; i++){
|
for (i=0; i<yspec->yp_len; i++){
|
||||||
ymod = yspec->yp_stmt[i];
|
ymod = yspec->yp_stmt[i];
|
||||||
if ((y = yang_find((yang_node*)ymod, Y_RPC, xml_name(x))) != NULL)
|
if ((y = yang_find((yang_node*)ymod, Y_RPC, xml_name(x))) != NULL)
|
||||||
|
|
@ -1633,16 +1716,8 @@ xml_spec_populate_rpc(clicon_handle h,
|
||||||
if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){
|
if ((yi = yang_find((yang_node*)y, Y_INPUT, NULL)) != NULL){
|
||||||
/* kludge rpc -> input */
|
/* kludge rpc -> input */
|
||||||
xml_spec_set(x, yi);
|
xml_spec_set(x, yi);
|
||||||
#if 1
|
|
||||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#else
|
|
||||||
xi = NULL;
|
|
||||||
while ((xi = xml_child_each(x, xi, CX_ELMNT)) != NULL) {
|
|
||||||
if ((ya = yang_find_datanode((yang_node*)yi, xml_name(xi))) != NULL)
|
|
||||||
xml_spec_set(xi, ya);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1651,13 +1726,14 @@ xml_spec_populate_rpc(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Add yang specification backpointer to XML node
|
/*! Add yang specification backpointer to XML node
|
||||||
* @param[in] xt XML tree node
|
* @param[in] xt XML tree node
|
||||||
* @param[in] arg Yang spec
|
* @param[in] arg Yang spec
|
||||||
* @note This may be unnecessary if yspec is set on creation
|
* @note This may be unnecessary if yspec is set on creation
|
||||||
* @note For subs to anyxml nodes will not have spec set
|
* @note For subs to anyxml nodes will not have spec set
|
||||||
* @note No validation is done,... XXX
|
* @note No validation is done,... XXX
|
||||||
* @note relies on kludge _CLICON_XML_NS_ITERATE
|
* @note relies on kludge _CLICON_XML_NS_STRICT
|
||||||
* @code
|
* @code
|
||||||
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
|
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
|
||||||
* @endcode
|
* @endcode
|
||||||
|
|
@ -1674,7 +1750,6 @@ xml_spec_populate(cxobj *x,
|
||||||
yang_stmt *ymod; /* yang module */
|
yang_stmt *ymod; /* yang module */
|
||||||
cxobj *xp; /* xml parent */
|
cxobj *xp; /* xml parent */
|
||||||
char *name;
|
char *name;
|
||||||
int i;
|
|
||||||
|
|
||||||
yspec = (yang_spec*)arg;
|
yspec = (yang_spec*)arg;
|
||||||
if (xml_spec(x))
|
if (xml_spec(x))
|
||||||
|
|
@ -1688,15 +1763,12 @@ xml_spec_populate(cxobj *x,
|
||||||
goto done;
|
goto done;
|
||||||
if (ymod != NULL)
|
if (ymod != NULL)
|
||||||
y = yang_find_datanode((yang_node*)ymod, name);
|
y = yang_find_datanode((yang_node*)ymod, name);
|
||||||
/* Loose semantics: loop through all modules to find the node
|
/* Non-strict semantics: loop through all modules to find the node
|
||||||
* XXX clicon_option_bool(h, "CLICON_XML_NS_ITERATE")
|
* XXX clicon_option_bool(h, "CLICON_XML_NS_STRICT")
|
||||||
*/
|
*/
|
||||||
if (y == NULL && _CLICON_XML_NS_ITERATE){
|
if (y == NULL && !_CLICON_XML_NS_STRICT){
|
||||||
for (i=0; i<yspec->yp_len; i++){
|
if (xml_yang_find_non_strict(x, yspec, &y) < 0)
|
||||||
ymod = yspec->yp_stmt[i];
|
goto done;
|
||||||
if ((y = yang_find_datanode((yang_node*)ymod, name)) != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (y)
|
if (y)
|
||||||
|
|
@ -1713,15 +1785,19 @@ xml_spec_populate(cxobj *x,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Translate from restconf api-path in cvv form to xml xpath
|
/*! Translate from restconf api-path in cvv form to xml xpath
|
||||||
* eg a/b=c -> a/[b=c]
|
* eg a/b=c -> a/[b=c]
|
||||||
|
* eg example:a/b -> ex:a/b
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in] pcvec api-path as cvec
|
* @param[in] cvv api-path as cvec
|
||||||
* @param[in] pi Offset of cvec, where api-path starts
|
* @param[in] offset Offset of cvec, where api-path starts
|
||||||
* @param[out] path The xpath as cbuf variable string, must be initializeed
|
* @param[out] xpath The xpath as cbuf variable string, must be initializeed
|
||||||
* The api-path has some wierd encoding, use api_path2xpath() if you are
|
* @retval 1 OK
|
||||||
* confused.
|
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||||
|
* @retval -1 Fatal error, clicon_err called
|
||||||
|
*
|
||||||
|
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||||
|
* @note Not proper namespace translation from api-path 2 xpath
|
||||||
* It works like this:
|
* It works like this:
|
||||||
* Assume origin incoming path is
|
* Assume origin incoming path is
|
||||||
* "www.foo.com/restconf/a/b=c", pi is 2 and pcvec is:
|
* "www.foo.com/restconf/a/b=c", pi is 2 and pcvec is:
|
||||||
|
|
@ -1729,15 +1805,24 @@ xml_spec_populate(cxobj *x,
|
||||||
* which means the api-path is ["a" "b=c"] corresponding to "a/b=c"
|
* which means the api-path is ["a" "b=c"] corresponding to "a/b=c"
|
||||||
* @code
|
* @code
|
||||||
* cbuf *xpath = cbuf_new();
|
* cbuf *xpath = cbuf_new();
|
||||||
* if (api_path2xpath_cvv(yspec, cvv, i, xpath)
|
* cvec *cvv = NULL;
|
||||||
|
* if (str2cvec("www.foo.com/restconf/a/b=c", '/', '=', &cvv) < 0)
|
||||||
* err;
|
* err;
|
||||||
|
* if ((ret = api_path2xpath(yspec, cvv, 0, cxpath)) < 0)
|
||||||
|
* err;
|
||||||
|
* if (ret == 0){
|
||||||
|
* ... access error string in clicon_err_reason
|
||||||
|
* clicon_err_reset();
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
* ... access xpath as cbuf_get(xpath)
|
* ... access xpath as cbuf_get(xpath)
|
||||||
* cbuf_free(xpath)
|
* cbuf_free(xpath)
|
||||||
* @endcode
|
* @endcode
|
||||||
* @see api_path2xpath for string api-path argument
|
* @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
|
* @see api_path2xml For api-path to xml tree
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_path2xpath_cvv(yang_spec *yspec,
|
api_path2xpath(yang_spec *yspec,
|
||||||
cvec *cvv,
|
cvec *cvv,
|
||||||
int offset,
|
int offset,
|
||||||
cbuf *xpath)
|
cbuf *xpath)
|
||||||
|
|
@ -1745,30 +1830,38 @@ api_path2xpath_cvv(yang_spec *yspec,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int i;
|
int i;
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
char *name;
|
char *nodeid;
|
||||||
|
char *prefix = NULL;
|
||||||
|
char *name = NULL;
|
||||||
cvec *cvk = NULL; /* vector of index keys */
|
cvec *cvk = NULL; /* vector of index keys */
|
||||||
yang_stmt *y = NULL;
|
yang_stmt *y = NULL;
|
||||||
|
yang_stmt *ymod;
|
||||||
char *val;
|
char *val;
|
||||||
char *v;
|
char *v;
|
||||||
cg_var *cvi;
|
cg_var *cvi;
|
||||||
|
|
||||||
for (i=offset; i<cvec_len(cvv); i++){
|
for (i=offset; i<cvec_len(cvv); i++){
|
||||||
cv = cvec_i(cvv, i);
|
cv = cvec_i(cvv, i);
|
||||||
name = cv_name_get(cv);
|
nodeid = cv_name_get(cv);
|
||||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
if (nodeid_split(nodeid, &prefix, &name) < 0)
|
||||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
|
||||||
if (i == offset){
|
|
||||||
if ((y = yang_find_topnode(yspec, name, YC_DATANODE)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
goto done;
|
||||||
|
clicon_debug(1, "%s [%d] cvname:%s", __FUNCTION__, i, name);
|
||||||
|
if (i == offset){ /* top-node */
|
||||||
|
if (prefix == NULL){
|
||||||
|
clicon_err(OE_XML, EINVAL, "'%s': Expected prefix:name", nodeid);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No such yang module: %s", prefix);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
else{
|
y = yang_find_datanode((yang_node*)ymod, name);
|
||||||
assert(y!=NULL);
|
|
||||||
if ((y = yang_find_datanode((yang_node*)y, name)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
y = yang_find_datanode((yang_node*)y, name);
|
||||||
|
if (y == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "Unknown element: '%s'", name);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
/* Check if has value, means '=' */
|
/* Check if has value, means '=' */
|
||||||
if (cv2str(cv, NULL, 0) > 0){
|
if (cv2str(cv, NULL, 0) > 0){
|
||||||
|
|
@ -1783,6 +1876,7 @@ api_path2xpath_cvv(yang_spec *yspec,
|
||||||
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
cvk = y->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
|
||||||
cvi = NULL;
|
cvi = NULL;
|
||||||
/* Iterate over individual yang keys */
|
/* Iterate over individual yang keys */
|
||||||
|
|
||||||
cprintf(xpath, "/%s", name);
|
cprintf(xpath, "/%s", name);
|
||||||
v = val;
|
v = val;
|
||||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||||
|
|
@ -1792,46 +1886,27 @@ api_path2xpath_cvv(yang_spec *yspec,
|
||||||
if (val)
|
if (val)
|
||||||
free(val);
|
free(val);
|
||||||
}
|
}
|
||||||
else{
|
else
|
||||||
cprintf(xpath, "%s%s", (i==offset?"":"/"), name);
|
cprintf(xpath, "%s%s", (i==offset?"":"/"), name);
|
||||||
|
if (prefix){
|
||||||
|
free(prefix);
|
||||||
|
prefix = NULL;
|
||||||
}
|
}
|
||||||
|
if (name){
|
||||||
|
free(name);
|
||||||
|
name = NULL;
|
||||||
}
|
}
|
||||||
retval = 0;
|
} /* for */
|
||||||
|
retval = 1; /* OK */
|
||||||
done:
|
done:
|
||||||
|
if (prefix)
|
||||||
|
free(prefix);
|
||||||
|
if (name)
|
||||||
|
free(name);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
fail:
|
||||||
|
retval = 0; /* Validation failed */
|
||||||
/*! Translate from restconf api-path to xml xpath
|
|
||||||
* eg a/b=c -> a/[b=c]
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[in] str API-path as string
|
|
||||||
* @param[out] path The xpath as cbuf variable string, must be initializeed
|
|
||||||
* @code
|
|
||||||
* cbuf *xpath = cbuf_new();
|
|
||||||
* if (api_path2xpath(yspec, "a/b=c", xpath)
|
|
||||||
* err;
|
|
||||||
* ... access xpath as cbuf_get(xpath)
|
|
||||||
* cbuf_free(xpath)
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
api_path2xpath(yang_spec *yspec,
|
|
||||||
char *api_path,
|
|
||||||
cbuf *xpath)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cvec *api_path_cvv = NULL;
|
|
||||||
|
|
||||||
/* rest url eg /album=ricky/foo */
|
|
||||||
if (str2cvec(api_path, '/', '=', &api_path_cvv) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (api_path2xpath_cvv(yspec, api_path_cvv, 0, xpath) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (api_path_cvv)
|
|
||||||
cvec_free(api_path_cvv);
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create xml tree from api-path as vector
|
/*! Create xml tree from api-path as vector
|
||||||
|
|
@ -1842,6 +1917,12 @@ api_path2xpath(yang_spec *yspec,
|
||||||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[out] xpathp Resulting xml tree
|
* @param[out] xpathp Resulting xml tree
|
||||||
* @param[out] ypathp Yang spec matching xpathp
|
* @param[out] ypathp Yang spec matching xpathp
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||||
|
* @retval -1 Fatal error, clicon_err called
|
||||||
|
*
|
||||||
|
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||||
|
* @see api_path2xpath For api-path to xml xpath translation
|
||||||
* @see api_path2xml
|
* @see api_path2xml
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -1855,7 +1936,9 @@ api_path2xml_vec(char **vec,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int j;
|
int j;
|
||||||
char *name;
|
char *nodeid;
|
||||||
|
char *name = NULL;
|
||||||
|
char *prefix = NULL;
|
||||||
char *restval = NULL;
|
char *restval = NULL;
|
||||||
char *restval_enc;
|
char *restval_enc;
|
||||||
cxobj *xn = NULL; /* new */
|
cxobj *xn = NULL; /* new */
|
||||||
|
|
@ -1868,43 +1951,50 @@ api_path2xml_vec(char **vec,
|
||||||
int nvalvec;
|
int nvalvec;
|
||||||
cxobj *x = NULL;
|
cxobj *x = NULL;
|
||||||
yang_stmt *y = NULL;
|
yang_stmt *y = NULL;
|
||||||
char *local;
|
yang_stmt *ymod;
|
||||||
|
char *namespace = NULL;
|
||||||
|
|
||||||
if ((name = vec[0]) == NULL){
|
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
||||||
if (xpathp)
|
if (xpathp)
|
||||||
*xpathp = x0;
|
*xpathp = x0;
|
||||||
if (ypathp)
|
if (ypathp)
|
||||||
*ypathp = y0;
|
*ypathp = y0;
|
||||||
return 0;
|
goto ok;
|
||||||
} /* E.g "x=1,2" -> name:x restval=1,2 */
|
} /* E.g "x=1,2" -> nodeid:x restval=1,2 */
|
||||||
/* restval is RFC 3896 encoded */
|
/* restval is RFC 3896 encoded */
|
||||||
if ((restval_enc = index(name, '=')) != NULL){
|
if ((restval_enc = index(nodeid, '=')) != NULL){
|
||||||
*restval_enc = '\0';
|
*restval_enc = '\0';
|
||||||
restval_enc++;
|
restval_enc++;
|
||||||
if (uri_percent_decode(restval_enc, &restval) < 0)
|
if (uri_percent_decode(restval_enc, &restval) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Split into prefix and localname, ignore prefix for now */
|
/* Split into prefix and localname */
|
||||||
if ((local = index(name, ':')) != NULL){
|
if (nodeid_split(nodeid, &prefix, &name) < 0)
|
||||||
*local = '\0';
|
|
||||||
local++;
|
|
||||||
name = local;
|
|
||||||
}
|
|
||||||
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
|
||||||
y = yang_find_topnode((yang_spec*)y0, name, nodeclass);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
y = (nodeclass==YC_SCHEMANODE)?yang_find_schemanode((yang_node*)y0, name):
|
|
||||||
yang_find_datanode((yang_node*)y0, name);
|
|
||||||
}
|
|
||||||
if (y == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
goto done;
|
||||||
|
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
||||||
|
if (prefix == NULL){
|
||||||
|
clicon_err(OE_XML, EINVAL, "api-path element '%s', expected prefix:name", nodeid);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if ((ymod = yang_find_module_by_name((yang_spec*)y0, prefix)) == NULL){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
namespace = yang_find_mynamespace(ymod);
|
||||||
|
y0 = (yang_node*)ymod;
|
||||||
|
|
||||||
|
}
|
||||||
|
y = (nodeclass==YC_SCHEMANODE)?
|
||||||
|
yang_find_schemanode((yang_node*)y0, name):
|
||||||
|
yang_find_datanode((yang_node*)y0, name);
|
||||||
|
if (y == NULL){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "api-path name: '%s', no such yang element", name);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
switch (y->ys_keyword){
|
switch (y->ys_keyword){
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
if (0 && restval==NULL){
|
if (0 && restval==NULL){
|
||||||
clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'");
|
clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((x = xml_new(y->ys_argument, x0, y)) == NULL)
|
if ((x = xml_new(y->ys_argument, x0, y)) == NULL)
|
||||||
|
|
@ -1924,15 +2014,15 @@ api_path2xml_vec(char **vec,
|
||||||
}
|
}
|
||||||
if (restval==NULL){
|
if (restval==NULL){
|
||||||
// XXX patch to allow for lists without restval to be backward compat
|
// XXX patch to allow for lists without restval to be backward compat
|
||||||
// clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'");
|
// clicon_err(OE_XML, 0, "malformed key, expected '=restval'");
|
||||||
// goto done;
|
// goto done;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (nvalvec > cvec_len(cvk)){
|
if (nvalvec > cvec_len(cvk)){
|
||||||
clicon_err(OE_XML, errno, "List %s key length mismatch", name);
|
clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name);
|
||||||
goto done;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cvi = NULL;
|
cvi = NULL;
|
||||||
|
|
@ -1961,27 +2051,43 @@ api_path2xml_vec(char **vec,
|
||||||
xml_type_set(x, CX_ELMNT);
|
xml_type_set(x, CX_ELMNT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (api_path2xml_vec(vec+1, nvec-1,
|
if (x && namespace){
|
||||||
|
if (xmlns_set(x, NULL, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, (yang_node*)y,
|
x, (yang_node*)y,
|
||||||
nodeclass,
|
nodeclass,
|
||||||
xpathp, ypathp) < 0)
|
xpathp, ypathp)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
ok:
|
||||||
|
retval = 1; /* OK */
|
||||||
done:
|
done:
|
||||||
|
if (prefix)
|
||||||
|
free(prefix);
|
||||||
|
if (name)
|
||||||
|
free(name);
|
||||||
if (restval)
|
if (restval)
|
||||||
free(restval);
|
free(restval);
|
||||||
if (valvec)
|
if (valvec)
|
||||||
free(valvec);
|
free(valvec);
|
||||||
return retval;
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0; /* invalid api-path or XML */
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create xml tree from api-path
|
/*! Create xml tree from api-path
|
||||||
* @param[in] api_path API-path as defined in RFC 8040
|
* @param[in] api_path (Absolute) API-path as defined in RFC 8040
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in,out] xtop Incoming XML tree
|
* @param[in,out] xtop Incoming XML tree
|
||||||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[out] xbotp Resulting xml tree (end of xpath)
|
* @param[out] xbotp Resulting xml tree (end of xpath)
|
||||||
* @param[out] ybotp Yang spec matching xbotp
|
* @param[out] ybotp Yang spec matching xbotp
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Invalid api_path or associated XML, clicon_err called
|
||||||
|
* @retval -1 Fatal error, clicon_err called
|
||||||
|
* @note both retval 0 and -1 set clicon_err, but the later is fatal
|
||||||
* @example
|
* @example
|
||||||
* api_path: /subif-entry=foo/subid
|
* api_path: /subif-entry=foo/subid
|
||||||
* xtop[in] <config/>
|
* xtop[in] <config/>
|
||||||
|
|
@ -1990,7 +2096,8 @@ api_path2xml_vec(char **vec,
|
||||||
* </subif-entry></config>
|
* </subif-entry></config>
|
||||||
* xbotp: <subid/>
|
* xbotp: <subid/>
|
||||||
* ybotp: Y_LEAF subid
|
* ybotp: Y_LEAF subid
|
||||||
* @see api_path2xml_vec
|
* @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
|
* @see api_path2xpath For api-path to xml xpath translation
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_path2xml(char *api_path,
|
api_path2xml(char *api_path,
|
||||||
|
|
@ -2003,10 +2110,13 @@ api_path2xml(char *api_path,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char **vec = NULL;
|
char **vec = NULL;
|
||||||
int nvec;
|
int nvec;
|
||||||
|
cxobj *xroot;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (*api_path!='/'){
|
if (*api_path!='/'){
|
||||||
clicon_log(LOG_WARNING, "Invalid key: %s (must start with '/')", api_path);
|
clicon_err(OE_XML, EINVAL, "Invalid api-path: %s (must start with '/')",
|
||||||
goto ok;
|
api_path);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL)
|
if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2014,19 +2124,70 @@ api_path2xml(char *api_path,
|
||||||
if (nvec > 1 && !strlen(vec[nvec-1]))
|
if (nvec > 1 && !strlen(vec[nvec-1]))
|
||||||
nvec--;
|
nvec--;
|
||||||
if (nvec < 1){
|
if (nvec < 1){
|
||||||
clicon_err(OE_XML, 0, "Malformed key: %s", api_path);
|
clicon_err(OE_XML, EINVAL, "Malformed api-path: %s", api_path);
|
||||||
goto done;
|
goto fail;
|
||||||
}
|
}
|
||||||
nvec--; /* NULL-terminated */
|
nvec--; /* NULL-terminated */
|
||||||
if (api_path2xml_vec(vec+1, nvec,
|
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||||
xtop, (yang_node*)yspec, nodeclass,
|
xtop, (yang_node*)yspec, nodeclass,
|
||||||
xbotp, ybotp) < 0)
|
xbotp, ybotp)) < 1)
|
||||||
|
goto done;
|
||||||
|
xml_yang_root(*xbotp, &xroot);
|
||||||
|
if (xmlns_assign(xroot) < 0)
|
||||||
|
goto done;
|
||||||
|
// ok:
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Check if the module tree x is in is assigned right XML namespace, assign if not
|
||||||
|
* @param[in] x XML node
|
||||||
|
*(0. You should probably find the XML root and apply this function to that.)
|
||||||
|
* 1. Check which namespace x should have (via yang). This is correct namespace.
|
||||||
|
* 2. Check which namespace x has via its XML tree
|
||||||
|
* 3. If equal, OK
|
||||||
|
* 4. Assign the correct namespace to the XML node
|
||||||
|
* Assign default namespace to x. Create an "xmlns"
|
||||||
|
* @code
|
||||||
|
* xml_yang_root(x, &xroot);
|
||||||
|
* xmlns_assign(xroot);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xmlns_assign(cxobj *x)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *y;
|
||||||
|
char *ns_correct; /* correct uri */
|
||||||
|
char *ns_xml; /* may be null or incorrect */
|
||||||
|
|
||||||
|
if ((y = xml_spec(x)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec", xml_name(x));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* 1. Check which namespace x should have (via yang). This is correct namespace. */
|
||||||
|
if ((ns_correct = yang_find_mynamespace(y)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", y->ys_argument);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* 2. Check which namespace x has via its XML tree */
|
||||||
|
if (xml2ns(x, NULL, &ns_xml) < 0)
|
||||||
|
goto done;
|
||||||
|
/* 3. If equal, OK, 4. Else, find root of XML tree */
|
||||||
|
if (ns_xml && strcmp(ns_xml, ns_correct)==0)
|
||||||
|
goto ok;
|
||||||
|
/* 4. Assign the correct namespace */
|
||||||
|
if (xmlns_set(x, NULL, ns_correct) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (vec)
|
|
||||||
free(vec);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2053,6 +2214,8 @@ xml_merge1(cxobj *x0,
|
||||||
cxobj *x0c; /* base child */
|
cxobj *x0c; /* base child */
|
||||||
cxobj *x0b; /* base body */
|
cxobj *x0b; /* base body */
|
||||||
cxobj *x1c; /* mod child */
|
cxobj *x1c; /* mod child */
|
||||||
|
cxobj *x0a; /* x0 xmlns attribute */
|
||||||
|
cxobj *x1a; /* x1 xmlns attribute */
|
||||||
char *x1bstr; /* mod body string */
|
char *x1bstr; /* mod body string */
|
||||||
yang_stmt *yc; /* yang child */
|
yang_stmt *yc; /* yang child */
|
||||||
cbuf *cbr = NULL; /* Reason buffer */
|
cbuf *cbr = NULL; /* Reason buffer */
|
||||||
|
|
@ -2109,7 +2272,7 @@ xml_merge1(cxobj *x0,
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (yc && match_base_child(x0, x1c, &x0c, xml_child_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2117,6 +2280,17 @@ xml_merge1(cxobj *x0,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
} /* else Y_CONTAINER */
|
} /* else Y_CONTAINER */
|
||||||
|
assert(x0);
|
||||||
|
/* Copy xmlns attributes (if it does not already exist) */
|
||||||
|
if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != NULL)
|
||||||
|
if (xml_find_type(x0, NULL, "xmlns", CX_ATTR)==NULL){
|
||||||
|
if ((x0a = xml_dup(x1a)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_addsub(x0, x0a) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -2147,20 +2321,31 @@ xml_merge(cxobj *x0,
|
||||||
cxobj *x0c; /* base child */
|
cxobj *x0c; /* base child */
|
||||||
cxobj *x1c; /* mod child */
|
cxobj *x1c; /* mod child */
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
|
yang_stmt *ymod;
|
||||||
cbuf *cbr = NULL; /* Reason buffer */
|
cbuf *cbr = NULL; /* Reason buffer */
|
||||||
|
|
||||||
/* Loop through children of the modification tree */
|
/* Loop through children of the modification tree */
|
||||||
x1c = NULL;
|
x1c = NULL;
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
|
if ((ys_module_by_xml(yspec, x1c, &ymod)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ymod == NULL){
|
||||||
|
if (reason &&
|
||||||
|
(*reason = strdup("No namespace in XML tree found")) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) == NULL){
|
||||||
if (reason){
|
if (reason){
|
||||||
if ((cbr = cbuf_new()) == NULL){
|
if ((cbr = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
|
cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?)", xml_name(x1), x1cname);
|
||||||
if ((*reason = strdup(cbuf_get(cbr))) == NULL){
|
if ((*reason = strdup(cbuf_get(cbr))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2169,13 +2354,14 @@ xml_merge(cxobj *x0,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* 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, &x0c, xml_child_sort, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (*reason != NULL)
|
if (*reason != NULL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ok:
|
||||||
retval = 0; /* OK */
|
retval = 0; /* OK */
|
||||||
done:
|
done:
|
||||||
if (cbr)
|
if (cbr)
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
|
||||||
* @param[in] ya XML parser yacc handler struct
|
* @param[in] ya XML parser yacc handler struct
|
||||||
* @param[in] prefix Prefix, namespace, or NULL
|
* @param[in] prefix Prefix, namespace, or NULL
|
||||||
* @param[in] localpart Name
|
* @param[in] localpart Name
|
||||||
|
* @note the call to xml_child_spec() may not have xmlns attribute read yet XXX
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
|
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
|
||||||
|
|
@ -141,9 +142,11 @@ xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
|
||||||
cxobj *xp; /* xml parent */
|
cxobj *xp; /* xml parent */
|
||||||
|
|
||||||
xp = ya->ya_xparent;
|
xp = ya->ya_xparent;
|
||||||
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
|
if ((x = xml_new(name, xp, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if ((x = xml_new(name, xp, y)) == NULL)
|
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
|
||||||
|
goto done;
|
||||||
|
if (y && xml_spec_set(x, y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ya->ya_xelement = x;
|
ya->ya_xelement = x;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -168,9 +171,11 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
|
||||||
cxobj *xp; /* xml parent */
|
cxobj *xp; /* xml parent */
|
||||||
|
|
||||||
xp = ya->ya_xparent;
|
xp = ya->ya_xparent;
|
||||||
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
|
if ((x = xml_new(name, xp, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if ((x = xml_new(name, xp, y)) == NULL)
|
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
|
||||||
|
goto done;
|
||||||
|
if (y && xml_spec_set(x, y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_prefix_set(x, prefix) < 0)
|
if (xml_prefix_set(x, prefix) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -313,19 +318,15 @@ xml_parse_attr(struct xml_parse_yacc_arg *ya,
|
||||||
char *attval)
|
char *attval)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xa;
|
cxobj *xa = NULL;
|
||||||
|
|
||||||
#ifdef ENABLE_XMLNS
|
if ((xa = xml_find_type(ya->ya_xelement, prefix, name, CX_ATTR)) == NULL){
|
||||||
if (prefix && strcmp(prefix,"xmlns")==0)
|
|
||||||
fprintf(stderr, "PrefixedAttName NCNAME:%s = %s\n", name, attval);
|
|
||||||
if (prefix==NULL && strcmp(name,"xmlns")==0)
|
|
||||||
fprintf(stderr, "DefaultAttName = %s\n", attval);
|
|
||||||
#endif /* notyet */
|
|
||||||
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
|
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xml_type_set(xa, CX_ATTR);
|
xml_type_set(xa, CX_ATTR);
|
||||||
if (prefix && xml_prefix_set(xa, prefix) < 0)
|
if (prefix && xml_prefix_set(xa, prefix) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
if (xml_value_set(xa, attval) < 0)
|
if (xml_value_set(xa, attval) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_options.h"
|
||||||
|
#include "clixon_xml_map.h"
|
||||||
#include "clixon_xml_sort.h"
|
#include "clixon_xml_sort.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -68,7 +70,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Sort and binary search of XML children
|
/* Sort and binary search of XML children
|
||||||
* Experimental
|
* XXX kludge since low-level functions xml_merge/xml_diff calls
|
||||||
|
* match_base_child without handle
|
||||||
|
* @see clicon_xml_sort
|
||||||
*/
|
*/
|
||||||
int xml_child_sort = 1;
|
int xml_child_sort = 1;
|
||||||
|
|
||||||
|
|
@ -83,7 +87,7 @@ int xml_child_sort = 1;
|
||||||
* xmlns and xmlns:ns are used.
|
* xmlns and xmlns:ns are used.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_child_spec(char *name,
|
xml_child_spec(cxobj *x,
|
||||||
cxobj *xp,
|
cxobj *xp,
|
||||||
yang_spec *yspec,
|
yang_spec *yspec,
|
||||||
yang_stmt **yresult)
|
yang_stmt **yresult)
|
||||||
|
|
@ -93,8 +97,9 @@ xml_child_spec(char *name,
|
||||||
yang_stmt *yparent; /* parent yang */
|
yang_stmt *yparent; /* parent yang */
|
||||||
yang_stmt *ymod = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
yang_stmt *yi;
|
yang_stmt *yi;
|
||||||
int i;
|
char *name;
|
||||||
|
|
||||||
|
name = xml_name(x);
|
||||||
if (xp && (yparent = xml_spec(xp)) != NULL){
|
if (xp && (yparent = xml_spec(xp)) != NULL){
|
||||||
if (yparent->ys_keyword == Y_RPC){
|
if (yparent->ys_keyword == Y_RPC){
|
||||||
if ((yi = yang_find((yang_node*)yparent, Y_INPUT, NULL)) != NULL)
|
if ((yi = yang_find((yang_node*)yparent, Y_INPUT, NULL)) != NULL)
|
||||||
|
|
@ -108,12 +113,9 @@ xml_child_spec(char *name,
|
||||||
goto done;
|
goto done;
|
||||||
if (ymod != NULL)
|
if (ymod != NULL)
|
||||||
y = yang_find_schemanode((yang_node*)ymod, name);
|
y = yang_find_schemanode((yang_node*)ymod, name);
|
||||||
if (y == NULL && _CLICON_XML_NS_ITERATE){
|
if (y == NULL && !_CLICON_XML_NS_STRICT){
|
||||||
for (i=0; i<yspec->yp_len; i++){
|
if (xml_yang_find_non_strict(x, yspec, &y) < 0) /* schemanode */
|
||||||
ymod = yspec->yp_stmt[i];
|
goto done;
|
||||||
if ((y = yang_find_schemanode((yang_node*)ymod, name)) != NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -265,6 +267,7 @@ xml_cmp1(cxobj *x,
|
||||||
* Assume populated by yang spec.
|
* Assume populated by yang spec.
|
||||||
* @param[in] x0 XML node
|
* @param[in] x0 XML node
|
||||||
* @param[in] arg Dummy so it can be called by xml_apply()
|
* @param[in] arg Dummy so it can be called by xml_apply()
|
||||||
|
* @see xml_order XXX: how do they relate?
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_sort(cxobj *x,
|
xml_sort(cxobj *x,
|
||||||
|
|
@ -563,6 +566,7 @@ xml_sort_verify(cxobj *x0,
|
||||||
* param[in] x0 Base tree node
|
* param[in] x0 Base tree node
|
||||||
* param[in] x1c Modification tree child
|
* param[in] x1c Modification tree child
|
||||||
* param[in] yc Yang spec of tree child
|
* param[in] yc Yang spec of tree child
|
||||||
|
* param[in] xml_sort Value of CLICON_XML_SORT option
|
||||||
* param[out] x0cp Matching base tree child (if any)
|
* param[out] x0cp Matching base tree child (if any)
|
||||||
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
|
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
|
||||||
500K xml_child_each/cvec_each calls.
|
500K xml_child_each/cvec_each calls.
|
||||||
|
|
@ -575,6 +579,7 @@ int
|
||||||
match_base_child(cxobj *x0,
|
match_base_child(cxobj *x0,
|
||||||
cxobj *x1c,
|
cxobj *x1c,
|
||||||
cxobj **x0cp,
|
cxobj **x0cp,
|
||||||
|
int xml_sort,
|
||||||
yang_stmt *yc)
|
yang_stmt *yc)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -632,7 +637,7 @@ match_base_child(cxobj *x0,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Get match. Sorting mode(optimized) or not?*/
|
/* Get match. Sorting mode(optimized) or not?*/
|
||||||
if (xml_child_sort==0)
|
if (xml_sort==0)
|
||||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||||
else{
|
else{
|
||||||
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
|
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
|
||||||
|
|
@ -640,12 +645,6 @@ match_base_child(cxobj *x0,
|
||||||
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
#if 1 /* This is just a warning, but a catcher for when xml tree is not
|
|
||||||
populated with yang spec. If you see this, a previous invacation of,
|
|
||||||
for example xml_spec_populate() may be missing
|
|
||||||
*/
|
|
||||||
clicon_log(LOG_WARNING, "%s No yspec", __FUNCTION__);
|
|
||||||
#endif
|
|
||||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -469,94 +469,6 @@ yang_match(yang_node *yn,
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
#ifdef NOTYET
|
|
||||||
/*! Prototype more generic than yang_find_datanode and yang_find_schemanode
|
|
||||||
*/
|
|
||||||
yang_stmt *
|
|
||||||
yang_find_class(yang_node *yn,
|
|
||||||
char *argument,
|
|
||||||
yang_class class)
|
|
||||||
{
|
|
||||||
yang_stmt *ys = NULL;
|
|
||||||
yang_stmt *yc = NULL;
|
|
||||||
yang_stmt *ysmatch = NULL;
|
|
||||||
int i, j;
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
for (i=0; i<yn->yn_len; i++){
|
|
||||||
ys = yn->yn_stmt[i];
|
|
||||||
switch(class){
|
|
||||||
case YC_NONE:
|
|
||||||
ok = 1;
|
|
||||||
break;
|
|
||||||
case YC_DATANODE:
|
|
||||||
ok = yang_datanode(ys);
|
|
||||||
break;
|
|
||||||
case YC_DATADEFINITION:
|
|
||||||
ok = yang_datadefinition(ys);
|
|
||||||
break;
|
|
||||||
case YC_SCHEMANODE:
|
|
||||||
ok = yang_schemanode(ys);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!ok)
|
|
||||||
continue;
|
|
||||||
switch(class){
|
|
||||||
case YC_NONE:
|
|
||||||
if (argument == NULL)
|
|
||||||
ysmatch = ys;
|
|
||||||
else
|
|
||||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
|
||||||
ysmatch = ys;
|
|
||||||
if (ysmatch)
|
|
||||||
goto match;
|
|
||||||
break;
|
|
||||||
case YC_DATANODE:
|
|
||||||
case YC_DATADEFINITION:
|
|
||||||
if (argument == NULL)
|
|
||||||
ysmatch = ys;
|
|
||||||
else
|
|
||||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
|
||||||
ysmatch = ys;
|
|
||||||
if (ysmatch)
|
|
||||||
goto match;
|
|
||||||
break;
|
|
||||||
case YC_SCHEMANODE:
|
|
||||||
if (ys->ys_keyword == Y_CHOICE){ /* Look for its children */
|
|
||||||
for (j=0; j<ys->ys_len; j++){
|
|
||||||
yc = ys->ys_stmt[j];
|
|
||||||
if (yc->ys_keyword == Y_CASE) /* Look for its children */
|
|
||||||
ysmatch = yang_find_class((yang_node*)yc, argument, class);
|
|
||||||
else{
|
|
||||||
if (yang_schemanode(yc)){
|
|
||||||
if (argument == NULL)
|
|
||||||
ysmatch = yc;
|
|
||||||
else
|
|
||||||
if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
|
|
||||||
ysmatch = yc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ysmatch)
|
|
||||||
goto match;
|
|
||||||
}
|
|
||||||
} /* Y_CHOICE */
|
|
||||||
else{
|
|
||||||
if (argument == NULL)
|
|
||||||
ysmatch = ys;
|
|
||||||
else
|
|
||||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
|
||||||
ysmatch = ys;
|
|
||||||
if (ysmatch)
|
|
||||||
goto match;
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} /* switch */
|
|
||||||
} /* for */
|
|
||||||
match:
|
|
||||||
return ysmatch;
|
|
||||||
}
|
|
||||||
#endif /* NOTYET */
|
|
||||||
|
|
||||||
/*! Find child data node with matching argument (container, leaf, etc)
|
/*! Find child data node with matching argument (container, leaf, etc)
|
||||||
*
|
*
|
||||||
|
|
@ -658,69 +570,6 @@ yang_find_schemanode(yang_node *yn,
|
||||||
return ysmatch;
|
return ysmatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Find first matching data node in all modules in a yang spec (prefixes)
|
|
||||||
*
|
|
||||||
* @param[in] ysp Yang specification
|
|
||||||
* @param[in] nodeid Name of node. If NULL match first
|
|
||||||
* @param[in] class See yang_class for class of yang nodes
|
|
||||||
* A yang specification has modules as children which in turn can have
|
|
||||||
* syntax-nodes as children. This function goes through all the modules to
|
|
||||||
* look for nodes. Note that if a child to a module is a choice,
|
|
||||||
* the search is made recursively made to the choice's children.
|
|
||||||
* @note works for import prefix, but not work for generic XML parsing where
|
|
||||||
* xmlns and xmlns:ns are used.
|
|
||||||
* @see yang_find_top_ns
|
|
||||||
*/
|
|
||||||
yang_stmt *
|
|
||||||
yang_find_topnode(yang_spec *ysp,
|
|
||||||
char *nodeid,
|
|
||||||
yang_class class)
|
|
||||||
{
|
|
||||||
yang_stmt *ymod = NULL; /* module */
|
|
||||||
yang_stmt *yres = NULL; /* result */
|
|
||||||
char *prefix = NULL;
|
|
||||||
char *id = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
|
|
||||||
goto done;
|
|
||||||
if (prefix){
|
|
||||||
if ((ymod = yang_find((yang_node*)ysp, Y_MODULE, prefix)) != NULL ||
|
|
||||||
(ymod = yang_find((yang_node*)ysp, Y_SUBMODULE, prefix)) != NULL){
|
|
||||||
if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
|
|
||||||
goto ok;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* No prefix given - loop through and find first */
|
|
||||||
for (i=0; i<ysp->yp_len; i++){
|
|
||||||
ymod = ysp->yp_stmt[i];
|
|
||||||
switch (class){
|
|
||||||
case YC_NONE:
|
|
||||||
if ((yres = yang_find((yang_node*)ymod, 0, id)) != NULL)
|
|
||||||
goto ok;
|
|
||||||
break;
|
|
||||||
case YC_DATANODE:
|
|
||||||
if ((yres = yang_find_datanode((yang_node*)ymod, id)) != NULL)
|
|
||||||
goto ok;
|
|
||||||
break;
|
|
||||||
case YC_SCHEMANODE:
|
|
||||||
if ((yres = yang_find_schemanode((yang_node*)ymod, id)) != NULL)
|
|
||||||
goto ok;
|
|
||||||
break;
|
|
||||||
case YC_DATADEFINITION:
|
|
||||||
break; /* nyi */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ok:
|
|
||||||
done:
|
|
||||||
if (prefix)
|
|
||||||
free(prefix);
|
|
||||||
if (id)
|
|
||||||
free(id);
|
|
||||||
return yres;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Given a yang statement, find the prefix associated to this module
|
/*! Given a yang statement, find the prefix associated to this module
|
||||||
* @param[in] ys Yang statement in module tree (or module itself)
|
* @param[in] ys Yang statement in module tree (or module itself)
|
||||||
* @retval NULL Not found
|
* @retval NULL Not found
|
||||||
|
|
@ -775,7 +624,6 @@ yang_find_mynamespace(yang_stmt *ys)
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
||||||
* @retval 0 not found
|
* @retval 0 not found
|
||||||
* @retval 1 found
|
* @retval 1 found
|
||||||
|
|
@ -972,60 +820,15 @@ yarg_prefix(yang_stmt *ys)
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Split yang node identifier into prefix and identifer.
|
/*! Given a yang statement and a prefix, return yang module to that relative prefix
|
||||||
* @param[in] node-id
|
|
||||||
* @param[out] prefix Malloced string. May be NULL.
|
|
||||||
* @param[out] id Malloced identifier.
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* @code
|
|
||||||
* char *prefix = NULL;
|
|
||||||
* char *id = NULL;
|
|
||||||
* if (yang_nodeid_split(nodeid, &prefix, &id) < 0)
|
|
||||||
* goto done;
|
|
||||||
* if (prefix)
|
|
||||||
* free(prefix);
|
|
||||||
* if (id)
|
|
||||||
* free(id);
|
|
||||||
* @note caller need to free id and prefix after use
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
yang_nodeid_split(char *nodeid,
|
|
||||||
char **prefix,
|
|
||||||
char **id)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
char *str;
|
|
||||||
|
|
||||||
if ((str = strchr(nodeid, ':')) == NULL){
|
|
||||||
if ((*id = strdup(nodeid)) == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "strdup");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if ((*prefix = strdup(nodeid)) == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "strdup");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
(*prefix)[str-nodeid] = '\0';
|
|
||||||
str++;
|
|
||||||
if ((*id = strdup(str)) == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "strdup");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Given a yang statement and a prefix, return yang module to that prefix
|
|
||||||
* Note, not the other module but the proxy import statement only
|
* Note, not the other module but the proxy import statement only
|
||||||
* @param[in] ys A yang statement
|
* @param[in] ys A yang statement
|
||||||
* @param[in] prefix prefix
|
* @param[in] prefix prefix
|
||||||
* @retval ymod Yang module statement if found
|
* @retval ymod Yang module statement if found
|
||||||
* @retval NULL not found
|
* @retval NULL not found
|
||||||
|
* @node Prefixes are relative to the module they are defined
|
||||||
|
* @see yang_find_module_by_name
|
||||||
|
* @see yang_find_module_by_namespace
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
yang_find_module_by_prefix(yang_stmt *ys,
|
yang_find_module_by_prefix(yang_stmt *ys,
|
||||||
|
|
@ -1081,12 +884,14 @@ yang_find_module_by_prefix(yang_stmt *ys,
|
||||||
return ymod;
|
return ymod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given a yang statement and a namespace, return yang module
|
/*! Given a yang spec and a namespace, return yang module
|
||||||
*
|
*
|
||||||
* @param[in] yspec A yang specification
|
* @param[in] yspec A yang specification
|
||||||
* @param[in] namespace namespace
|
* @param[in] namespace namespace
|
||||||
* @retval ymod Yang module statement if found
|
* @retval ymod Yang module statement if found
|
||||||
* @retval NULL not found
|
* @retval NULL not found
|
||||||
|
* @see yang_find_module_by_name
|
||||||
|
* @see yang_find_module_by_prefix module-specific prefix
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
yang_find_module_by_namespace(yang_spec *yspec,
|
yang_find_module_by_namespace(yang_spec *yspec,
|
||||||
|
|
@ -1104,6 +909,28 @@ yang_find_module_by_namespace(yang_spec *yspec,
|
||||||
return ymod;
|
return ymod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Given a yang spec and a module name, return yang module
|
||||||
|
*
|
||||||
|
* @param[in] yspec A yang specification
|
||||||
|
* @param[in] name Name of module
|
||||||
|
* @retval ymod Yang module statement if found
|
||||||
|
* @retval NULL not found
|
||||||
|
* @see yang_find_module_by_namespace
|
||||||
|
* @see yang_find_module_by_prefix module-specific prefix
|
||||||
|
*/
|
||||||
|
yang_stmt *
|
||||||
|
yang_find_module_by_name(yang_spec *yspec,
|
||||||
|
char *name)
|
||||||
|
{
|
||||||
|
yang_stmt *ymod = NULL;
|
||||||
|
|
||||||
|
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL)
|
||||||
|
if ((ymod->ys_keyword == Y_MODULE || ymod->ys_keyword == Y_SUBMODULE) &&
|
||||||
|
strcmp(ymod->ys_argument, name)==0)
|
||||||
|
return ymod;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! string is quoted if it contains space or tab, needs double '' */
|
/*! string is quoted if it contains space or tab, needs double '' */
|
||||||
static int inline
|
static int inline
|
||||||
quotedstring(char *s)
|
quotedstring(char *s)
|
||||||
|
|
@ -1541,7 +1368,7 @@ ys_populate_feature(clicon_handle h,
|
||||||
if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
|
if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
|
||||||
continue;
|
continue;
|
||||||
/* get m and f from configuration feature rules */
|
/* get m and f from configuration feature rules */
|
||||||
if (yang_nodeid_split(xml_body(xc), &m, &f) < 0)
|
if (nodeid_split(xml_body(xc), &m, &f) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (m && f &&
|
if (m && f &&
|
||||||
(strcmp(m,"*")==0 ||
|
(strcmp(m,"*")==0 ||
|
||||||
|
|
@ -1558,7 +1385,8 @@ ys_populate_feature(clicon_handle h,
|
||||||
}
|
}
|
||||||
cv_name_set(cv, feature);
|
cv_name_set(cv, feature);
|
||||||
cv_bool_set(cv, found);
|
cv_bool_set(cv, found);
|
||||||
clicon_debug(1, "%s %s:%s %d", __FUNCTION__, module, feature, found);
|
if (found)
|
||||||
|
clicon_debug(1, "%s %s:%s", __FUNCTION__, module, feature);
|
||||||
ys->ys_cv = cv;
|
ys->ys_cv = cv;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -2083,7 +1911,7 @@ yang_parse_filename(const char *filename,
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
// clicon_debug(1, "%s %s", __FUNCTION__, filename);
|
clicon_debug(1, "%s %s", __FUNCTION__, filename);
|
||||||
if (stat(filename, &st) < 0){
|
if (stat(filename, &st) < 0){
|
||||||
clicon_err(OE_YANG, errno, "%s not found", filename);
|
clicon_err(OE_YANG, errno, "%s not found", filename);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2258,7 +2086,7 @@ yang_features(clicon_handle h,
|
||||||
while (i<yt->ys_len){ /* Note, children may be removed */
|
while (i<yt->ys_len){ /* Note, children may be removed */
|
||||||
ys = yt->ys_stmt[i];
|
ys = yt->ys_stmt[i];
|
||||||
if (ys->ys_keyword == Y_IF_FEATURE){
|
if (ys->ys_keyword == Y_IF_FEATURE){
|
||||||
if (yang_nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
|
if (nodeid_split(ys->ys_argument, &prefix, &feature) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Specifically need to handle? strcmp(prefix, myprefix)) */
|
/* Specifically need to handle? strcmp(prefix, myprefix)) */
|
||||||
if (prefix == NULL)
|
if (prefix == NULL)
|
||||||
|
|
@ -2680,7 +2508,6 @@ yang_abs_schema_nodeid(yang_spec *yspec,
|
||||||
char *id;
|
char *id;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
yang_stmt *yprefix;
|
yang_stmt *yprefix;
|
||||||
yang_stmt *ys;
|
|
||||||
|
|
||||||
/* check absolute schema_nodeid */
|
/* check absolute schema_nodeid */
|
||||||
if (schema_nodeid[0] != '/'){
|
if (schema_nodeid[0] != '/'){
|
||||||
|
|
@ -2719,21 +2546,6 @@ yang_abs_schema_nodeid(yang_spec *yspec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ymod == NULL){ /* Try find id from topnode without prefix XXX remove?*/
|
|
||||||
if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){
|
|
||||||
clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((ymod = ys_module(ys)) == NULL){
|
|
||||||
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found2", prefix,id);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL &&
|
|
||||||
strcmp(yprefix->ys_argument, prefix) != 0){
|
|
||||||
clicon_err(OE_YANG, 0, "Module with id:\"%s:%s\" not found", prefix,id);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0)
|
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok: /* yres may not be set */
|
ok: /* yres may not be set */
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ yang_modules_state_get(clicon_handle h,
|
||||||
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
|
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
|
||||||
else
|
else
|
||||||
cprintf(cb,"<namespace></namespace>");
|
cprintf(cb,"<namespace></namespace>");
|
||||||
cprintf(cb, "<conformance-type>implement</conformance-type>");
|
/* This follows order in rfc 7895: feature, conformance-type, submodules */
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
||||||
switch(yc->ys_keyword){
|
switch(yc->ys_keyword){
|
||||||
|
|
@ -211,6 +211,14 @@ yang_modules_state_get(clicon_handle h,
|
||||||
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
|
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
|
||||||
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
|
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cprintf(cb, "<conformance-type>implement</conformance-type>");
|
||||||
|
yc = NULL;
|
||||||
|
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
||||||
|
switch(yc->ys_keyword){
|
||||||
case Y_SUBMODULE:
|
case Y_SUBMODULE:
|
||||||
cprintf(cb,"<submodule>");
|
cprintf(cb,"<submodule>");
|
||||||
cprintf(cb,"<name>%s</name>", yc->ys_argument);
|
cprintf(cb,"<name>%s</name>", yc->ys_argument);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ if [ $err -eq 0 ]; then
|
||||||
else
|
else
|
||||||
echo -e "\e[31mError"
|
echo -e "\e[31mError"
|
||||||
echo -ne "\e[0m"
|
echo -ne "\e[0m"
|
||||||
|
exit -1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ expectfn "$clixon_cli -1 -f $cfg delete interfaces" 0 "^$"
|
||||||
new "cli show configuration delete top"
|
new "cli show configuration delete top"
|
||||||
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$"
|
||||||
|
|
||||||
new "cli configure"
|
new "cli configure set interfaces"
|
||||||
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" 0 "^$"
|
||||||
|
|
||||||
new "cli show configuration"
|
new "cli show configuration"
|
||||||
|
|
|
||||||
116
test/test_compat.sh
Executable file
116
test/test_compat.sh
Executable file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Test of backward compatibility
|
||||||
|
# 1) Load <3.9 startup/running/extra files without namespaces - ensure it returns namespaces
|
||||||
|
#
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
# include err() and new() functions and creates $dir
|
||||||
|
. ./lib.sh
|
||||||
|
cfg=$dir/conf_startup.xml
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<config>
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
|
<CLICON_NETCONF_DIR>/usr/local/lib/$APPNAME/netconf</CLICON_NETCONF_DIR>
|
||||||
|
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||||
|
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
|
||||||
|
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
|
||||||
|
<CLICON_XML_SORT>true</CLICON_XML_SORT>
|
||||||
|
</config>
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
run(){
|
||||||
|
mode=$1
|
||||||
|
expect=$2
|
||||||
|
|
||||||
|
dbdir=$dir/db
|
||||||
|
cat <<EOF > $dbdir
|
||||||
|
<config>
|
||||||
|
<interfaces>
|
||||||
|
<interface>
|
||||||
|
<name>run</name>
|
||||||
|
<type>ex:eth</type>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
sudo mv $dbdir /usr/local/var/$APPNAME/running_db
|
||||||
|
|
||||||
|
cat <<EOF > $dbdir
|
||||||
|
<config>
|
||||||
|
<interfaces>
|
||||||
|
<interface>
|
||||||
|
<name>startup</name>
|
||||||
|
<type>ex:eth</type>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
sudo mv $dbdir /usr/local/var/$APPNAME/startup_db
|
||||||
|
|
||||||
|
cat <<EOF > $dir/config
|
||||||
|
<config>
|
||||||
|
<interfaces>
|
||||||
|
<interface>
|
||||||
|
<name>extra</name>
|
||||||
|
<type>ex:eth</type>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg -s $mode -c $dir/config"
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "start backend -f $cfg -s $mode -c $dir/config"
|
||||||
|
sudo $clixon_backend -f $cfg -s $mode -c $dir/config
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Check $mode"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply>$expect</rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
if [ $BE -eq 0 ]; then
|
||||||
|
exit # BE
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=`pgrep -u root -f clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
sudo clixon_backend -z -f $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "kill backend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
} # run
|
||||||
|
|
||||||
|
run running '<data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>run</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
||||||
|
run startup '<data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>startup</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
@ -50,9 +50,6 @@ EOF
|
||||||
|
|
||||||
xml='<config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>first-entry</c></y><y><a>1</a><b>3</b><c>second-entry</c></y><y><a>2</a><b>3</b><c>third-entry</c></y><d/><f><e>a</e><e>b</e><e>c</e></f><g>astring</g></x></config>'
|
xml='<config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>first-entry</c></y><y><a>1</a><b>3</b><c>second-entry</c></y><y><a>2</a><b>3</b><c>third-entry</c></y><d/><f><e>a</e><e>b</e><e>c</e></f><g>astring</g></x></config>'
|
||||||
|
|
||||||
# Without xmlns
|
|
||||||
xmlxxx='<config><x><y><a>1</a><b>2</b><c>first-entry</c></y><y><a>1</a><b>3</b><c>second-entry</c></y><y><a>2</a><b>3</b><c>third-entry</c></y><d/><f><e>a</e><e>b</e><e>c</e></f><g>astring</g></x></config>'
|
|
||||||
|
|
||||||
run(){
|
run(){
|
||||||
name=$1
|
name=$1
|
||||||
mydir=$dir/$name
|
mydir=$dir/$name
|
||||||
|
|
@ -72,7 +69,7 @@ run(){
|
||||||
expectmatch "$ret" $? "0" ""
|
expectmatch "$ret" $? "0" ""
|
||||||
|
|
||||||
new "datastore $name get"
|
new "datastore $name get"
|
||||||
expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
|
expectfn "$datastore $conf get /" 0 "^$xml$"
|
||||||
|
|
||||||
new "datastore $name put all remove"
|
new "datastore $name put all remove"
|
||||||
expectfn "$datastore $conf put remove <config/>" 0 ""
|
expectfn "$datastore $conf put remove <config/>" 0 ""
|
||||||
|
|
@ -87,7 +84,7 @@ run(){
|
||||||
# expectfn "$datastore $conf put merge $xml" 0 ""
|
# expectfn "$datastore $conf put merge $xml" 0 ""
|
||||||
|
|
||||||
new "datastore $name get"
|
new "datastore $name get"
|
||||||
expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
|
expectfn "$datastore $conf get /" 0 "^$xml$"
|
||||||
|
|
||||||
new "datastore $name put all delete"
|
new "datastore $name put all delete"
|
||||||
expectfn "$datastore $conf put remove <config/>" 0 ""
|
expectfn "$datastore $conf put remove <config/>" 0 ""
|
||||||
|
|
@ -100,7 +97,7 @@ run(){
|
||||||
expectmatch "$ret" $? "0" ""
|
expectmatch "$ret" $? "0" ""
|
||||||
|
|
||||||
new "datastore $name get"
|
new "datastore $name get"
|
||||||
expectfn "$datastore $conf get /" 0 "^$xmlxxx$"
|
expectfn "$datastore $conf get /" 0 "^$xml$"
|
||||||
|
|
||||||
new "datastore $name put top create"
|
new "datastore $name put top create"
|
||||||
expectfn "$datastore $conf put create <config><x/></config>" 0 "" # error
|
expectfn "$datastore $conf put create <config><x/></config>" 0 "" # error
|
||||||
|
|
|
||||||
|
|
@ -90,13 +90,13 @@ new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf enabled feature"
|
new "netconf enabled feature"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x>foo</x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon">foo</x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate enabled feature"
|
new "netconf validate enabled feature"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf disabled feature"
|
new "netconf disabled feature"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><A>foo</A></config></edit-config></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y xmlns="urn:example:clixon">foo</y></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>y</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# This test has been broken up into all different modules instead of one large
|
# This test has been broken up into all different modules instead of one large
|
||||||
# reply since the modules change so often
|
# reply since the modules change so often
|
||||||
|
|
@ -105,6 +105,13 @@ ret=$($clixon_netconf -qf $cfg -y $fyang<<EOF
|
||||||
<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>
|
<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
new "netconf modules-state header"
|
||||||
|
expect='^<rpc-reply><data><modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module><name>'
|
||||||
|
match=`echo "$ret" | grep -GZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
new "netconf module A"
|
new "netconf module A"
|
||||||
expect="<module><name>example</name><revision/><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type></module>"
|
expect="<module><name>example</name><revision/><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type></module>"
|
||||||
match=`echo "$ret" | grep -GZo "$expect"`
|
match=`echo "$ret" | grep -GZo "$expect"`
|
||||||
|
|
@ -135,8 +142,9 @@ if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Note order of features in ietf-netconf yang is alphabetically: candidate, startup, validate, xpath
|
||||||
new "netconf module ietf-netconf"
|
new "netconf module ietf-netconf"
|
||||||
expect="module><name>ietf-netconf</name><revision>2011-06-01</revision><namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace><feature>candidate</feature><feature>startup</feature><feature>validate</feature><feature>xpath</feature><conformance-type>implement</conformance-type></module>"
|
expect="<module><name>ietf-netconf</name><revision>2011-06-01</revision><namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace><feature>candidate</feature><feature>startup</feature><feature>validate</feature><feature>xpath</feature><conformance-type>implement</conformance-type></module>"
|
||||||
match=`echo "$ret" | grep -GZo "$expect"`
|
match=`echo "$ret" | grep -GZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
|
|
|
||||||
|
|
@ -122,44 +122,44 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set crypto to aes"
|
new "Set crypto to aes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><crypto>aes</crypto></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">aes</crypto></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf validate "
|
new "netconf validate "
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Set crypto to mc:aes"
|
new "Set crypto to mc:aes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>mc:aes</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:aes</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Set crypto to des:des3"
|
new "Set crypto to des:des3"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>des:des3</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Set crypto to mc:foo"
|
new "Set crypto to mc:foo"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>mc:foo</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">mc:foo</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Set crypto to des:des3 using xmlns"
|
new "Set crypto to des:des3 using xmlns"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto xmlns:des=\"urn:example:des\">des:des3</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:des="urn:example:des">des:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
# XXX this is not supported
|
# XXX this is not supported
|
||||||
#new "Set crypto to x:des3 using xmlns"
|
#new "Set crypto to x:des3 using xmlns"
|
||||||
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto xmlns:x=\"urn:example:des\">x:des3</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto" xmlns:x="urn:example:des">x:des3</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
#new "netconf validate"
|
new "netconf validate"
|
||||||
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Set crypto to foo:bar"
|
new "Set crypto to foo:bar"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><crypto>foo:bar</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><crypto xmlns="urn:example:my-crypto">foo:bar</crypto></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
|
||||||
|
|
@ -88,22 +88,19 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "leafref base config"
|
new "leafref base config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces>
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name><type>ex:eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface><interface><name>lo</name><type>ex:lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
<interface><name>eth0</name><type>ex:eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface>
|
|
||||||
<interface><name>lo</name><type>ex:lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface>
|
|
||||||
</interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "leafref get config"
|
new "leafref get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
||||||
|
|
||||||
new "leafref base commit"
|
new "leafref base commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "leafref get config"
|
new "leafref get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
||||||
|
|
||||||
new "leafref add wrong ref"
|
new "leafref add wrong ref"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "leafref validate"
|
new "leafref validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
@ -112,10 +109,10 @@ new "leafref discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "leafref add correct absref"
|
new "leafref add correct absref"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><default-address><absname>eth0</absname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><absname>eth0</absname></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "leafref add correct relref"
|
new "leafref add correct relref"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><default-address><relname>eth0</relname></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><default-address xmlns="urn:example:clixon"><relname>eth0</relname></default-address></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# XXX add address
|
# XXX add address
|
||||||
|
|
||||||
|
|
@ -123,7 +120,7 @@ new "leafref validate (ok)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
||||||
|
|
||||||
new "leafref delete leaf"
|
new "leafref delete leaf"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface operation="delete"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>'
|
||||||
|
|
||||||
new "leafref validate (should fail)"
|
new "leafref validate (should fail)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No such leaf</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
|
||||||
|
|
@ -81,19 +81,19 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "minmax: minimal"
|
new "minmax: minimal"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><a1><k>0</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: minimal validate ok"
|
new "minmax: minimal validate ok"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: maximal"
|
new "minmax: maximal"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>0</b1><b0>1</b0></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>0</b1><b0>1</b0></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "minmax: validate ok"
|
new "minmax: validate ok"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: empty"
|
new "minmax: empty"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c/></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"/></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# NYI
|
# NYI
|
||||||
if false; then
|
if false; then
|
||||||
|
|
@ -101,25 +101,25 @@ new "minmax: validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: no list"
|
new "minmax: no list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><b1>0</b1></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
new "minmax: validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: no leaf-list"
|
new "minmax: no leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><a1><k>0</k></a1></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
new "minmax: validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: Too large list"
|
new "minmax: Too large list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><a1><k>0</k></a1><a1><k>1</k></a1><a1><k>2</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><a1><k>1</k></a1><a1><k>2</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
new "minmax: validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "minmax: Too large leaf-list"
|
new "minmax: Too large leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c><a1><k>0</k></a1><b1>0</b1><b1>1</b1><b1>2</b1></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><b1>0</b1><b1>1</b1><b1>2</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
new "minmax: validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ EOF
|
||||||
# The groups are slightly modified from RFC8341 A.1
|
# The groups are slightly modified from RFC8341 A.1
|
||||||
# The rule-list is from A.2
|
# The rule-list is from A.2
|
||||||
RULES=$(cat <<EOF
|
RULES=$(cat <<EOF
|
||||||
<nacm>
|
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||||
<enable-nacm>false</enable-nacm>
|
<enable-nacm>false</enable-nacm>
|
||||||
<read-default>deny</read-default>
|
<read-default>deny</read-default>
|
||||||
<write-default>deny</write-default>
|
<write-default>deny</write-default>
|
||||||
|
|
@ -99,7 +99,7 @@ RULES=$(cat <<EOF
|
||||||
$NADMIN
|
$NADMIN
|
||||||
|
|
||||||
</nacm>
|
</nacm>
|
||||||
<x>0</x>
|
<x xmlns="urn:example:clixon">0</x>
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ new "restconf DELETE whole datastore"
|
||||||
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
|
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
|
||||||
|
|
||||||
new2 "auth get"
|
new2 "auth get"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" 'null
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null
|
||||||
'
|
'
|
||||||
|
|
||||||
new "auth set authentication config"
|
new "auth set authentication config"
|
||||||
|
|
@ -147,33 +147,33 @@ new2 "auth get (wrong passwd: access denied)"
|
||||||
expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
||||||
|
|
||||||
new2 "auth get (access)"
|
new2 "auth get (access)"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
#----------------Enable NACM
|
#----------------Enable NACM
|
||||||
|
|
||||||
new "enable nacm"
|
new "enable nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" ""
|
||||||
|
|
||||||
new2 "admin get nacm"
|
new2 "admin get nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "limited get nacm"
|
new2 "limited get nacm"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "guest get nacm"
|
new2 "guest get nacm"
|
||||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
new "admin edit nacm"
|
new "admin edit nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" ""
|
||||||
|
|
||||||
new2 "limited edit nacm"
|
new2 "limited edit nacm"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||||
|
|
||||||
new2 "guest edit nacm"
|
new2 "guest edit nacm"
|
||||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
|
||||||
|
|
@ -163,11 +163,11 @@ new "restconf DELETE whole datastore"
|
||||||
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
|
expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" ""
|
||||||
|
|
||||||
new2 "auth get"
|
new2 "auth get"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "Set x to 0"
|
new "Set x to 0"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 0}' http://localhost/restconf/data/x)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 0}' http://localhost/restconf/data/example:x)" ""
|
||||||
|
|
||||||
new2 "auth get (no user: access denied)"
|
new2 "auth get (no user: access denied)"
|
||||||
expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
||||||
|
|
@ -176,28 +176,28 @@ new2 "auth get (wrong passwd: access denied)"
|
||||||
expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
||||||
|
|
||||||
new2 "auth get (access)"
|
new2 "auth get (access)"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "admin get nacm"
|
new2 "admin get nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "limited get nacm"
|
new2 "limited get nacm"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "guest get nacm"
|
new2 "guest get nacm"
|
||||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
new "admin edit nacm"
|
new "admin edit nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" ""
|
||||||
|
|
||||||
new2 "limited edit nacm"
|
new2 "limited edit nacm"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||||
|
|
||||||
new2 "guest edit nacm"
|
new2 "guest edit nacm"
|
||||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
new "cli show conf as admin"
|
new "cli show conf as admin"
|
||||||
expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang show conf" 0 "^x 1;$"
|
expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang show conf" 0 "^x 1;$"
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ RULES=$(cat <<EOF
|
||||||
$NADMIN
|
$NADMIN
|
||||||
|
|
||||||
</nacm>
|
</nacm>
|
||||||
<x>0</x>
|
<x xmlns="urn:example:clixon">0</x>
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -158,10 +158,10 @@ new "commit it"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "enable nacm"
|
new "enable nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" ""
|
||||||
|
|
||||||
new2 "admin get nacm"
|
new2 "admin get nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
# Rule 1: deny-kill-session
|
# Rule 1: deny-kill-session
|
||||||
|
|
@ -186,7 +186,7 @@ new "deny-delete-config: limited fail (restconf) ok"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data)" ''
|
expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data)" ''
|
||||||
|
|
||||||
new2 "admin get nacm (should be null)"
|
new2 "admin get nacm (should be null)"
|
||||||
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/x)" 'null
|
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null
|
||||||
'
|
'
|
||||||
|
|
||||||
new "deny-delete-config: admin ok (restconf)"
|
new "deny-delete-config: admin ok (restconf)"
|
||||||
|
|
@ -200,14 +200,14 @@ new "commit it"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "enable nacm"
|
new "enable nacm"
|
||||||
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
|
expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" ""
|
||||||
|
|
||||||
# Rule 3: permit-edit-config
|
# Rule 3: permit-edit-config
|
||||||
new "permit-edit-config: limited ok restconf"
|
new "permit-edit-config: limited ok restconf"
|
||||||
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" ''
|
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" ''
|
||||||
|
|
||||||
new2 "permit-edit-config: guest fail restconf"
|
new2 "permit-edit-config: guest fail restconf"
|
||||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
|
||||||
|
|
@ -114,14 +114,13 @@ new "netconf get-config single quotes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc message-id='101' xmlns='urn:ietf:params:xml:ns:netconf:base:1.0'><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><data/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc message-id='101' xmlns='urn:ietf:params:xml:ns:netconf:base:1.0'><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><data/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Add subtree eth/0/0 using none which should not change anything"
|
new "Add subtree eth/0/0 using none which should not change anything"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "Check nothing added"
|
new "Check nothing added"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Add subtree eth/0/0 using none and create which should add eth/0/0"
|
new "Add subtree eth/0/0 using none and create which should add eth/0/0"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface operation="create"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
|
||||||
# Too many quotes, (single inside double inside single) need to fool bash
|
# Too many quotes, (single inside double inside single) need to fool bash
|
||||||
cat <<EOF > $tmp # new
|
cat <<EOF > $tmp # new
|
||||||
|
|
@ -129,22 +128,22 @@ cat <<EOF > $tmp # new
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
new "Check eth/0/0 added using xpath"
|
new "Check eth/0/0 added using xpath"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Re-create same eth/0/0 which should generate error"
|
new "Re-create same eth/0/0 which should generate error"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface operation="create"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error>'
|
||||||
|
|
||||||
new "Delete eth/0/0 using none config"
|
new "Delete eth/0/0 using none config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface operation="delete"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Check deleted eth/0/0 (non-presence container)"
|
new "Check deleted eth/0/0 (non-presence container)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Re-Delete eth/0/0 using none should generate error"
|
new "Re-Delete eth/0/0 using none should generate error"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface operation="delete"><name>eth/0/0</name><type>ex:eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error>'
|
||||||
|
|
||||||
new "netconf edit config"
|
new "netconf edit config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
# Too many quotes
|
# Too many quotes
|
||||||
cat <<EOF > $tmp # new
|
cat <<EOF > $tmp # new
|
||||||
|
|
@ -152,7 +151,7 @@ cat <<EOF > $tmp # new
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
new "netconf get config xpath"
|
new "netconf get config xpath"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^<rpc-reply><data><interfaces><interface><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# Too many quotes
|
# Too many quotes
|
||||||
cat <<EOF > $tmp # new
|
cat <<EOF > $tmp # new
|
||||||
|
|
@ -160,7 +159,7 @@ cat <<EOF > $tmp # new
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
new "netconf get config xpath parent"
|
new "netconf get config xpath parent"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" "^<rpc-reply><data><interfaces><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf validate missing type"
|
new "netconf validate missing type"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
||||||
|
|
@ -172,13 +171,13 @@ new "netconf get empty config2"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit extra xml"
|
new "netconf edit extra xml"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><extra/></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><extra/></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit config eth1"
|
new "netconf edit config eth1"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>ex:eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -187,32 +186,32 @@ new "netconf commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit config merge"
|
new "netconf edit config merge"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>ex:eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth2</name><type>ex:eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit ampersand encoding(<&): name:'eth&' type:'t<>'"
|
new "netconf edit ampersand encoding(<&): name:'eth&' type:'t<>'"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth&</name><type>t<></type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth&</name><type>t<></type></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get replaced config"
|
new "netconf get replaced config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth&</name><type>t<></type><enabled>true</enabled></interface><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth&</name><type>t<></type><enabled>true</enabled></interface><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "cli show configuration eth& - encoding tests"
|
new "cli show configuration eth& - encoding tests"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" 0 "interfaces interface eth& type t<>
|
expectfn "$clixon_cli -1 -f $cfg -y $fyang show conf cli" 0 "interfaces interface eth& type t<>
|
||||||
interfaces interface eth& enabled true"
|
interfaces interface eth& enabled true"
|
||||||
|
|
||||||
new "netconf edit CDATA"
|
new "netconf edit CDATA"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth/0/0</name><type>ex:eth</type><description><![CDATA[myeth&]]></description></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><type>ex:eth</type><description><![CDATA[myeth&]]></description></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
#new "netconf get CDATA"
|
#new "netconf get CDATA"
|
||||||
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/interfaces/interface[name='eth/0/0']/description\" /></get-config></rpc>]]>]]>" "<rpc-reply><data><interfaces><interface><name>eth/0/0</name><description><![CDATA[myeth&]]></description><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>"
|
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/interfaces/interface[name='eth/0/0']/description\" /></get-config></rpc>]]>]]>' '<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><description><![CDATA[myeth&]]></description><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit state operation should fail"
|
new "netconf edit state operation should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><state xmlns="urn:example:clixon"><op>42</op></state></config></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag>"
|
||||||
|
|
||||||
new "netconf get state operation"
|
new "netconf get state operation"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><state><op>42</op></state></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/state\"/></get></rpc>]]>]]>" '^<rpc-reply><data><state xmlns="urn:example:clixon"><op>42</op></state></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf lock/unlock"
|
new "netconf lock/unlock"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -234,7 +233,7 @@ new "copy startup"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get startup"
|
new "netconf get startup"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><interfaces><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf delete startup"
|
new "netconf delete startup"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -243,16 +242,16 @@ new "netconf check empty startup"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf rpc"
|
new "netconf rpc"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>' "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></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>'
|
||||||
|
|
||||||
new "netconf rpc without namespace (iterate kludge should work)"
|
#new "netconf rpc without namespace (iterate kludge should work)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
#expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
||||||
|
|
||||||
new "netconf empty rpc"
|
new "netconf empty rpc"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><ex:empty/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><empty xmlns="urn:example:clixon"/></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf client-side rpc"
|
new "netconf client-side rpc"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><ex:client-rpc><request>example</request></ex:client-rpc></rpc>]]>]]>" "^<rpc-reply><result>ok</result></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><client-rpc xmlns="urn:example:clixon"><request>example</request></client-rpc></rpc>]]>]]>' '^<rpc-reply><result xmlns="urn:example:clixon">ok</result></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
if [ $BE -eq 0 ]; then
|
if [ $BE -eq 0 ]; then
|
||||||
exit # BE
|
exit # BE
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ fyang=$dir/order.yang
|
||||||
|
|
||||||
dbdir=$dir/order
|
dbdir=$dir/order
|
||||||
|
|
||||||
new "Set up $dbdir"
|
|
||||||
rm -rf $dbdir
|
rm -rf $dbdir
|
||||||
if [ ! -d $dbdir ]; then
|
if [ ! -d $dbdir ]; then
|
||||||
mkdir $dbdir
|
mkdir $dbdir
|
||||||
|
|
@ -84,30 +83,32 @@ module example{
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
rm -f $dbdir/candidate_db
|
||||||
|
# alt
|
||||||
cat <<EOF > $dbdir/running_db
|
cat <<EOF > $dbdir/running_db
|
||||||
<config>
|
<config>
|
||||||
<y0>d</y0>
|
<y0 xmlns="urn:example:clixon">d</y0>
|
||||||
<y1>d</y1>
|
<y1 xmlns="urn:example:clixon">d</y1>
|
||||||
<y2><k>d</k><a>bar</a></y2>
|
<y2 xmlns="urn:example:clixon"><k>d</k><a>bar</a></y2>
|
||||||
<y3><k>d</k><a>bar</a></y3>
|
<y3 xmlns="urn:example:clixon"><k>d</k><a>bar</a></y3>
|
||||||
<y0>b</y0>
|
<y0 xmlns="urn:example:clixon">b</y0>
|
||||||
<y1>b</y1>
|
<y1 xmlns="urn:example:clixon">b</y1>
|
||||||
<c><d>hej</d></c>
|
<c xmlns="urn:example:clixon"><d>hej</d></c>
|
||||||
<y0>c</y0>
|
<y0 xmlns="urn:example:clixon">c</y0>
|
||||||
<y1>c</y1>
|
<y1 xmlns="urn:example:clixon">c</y1>
|
||||||
<y2><k>a</k><a>bar</a></y2>
|
<y2 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y2>
|
||||||
<y3><k>a</k><a>bar</a></y3>
|
<y3 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y3>
|
||||||
<l>hopp</l>
|
<l xmlns="urn:example:clixon">hopp</l>
|
||||||
<y0>a</y0>
|
<y0 xmlns="urn:example:clixon">a</y0>
|
||||||
<y1>a</y1>
|
<y1 xmlns="urn:example:clixon">a</y1>
|
||||||
<y2><k>c</k><a>bar</a></y2>
|
<y2 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y2>
|
||||||
<y3><k>c</k><a>bar</a></y3>
|
<y3 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y3>
|
||||||
<y2><k>b</k><a>bar</a></y2>
|
<y2 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y2>
|
||||||
<y3><k>b</k><a>bar</a></y3>
|
<y3 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y3>
|
||||||
</config>
|
</config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
new "test params: -f $cfg -y $fyang"
|
new "test params: -s running -f $cfg -y $fyang"
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
|
|
@ -123,20 +124,20 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check as file
|
# Check as file
|
||||||
new "verify running from start, should be: l,c,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if XML_SORT set to false"
|
new "verify running from start, should be: c,l,y0,y1,y2,y3; y1 and y3 sorted. Note this fails if CLICON_XML_SORT set to false"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><c><d>hej</d></c><l>hopp</l><y0>d</y0><y0>b</y0><y0>c</y0><y0>a</y0><y1>a</y1><y1>b</y1><y1>c</y1><y1>d</y1><y2><k>d</k><a>bar</a></y2><y2><k>a</k><a>bar</a></y2><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>bar</a></y2><y3><k>a</k><a>bar</a></y3><y3><k>b</k><a>bar</a></y3><y3><k>c</k><a>bar</a></y3><y3><k>d</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><c xmlns="urn:example:clixon"><d>hej</d></c><l xmlns="urn:example:clixon">hopp</l><y0 xmlns="urn:example:clixon">d</y0><y0 xmlns="urn:example:clixon">b</y0><y0 xmlns="urn:example:clixon">c</y0><y0 xmlns="urn:example:clixon">a</y0><y1 xmlns="urn:example:clixon">a</y1><y1 xmlns="urn:example:clixon">b</y1><y1 xmlns="urn:example:clixon">c</y1><y1 xmlns="urn:example:clixon">d</y1><y2 xmlns="urn:example:clixon"><k>d</k><a>bar</a></y2><y2 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y2><y2 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y2><y3 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y3><y3 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y3><y3 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y3><y3 xmlns="urn:example:clixon"><k>d</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "get each ordered-by user leaf-list"
|
new "get each ordered-by user leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='a']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>a</k><a>bar</a></y2></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='a']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "get each ordered-by user leaf-list"
|
new "get each ordered-by user leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='a']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>a</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='a']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:clixon"><k>a</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "get each ordered-by user leaf-list"
|
new "get each ordered-by user leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='b']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>b</k><a>bar</a></y2></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y2[k='b']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y2 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y2></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "get each ordered-by user leaf-list"
|
new "get each ordered-by user leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='b']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y3><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y3[k='b']\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><y3 xmlns="urn:example:clixon"><k>b</k><a>bar</a></y3></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "delete candidate"
|
new "delete candidate"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>none</default-operation><config operation="delete"/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -144,33 +145,33 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></
|
||||||
# LEAF_LISTS
|
# LEAF_LISTS
|
||||||
|
|
||||||
new "add two entries to leaf-list user order"
|
new "add two entries to leaf-list user order"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><y0>c</y0><y0>b</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:clixon">c</y0><y0 xmlns="urn:example:clixon">b</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "add one entry to leaf-list user order"
|
new "add one entry to leaf-list user order"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><y0>a</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:clixon">a</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf commit"
|
new "netconf commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "add one entry to leaf-list user order after commit"
|
new "add one entry to leaf-list user order after commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><y0>0</y0></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y0 xmlns="urn:example:clixon">0</y0></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf commit"
|
new "netconf commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "verify leaf-list user order in running (as entered)"
|
new "verify leaf-list user order in running (as entered)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"/y0\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y0>c</y0><y0>b</y0><y0>a</y0><y0>0</y0></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><running/></source><filter type="xpath" select="/y0"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y0 xmlns="urn:example:clixon">c</y0><y0 xmlns="urn:example:clixon">b</y0><y0 xmlns="urn:example:clixon">a</y0><y0 xmlns="urn:example:clixon">0</y0></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# LISTS
|
# LISTS
|
||||||
|
|
||||||
new "add two entries to list user order"
|
new "add two entries to list user order"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:clixon"><k>b</k><a>foo</a></y2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "add one entry to list user order"
|
new "add one entry to list user order"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><y2><k>a</k><a>fie</a></y2></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><y2 xmlns="urn:example:clixon"><k>a</k><a>fie</a></y2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "verify list user order (as entered)"
|
new "verify list user order (as entered)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/y2\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><y2><k>c</k><a>bar</a></y2><y2><k>b</k><a>foo</a></y2><y2><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/y2"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><y2 xmlns="urn:example:clixon"><k>c</k><a>bar</a></y2><y2 xmlns="urn:example:clixon"><k>b</k><a>foo</a></y2><y2 xmlns="urn:example:clixon"><k>a</k><a>fie</a></y2></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
if [ $BE -eq 0 ]; then
|
if [ $BE -eq 0 ]; then
|
||||||
exit # BE
|
exit # BE
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
|
||||||
sleep $RCWAIT
|
sleep $RCWAIT
|
||||||
|
|
||||||
new "generate 'large' config with $number list entries"
|
new "generate 'large' config with $number list entries"
|
||||||
echo -n "<rpc><edit-config><target><candidate/></target><config><x>" > $fconfig
|
echo -n "<rpc><edit-config><target><candidate/></target><config><x xmlns=\"urn:example:clixon\">" > $fconfig
|
||||||
for (( i=0; i<$number; i++ )); do
|
for (( i=0; i<$number; i++ )); do
|
||||||
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
|
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
|
||||||
done
|
done
|
||||||
|
|
@ -111,7 +111,7 @@ new "netconf commit large config again"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf add small (1 entry) config"
|
new "netconf add small (1 entry) config"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get $req small config"
|
new "netconf get $req small config"
|
||||||
time -p for (( i=0; i<$req; i++ )); do
|
time -p for (( i=0; i<$req; i++ )); do
|
||||||
|
|
@ -128,7 +128,7 @@ done
|
||||||
new "netconf add $req small config"
|
new "netconf add $req small config"
|
||||||
time -p for (( i=0; i<$req; i++ )); do
|
time -p for (( i=0; i<$req; i++ )); do
|
||||||
rnd=$(( ( RANDOM % $number ) ))
|
rnd=$(( ( RANDOM % $number ) ))
|
||||||
echo "<rpc><edit-config><target><candidate/></target><config><x><y><a>$rnd</a><b>$rnd</b></y></x></config></edit-config></rpc>]]>]]>"
|
echo "<rpc><edit-config><target><candidate/></target><config><x xmlns=\"urn:example:clixon\"><y><a>$rnd</a><b>$rnd</b></y></x></config></edit-config></rpc>]]>]]>"
|
||||||
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
|
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
|
||||||
|
|
||||||
new "netconf add $req restconf small config"
|
new "netconf add $req restconf small config"
|
||||||
|
|
@ -138,10 +138,10 @@ time -p for (( i=0; i<$req; i++ )); do
|
||||||
done
|
done
|
||||||
|
|
||||||
new "netconf get large config"
|
new "netconf get large config"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>'
|
||||||
|
|
||||||
new "generate large leaf-list config"
|
new "generate large leaf-list config"
|
||||||
echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x>" > $fconfig
|
echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x xmlns=\"urn:example:clixon\">" > $fconfig
|
||||||
for (( i=0; i<$number; i++ )); do
|
for (( i=0; i<$number; i++ )); do
|
||||||
echo -n "<c>$i</c>" >> $fconfig
|
echo -n "<c>$i</c>" >> $fconfig
|
||||||
done
|
done
|
||||||
|
|
@ -158,17 +158,17 @@ expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc
|
||||||
new "netconf add $req small leaf-list config"
|
new "netconf add $req small leaf-list config"
|
||||||
time -p for (( i=0; i<$req; i++ )); do
|
time -p for (( i=0; i<$req; i++ )); do
|
||||||
rnd=$(( ( RANDOM % $number ) ))
|
rnd=$(( ( RANDOM % $number ) ))
|
||||||
echo "<rpc><edit-config><target><candidate/></target><config><x><c>$rnd</c></x></config></edit-config></rpc>]]>]]>"
|
echo "<rpc><edit-config><target><candidate/></target><config><x xmlns=\"urn:example:clixon\"><c>$rnd</c></x></config></edit-config></rpc>]]>]]>"
|
||||||
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
|
done | $clixon_netconf -qf $cfg -y $fyang > /dev/null
|
||||||
|
|
||||||
new "netconf add small leaf-list config"
|
new "netconf add small leaf-list config"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><c>x</c></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><c>x</c></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf commit small leaf-list config"
|
new "netconf commit small leaf-list config"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get large leaf-list config"
|
new "netconf get large leaf-list config"
|
||||||
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><c>0</c><c>1</c>"
|
expecteof "time -f %e $clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><c>0</c><c>1</c>'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ module example{
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
|
# This is a fixed 'state' implemented in routing_backend. It is assumed to be always there
|
||||||
state='{"state": {"op": "42"}}'
|
state='{"example:state": {"op": "42"}}'
|
||||||
|
|
||||||
new "test params: -f $cfg -y $fyang"
|
new "test params: -f $cfg -y $fyang"
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
|
|
@ -102,9 +102,6 @@ sleep $RCWAIT
|
||||||
|
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
new2 "restconf rpc using POST json without mandatory element"
|
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}}
'
|
|
||||||
|
|
||||||
new2 "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
new2 "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||||
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
|
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
|
||||||
<Link rel='restconf' href='/restconf'/>
|
<Link rel='restconf' href='/restconf'/>
|
||||||
|
|
@ -119,13 +116,14 @@ new2 "restconf get restconf resource. RFC 8040 3.3 (xml)"
|
||||||
expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" '<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>
|
expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" '<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Should be alphabetically ordered
|
||||||
new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)"
|
new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)"
|
||||||
expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:client-rpc": null,"ietf-routing:fib-route": null,"ietf-routing:route-count": null,"example:empty": null}}
|
expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"example:empty": null,"example:client-rpc": null}{"ietf-routing:fib-route": null,"ietf-routing:route-count": null}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
||||||
expect='<operations><client-rpc xmlns="urn:example:clixon"/><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><route-count xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><empty xmlns="urn:example:clixon"/></operations>'
|
expect='<operations><empty xmlns="urn:example:clixon"/><client-rpc xmlns="urn:example:clixon"/><fib-route xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/><route-count xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"/></operations>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
|
|
@ -142,8 +140,8 @@ if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new2 "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895"
|
new2 "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)"
|
||||||
expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"module": [{"name": "ietf-routing","revision": "2014-10-26","namespace": "urn:ietf:params:xml:ns:yang:ietf-routing","conformance-type": "implement"}]}
|
expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-routing,2014-10-26/)" '{"ietf-yang-library:module": [{"name": "ietf-routing","revision": "2014-10-26","namespace": "urn:ietf:params:xml:ns:yang:ietf-routing","conformance-type": "implement"}]}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf options. RFC 8040 4.1"
|
new "restconf options. RFC 8040 4.1"
|
||||||
|
|
@ -154,132 +152,133 @@ expectfn "curl -s -I http://localhost/restconf/data" 0 "HTTP/1.1 200 OK"
|
||||||
#Content-Type: application/yang-data+json"
|
#Content-Type: application/yang-data+json"
|
||||||
|
|
||||||
new "restconf empty rpc"
|
new "restconf empty rpc"
|
||||||
expecteq "$(curl -s -X POST -d {\"input\":null} http://localhost/restconf/operations/example:empty)" ""
|
expecteq "$(curl -s -X POST -d {\"example:input\":null} http://localhost/restconf/operations/example:empty)" ""
|
||||||
|
|
||||||
new2 "restconf empty rpc with extra args (should fail)"
|
new2 "restconf empty rpc with extra args (should fail)"
|
||||||
expecteq "$(curl -s -X POST -d {\"input\":{\"extra\":null}} http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
|
expecteq "$(curl -s -X POST -d {\"example:input\":{\"extra\":null}} http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
|
||||||
|
|
||||||
new2 "restconf get empty config + state json"
|
new2 "restconf get empty config + state json"
|
||||||
expecteq "$(curl -sSG http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
|
expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf get empty config + state json + module"
|
new2 "restconf get empty config + state json + module"
|
||||||
expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"state": {"op": "42"}}
|
expecteq "$(curl -sSG http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf get empty config + state json with wrong module name"
|
new2 "restconf get empty config + state json with wrong module name"
|
||||||
expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No yang node found: badmodule:state"}}}
'
|
expecteq "$(curl -sSG http://localhost/restconf/data/badmodule:state)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "No such yang module: badmodule"}}}
'
|
||||||
|
|
||||||
new "restconf get empty config + state xml"
|
new "restconf get empty config + state xml"
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state)
|
||||||
expect="<state><op>42</op></state>"
|
expect='<state xmlns="urn:example:clixon"><op>42</op></state>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new2 "restconf get data/ json"
|
new2 "restconf get data/ json"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/state/op=42)" '{"op": "42"}
|
expecteq "$(curl -s -G http://localhost/restconf/data/example:state/op=42)" '{"example:op": "42"}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf get state operation eth0 xml"
|
new "restconf get state operation eth0 xml"
|
||||||
# Cant get shell macros to work, inline matching from lib.sh
|
# Cant get shell macros to work, inline matching from lib.sh
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state/op=42)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state/op=42)
|
||||||
expect="<op>42</op>"
|
expect='<op xmlns="urn:example:clixon">42</op>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new2 "restconf get state operation eth0 type json"
|
new2 "restconf get state operation eth0 type json"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/state/op=42)" '{"op": "42"}
|
expecteq "$(curl -s -G http://localhost/restconf/data/example:state/op=42)" '{"example:op": "42"}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf get state operation eth0 type xml"
|
new "restconf get state operation eth0 type xml"
|
||||||
# Cant get shell macros to work, inline matching from lib.sh
|
# Cant get shell macros to work, inline matching from lib.sh
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/state/op=42)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:state/op=42)
|
||||||
expect="<op>42</op>"
|
expect='<op xmlns="urn:example:clixon">42</op>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new2 "restconf GET datastore"
|
new2 "restconf GET datastore"
|
||||||
expecteq "$(curl -s -X GET http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
|
expecteq "$(curl -s -X GET http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
# Exact match
|
# Exact match
|
||||||
new "restconf Add subtree to datastore using POST"
|
new "restconf Add subtree to datastore using POST"
|
||||||
expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK'
|
expectfn 'curl -s -i -X POST -H "Accept: application/yang-data+json" -d {"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 'HTTP/1.1 200 OK'
|
||||||
|
|
||||||
new "restconf Re-add subtree which should give error"
|
new "restconf Re-add subtree which should give error"
|
||||||
expectfn 'curl -s -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
|
expectfn 'curl -s -X POST -d {"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}} http://localhost/restconf/data' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
|
||||||
|
|
||||||
# XXX Cant get this to work
|
# XXX Cant get this to work
|
||||||
#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
|
#expecteq "$(curl -s -X POST -d {\"interfaces\":{\"interface\":{\"name\":\"eth/0/0\",\"type\":\"ex:eth\",\"enabled\":true}}} http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}'
|
||||||
|
|
||||||
new "restconf Check interfaces eth/0/0 added"
|
new "restconf Check interfaces eth/0/0 added"
|
||||||
expectfn "curl -s -G http://localhost/restconf/data" 0 '{"interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]},"state": {"op": "42"}}
|
expectfn "curl -s -G http://localhost/restconf/data" 0 '"ietf-interfaces:interfaces": {"interface": \[{"name": "eth/0/0","type": "ex:eth","enabled": true}\]}'
|
||||||
'
|
|
||||||
|
|
||||||
new "restconf delete interfaces"
|
new "restconf delete interfaces"
|
||||||
expecteq $(curl -s -X DELETE http://localhost/restconf/data/interfaces) ""
|
expecteq $(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces) ""
|
||||||
|
|
||||||
new "restconf Check empty config"
|
new "restconf Check empty config"
|
||||||
expectfn "curl -sG http://localhost/restconf/data/state" 0 "$state"
|
expectfn "curl -sG http://localhost/restconf/data/example:state" 0 "$state"
|
||||||
|
|
||||||
|
# XXX: gives <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
|
# <interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
new "restconf Add interfaces subtree eth/0/0 using POST"
|
new "restconf Add interfaces subtree eth/0/0 using POST"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/interfaces' 0 ""
|
expectfn 'curl -s -X POST -d {"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}} http://localhost/restconf/data/ietf-interfaces:interfaces' 0 ""
|
||||||
# XXX cant get this to work
|
# XXX cant get this to work
|
||||||
#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" ""
|
#expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type\":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" ""
|
||||||
|
|
||||||
new2 "restconf Check eth/0/0 added config"
|
new2 "restconf Check eth/0/0 added config"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
|
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf Check eth/0/0 added state"
|
new2 "restconf Check eth/0/0 added state"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/state)" '{"state": {"op": "42"}}
|
expecteq "$(curl -s -G http://localhost/restconf/data/example:state)" '{"example:state": {"op": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf Re-post eth/0/0 which should generate error"
|
new2 "restconf Re-post eth/0/0 which should generate error"
|
||||||
expecteq "$(curl -s -X POST -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
expecteq "$(curl -s -X POST -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||||
|
|
||||||
new "Add leaf description using POST"
|
new "Add leaf description using POST"
|
||||||
expecteq "$(curl -s -X POST -d '{"description":"The-first-interface"}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
|
expecteq "$(curl -s -X POST -d '{"ietf-interfaces:description":"The-first-interface"}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
|
||||||
|
|
||||||
new "Add nothing using POST"
|
new "Add nothing using POST"
|
||||||
expectfn 'curl -s -X POST http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": " on line 1: syntax error at or before:'
|
expectfn 'curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0' 0 '"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": " on line 1: syntax error at or before:'
|
||||||
|
|
||||||
new2 "restconf Check description added"
|
new2 "restconf Check description added"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}}
|
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","description": "The-first-interface","type": "ex:eth","enabled": true}]}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf delete eth/0/0"
|
new "restconf delete eth/0/0"
|
||||||
expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
|
expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
|
||||||
|
|
||||||
new "Check deleted eth/0/0"
|
new "Check deleted eth/0/0"
|
||||||
expectfn 'curl -s -G http://localhost/restconf/data' 0 $state
|
expectfn 'curl -s -G http://localhost/restconf/data' 0 $state
|
||||||
|
|
||||||
new2 "restconf Re-Delete eth/0/0 using none should generate error"
|
new2 "restconf Re-Delete eth/0/0 using none should generate error"
|
||||||
expecteq "$(curl -s -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}}
'
|
expecteq "$(curl -s -X DELETE http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-missing","error-severity": "error","error-message": "Data does not exist; cannot delete resource"}}}
'
|
||||||
|
|
||||||
new "restconf Add subtree eth/0/0 using PUT"
|
new "restconf Add subtree eth/0/0 using PUT"
|
||||||
expecteq "$(curl -s -X PUT -d '{"interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0)" ""
|
expecteq "$(curl -s -X PUT -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"ex:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" ""
|
||||||
|
|
||||||
new2 "restconf get subtree"
|
new2 "restconf get subtree"
|
||||||
expecteq "$(curl -s -G http://localhost/restconf/data/interfaces)" '{"interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
|
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" '{"ietf-interfaces:interfaces": {"interface": [{"name": "eth/0/0","type": "ex:eth","enabled": true}]}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf rpc using POST json"
|
new2 "restconf rpc using POST json"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4","destination-address":{"address-family":"ipv6"}}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"},"source-protocol": "static"}}}
|
expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"routing-instance-name":"ipv4","destination-address":{"address-family":"ipv6"}}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-routing:output": {"route": {"address-family": "ipv4","next-hop": {"next-hop-list": "2.3.4.5"},"source-protocol": "static"}}}
|
||||||
'
|
'
|
||||||
|
|
||||||
# Cant get this to work due to quoting
|
# Cant get this to work due to quoting
|
||||||
#new2 "restconf rpc using POST wrong JSON"
|
#new2 "restconf rpc using POST wrong JSON"
|
||||||
#expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}
'
|
#expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"routing-instance-name":ipv4}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": " on line 1: syntax error at or before: i"}}}
'
|
||||||
|
|
||||||
new2 "restconf rpc using POST json without mandatory element"
|
new2 "restconf rpc using POST json without mandatory element"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}}
'
|
expecteq "$(curl -s -X POST -d '{"ietf-routing:input":{"wrongelement":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "wrongelement"},"error-severity": "error"}}}
'
|
||||||
|
|
||||||
new2 "restconf rpc non-existing rpc without namespace"
|
new2 "restconf rpc non-existing rpc without namespace"
|
||||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/kalle)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "kalle"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||||
|
|
@ -291,29 +290,29 @@ new2 "restconf rpc missing name"
|
||||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}}
'
|
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "Operation name expected"}}}
'
|
||||||
|
|
||||||
new2 "restconf rpc missing input"
|
new2 "restconf rpc missing input"
|
||||||
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "routing-instance-name"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
expecteq "$(curl -s -X POST -d '{}' http://localhost/restconf/operations/ietf-routing:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "rpc","error-tag": "malformed-message","error-severity": "error","error-message": "restconf RPC does not have input statement"}}}
'
|
||||||
|
|
||||||
new "restconf rpc using POST xml"
|
new "restconf rpc using POST xml"
|
||||||
ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)
|
ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"ietf-routing:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/ietf-routing:fib-route)
|
||||||
expect="<output><route><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></output>"
|
expect='<output xmlns="urn:ietf:params:xml:ns:yang:ietf-routing"><route><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></output>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new2 "restconf rpc using wrong prefix"
|
new2 "restconf rpc using wrong prefix"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
expecteq "$(curl -s -X POST -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/wrong:fib-route)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
||||||
|
|
||||||
new "restconf local client rpc using POST xml"
|
new "restconf local client rpc using POST xml"
|
||||||
ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc)
|
ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"example:input":{"request":"example"}}' http://localhost/restconf/operations/example:client-rpc)
|
||||||
expect="<output><result>ok</result></output>"
|
expect='<output xmlns="urn:example:clixon"><result>ok</result></output>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# XXX cant get -H to work
|
# XXX cant get -H to work
|
||||||
#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' '<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>'
|
#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"ietf-routing:input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/ietf-routing:fib-route' '<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>'
|
||||||
|
|
||||||
# Cant get shell macros to work, inline matching from lib.sh
|
# Cant get shell macros to work, inline matching from lib.sh
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,73 +72,82 @@ sleep $RCWAIT
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
new "restconf POST initial tree"
|
new "restconf POST initial tree"
|
||||||
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf GET datastore intial"
|
new "restconf GET datastore intial"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
||||||
|
|
||||||
new "restconf GET interface"
|
new "restconf GET interface subtree"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0" 0 '{"interface": \[{"name": "local0","type": "regular"}\]}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1/interface=local0" 0 '{"example:interface": \[{"name": "local0","type": "regular"}\]}'
|
||||||
|
|
||||||
|
new "restconf GET interface subtree xml"
|
||||||
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/example:cont1/interface=local0)
|
||||||
|
expect='<interface xmlns="urn:example:clixon"><name>local0</name><type>regular</type></interface>'
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
new "restconf GET if-type"
|
new "restconf GET if-type"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1/interface=local0/type" 0 '{"type": "regular"}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1/interface=local0/type" 0 '{"example:type": "regular"}'
|
||||||
|
|
||||||
new "restconf POST interface without mandatory type"
|
new "restconf POST interface without mandatory type"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/cont1' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "type"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST"}} http://localhost/restconf/data/example:cont1' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "type"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
||||||
|
|
||||||
new "restconf POST interface"
|
new "restconf POST interface"
|
||||||
expectfn 'curl -s -X POST -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1' 0 ""
|
expectfn 'curl -s -X POST -d {"example:interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/example:cont1' 0 ""
|
||||||
|
|
||||||
|
# XXX should it be example:interface?
|
||||||
new2 "restconf POST again"
|
new2 "restconf POST again"
|
||||||
expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/cont1)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
expecteq "$(curl -s -X POST -d '{"interface":{"name":"TEST","type":"eth0"}}' http://localhost/restconf/data/example:cont1)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||||
|
|
||||||
new2 "restconf POST from top"
|
new2 "restconf POST from top"
|
||||||
expecteq "$(curl -s -X POST -d '{"cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}' http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "data-exists","error-severity": "error","error-message": "Data already exists; cannot create new resource"}}}
'
|
||||||
|
|
||||||
new "restconf DELETE"
|
new "restconf DELETE"
|
||||||
expectfn 'curl -s -X DELETE http://localhost/restconf/data/cont1' 0 ""
|
expectfn 'curl -s -X DELETE http://localhost/restconf/data/example:cont1' 0 ""
|
||||||
|
|
||||||
new "restconf GET null datastore"
|
new "restconf GET null datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
|
||||||
|
|
||||||
new "restconf POST initial tree"
|
new "restconf POST initial tree"
|
||||||
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X POST -d {"example:cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf GET initial tree"
|
new "restconf GET initial tree"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
||||||
|
|
||||||
new "restconf DELETE whole datastore"
|
new "restconf DELETE whole datastore"
|
||||||
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X DELETE http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf GET null datastore"
|
new "restconf GET null datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 'null'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 'null'
|
||||||
|
|
||||||
new "restconf PUT initial datastore"
|
new "restconf PUT initial datastore"
|
||||||
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X PUT -d {"data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf GET datastore"
|
new "restconf GET datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "regular"}\]}}'
|
||||||
|
|
||||||
new "restconf PUT replace datastore"
|
new "restconf PUT replace datastore"
|
||||||
expectfn 'curl -s -X PUT -d {"data":{"cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X PUT -d {"data":{"example:cont2":{"name":"foo"}}} http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf GET replaced datastore"
|
new "restconf GET replaced datastore"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont2" 0 '{"cont2": {"name": "foo"}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont2" 0 '{"example:cont2": {"name": "foo"}}'
|
||||||
|
|
||||||
new "restconf PUT initial datastore again"
|
new "restconf PUT initial datastore again"
|
||||||
expectfn 'curl -s -X PUT -d {"data":{"cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
|
expectfn 'curl -s -X PUT -d {"data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}} http://localhost/restconf/data' 0 ""
|
||||||
|
|
||||||
new "restconf PUT change interface"
|
new "restconf PUT change interface"
|
||||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/cont1/interface=local0' 0 ""
|
expectfn 'curl -s -X PUT -d {"interface":{"name":"local0","type":"atm0"}} http://localhost/restconf/data/example:cont1/interface=local0' 0 ""
|
||||||
|
|
||||||
new "restconf GET datastore atm"
|
new "restconf GET datastore atm"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/cont1" 0 '{"cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/example:cont1" 0 '{"example:cont1": {"interface": \[{"name": "local0","type": "atm0"}\]}}'
|
||||||
|
|
||||||
new "restconf PUT add interface"
|
new "restconf PUT add interface"
|
||||||
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 ""
|
expectfn 'curl -s -X PUT -d {"interface":{"name":"TEST","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 ""
|
||||||
|
|
||||||
new "restconf PUT change key error"
|
new "restconf PUT change key error"
|
||||||
expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
expectfn 'curl -is -X PUT -d {"interface":{"name":"ALPHA","type":"eth0"}} http://localhost/restconf/data/example:cont1/interface=TEST' 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "api-path keys do not match data keys"}}}'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
|
||||||
|
|
@ -53,50 +53,70 @@ sleep $RCWAIT
|
||||||
new "rpc tests"
|
new "rpc tests"
|
||||||
|
|
||||||
# 1.First some positive tests vary media types
|
# 1.First some positive tests vary media types
|
||||||
#
|
# extra complex because pattern matching on return haders
|
||||||
|
new "restconf empty rpc"
|
||||||
|
ret=$(curl -is -X POST http://localhost/restconf/operations/example:empty)
|
||||||
|
expect="204 No Content"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "netconf empty rpc"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><empty xmlns="urn:example:clixon"/></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new2 "restconf example rpc json/json default - no http media headers"
|
new2 "restconf example rpc json/json default - no http media headers"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}}
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":0}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf example rpc json/json change y default"
|
new2 "restconf example rpc json/json change y default"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "99"}}
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0","y":"99"}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "99"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf example rpc json/json"
|
new2 "restconf example rpc json/json"
|
||||||
# XXX example:input example:output
|
# XXX example:input example:output
|
||||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}}
|
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Content-Type: application/yang-data+json' -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf example rpc xml/json"
|
new2 "restconf example rpc xml/json"
|
||||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '{"output": {"x": "0","y": "42"}}
|
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Content-Type: application/yang-data+json' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '{"example:output": {"x": "0","y": "42"}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf example rpc json/xml"
|
new2 "restconf example rpc json/xml"
|
||||||
# <output xmlns="rn:example:clixon"
|
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '<output xmlns="urn:example:clixon"><x>0</x><y>42</y></output>
|
||||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:example)" '<output><x>0</x><y>42</y></output>
|
|
||||||
'
|
'
|
||||||
|
|
||||||
new2 "restconf example rpc xml/xml"
|
new2 "restconf example rpc xml/xml"
|
||||||
# <output xmlns="rn:example:clixon"
|
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '<output xmlns="urn:example:clixon"><x>0</x><y>42</y></output>
|
||||||
expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' http://localhost/restconf/operations/example:example)" '<output><x>0</x><y>42</y></output>
|
|
||||||
'
|
'
|
||||||
|
|
||||||
new "netconf example rpc"
|
new "netconf example rpc"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example><x>0</x></example></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x>0</x><y>42</y></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# 2. Then error cases
|
# 2. Then error cases
|
||||||
#
|
#
|
||||||
|
new "restconf empy rpc with null input"
|
||||||
|
ret=$(curl -is -X POST -d '{"example:input":null}' http://localhost/restconf/operations/example:empty)
|
||||||
|
expect="204 No Content"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new2 "restconf empy rpc with input x"
|
||||||
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":0}}' http://localhost/restconf/operations/example:empty)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "x"},"error-severity": "error"}}}
'
|
||||||
|
|
||||||
new2 "restconf omit mandatory"
|
new2 "restconf omit mandatory"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":null}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "x"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
|
||||||
|
|
||||||
new2 "restconf add extra"
|
new2 "restconf add extra"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0","extra":"0"}}' http://localhost/restconf/operations/example:example)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "unknown-element","error-info": {"bad-element": "extra"},"error-severity": "error"}}}
'
|
||||||
|
|
||||||
new2 "restconf wrong method"
|
new2 "restconf wrong method"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||||
|
|
||||||
new2 "restconf edit-config missing mandatory"
|
new2 "restconf edit-config missing mandatory"
|
||||||
expecteq "$(curl -s -X POST -d '{"input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
||||||
|
|
||||||
new "netconf kill-session missing session-id mandatory"
|
new "netconf kill-session missing session-id mandatory"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ run(){
|
||||||
dbdir=$dir/db
|
dbdir=$dir/db
|
||||||
cat <<EOF > $dbdir
|
cat <<EOF > $dbdir
|
||||||
<config>
|
<config>
|
||||||
<interfaces>
|
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
<interface>
|
<interface>
|
||||||
<name>run</name>
|
<name>run</name>
|
||||||
<type>ex:eth</type>
|
<type>ex:eth</type>
|
||||||
|
|
@ -53,7 +53,7 @@ EOF
|
||||||
|
|
||||||
cat <<EOF > $dbdir
|
cat <<EOF > $dbdir
|
||||||
<config>
|
<config>
|
||||||
<interfaces>
|
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
<interface>
|
<interface>
|
||||||
<name>startup</name>
|
<name>startup</name>
|
||||||
<type>ex:eth</type>
|
<type>ex:eth</type>
|
||||||
|
|
@ -65,7 +65,7 @@ EOF
|
||||||
|
|
||||||
cat <<EOF > $dir/config
|
cat <<EOF > $dir/config
|
||||||
<config>
|
<config>
|
||||||
<interfaces>
|
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||||
<interface>
|
<interface>
|
||||||
<name>extra</name>
|
<name>extra</name>
|
||||||
<type>ex:eth</type>
|
<type>ex:eth</type>
|
||||||
|
|
@ -104,8 +104,8 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
run init '<data/>'
|
run init '<data/>'
|
||||||
run none '<data><interfaces><interface><name>run</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
run none '<data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>run</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
||||||
run running '<data><interfaces><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>run</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
run running '<data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>run</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
||||||
run startup '<data><interfaces><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>startup</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
run startup '<data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>extra</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>ex:loopback</type><enabled>true</enabled></interface><interface><name>startup</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces></data>'
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ EOF
|
||||||
# using reportingEntity (rfc5277) not reporting-entity (rfc8040)
|
# using reportingEntity (rfc5277) not reporting-entity (rfc8040)
|
||||||
cat <<EOF > $fyang
|
cat <<EOF > $fyang
|
||||||
module example {
|
module example {
|
||||||
namespace "http://example.com/event/1.0";
|
namespace "urn:example:clixon";
|
||||||
prefix ex;
|
prefix ex;
|
||||||
organization "Example, Inc.";
|
organization "Example, Inc.";
|
||||||
contact "support at example.com";
|
contact "support at example.com";
|
||||||
|
|
@ -130,35 +130,35 @@ sleep $RCWAIT
|
||||||
new "1. Netconf RFC5277 stream testing"
|
new "1. Netconf RFC5277 stream testing"
|
||||||
# 1.1 Stream discovery
|
# 1.1 Stream discovery
|
||||||
new "netconf event stream discovery RFC5277 Sec 3.2.5"
|
new "netconf event stream discovery RFC5277 Sec 3.2.5"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="netconf/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support></stream></streams></netconf></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "netconf event stream discovery RFC8040 Sec 6.2"
|
new "netconf event stream discovery RFC8040 Sec 6.2"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams" xmlns="urn:ietf:params:xml:ns:netmod:notification"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support><access><encoding>xml</encoding><location>https://localhost/streams/EXAMPLE</location></access></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="restconf-state/streams"/></get></rpc>]]>]]>' '<rpc-reply><data><restconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><streams><stream><name>EXAMPLE</name><description>Example event stream</description><replay-support>true</replay-support><access><encoding>xml</encoding><location>https://localhost/streams/EXAMPLE</location></access></stream></streams></restconf-state></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
#
|
#
|
||||||
# 1.2 Netconf stream subscription
|
# 1.2 Netconf stream subscription
|
||||||
new "netconf EXAMPLE subscription"
|
new "netconf EXAMPLE subscription"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||||
|
|
||||||
new "netconf subscription with empty startTime"
|
new "netconf subscription with empty startTime"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime/></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime/></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||||
|
|
||||||
new "netconf EXAMPLE subscription with simple filter"
|
new "netconf EXAMPLE subscription with simple filter"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><filter type="xpath" select="event"/></create-subscription></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||||
|
|
||||||
new "netconf EXAMPLE subscription with filter classifier"
|
new "netconf EXAMPLE subscription with filter classifier"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><filter type=\"xpath\" select=\"event[event-class='fault']\"/></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' $NCWAIT
|
||||||
|
|
||||||
new "netconf NONEXIST subscription"
|
new "netconf NONEXIST subscription"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>NONEXIST</stream></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such stream</error-message></rpc-error></rpc-reply>]]>]]>$' $NCWAIT
|
||||||
|
|
||||||
new "netconf EXAMPLE subscription with wrong date"
|
new "netconf EXAMPLE subscription with wrong date"
|
||||||
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Invalid time: kallekaka</error-message></rpc-error></rpc-reply>]]>]]>$' 0
|
expectwait "$clixon_netconf -qf $cfg -y $fyang" '<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream><startTime>kallekaka</startTime></create-subscription></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>startTime</bad-element></error-info><error-severity>error</error-severity><error-message>Invalid time: kallekaka</error-message></rpc-error></rpc-reply>]]>]]>$' 0
|
||||||
|
|
||||||
#new "netconf EXAMPLE subscription with replay"
|
#new "netconf EXAMPLE subscription with replay"
|
||||||
#NOW=$(date +"%Y-%m-%dT%H:%M:%S")
|
#NOW=$(date +"%Y-%m-%dT%H:%M:%S")
|
||||||
#sleep 10
|
#sleep 10
|
||||||
#expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription><stream>EXAMPLE</stream><startTime>$NOW</startTime></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' 10
|
#expectwait "$clixon_netconf -qf $cfg -y $fyang" "<rpc><create-subscription xmlns=\"urn:ietf:params:xml:ns:netmod:notification\"><stream>EXAMPLE</stream><startTime>$NOW</startTime></create-subscription></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]><notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>20' 10
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -166,11 +166,11 @@ sleep 2
|
||||||
new "2. Restconf RFC8040 stream testing"
|
new "2. Restconf RFC8040 stream testing"
|
||||||
# 2.1 Stream discovery
|
# 2.1 Stream discovery
|
||||||
new "restconf event stream discovery RFC8040 Sec 6.2"
|
new "restconf event stream discovery RFC8040 Sec 6.2"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"streams": {"stream": \[{"name": "EXAMPLE","description": "Example event stream","replay-support": true,"access": \[{"encoding": "xml","location": "https://localhost/streams/EXAMPLE"}\]}\]}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams" 0 '{"ietf-restconf-monitoring:streams": {"stream": \[{"name": "EXAMPLE","description": "Example event stream","replay-support": true,"access": \[{"encoding": "xml","location": "https://localhost/streams/EXAMPLE"}\]}\]}'
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
new "restconf subscribe RFC8040 Sec 6.3, get location"
|
new "restconf subscribe RFC8040 Sec 6.3, get location"
|
||||||
expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location" 0 '{"location": "https://localhost/streams/EXAMPLE"}'
|
expectfn "curl -s -X GET http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location" 0 '{"ietf-restconf-monitoring:location": "https://localhost/streams/EXAMPLE"}'
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
# Restconf stream subscription RFC8040 Sec 6.3
|
# Restconf stream subscription RFC8040 Sec 6.3
|
||||||
|
|
@ -246,13 +246,12 @@ if [ $nr -lt 9 -o $nr -gt 14 ]; then
|
||||||
err 10 "$nr"
|
err 10 "$nr"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Try parallell
|
# Try parallell
|
||||||
# start background job
|
# start background job
|
||||||
curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null &
|
curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no-cache" -H "Connection: keep-alive" "http://localhost/streams/EXAMPLE" > /dev/null &
|
||||||
PID=$!
|
PID=$!
|
||||||
|
|
||||||
new "Start subscription in parallell"
|
new "Start subscriptions in parallell"
|
||||||
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8)
|
ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8)
|
||||||
expect="data: <notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\"><eventTime>${DATE}T[0-9:.]*</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event>"
|
expect="data: <notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\"><eventTime>${DATE}T[0-9:.]*</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event>"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ new "cli set transitive string error"
|
||||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle 9xx" 255 "^$"
|
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle 9xx" 255 "^$"
|
||||||
|
|
||||||
new "netconf set transitive string error"
|
new "netconf set transitive string error"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><talle>9xx</talle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><c xmlns="urn:example:clixon"><talle>9xx</talle></c></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>"
|
||||||
|
|
||||||
new "netconf validate should fail"
|
new "netconf validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>talle</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail: "9xx" does not match \[a-z\]\[0-9\]\*</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>talle</bad-element></error-info><error-severity>error</error-severity><error-message>regexp match fail: "9xx" does not match \[a-z\]\[0-9\]\*</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
@ -241,7 +241,7 @@ new "cli set transitive union error int"
|
||||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 55" 255 ""
|
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c ulle 55" 255 ""
|
||||||
|
|
||||||
new "netconf set transitive union error int"
|
new "netconf set transitive union error int"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><c><ulle>55</ulle></c></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><c xmlns="urn:example:clixon"><ulle>55</ulle></c></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>"
|
||||||
|
|
||||||
new "netconf validate should fail"
|
new "netconf validate should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>ulle</bad-element></error-info><error-severity>error</error-severity><error-message>'55' does not match enumeration</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>ulle</bad-element></error-info><error-severity>error</error-severity><error-message>'55' does not match enumeration</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -276,7 +276,7 @@ new "netconf validate ok"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf set ab wrong"
|
new "netconf set ab wrong"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><list><ip>a.b& c.d</ip></list></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><list xmlns="urn:example:clixon"><ip>a.b& c.d</ip></list></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate"
|
new "netconf validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
||||||
|
|
@ -298,7 +298,7 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits create" 0 "^$"
|
||||||
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits \"create read\"" 0 "^$"
|
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set mbits \"create read\"" 0 "^$"
|
||||||
|
|
||||||
new "netconf bits two values"
|
new "netconf bits two values"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><mbits>create read</mbits></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><mbits xmlns="urn:example:clixon">create read</mbits></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "cli bits validate"
|
new "cli bits validate"
|
||||||
expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$"
|
expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$"
|
||||||
|
|
|
||||||
|
|
@ -106,16 +106,16 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "when: add static route"
|
new "when: add static route"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><whenex><type>static</type><name>r1</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "when: validate ok"
|
new "when: validate ok"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "when: add direct route"
|
new "when: add direct route"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><whenex><type>direct</type><name>r2</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "when get config"
|
new "when get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><whenex><type>direct</type><name>r2</name><static-routes/></whenex><whenex><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><whenex xmlns="urn:example:clixon"><type>direct</type><name>r2</name><static-routes/></whenex><whenex xmlns="urn:example:clixon"><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "when: validate fail"
|
new "when: validate fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -124,19 +124,19 @@ new "when: discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: add interface"
|
new "must: add interface"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>ethernet</ifType><ifMTU>1500</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>1500</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: validate ok"
|
new "must: validate ok"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: add atm interface"
|
new "must: add atm interface"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>atm</ifType><ifMTU>32</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: atm validate fail"
|
new "must: atm validate fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An ATM MTU must be 64 .. 17966</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: add eth interface"
|
new "must: add eth interface"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><interface><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interface xmlns="urn:example:clixon"><ifType>ethernet</ifType><ifMTU>989</ifMTU></interface></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "must: eth validate fail"
|
new "must: eth validate fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>An Ethernet MTU must be 1500</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||||
|
|
|
||||||
|
|
@ -160,13 +160,13 @@ new "cli defined extension"
|
||||||
expectfn "$clixon_cli -1f $cfg -y $fyang show version" 0 "3."
|
expectfn "$clixon_cli -1f $cfg -y $fyang show version" 0 "3."
|
||||||
|
|
||||||
new "empty values in leaf-list"
|
new "empty values in leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><f><e>a</e></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e>a</e></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "empty values in leaf-list2"
|
new "empty values in leaf-list2"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><f><e/></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e/></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get config"
|
new "netconf get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e/><e>a</e></f></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e/><e>a</e></f></x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -182,7 +182,7 @@ new "netconf schema resource, RFC 7895"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace><conformance-type>implement</conformance-type></module>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get><filter type="xpath" select="modules-state/module" xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"/></get></rpc>]]>]]>' '<module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace><conformance-type>implement</conformance-type></module>'
|
||||||
|
|
||||||
new "netconf edit config"
|
new "netconf edit config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf commit"
|
new "netconf commit"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -192,33 +192,34 @@ new "netconf commit 2nd"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get config xpath"
|
new "netconf get config xpath"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2][c=5]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2][c=5]\"/></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y></x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf edit leaf-list"
|
new "netconf edit leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get leaf-list"
|
new "netconf get leaf-list"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f/e\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/f/e"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get leaf-list path"
|
new "netconf get leaf-list path"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e='hej']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e='hej']\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><x xmlns=\"urn:example:clixon\"><f><e>hej</e><e>hopp</e></f></x></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get (should be some)"
|
new "netconf get (should be some)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x>"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>5</c><val>one</val></y><d/></x>'
|
||||||
|
|
||||||
new "cli set leaf-list"
|
new "cli set leaf-list"
|
||||||
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
|
expectfn "$clixon_cli -1f $cfg -y $fyang set x f e foo" 0 ""
|
||||||
|
|
||||||
new "cli show leaf-list"
|
new "cli show leaf-list"
|
||||||
expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "<e>foo</e>"
|
expectfn "$clixon_cli -1f $cfg -y $fyang show xpath /x/f/e" 0 "<e>foo</e>"
|
||||||
|
|
||||||
new "netconf set state data (not allowed)"
|
new "netconf set state data (not allowed)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><state xmlns="urn:example:clixon"><op>42</op></state></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>State data not allowed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf set presence and not present"
|
new "netconf set presence and not present"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get presence only"
|
new "netconf get presence only"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/presence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><x><presence/></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/presence"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><x xmlns="urn:example:clixon"><presence/></x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get presence only"
|
new "netconf get presence only"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/nopresence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/x/nopresence"/></get-config></rpc>]]>]]>' "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -227,7 +228,7 @@ new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf anyxml"
|
new "netconf anyxml"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><any><foo><bar a=\"nisse\"/></foo></any></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><any><foo><bar a="nisse"/></foo></any></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate anyxml"
|
new "netconf validate anyxml"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
@ -237,28 +238,28 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><can
|
||||||
|
|
||||||
# Check 3-keys
|
# Check 3-keys
|
||||||
new "netconf add one 3-key entry"
|
new "netconf add one 3-key entry"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf check add one 3-key"
|
new "netconf check add one 3-key"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y></x></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "netconf add another (with same 1st key)"
|
new "netconf add another (with same 1st key)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf check add another"
|
new "netconf check add another"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x><y><a>1</a><b>1</b><c>1</c><val>one</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>one</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "netconf replace first"
|
new "netconf replace first"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>1</b><c>1</c><val>replace</val></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf check replace"
|
new "netconf check replace"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x><y><a>1</a><b>1</b><c>1</c><val>replace</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>1</b><c>1</c><val>replace</val></y><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "netconf delete first"
|
new "netconf delete first"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x><y operation="remove"><a>1</a><b>1</b><c>1</c></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon"><y operation="remove"><a>1</a><b>1</b><c>1</c></y></x></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf check delete"
|
new "netconf check delete"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '<rpc-reply><data><x xmlns="urn:example:clixon"><y><a>1</a><b>2</b><c>1</c><val>two</val></y></x></data></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
# clear db for next test
|
# clear db for next test
|
||||||
new "netconf delete candidate"
|
new "netconf delete candidate"
|
||||||
|
|
@ -268,10 +269,10 @@ new "netconf commit empty candidate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconfig config submodule"
|
new "netconfig config submodule"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><edit-config><target><candidate/></target><config><mylist><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf submodule get config"
|
new "netconf submodule get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><mylist><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><mylist xmlns="urn:example:clixon"><x>a</x><subm-container><subm-leaf>foo</subm-leaf></subm-container></mylist></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf submodule validate"
|
new "netconf submodule validate"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ cat <<EOF > $fyang1
|
||||||
module $APPNAME{
|
module $APPNAME{
|
||||||
prefix ex;
|
prefix ex;
|
||||||
revision 2018-12-02;
|
revision 2018-12-02;
|
||||||
namespace "urn:example:example";
|
namespace "urn:example:clixon";
|
||||||
leaf newex{
|
leaf newex{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ cat <<EOF > $fyang2
|
||||||
module $APPNAME{
|
module $APPNAME{
|
||||||
prefix ex;
|
prefix ex;
|
||||||
revision 2018-01-01;
|
revision 2018-01-01;
|
||||||
namespace "urn:example:example";
|
namespace "urn:example:clixon";
|
||||||
leaf oldex{
|
leaf oldex{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ cat <<EOF > $fyang3
|
||||||
module other{
|
module other{
|
||||||
prefix oth;
|
prefix oth;
|
||||||
revision 2018-01-01;
|
revision 2018-01-01;
|
||||||
namespace "urn:example:example2";
|
namespace "urn:example:clixon2";
|
||||||
leaf other{
|
leaf other{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
|
@ -85,13 +85,13 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "1. Set newex"
|
new "1. Set newex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set oldex should fail (since oldex is in old revision and only the new is loaded)"
|
new "Set oldex should fail (since oldex is in old revision and only the new is loaded)"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>oldex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other should fail"
|
new "Set other should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>other</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
if [ $BE -eq 0 ]; then
|
if [ $BE -eq 0 ]; then
|
||||||
exit # BE
|
exit # BE
|
||||||
|
|
@ -137,13 +137,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set oldex"
|
new "Set oldex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set newex should fail"
|
new "Set newex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>newex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other should fail"
|
new "Set other should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Validation failed</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>other</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -181,13 +181,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set newex"
|
new "Set newex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set oldex should fail"
|
new "Set oldex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>oldex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other should fail"
|
new "Set other should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>other</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -226,13 +226,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set oldex"
|
new "Set oldex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set newex should fail"
|
new "Set newex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>newex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other should fail"
|
new "Set other should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>other</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -270,13 +270,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set newex"
|
new "Set newex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set oldex should fail"
|
new "Set oldex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>oldex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other"
|
new "Set other"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -315,13 +315,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set oldex"
|
new "Set oldex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set newex should fail"
|
new "Set newex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>newex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
new "Set other"
|
new "Set other"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -362,13 +362,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set oldex"
|
new "Set oldex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set newex should fail"
|
new "Set newex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>newex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other"
|
new "Set other"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
@ -408,13 +408,13 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "Set oldex"
|
new "Set oldex"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex>str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><oldex xmlns="urn:example:clixon">str</oldex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set newex should fail"
|
new "Set newex should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex>str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><newex xmlns="urn:example:clixon">str</newex></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>newex</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Set other should fail"
|
new "Set other should fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other>str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><other xmlns="urn:example:clixon2">str</other></config></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>other</bad-element></error-info><error-severity>error</error-severity><error-message>Unassigned yang spec</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Yang specifics: multi-keys and empty type
|
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
# include err() and new() functions and creates $dir
|
# test two modules example1 and example2 with overlapping statements x.
|
||||||
|
# x is leaf in example1 and list on example2.
|
||||||
|
# Test netconf and restconf
|
||||||
|
# BTW, this is not supported in generated CLI
|
||||||
|
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
||||||
cfg=$dir/conf_yang.xml
|
cfg=$dir/conf_yang.xml
|
||||||
fyang=$dir/example.yang
|
fyang1=$dir/example1.yang
|
||||||
fyang2=$dir/example2.yang
|
fyang2=$dir/example2.yang
|
||||||
|
|
||||||
# <CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
|
# <CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
|
||||||
|
|
@ -18,17 +22,35 @@ cat <<EOF > $cfg
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||||
|
<CLICON_XML_NS_STRICT>true</CLICON_XML_NS_STRICT> <!-- Must be strict -->
|
||||||
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
|
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
|
||||||
</config>
|
</config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
cat <<EOF > $fyang1
|
||||||
|
module example1{
|
||||||
|
yang-version 1.1;
|
||||||
|
prefix ex1;
|
||||||
|
namespace "urn:example:clixon1";
|
||||||
|
import ietf-routing {
|
||||||
|
description "defines fib-route";
|
||||||
|
prefix rt;
|
||||||
|
}
|
||||||
|
leaf x{
|
||||||
|
type int32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
# For testing namespaces -
|
# For testing namespaces -
|
||||||
# x.y is different type. Here it is string whereas in fyang it is list.
|
# x.y is different type. Here it is string whereas in fyang1 it is list.
|
||||||
#
|
#
|
||||||
cat <<EOF > $fyang2
|
cat <<EOF > $fyang2
|
||||||
module example2{
|
module example2{
|
||||||
|
|
@ -43,37 +65,6 @@ module example2{
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat <<EOF > $fyang
|
|
||||||
module example{
|
|
||||||
yang-version 1.1;
|
|
||||||
prefix ex;
|
|
||||||
namespace "urn:example:clixon";
|
|
||||||
import ietf-routing {
|
|
||||||
description "defines fib-route";
|
|
||||||
prefix rt;
|
|
||||||
}
|
|
||||||
leaf x{
|
|
||||||
type int32;
|
|
||||||
}
|
|
||||||
rpc client-rpc {
|
|
||||||
description "Example local client-side RPC that is processed by the
|
|
||||||
the netconf/restconf and not sent to the backend.
|
|
||||||
This is a clixon implementation detail: some rpc:s
|
|
||||||
are better processed by the client for API or perf reasons";
|
|
||||||
input {
|
|
||||||
leaf request {
|
|
||||||
type string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output {
|
|
||||||
leaf result{
|
|
||||||
type string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
new "test params: -f $cfg"
|
new "test params: -f $cfg"
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
|
|
@ -91,36 +82,57 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "start restconf daemon"
|
||||||
|
sudo su -c "$clixon_restconf -f $cfg -D $DBG" -s /bin/sh www-data &
|
||||||
|
|
||||||
new "netconf xmlns module ex"
|
new "netconf set x in example1"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon">42</x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon1">42</x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get config XXX xmlfn in return"
|
new "netconf get config example1"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x>42</x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon1">42</x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf xmlns module ex2"
|
new "netconf set x in example2"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon2"><y>99</y></x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><x xmlns="urn:example:clixon2"><y>99</y></x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get config XXX xmlns"
|
new "netconf get config example1 and example2"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x>42</x><x><y>99</y></x></data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:clixon1">42</x><x xmlns="urn:example:clixon2"><y>99</y></x></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang1" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf xmlns:ex"
|
new "restconf set x in example1"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:ex="urn:example:clixon"><edit-config><target><candidate/></target><config><ex:x>4422</ex:x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:ex="urn:example:clixon"><ok/></rpc-reply>]]>]]>$'
|
expecteq "$(curl -s -X POST -d '{"example1:x":42}' http://localhost/restconf/data)" ''
|
||||||
|
|
||||||
new "netconf get config XXX xmlns:ex"
|
new2 "restconf get config example1"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x>4422</x></data></rpc-reply>]]>]]>$"
|
expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42}
|
||||||
|
'
|
||||||
|
|
||||||
new "netconf xmlns:ex2"
|
new "restconf set x in example2"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:ex2="urn:example:clixon2"><edit-config><target><candidate/></target><config><ex2:x><ex2:y>9999</ex2:y></ex2:x></config></edit-config></rpc>]]>]]>' '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:ex2="urn:example:clixon2"><ok/></rpc-reply>]]>]]>$'
|
expecteq "$(curl -s -X POST -d '{"example2:x":{"y":99}}' http://localhost/restconf/data)" ''
|
||||||
|
|
||||||
new "netconf get config XXX xmlns:ex2"
|
# XXX GET ../example1:x is translated to select=/x which gets both example1&2
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x>4422</x><x><y>9999</y></x></data></rpc-reply>]]>]]>$"
|
#new "restconf get config example1"
|
||||||
|
#expecteq "$(curl -s -X GET http://localhost/restconf/data/example1:x)" '{"example1:x": 42}
|
||||||
|
#
'
|
||||||
|
|
||||||
# rpc
|
# XXX GET ../example2:x is translated to select=/x which gets both example1&2
|
||||||
|
#new "restconf get config example2"
|
||||||
|
#expecteq "$(curl -s -X GET http://localhost/restconf/data/example2:x)" '{"example2:x": {"y":42}}
|
||||||
|
#
'
|
||||||
|
|
||||||
|
new "restconf get config example1 and example2"
|
||||||
|
ret=$(curl -s -X GET http://localhost/restconf/data)
|
||||||
|
expect='"example1:x": 42,"example2:x": {"y": 99}'
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
||||||
if [ $BE -eq 0 ]; then
|
if [ $BE -eq 0 ]; then
|
||||||
exit # BE
|
exit # BE
|
||||||
|
|
|
||||||
|
|
@ -353,14 +353,19 @@ module clixon-config {
|
||||||
Only works for Yang specified XML.
|
Only works for Yang specified XML.
|
||||||
If not set, all lists accessed via linear search.";
|
If not set, all lists accessed via linear search.";
|
||||||
}
|
}
|
||||||
leaf CLICON_XML_NS_ITERATE {
|
leaf CLICON_XML_NS_STRICT {
|
||||||
type boolean;
|
type boolean;
|
||||||
default true;
|
default true;
|
||||||
description
|
description
|
||||||
"If set, iterate through modules to find the matching datanode
|
"If not set, make non-strict laze namespace checks, by iterating
|
||||||
|
through modules to find the matching datanode
|
||||||
or rpc if no xmlns attribute specifies namespace.
|
or rpc if no xmlns attribute specifies namespace.
|
||||||
This is loose semantics of finding namespaces.
|
This is lazy semantics of finding namespaces, which means you
|
||||||
And it is wrong, but is the way Clixon originally was written.";
|
do not need to explicitly give the namespace if the symbol exists
|
||||||
|
in some loaded module.
|
||||||
|
Example: <x/> is enough, instead of <x xmlns='urn:example:clixon'/>
|
||||||
|
But it is wrong, but is the way Clixon originally was written.
|
||||||
|
Strict semantics is the default.";
|
||||||
}
|
}
|
||||||
leaf CLICON_USE_STARTUP_CONFIG {
|
leaf CLICON_USE_STARTUP_CONFIG {
|
||||||
type int32;
|
type int32;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
module ietf-netconf-notification {
|
module ietf-netconf-notification {
|
||||||
namespace "urn:ietf:params:xml:ns:netconf:notification:1:0";
|
/* namespace "urn:ietf:params:xml:ns:netconf:notification:1.0";*/
|
||||||
|
namespace "urn:ietf:params:xml:ns:netmod:notification";
|
||||||
prefix "rcmon";
|
prefix "rcmon";
|
||||||
|
|
||||||
import ietf-yang-types { prefix yang; }
|
import ietf-yang-types { prefix yang; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue