* State callbacks provided by user are validated. If they are invalid an internal error is returned.
* Fixed multi-namespace for augmented state which was not covered in 4.2.0. * The multi-namespace augment state may rearrange the XML namespace attributes. * Mandatory variables can no longer be deleted.
This commit is contained in:
parent
728fe9c6ac
commit
835f9030d2
20 changed files with 299 additions and 229 deletions
|
|
@ -5,8 +5,17 @@
|
|||
### Minor changes
|
||||
* Added wildcard `*` as a mode to `CLICON_MODE` in clispec files
|
||||
* If you set "CLICON_MODE="*";" in a clispec file it means that syntax will appear in all CLI spec modes.
|
||||
* State callbacks provided by user are validated. If they are invalid an internal error is returned.
|
||||
* Fixed multi-namespace for augmented state which was not covered in 4.2.0.
|
||||
|
||||
|
||||
### API changes on existing features (you may need to change your code)
|
||||
* The multi-namespace augment state may rearrange the XML namespace attributes.
|
||||
* Main example yang changed to incorporate augmented state, new revision is 2019-11-15.
|
||||
|
||||
### Corrected Bugs
|
||||
|
||||
* Mandatory variables can no longer be deleted.
|
||||
* [Add missing includes](https://github.com/clicon/clixon/pulls)
|
||||
|
||||
## 4.2.0 (October 27 2019)
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ client_get_capabilities(clicon_handle h,
|
|||
int retval = -1;
|
||||
cxobj *xrstate = NULL; /* xml restconf-state node */
|
||||
cxobj *xcap = NULL; /* xml capabilities node */
|
||||
|
||||
|
||||
if ((xrstate = xpath_first(*xret, "restconf-state")) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "restconf-state not found in config node");
|
||||
goto done;
|
||||
|
|
@ -321,7 +321,7 @@ client_statedata(clicon_handle h,
|
|||
if (ret == 0)
|
||||
goto fail;
|
||||
/* Code complex to filter out anything that is outside of xpath
|
||||
* Actually this is a safety catch, should realy be done in plugins
|
||||
* Actually this is a safety catch, should really be done in plugins
|
||||
* and modules_state functions.
|
||||
*/
|
||||
if (xpath_vec_nsc(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
|
|
|
|||
|
|
@ -118,10 +118,14 @@ generic_validate(clicon_handle h,
|
|||
for (i=0; i<td->td_dlen; i++){
|
||||
x1 = td->td_dvec[i];
|
||||
ys = xml_spec(x1);
|
||||
if (ys && yang_mandatory(ys) && yang_config(ys)==0){
|
||||
if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "Missing mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
if (ys && yang_mandatory(ys) && yang_config(ys)==1){
|
||||
yang_stmt *yp =yang_parent_get(ys);
|
||||
if (yp== NULL ||
|
||||
(yang_keyword_get(yp)!=Y_MODULE && yang_keyword_get(yp)!=Y_SUBMODULE)){
|
||||
if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "May not remove mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* added entries */
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
cxobj *xerr = NULL;
|
||||
cxobj *x = NULL;
|
||||
clixon_plugin *cp = NULL;
|
||||
plgstatedata_t *fn; /* Plugin statedata fn */
|
||||
|
|
@ -124,6 +125,31 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
goto fail; /* Dont quit here on user callbacks */
|
||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
/* Check XML from state callback by validating it. return internal
|
||||
* error with error cause
|
||||
*/
|
||||
if ((ret = xml_yang_validate_all_top(h, x, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret > 0 && (ret = xml_yang_validate_add(h, x, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){
|
||||
cbuf *cberr = NULL; /* XXX Cumbersome, try to fold into one cb */
|
||||
cbuf *cberr2 = NULL;
|
||||
if (netconf_err2cb(xpath_first(xerr, "rpc-error"), &cberr) < 0)
|
||||
goto done;
|
||||
if ((cberr2 = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr2, "Internal error: state callback returned invalid XML: %s", cbuf_get(cberr));
|
||||
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr2))< 0)
|
||||
goto done;
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (cberr2)
|
||||
cbuf_free(cberr2);
|
||||
goto fail;
|
||||
}
|
||||
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -137,6 +163,8 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
done:
|
||||
if (x)
|
||||
xml_free(x);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ Start restconf daemon
|
|||
|
||||
Start sending restconf commands (using Curl):
|
||||
```
|
||||
olof@vandal> curl -X POST http://localhost/restconf/data -d '{"clixon-hello:hello":{"world":null}}'
|
||||
olof@vandal> curl -X POST http://localhost/restconf/data -H "Content-Type: application/yang-data+json" -d '{"clixon-hello:hello":{"world":null}}'
|
||||
olof@vandal> curl -X GET http://localhost/restconf/data
|
||||
{
|
||||
"data": {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ all: $(PLUGINS)
|
|||
|
||||
CLISPECS = $(APPNAME)_cli.cli
|
||||
|
||||
YANGSPECS = clixon-example@2019-07-23.yang
|
||||
YANGSPECS = clixon-example@2019-11-05.yang
|
||||
|
||||
# Backend plugin
|
||||
BE_SRC = $(APPNAME)_backend.c
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ clixon_netconf -qf /usr/local/etc/example.xml
|
|||
Restconf (assuming nginx started):
|
||||
```
|
||||
sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data&
|
||||
curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}'
|
||||
curl -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"ipv4"}}'
|
||||
{
|
||||
"clixon-example:output": {
|
||||
"x": "ipv4",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ module clixon-example {
|
|||
yang-version 1.1;
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
revision 2019-11-05 {
|
||||
description "Augment interface. Released in Clixon 4.3.0";
|
||||
}
|
||||
revision 2019-07-23 {
|
||||
description "Extension e4. Released in Clixon 4.1.0";
|
||||
}
|
||||
|
|
@ -42,6 +45,18 @@ module clixon-example {
|
|||
type string;
|
||||
}
|
||||
}
|
||||
augment "/if:interfaces/if:interface" {
|
||||
container my-status {
|
||||
config false;
|
||||
description "For testing augment+state";
|
||||
leaf int {
|
||||
type int32;
|
||||
}
|
||||
leaf str {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* yang extension implemented by the example backend code. */
|
||||
extension e4 {
|
||||
description
|
||||
|
|
@ -317,30 +317,23 @@ example_statedata(clicon_handle h,
|
|||
* state information. In this case adding dummy interface operation state
|
||||
* to configured interfaces.
|
||||
* Get config according to xpath */
|
||||
if (xmldb_get0(h, "running", nsc, xpath, 1, &xt, NULL) < 0)
|
||||
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
|
||||
goto done;
|
||||
|
||||
if (yang_find_module_by_namespace(yspec, "urn:ietf:params:xml:ns:yang:ietf-interfaces") != NULL){
|
||||
/* Here a separate namespace context nsc1 is created. The original nsc
|
||||
* created by the system cannot be used trivially, since we dont know
|
||||
* the prefixes, although we could by a complex mechanism find the prefix
|
||||
* (if it exists) and use that when creating our xpath.
|
||||
* But it is easier creating a new namespace context nsc1.
|
||||
*/
|
||||
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
|
||||
goto done;
|
||||
if (xpath_vec_nsc(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
if (xlen){
|
||||
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
|
||||
for (i=0; i<xlen; i++){
|
||||
name = xml_body(xvec[i]);
|
||||
cprintf(cb, "<interface><name>%s</name><oper-status>up</oper-status></interface>", name);
|
||||
}
|
||||
cprintf(cb, "</interfaces>");
|
||||
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
|
||||
goto done;
|
||||
if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
|
||||
goto done;
|
||||
if (xpath_vec_nsc(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
if (xlen){
|
||||
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
|
||||
for (i=0; i<xlen; i++){
|
||||
name = xml_body(xvec[i]);
|
||||
cprintf(cb, "<interface xmlns:ex=\"urn:example:clixon\"><name>%s</name><type>ex:eth</type><oper-status>up</oper-status>", name);
|
||||
cprintf(cb, "<ex:my-status><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status>");
|
||||
cprintf(cb, "</interface>");
|
||||
}
|
||||
cprintf(cb, "</interfaces>");
|
||||
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* State in test_yang.sh , test_restconf.sh and test_order.sh */
|
||||
if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){
|
||||
|
|
@ -356,32 +349,14 @@ example_statedata(clicon_handle h,
|
|||
* (2) event-count is XOR on name, so is not 42 and 4
|
||||
*/
|
||||
if (yang_find_module_by_namespace(yspec, "urn:example:events") != NULL){
|
||||
if ((nsc2 = xml_nsctx_init(NULL, "urn:example:events")) == NULL)
|
||||
cbuf_reset(cb);
|
||||
cprintf(cb, "<events xmlns=\"urn:example:events\">");
|
||||
cprintf(cb, "<event><name>interface-down</name><event-count>90</event-count></event>");
|
||||
cprintf(cb, "<event><name>interface-up</name><event-count>77</event-count></event>");
|
||||
cprintf(cb, "</events>");
|
||||
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
|
||||
goto done;
|
||||
if (xvec){
|
||||
free(xvec);
|
||||
xvec = NULL;
|
||||
}
|
||||
if (xpath_vec_nsc(xt, nsc2, "/events/event/name", &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
if (xlen){
|
||||
int j = 0;
|
||||
int c;
|
||||
cprintf(cb, "<events xmlns=\"urn:example:events\">");
|
||||
|
||||
for (i=0; i<xlen; i++){
|
||||
name = xml_body(xvec[i]);
|
||||
c = 0;
|
||||
for (j=0; j<strlen(name); j++)
|
||||
c ^= name[j];
|
||||
cprintf(cb, "<event><name>%s</name><event-count>%d</event-count></event>", name, c);
|
||||
}
|
||||
cprintf(cb, "</events>");
|
||||
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
|
|||
int *sock0);
|
||||
int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
|
||||
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
|
||||
int clicon_rpc_generate_error(char *format, cxobj *xerr);
|
||||
int clicon_rpc_generate_error(const char *format, cxobj *xerr);
|
||||
int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, cxobj **xret);
|
||||
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
|
||||
char *xml);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
|||
|
||||
int xml2xpath(cxobj *x, char **xpath);
|
||||
int xml2api_path_1(cxobj *x, cbuf *cb);
|
||||
int check_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
|
||||
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
|
||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||
|
||||
|
|
|
|||
|
|
@ -132,138 +132,6 @@ attr_ns_value(cxobj *x,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
|
||||
* @param[in] x0 Source XML tree
|
||||
* @param[in] x1 Target XML tree
|
||||
* 1. Find N=namespace(x0)
|
||||
* 2. Detect if N is declared in x1 parent
|
||||
* 3. If yes, assign prefix to x1
|
||||
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
|
||||
* 5. Add prefix to x1, if any
|
||||
* 6. Ensure x1 cache is updated
|
||||
* @note switch use of x0 and x1 compared to datastore text_modify
|
||||
* @see xml2ns
|
||||
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
*/
|
||||
static int
|
||||
check_namespaces(cxobj *x0,
|
||||
cxobj *x1,
|
||||
cxobj *x1p)
|
||||
{
|
||||
int retval = -1;
|
||||
char *namespace = NULL;
|
||||
char *prefix0 = NULL;;
|
||||
char *prefix10 = NULL; /* extra just for malloc problem */
|
||||
char *prefix1 = NULL;;
|
||||
char *prefixb = NULL; /* identityref body prefix */
|
||||
cvec *nsc0 = NULL;
|
||||
cvec *nsc = NULL;
|
||||
cxobj *xa = NULL;
|
||||
cxobj *x;
|
||||
int isroot;
|
||||
|
||||
/* XXX: need to identify root better than hiereustics and strcmp,... */
|
||||
isroot = xml_parent(x1p)==NULL &&
|
||||
strcmp(xml_name(x1p), "config") == 0 &&
|
||||
xml_prefix(x1p)==NULL;
|
||||
|
||||
/* 1. Find N=namespace(x0) */
|
||||
prefix0 = xml_prefix(x0);
|
||||
if (xml2ns(x0, prefix0, &namespace) < 0)
|
||||
goto done;
|
||||
if (namespace == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
|
||||
prefix0?prefix0:"NULL");
|
||||
goto done;
|
||||
}
|
||||
/* 2. Detect if namespace is declared in x1:s parent */
|
||||
if (xml2prefix(x1p, namespace, &prefix10) == 1){
|
||||
if (prefix10){
|
||||
if ((prefix1 = strdup(prefix10)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
prefix1 = NULL;
|
||||
/* 3. If yes, assign prefix to x1 */
|
||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||
goto done;
|
||||
/* And copy namespace context from parent to child */
|
||||
if ((nsc0 = nscache_get_all(x1p)) != NULL){
|
||||
if ((nsc = cvec_dup(nsc0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
nscache_replace(x1, nsc);
|
||||
}
|
||||
/* Just in case */
|
||||
if (nscache_set(x1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
/* 4. If no, create new prefix/namespace binding and assign that to x1p
|
||||
* use modules own default prefix (some chance for clash)
|
||||
*/
|
||||
if (prefix0 == NULL && !isroot){
|
||||
assert(xml_spec(x1) != NULL);
|
||||
prefix0 = yang_find_myprefix(xml_spec(x1));
|
||||
}
|
||||
if (prefix0)
|
||||
if ((prefix1 = strdup(prefix0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Add binding to x1p. We add to parent due to heurestics, so we dont
|
||||
* end up in adding it to large number of siblings
|
||||
*/
|
||||
if (isroot)
|
||||
x = x1;
|
||||
else
|
||||
x = x1p;
|
||||
if (nscache_set(x, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
if (x == x1p){
|
||||
if ((nsc0 = nscache_get_all(x1p)) != NULL)
|
||||
if ((nsc = cvec_dup(nsc0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
/* Copy x1p cache to x1 */
|
||||
nscache_replace(x1, nsc);
|
||||
}
|
||||
/* Create xmlns attribute to x1p/x1 XXX same code v */
|
||||
if (prefix1){
|
||||
if ((xa = xml_new(prefix1, x, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "xmlns") < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
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;
|
||||
xml_sort(x, NULL); /* Ensure attr is first / XXX xml_insert? */
|
||||
|
||||
/* 5. Add prefix to x1, if any */
|
||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefixb)
|
||||
free(prefixb);
|
||||
if (prefix1)
|
||||
free(prefix1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
check_identityref(cxobj *x0,
|
||||
cxobj *x1,
|
||||
|
|
|
|||
|
|
@ -1338,7 +1338,7 @@ netconf_db_find(cxobj *xn,
|
|||
}
|
||||
|
||||
/*! Generate netconf error msg to cbuf to use in string printout or logs
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-error>
|
||||
* @param[out] cberr Translation from netconf err to cbuf. Free with cbuf_free.
|
||||
* @retval 0 OK, with cberr set
|
||||
* @retval -1 Error
|
||||
|
|
|
|||
|
|
@ -227,11 +227,11 @@ clicon_rpc_netconf_xml(clicon_handle h,
|
|||
|
||||
/*! Generate and log clicon error function call from Netconf error message
|
||||
* @param[in] prefix Print this string (if given) before: "<prefix>: <error>"
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||
* @param[in] xerr Netconf error message on the level: <rpc-error>
|
||||
*/
|
||||
int
|
||||
clicon_rpc_generate_error(char *prefix,
|
||||
cxobj *xerr)
|
||||
clicon_rpc_generate_error(const char *prefix,
|
||||
cxobj *xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
|
|
|||
|
|
@ -2153,6 +2153,46 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Add prefix:namespace pair to xml node, set cache, prefix, etc
|
||||
*/
|
||||
static int
|
||||
add_namespace(cxobj *x1, /* target */
|
||||
cxobj *x1p,
|
||||
char *prefix1,
|
||||
char *namespace)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xa = NULL;
|
||||
|
||||
/* Add binding to x1p. We add to parent due to heurestics, so we dont
|
||||
* end up in adding it to large number of siblings
|
||||
*/
|
||||
if (nscache_set(x1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
/* Create xmlns attribute to x1p/x1 XXX same code v */
|
||||
if (prefix1){
|
||||
if ((xa = xml_new(prefix1, x1, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "xmlns") < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
if ((xa = xml_new("xmlns", x1, NULL)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, namespace) < 0)
|
||||
goto done;
|
||||
xml_sort(x1, NULL); /* Ensure attr is first / XXX xml_insert? */
|
||||
|
||||
/* 5. Add prefix to x1, if any */
|
||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add default values (if not set)
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] arg Ignored
|
||||
|
|
@ -2175,6 +2215,7 @@ xml_default(cxobj *xt,
|
|||
int added=0;
|
||||
char *namespace;
|
||||
char *prefix;
|
||||
int ret;
|
||||
|
||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||
retval = 0;
|
||||
|
|
@ -2195,9 +2236,22 @@ xml_default(cxobj *xt,
|
|||
/* assign right prefix */
|
||||
if ((namespace = yang_find_mynamespace(y)) != NULL){
|
||||
prefix = NULL;
|
||||
if (xml2prefix(xt, namespace, &prefix))
|
||||
if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
|
||||
goto done;
|
||||
if (ret){
|
||||
if (xml_prefix_set(xc, prefix) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* namespace does not exist in target, use source prefix */
|
||||
char *prefix1 = NULL;
|
||||
if ((prefix1 = strdup(yang_find_myprefix(y))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (add_namespace(xc, xt, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
xml_flag_set(xc, XML_FLAG_DEFAULT);
|
||||
|
|
@ -3190,6 +3244,124 @@ xmlns_assign(cxobj *x)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
|
||||
* @param[in] x0 Source XML tree
|
||||
* @param[in] x1 Target XML tree
|
||||
* 1. Find N=namespace(x0)
|
||||
* 2. Detect if N is declared in x1 parent
|
||||
* 3. If yes, assign prefix to x1
|
||||
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
|
||||
* 5. Add prefix to x1, if any
|
||||
* 6. Ensure x1 cache is updated
|
||||
* @note switch use of x0 and x1 compared to datastore text_modify
|
||||
* @see xml2ns
|
||||
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
|
||||
goto done;
|
||||
*/
|
||||
int
|
||||
check_namespaces(cxobj *x0, /* source */
|
||||
cxobj *x1, /* target */
|
||||
cxobj *x1p)
|
||||
{
|
||||
int retval = -1;
|
||||
char *namespace = NULL;
|
||||
char *prefix0 = NULL;;
|
||||
char *prefix1 = NULL;
|
||||
char *prefixb = NULL; /* identityref body prefix */
|
||||
cvec *nsc0 = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int isroot;
|
||||
char *pexist = NULL;
|
||||
yang_stmt *y;
|
||||
|
||||
/* XXX: need to identify root better than hiereustics and strcmp,... */
|
||||
isroot = xml_parent(x1p)==NULL &&
|
||||
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
|
||||
xml_prefix(x1p)==NULL;
|
||||
|
||||
/* 1. Find N=namespace(x0) */
|
||||
prefix0 = xml_prefix(x0);
|
||||
if (xml2ns(x0, prefix0, &namespace) < 0)
|
||||
goto done;
|
||||
if (namespace == NULL){
|
||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
|
||||
prefix0?prefix0:"NULL");
|
||||
goto done;
|
||||
}
|
||||
/* 2a. Detect if namespace is declared in x1 target parent */
|
||||
if (xml2prefix(x1p, namespace, &pexist) == 1){
|
||||
/* Yes, and it has prefix pexist */
|
||||
if (pexist){
|
||||
if ((prefix1 = strdup(pexist)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
prefix1 = NULL;
|
||||
/* 3. If yes, assign prefix to x1 */
|
||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||
goto done;
|
||||
/* And copy namespace context from parent to child */
|
||||
if ((nsc0 = nscache_get_all(x1p)) != NULL){
|
||||
if ((nsc = cvec_dup(nsc0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
nscache_replace(x1, nsc);
|
||||
}
|
||||
/* Just in case */
|
||||
if (nscache_set(x1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* No, namespace does not exist in x1 _parent_
|
||||
* Check if it is exists in x1 itself */
|
||||
if (nscache_get_prefix(x1, namespace, &pexist) == 1){
|
||||
/* Yes it exists, but is it equal? */
|
||||
if ((pexist == NULL && prefix0 == NULL) ||
|
||||
(pexist && prefix0 &&
|
||||
strcmp(pexist, prefix0)==0)){ /* Equal, reuse */
|
||||
;
|
||||
}
|
||||
else{ /* namespace exist, but not equal, use existing */
|
||||
/* Add prefix to x1, if any */
|
||||
if (pexist && xml_prefix_set(x1, pexist) < 0)
|
||||
goto done;
|
||||
}
|
||||
goto ok; /* skip */
|
||||
}
|
||||
else
|
||||
{ /* namespace does not exist in target x1, use source prefix
|
||||
* use the prefix defined in the module
|
||||
*/
|
||||
if (isroot){
|
||||
if (prefix0 && (prefix1 = strdup(prefix0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
y = xml_spec(x0);
|
||||
if ((prefix1 = strdup(yang_find_myprefix(y))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add_namespace(x1, x1p, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefixb)
|
||||
free(prefixb);
|
||||
if (prefix1)
|
||||
free(prefix1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Merge a base tree x0 with x1 with yang spec y
|
||||
* @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
|
||||
|
|
@ -3201,20 +3373,18 @@ xmlns_assign(cxobj *x)
|
|||
* Assume x0 and x1 are same on entry and that y is the spec
|
||||
*/
|
||||
static int
|
||||
xml_merge1(cxobj *x0,
|
||||
xml_merge1(cxobj *x0, /* the target */
|
||||
yang_stmt *y0,
|
||||
cxobj *x0p,
|
||||
cxobj *x1,
|
||||
cxobj *x1, /* the source */
|
||||
char **reason)
|
||||
{
|
||||
int retval = -1;
|
||||
char *x1name;
|
||||
char *x1cname; /* child name */
|
||||
cxobj *x0c; /* base child */
|
||||
cxobj *x0b; /* base body */
|
||||
cxobj *x1c; /* mod child */
|
||||
cxobj *x0a; /* x0 xmlns attribute */
|
||||
cxobj *x1a; /* x1 xmlns attribute */
|
||||
char *x1name;
|
||||
char *x1bstr; /* mod body string */
|
||||
yang_stmt *yc; /* yang child */
|
||||
cbuf *cbr = NULL; /* Reason buffer */
|
||||
|
|
@ -3243,13 +3413,16 @@ xml_merge1(cxobj *x0,
|
|||
if (xml_value_set(x0b, x1bstr) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (check_namespaces(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
} /* if LEAF|LEAF_LIST */
|
||||
else { /* eg Y_CONTAINER, Y_LIST */
|
||||
if (x0==NULL){
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
if (check_namespaces(x1, x0, x0p) < 0)
|
||||
goto done;
|
||||
/* Loop through children of the modification tree */
|
||||
x1c = NULL;
|
||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||
|
|
@ -3277,19 +3450,11 @@ xml_merge1(cxobj *x0,
|
|||
goto done;
|
||||
if (*reason != NULL)
|
||||
goto ok;
|
||||
}
|
||||
} /* while */
|
||||
if (xml_parent(x0) == NULL &&
|
||||
xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
|
||||
goto done;
|
||||
} /* else Y_CONTAINER */
|
||||
assert(x0);
|
||||
/* Copy xmlns attributes (if it does not already exist) */
|
||||
if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != NULL)
|
||||
if (xml_find_type(x0, NULL, "xmlns", CX_ATTR)==NULL){
|
||||
if ((x0a = xml_dup(x1a)) == NULL)
|
||||
goto done;
|
||||
if (xml_addsub(x0, x0a) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
# 2. example-augment - urn:example:augment - mymod
|
||||
# (augmented): mandatory-leaf, me, other,
|
||||
# (uses/grouping): ip, port, lid, lport
|
||||
# Note augment+state not tested here (need plugin), simple test in test_restconf.sh
|
||||
#
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
|
|
@ -217,13 +219,12 @@ new "restconf get augment json"
|
|||
expectpart "$(curl -s -i -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}
'
|
||||
|
||||
new "restconf get augment xml"
|
||||
expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>'
|
||||
|
||||
expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface><name>e2</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:other xmlns:mymod="urn:example:augment">if:fddi</mymod:other><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface><interface><name>e3</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:me xmlns:mymod="urn:example:augment">mymod:you</mymod:me><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface></interfaces>'
|
||||
|
||||
#<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
|
||||
XML=$(cat <<EOF
|
||||
<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
EOF
|
||||
)
|
||||
|
||||
|
|
@ -240,10 +241,10 @@ new "restconf POST augment multi-namespace path e2 (middle path)"
|
|||
expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 ''
|
||||
|
||||
new "restconf GET augment multi-namespace top"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
|
||||
new "restconf GET augment multi-namespace level 1"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080}\]}'
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}'
|
||||
|
||||
new "restconf GET augment multi-namespace cross"
|
||||
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}'
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ cat <<EOF > $tmp # new
|
|||
EOF
|
||||
|
||||
new "netconf get config xpath"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 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
|
||||
cat <<EOF > $tmp # new
|
||||
|
|
@ -110,7 +110,7 @@ cat <<EOF > $tmp # new
|
|||
EOF
|
||||
|
||||
new "netconf get config xpath parent"
|
||||
expecteof "$clixon_netconf -qf $cfg" 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 xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><name>eth1</name><enabled>true</enabled><ip:ipv4><ip:enabled>true</ip:enabled><ip:forwarding>false</ip:forwarding><ip:address><ip:ip>9.2.3.4</ip:ip><ip:prefix-length>24</ip:prefix-length></ip:address></ip:ipv4></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 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><ip:ipv4 xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><ip:enabled>true</ip:enabled><ip:forwarding>false</ip:forwarding><ip:address><ip:ip>9.2.3.4</ip:ip><ip:prefix-length>24</ip:prefix-length></ip:address></ip:ipv4></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf validate missing type"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
||||
|
|
@ -163,7 +163,7 @@ new "netconf edit state operation should fail"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e0</name><oper-status>up</oper-status></interface></interfaces></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 get state operation"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></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><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></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><oper-status>up</oper-status><ex:my-status xmlns:ex="urn:example:clixon"><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf lock/unlock"
|
||||
expecteof "$clixon_netconf -qf $cfg" 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>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-
|
|||
new "netconf get test single req"
|
||||
sel="/if:interfaces/if:interface[if:name='e1']"
|
||||
msg="<rpc><get><filter type=\"xpath\" select=\"$sel\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status><ex:my-status xmlns:ex="urn:example:clixon"><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status></interface></interfaces></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "netconf get $perfreq single reqs"
|
||||
{ time -p for (( i=0; i<$perfreq; i++ )); do
|
||||
|
|
@ -101,7 +101,7 @@ done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
|
|||
|
||||
# RESTCONF get
|
||||
new "restconf get test single req"
|
||||
expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}
|
||||
expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}
|
||||
'
|
||||
|
||||
new "restconf get $perfreq single reqs"
|
||||
|
|
|
|||
|
|
@ -194,12 +194,16 @@ new "restconf Add interfaces subtree eth/0/0 using POST"
|
|||
expectpart "$(curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}')" 0 ""
|
||||
|
||||
new "restconf Check eth/0/0 added config"
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}\]}}
|
||||
'
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
|
||||
|
||||
new "restconf Check eth/0/0 GET augmented state level 1"
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 '{"ietf-interfaces:interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}'
|
||||
|
||||
new "restconf Check eth/0/0 GET augmented state level 2"
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0/clixon-example:my-status)" 0 '{"clixon-example:my-status":{"int":42,"str":"foo"}}'
|
||||
|
||||
new "restconf Check eth/0/0 added state"
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}}
|
||||
'
|
||||
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}}'
|
||||
|
||||
new "restconf Re-post eth/0/0 which should generate error"
|
||||
expectpart "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}
'
|
||||
|
|
@ -211,7 +215,7 @@ new "Add nothing using POST (expect fail)"
|
|||
expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}'
|
||||
|
||||
new "restconf Check description added"
|
||||
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}}
|
||||
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}}
|
||||
'
|
||||
|
||||
new "restconf delete eth/0/0"
|
||||
|
|
@ -227,7 +231,7 @@ new "restconf Add subtree eth/0/0 using PUT"
|
|||
expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 ""
|
||||
|
||||
new "restconf get subtree"
|
||||
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}}
|
||||
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}}
|
||||
'
|
||||
|
||||
new "restconf rpc using POST json"
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://loca
|
|||
# This just catches the header and the jukebox module, the RFC has foo and bar which
|
||||
# seems wrong to recreate
|
||||
new "B.1.2. Retrieve the Server Module Information"
|
||||
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":' '"module":\[{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}'
|
||||
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2019-08-13","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"clixon-rfc5277","revision":"2008-07-01","namespace":"urn:ietf:params:xml:ns:netmod:notification","conformance-type":"implement"},{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"'
|
||||
|
||||
new "B.1.3. Retrieve the Server Capability Information"
|
||||
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue