Added support for yang presence and no-presence containers.

This commit is contained in:
Olof hagsand 2017-07-23 12:59:02 +02:00
parent e56cf607a3
commit e5b625e722
19 changed files with 188 additions and 104 deletions

View file

@ -1,29 +1,23 @@
# Clixon CHANGELOG # Clixon CHANGELOG
- Extended ietf-ip example with ietf-routing. Major changes:
- Added support for yang presence and no-presence containers. Previous default was "presence".
- Added YANG RPC support, with example rpc documentation and testcase (test7.sh). - Added YANG RPC support for nentconf and CLI. With example rpc documentation and testcase. This replaces the previous "downcall" mechanism.
- Added completion for generated cli leafrefs for both absolute and relatve paths. - Enhanced leafref functionality: (1) validation for leafref forward and backward references; (2)CLI completion for generated cli leafrefs for both absolute and relatve paths.
- Added validation for leafref forward and backward references. - Added state data: Netconf <get> operation, new backend plugin callback: "plugin_statedata()" for retreiving state data.
- Added new backend plugin callback: "plugin_statedata()" for retreiving state data
Minor changes:
- Removed 'margin' parameter of yang_print().
- Extended example with ietf-routing (not only ietf-ip) for rpc operations.
- Added yang dir with ietf-netconf and clixon-config yang specs for internal usage. - Added yang dir with ietf-netconf and clixon-config yang specs for internal usage.
- Added state data: Netconf <get> operation introduced; Error when
adding state data in <edit-config>.
- Fixed bug where cli set of leaf-list were doubled, eg cli set foo -> foofoo - Fixed bug where cli set of leaf-list were doubled, eg cli set foo -> foofoo
- Restricted yang (sub)module file match to match RFC6020 exactly - Restricted yang (sub)module file match to match RFC6020 exactly
- Generalized yang type resolution to all included (sub)modules not just the topmost
- Generic map_str2int generic mapping tables - Generic map_str2int generic mapping tables
- Removed vector return values from xmldb_get() - Removed vector return values from xmldb_get()
- Generalized yang type resolution to all included (sub)modules not just the topmost
## 3.3.1 June 7 2017 ## 3.3.1 June 7 2017

View file

@ -242,14 +242,9 @@ from_client_get_config(clicon_handle h,
goto ok; goto ok;
} }
cprintf(cbret, "<rpc-reply><data>"); cprintf(cbret, "<rpc-reply><data>");
if (xret!=NULL){ if (xret!=NULL &&
if (xml_child_nr(xret)){ clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
if (xml_name_set(xret, "config") < 0) goto done;
goto done;
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
goto done;
}
}
cprintf(cbret, "</data></rpc-reply>"); cprintf(cbret, "</data></rpc-reply>");
ok: ok:
retval = 0; retval = 0;

View file

@ -387,7 +387,7 @@ show_yang(clicon_handle h,
} }
else else
yn = (yang_node*)yspec; yn = (yang_node*)yspec;
yang_print(stdout, yn, 0); yang_print(stdout, yn);
return 0; return 0;
} }
int show_yangv(clicon_handle h, cvec *vars, cvec *argv) int show_yangv(clicon_handle h, cvec *vars, cvec *argv)

View file

@ -238,6 +238,8 @@ netconf_plugin_callbacks(clicon_handle h,
yang_spec *yspec; yang_spec *yspec;
yang_stmt *yrpc; yang_stmt *yrpc;
yang_stmt *yinput; yang_stmt *yinput;
yang_stmt *youtput;
cxobj *xoutput;
find_rpc_arg fra = {0,0}; find_rpc_arg fra = {0,0};
int ret; int ret;
@ -279,6 +281,18 @@ netconf_plugin_callbacks(clicon_handle h,
*/ */
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done; goto done;
/* Sanity check of outgoing XML */
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, "/");
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yinput) < 0)
goto done;
if (xml_apply(xoutput, CX_ELMNT,
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
goto done;
if (xml_yang_validate_add(xoutput, NULL) < 0)
goto done;
}
retval = 1; /* handled by callback */ retval = 1; /* handled by callback */
goto done; goto done;
} }

View file

@ -673,7 +673,7 @@ put(char *dbfile,
clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument); clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
if (debug){ if (debug){
xml_print(stderr, xt); xml_print(stderr, xt);
// yang_print(stderr, (yang_node*)ys, 0); // yang_print(stderr, (yang_node*)ys);
} }
if ((opstr = xml_find_value(xt, "operation")) != NULL) if ((opstr = xml_find_value(xt, "operation")) != NULL)
if (xml_operation(opstr, &op) < 0) if (xml_operation(opstr, &op) < 0)

View file

@ -665,6 +665,50 @@ text_modify_top(cxobj *x0,
return retval; return retval;
} }
/*! For containers without presence and no children, remove
* @param[in] x XML tree node
* @note This should really be unnecessary since yspec should be set on creation
* @code
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
* @endcode
* See section 7.5.1 in rfc6020bis-02.txt:
* No presence:
* those that exist only for organizing the hierarchy of data nodes:
* the container has no meaning of its own, existing
* only to contain child nodes. This is the default style.
* (Remove these if no children)
* Presence:
* the presence of the container itself is
* configuration data, representing a single bit of configuration data.
* The container acts as both a configuration knob and a means of
* organizing related configuration. These containers are explicitly
* created and deleted.
* (Dont touch these)
*/
int
xml_container_presence(cxobj *x,
void *arg)
{
int retval = -1;
char *name;
yang_stmt *y; /* yang node */
name = xml_name(x);
if ((y = (yang_stmt*)xml_spec(x)) == NULL){
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, name);
retval = 0;
goto done;
}
/* Mark node that is: container, have no children, dont have presence */
if (y->ys_keyword == Y_CONTAINER &&
xml_child_nr(x)==0 &&
yang_find((yang_node*)y, Y_PRESENCE, NULL) == NULL)
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
retval = 0;
done:
return retval;
}
/*! Modify database provided an xml tree and an operation /*! Modify database provided an xml tree and an operation
* This is a clixon datastore plugin of the the xmldb api * This is a clixon datastore plugin of the the xmldb api
* @see xmldb_put * @see xmldb_put
@ -745,6 +789,12 @@ text_put(xmldb_handle xh,
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)XML_FLAG_NONE) < 0) (void*)XML_FLAG_NONE) < 0)
goto done; goto done;
/* Mark non-presence containers that do not have children */
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_container_presence, NULL) < 0)
goto done;
/* Remove (prune) nodes that are marked (non-presence containers w/o children) */
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
goto done;
// output: // output:
/* Print out top-level xml tree after modification to file */ /* Print out top-level xml tree after modification to file */
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){

View file

@ -197,8 +197,10 @@ They are documented in [CLIgen tutorial](https://github.com/olofhagsand/cligen/b
## How do I write a validation function? ## How do I write a validation function?
Similar to a commit function, but instead write the transaction_validate() function. Similar to a commit function, but instead write the transaction_validate() function.
Check for inconsistencies in the XML trees and if they fail, make an clicon_err() call. Check for inconsistencies in the XML trees and if they fail, make an clicon_err() call.
```
clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name); clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name);
return -1; return -1;
```
The validation or commit will then be aborted. The validation or commit will then be aborted.
## How do I write a state data callback function? ## How do I write a state data callback function?
@ -210,3 +212,45 @@ 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 the name "plugin_statedata()" where you return an XML tree.
Please look at the example for an example on how to write a state data callback. Please look at the example for an example on how to write a state data callback.
## How do I write an RPC function?
A YANG RPC is an application specific operation. Example:
```
rpc fib-route {
input {
leaf inarg { type string; }
}
output {
leaf outarg { type string; }
}
}
```
which defines the fib-route operation present in the example (the arguments have been changed).
Clixon automatically relays the RPC to the clixon backend. To
implement the RFC, you need to register an RPC callback in the backend plugin:
Example:
```
int
plugin_init(clicon_handle h)
{
...
backend_rpc_cb_register(h, fib_route, NULL, "fib-route");
...
}
```
And then define the callback itself:
```
static int
fib_route(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
return 0;
}
```
Here, the callback is over-simplified.

View file

@ -103,6 +103,13 @@ fib_route(clicon_handle h,
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>"); cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
return 0; return 0;
} }
int
plugin_init(clicon_handle h)
{
...
backend_rpc_cb_register(h, fib_route, NULL, "fib-route");
...
}
``` ```
## State data ## State data

View file

@ -121,13 +121,16 @@ notification_timer_setup(clicon_handle h)
/*! IETF Routing fib-route rpc */ /*! IETF Routing fib-route rpc */
static int static int
fib_route(clicon_handle h, fib_route(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */ cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */ struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg <rpc-reply>... */ cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg) /* Argument given at register */ void *arg) /* Argument given at register */
{ {
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>"); cprintf(cbret, "<rpc-reply><route>"
"<address-family>ipv4</address-family>"
"<next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop>"
"</route></rpc-reply>");
return 0; return 0;
} }

View file

@ -204,8 +204,8 @@ yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
yang_stmt *yang_find_syntax(yang_node *yn, char *argument); yang_stmt *yang_find_syntax(yang_node *yn, char *argument);
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name); yang_stmt *yang_find_topnode(yang_spec *ysp, char *name);
int yang_print(FILE *f, yang_node *yn);
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal); int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
int yang_print(FILE *f, yang_node *yn, int marginal);
int yang_parse(clicon_handle h, const char *yang_dir, int yang_parse(clicon_handle h, const char *yang_dir,
const char *module, const char *revision, yang_spec *ysp); const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn, int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,

View file

@ -682,43 +682,41 @@ quotedstring(char *s)
} }
/*! Print yang specification to file /*! Print yang specification to file
* @param[in] f File to print to.
* @param[in] yn Yang node to print
* @see yang_print_cbuf * @see yang_print_cbuf
*/ */
int int
yang_print(FILE *f, yang_print(FILE *f,
yang_node *yn, yang_node *yn)
int marginal)
{ {
yang_stmt *ys = NULL; int retval = -1;
cbuf *cb = NULL;
while ((ys = yn_each(yn, ys)) != NULL) { if ((cb = cbuf_new()) == NULL){
fprintf(f, "%*s%s", marginal, "", yang_key2str(ys->ys_keyword)); clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
fflush(f); goto done;
if (ys->ys_argument){
if (quotedstring(ys->ys_argument))
fprintf(f, " \"%s\"", ys->ys_argument);
else
fprintf(f, " %s", ys->ys_argument);
}
if (ys->ys_len){
fprintf(f, " {\n");
yang_print(f, (yang_node*)ys, marginal+3);
fprintf(f, "%*s%s\n", marginal, "", "}");
}
else
fprintf(f, ";\n");
} }
return 0; if (yang_print_cbuf(cb, yn, 0) < 0)
goto done;
fprintf(f, "%s", cbuf_get(cb));
if (cb)
cbuf_free(cb);
retval = 0;
done:
return retval;
} }
/*! Print yang specification to cligen buf /*! Print yang specification to cligen buf
* @param[in] cb Cligen buffer. This is where the pretty print is.
* @param[in] yn Yang node to print
* @param[in] marginal Tab indentation, mainly for recursion.
* @code * @code
* cbuf *cb = cbuf_new(); * cbuf *cb = cbuf_new();
* yang_print_cbuf(cb, yn, 0); * yang_print_cbuf(cb, yn, 0);
* // output is in cbuf_buf(cb); * // output is in cbuf_buf(cb);
* cbuf_free(cb); * cbuf_free(cb);
* @endcode * @endcode
* @see yang_print
*/ */
int int
yang_print_cbuf(cbuf *cb, yang_print_cbuf(cbuf *cb,
@ -1944,7 +1942,7 @@ yang_spec_main(clicon_handle h,
goto done; goto done;
clicon_dbspec_yang_set(h, yspec); clicon_dbspec_yang_set(h, yspec);
if (printspec) if (printspec)
yang_print(f, (yang_node*)yspec, 0); yang_print(f, (yang_node*)yspec);
retval = 0; retval = 0;
done: done:
return retval; return retval;

View file

@ -4,8 +4,10 @@ This directory contains testing code for clixon and the example
routing application: routing application:
- clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script - clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script
- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically. - all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically.
- test1.sh CLI tests - test_cli.sh CLI tests
- test2.sh Netconf tests - test_netconf.sh Netconf tests
- test3.sh Restconf tests - test_restconf.sh Restconf tests
- test4.sh Yang tests - test_yang.sh Yang tests for constructs not in the example.
- test5.sh Datastore tests - test_leafref.sh Yang leafref tests
- test_datastore.sh Datastore tests

View file

@ -1,38 +0,0 @@
#!/bin/bash
# Test6: Yang specifics: rpc and state info
# include err() and new() functions
. ./lib.sh
# For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf
clixon_cli=clixon_cli
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "start backend"
# start new backend
sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "netconf rpc"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name></fib-route></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err "kill backend"
fi

View file

@ -31,8 +31,8 @@ new "cli tests"
new "cli configure top" new "cli configure top"
expectfn "$clixon_cli -1f $clixon_cf set interfaces" "" expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
new "cli show configuration top" new "cli show configuration top (no presence)"
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces$" expectfn "$clixon_cli -1f $clixon_cf show conf cli" ""
new "cli configure delete top" new "cli configure delete top"
expectfn "$clixon_cli -1f $clixon_cf delete interfaces" "" expectfn "$clixon_cli -1f $clixon_cf delete interfaces" ""

View file

@ -24,13 +24,13 @@ fi
new "netconf tests" new "netconf tests"
new "netconf get empty config" new "netconf get empty config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config/></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 $clixon_cf" "<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 $clixon_cf" "<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>]]>]]>$"
new "Check nothing added" new "Check nothing added"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config/></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 $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -44,8 +44,8 @@ expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate
new "Delete eth/0/0 using none config" new "Delete eth/0/0 using none config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
#new "Check deleted eth/0/0" new "Check deleted eth/0/0 (non-presence container)"
#expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config><interfaces/></config></data></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config/></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 $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>" expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth/0/0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
@ -66,7 +66,7 @@ new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get empty config2" new "netconf get empty config2"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config/></data></rpc-reply>]]>]]>$"
new "netconf edit config eth1" new "netconf edit config eth1"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -117,7 +117,10 @@ new "netconf delete startup"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf check empty startup" new "netconf check empty startup"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config/></data></rpc-reply>]]>]]>$"
new "netconf rpc"
expecteof "$clixon_netconf -qf $clixon_cf" "<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 subscription" new "netconf subscription"
expectwait "$clixon_netconf -qf $clixon_cf" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30 expectwait "$clixon_netconf -qf $clixon_cf" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30

View file

@ -35,7 +35,15 @@ module example{
leaf g { leaf g {
type string; type string;
} }
container h { container nopresence {
description "No presence should be removed if no children";
leaf j {
type string;
}
}
container presence {
description "Presence should not be removed even if no children";
presence "even if empty should remain";
leaf j { leaf j {
type string; type string;
} }
@ -81,7 +89,7 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><sourc
new "netconf get leaf-list path" new "netconf get leaf-list path"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
new "netconf get (state data XXX should be some)" new "netconf get (should be some)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></data></rpc-reply>]]>]]>$"
new "cli set leaf-list" new "cli set leaf-list"
@ -89,10 +97,14 @@ expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test set x f e foo" ""
new "cli show leaf-list" new "cli show leaf-list"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test show xpath /x/f/e" "<e>foo</e>" expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test show xpath /x/f/e" "<e>foo</e>"
new "netconf set state data (not allowed)" new "netconf set state data (not allowed)"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value" expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value"
new "netconf set presence and not present"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><x><nopresence/><presence/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/*presence\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><presence/></x></config></data></rpc-reply>]]>]]>$"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive