diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1556a53e..ac27dac3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Clixon CHANGELOG
+- Extended ietf-ip example with ietf-routing.
+
+- Added YANG RPC support, with example rpc documentation and testcase (test7.sh).
+
- Added completion for generated cli leafrefs for both absolute and relatve paths.
- Added validation for leafref forward and backward references.
diff --git a/README.md b/README.md
index 55d380fa..adb627dd 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ The [YANG RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) is implemented w
- object-references
- if-feature
- unique
-- rpc
+
diff --git a/apps/netconf/netconf_plugin.c b/apps/netconf/netconf_plugin.c
index 474da891..c1ff19e1 100644
--- a/apps/netconf/netconf_plugin.c
+++ b/apps/netconf/netconf_plugin.c
@@ -196,26 +196,28 @@ catch:
return -1;
}
+/*! Struct to carry info into and out of ys_find_rpc callback
+ */
+typedef struct {
+ char *name; /* name of rpc */
+ yang_stmt *yrpc; /* matching yang statement */
+} find_rpc_arg;
+
+/*! Check yang rpc statement, return yang rpc statement if found
+ */
static int
ys_find_rpc(yang_stmt *ys,
void *arg)
{
- cxobj *xn = (cxobj*)arg;
- char *name = xml_name(xn);
+ find_rpc_arg *fra = (find_rpc_arg*)arg;
- if (ys->ys_keyword == Y_RPC && strcmp(name, ys->ys_argument) == 0){
- /*
- * XXX
- * 1. Check xn arguments with input statement.
- * 2. Send to backend as clicon_msg-encode()
- * 3. In backend to similar but there call actual backend
- */
+ if (strcmp(fra->name, ys->ys_argument) == 0){
+ fra->yrpc = ys;
return 1; /* handled */
}
return 0;
}
-
/*! See if there is any callback registered for this tag
*
* @param[in] h clicon handle
@@ -234,6 +236,10 @@ netconf_plugin_callbacks(clicon_handle h,
int retval = -1;
netconf_reg_t *nreg;
yang_spec *yspec;
+ yang_stmt *yrpc;
+ yang_stmt *yinput;
+ find_rpc_arg fra = {0,0};
+ int ret;
if (deps != NULL){
nreg = deps;
@@ -249,8 +255,33 @@ netconf_plugin_callbacks(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
- if (yang_apply((yang_node*)yspec, ys_find_rpc, xn) < 0)
+ /* Find yang rpc statement, return yang rpc statement if found */
+ fra.name = xml_name(xn);
+ if ((ret = yang_apply((yang_node*)yspec, Y_RPC, ys_find_rpc, &fra)) < 0)
goto done;
+ /* Check if found */
+ if (ret == 1){
+ yrpc = fra.yrpc;
+ if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
+ xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
+ if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
+ goto done;
+ if (xml_apply(xn, CX_ELMNT,
+ (xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
+ goto done;
+ if (xml_yang_validate_add(xn, NULL) < 0)
+ goto done;
+ }
+ /*
+ * 1. Check xn arguments with input statement.
+ * 2. Send to backend as clicon_msg-encode()
+ * 3. In backend to similar but there call actual backend
+ */
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ goto done;
+ retval = 1; /* handled by callback */
+ goto done;
+ }
retval = 0;
done:
return retval;
diff --git a/example/Makefile.in b/example/Makefile.in
index 0ebdc3ad..6c94b660 100644
--- a/example/Makefile.in
+++ b/example/Makefile.in
@@ -67,6 +67,7 @@ YANGSPECS += ietf-routing@2014-10-26.yang
YANGSPECS += ietf-ipv4-unicast-routing@2014-10-26.yang
YANGSPECS += ietf-ipv6-unicast-routing@2014-10-26.yang
YANGSPECS += ietf-ipsec@2016-03-09.yang
+YANGSPECS += example.yang
# Backend plugin
BE_SRC = routing_backend.c
diff --git a/example/README.md b/example/README.md
index b833feee..86806df6 100644
--- a/example/README.md
+++ b/example/README.md
@@ -65,22 +65,45 @@ Routing notification
...
```
-## Extending
+## Operation data
-Clixon has an extension mechanism which can be used to make extended internal
-netconf messages to the backend configuration engine. You may need this to
-make some special operation that is not covered by standard
-netconf functions. The example has a simple "echo" downcall
-mechanism that simply echoes what is sent down and is included for
-reference. A more realistic downcall would perform some action, such as
-reading some status.
+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 to use
+that, and the example includes such an example:
Example:
```
-cli> downcall "This is a string"
-This is a string
+cli> rpc ipv4
+
+
+
```
+The example works by creating a netconf rpc call and sending it to the backend: (see the fib_route_rpc() function).
+```
+
+
+ ipv4
+
+
+```
+
+The backend in turn registers a callback (fib_route()) which handles the RPC.
+```
+static int
+fib_route(clicon_handle h,
+ cxobj *xe, /* Request: */
+ struct client_entry *ce, /* Client session */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg) /* Argument given at register */
+{
+ cprintf(cbret, "");
+ return 0;
+}
+```
## State data
Netconf and restconf GET also returns state data, in contrast to
diff --git a/example/example.yang b/example/example.yang
new file mode 100644
index 00000000..3c6a6f3a
--- /dev/null
+++ b/example/example.yang
@@ -0,0 +1,10 @@
+module example {
+ import ietf-ip {
+ prefix ip;
+ }
+ import ietf-routing {
+ prefix rt;
+ }
+ description
+ "Example code that includes ietf-ip and ietf-routing";
+}
diff --git a/example/routing.conf.local b/example/routing.conf.local
index 698e56f4..4427abcf 100644
--- a/example/routing.conf.local
+++ b/example/routing.conf.local
@@ -5,11 +5,12 @@ CLICON_CLI_MODE routing
# Option used to construct initial yang file:
# [@]
-CLICON_YANG_MODULE_MAIN ietf-ip
+#CLICON_YANG_MODULE_MAIN ietf-ip
+CLICON_YANG_MODULE_MAIN example
# Option used to construct initial yang file:
# [@]
-CLICON_YANG_MODULE_REVISION 2014-06-16
+#CLICON_YANG_MODULE_REVISION 2014-06-16
# Generate code for CLI completion of existing db symbols
# CLICON_CLI_GENMODEL_COMPLETION 0
diff --git a/example/routing_backend.c b/example/routing_backend.c
index 33a86c11..e65bbd0d 100644
--- a/example/routing_backend.c
+++ b/example/routing_backend.c
@@ -119,14 +119,27 @@ notification_timer_setup(clicon_handle h)
return event_reg_timeout(t, notification_timer, h, "notification timer");
}
+/*! IETF Routing fib-route rpc */
static int
-routing_downcall(clicon_handle h,
- cxobj *xe, /* Request: */
- struct client_entry *ce, /* Client session */
- cbuf *cbret, /* Reply eg ... */
- void *arg) /* Argument given at register */
+fib_route(clicon_handle h,
+ cxobj *xe, /* Request: */
+ struct client_entry *ce, /* Client session */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg) /* Argument given at register */
{
- cprintf(cbret, "%s", xml_body(xe));
+ cprintf(cbret, "");
+ return 0;
+}
+
+/*! IETF Routing route-count rpc */
+static int
+route_count(clicon_handle h,
+ cxobj *xe, /* Request: */
+ struct client_entry *ce, /* Client session */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg) /* Argument given at register */
+{
+ cprintf(cbret, "");
return 0;
}
@@ -173,10 +186,15 @@ plugin_init(clicon_handle h)
if (notification_timer_setup(h) < 0)
goto done;
- /* Register callback for netconf application-specific rpc call */
- if (backend_rpc_cb_register(h, routing_downcall,
+ /* Register callback for routing rpc calls */
+ if (backend_rpc_cb_register(h, fib_route,
NULL,
- "myrouting"/* Xml tag when callback is made */
+ "fib-route"/* Xml tag when callback is made */
+ ) < 0)
+ goto done;
+ if (backend_rpc_cb_register(h, route_count,
+ NULL,
+ "route-count"/* Xml tag when callback is made */
) < 0)
goto done;
retval = 0;
diff --git a/example/routing_cli.c b/example/routing_cli.c
index e2e67b2b..38bf2125 100644
--- a/example/routing_cli.c
+++ b/example/routing_cli.c
@@ -92,40 +92,36 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
return retval;
}
-/*! get argument and send as string to backend as RPC (which returns the string)
- */
+/*! Example "downcall": ietf-routing fib-route RPC */
int
-downcall(clicon_handle h,
- cvec *vars,
- cvec *argv)
+fib_route_rpc(clicon_handle h,
+ cvec *cvv,
+ cvec *argv)
{
- int retval = -1;
- struct clicon_msg *msg = NULL;
- char *str="";
- cg_var *cv;
- cxobj *xret=NULL;
- cxobj *xerr;
- cxobj *xdata;
+ int retval = -1;
+ cg_var *instance;
+ cxobj *xtop = NULL;
+ cxobj *xrpc;
+ cxobj *xret = NULL;
- if (cvec_len(vars)==2){
- if ((cv = cvec_i(vars, 1)) != NULL)
- str = cv_string_get(cv);
- }
- if ((msg = clicon_msg_encode("%s", str)) == NULL)
+ /* User supplied variable in CLI command */
+ instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
+ /* Create XML for fib-route netconf RPC */
+ if (clicon_xml_parse(&xtop, "%s", instance) < 0)
goto done;
- if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
+ /* Skip top-level */
+ xrpc = xml_child_i(xtop, 0);
+ /* Send to backend */
+ if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0)
goto done;
- if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
- clicon_rpc_generate_error(xerr);
- goto done;
- }
- if ((xdata = xpath_first(xret, "//ok")) != NULL)
- cli_output(stdout, "%s\n", xml_body(xdata));
- retval = 0;
- done:
+ /* Print result */
+ xml_print(stdout, xml_child_i(xret, 0));
+ retval = 0;
+ done:
if (xret)
- xml_free(xret);
- if (msg)
- free(msg);
+ xml_free(xret);
+ if (xtop)
+ xml_free(xtop);
return retval;
}
+
diff --git a/example/routing_cli.cli b/example/routing_cli.cli
index 78899c77..2edacd01 100644
--- a/example/routing_cli.cli
+++ b/example/routing_cli.cli
@@ -4,10 +4,10 @@ CLICON_PROMPT="%U@%H> ";
CLICON_PLUGIN="routing_cli";
# Note, when switching to PT, change datamodel to only @datamodel
-set @datamodel:ietf-ip, cli_set();
-merge @datamodel:ietf-ip, cli_merge();
-create @datamodel:ietf-ip, cli_create();
-delete("Delete a configuration item") @datamodel:ietf-ip, cli_del();
+set @datamodel:example, cli_set();
+merge @datamodel:example, cli_merge();
+create @datamodel:example, cli_create();
+delete("Delete a configuration item") @datamodel:example, cli_del();
validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit();
@@ -49,7 +49,7 @@ load("Load configuration from XML file") ("Filename (local file
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
}
example("This is a comment") ("Just a random number"), mycallback("myarg");
-downcall("This is a downcall") , downcall();
+rpc("fib-route rpc") ("routing instance"), fib_route_rpc("myarg");
notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text");
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
lock,cli_lock("candidate");
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 79838b36..408567b9 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -54,7 +54,7 @@
* - Cant use the symbols in this file because yacc needs token definitions
*/
enum rfc_6020{
- Y_ANYXML,
+ Y_ANYXML = 0,
Y_ARGUMENT,
Y_AUGMENT,
Y_BASE,
@@ -208,7 +208,8 @@ 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,
const char *module, const char *revision, yang_spec *ysp);
-int yang_apply(yang_node *yn, yang_applyfn_t fn, void *arg);
+int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
+ void *arg);
yang_node *yang_xpath_abs(yang_node *yn, char *xpath);
yang_node *yang_xpath(yang_node *yn, char *xpath);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 43daa3c2..16576bb6 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -373,7 +373,7 @@ yn_each(yang_node *yn,
*
* @param[in] yn Yang node, current context node.
* @param[in] keyword if 0 match any keyword
- * @param[in] argument if NULL, match any argument.
+ * @param[in] argument String compare w wrgument. if NULL, match any.
* This however means that if you actually want to match only a yang-stmt with
* argument==NULL you cannot, but I have not seen any such examples.
* @see yang_find_syntax
@@ -1587,15 +1587,15 @@ yang_parse(clicon_handle h,
clicon_dbspec_name_set(h, ymod->ys_argument);
/* Resolve all types */
- yang_apply((yang_node*)ysp, ys_resolve_type, NULL);
+ yang_apply((yang_node*)ysp, Y_TYPE, ys_resolve_type, NULL);
/* Step 2: Macro expansion of all grouping/uses pairs. Expansion needs marking */
if (yang_expand((yang_node*)ysp) < 0)
goto done;
- yang_apply((yang_node*)ymod, ys_flag_reset, (void*)YANG_FLAG_MARK);
+ yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
/* Step 3: Go through parse tree and populate it with cv types */
- if (yang_apply((yang_node*)ysp, ys_populate, NULL) < 0)
+ if (yang_apply((yang_node*)ysp, -1, ys_populate, NULL) < 0)
goto done;
/* Step 4: Top-level augmentation of all modules */
@@ -1615,6 +1615,7 @@ yang_parse(clicon_handle h,
* The tree is traversed depth-first, which at least guarantees that a parent is
* traversed before a child.
* @param[in] yn yang node
+ * @param[in] key yang keyword to use as filer or -1 for all
* @param[in] fn Callback
* @param[in] arg Argument
* @retval -1 Error, aborted at first error encounter
@@ -1625,12 +1626,13 @@ yang_parse(clicon_handle h,
* {
* return 0;
* }
- * yang_apply((yang_node*)ys, ys_fn, NULL);
+ * yang_apply((yang_node*)ys, Y_TYPE, ys_fn, NULL);
* @endcode
* @note do not delete or move around any children during this function
*/
int
yang_apply(yang_node *yn,
+ enum rfc_6020 keyword,
yang_applyfn_t fn,
void *arg)
{
@@ -1641,9 +1643,15 @@ yang_apply(yang_node *yn,
for (i=0; iyn_len; i++){
ys = yn->yn_stmt[i];
- if (fn(ys, arg) < 0)
- goto done;
- if ((ret = yang_apply((yang_node*)ys, fn, arg)) < 0)
+ if (keyword == -1 || keyword == ys->ys_keyword){
+ if ((ret = fn(ys, arg)) < 0)
+ goto done;
+ if (ret > 0){
+ retval = ret;
+ goto done;
+ }
+ }
+ if ((ret = yang_apply((yang_node*)ys, keyword, fn, arg)) < 0)
goto done;
if (ret > 0){
retval = ret;
diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c
index 99100149..79874b88 100644
--- a/lib/src/clixon_yang_type.c
+++ b/lib/src/clixon_yang_type.c
@@ -218,8 +218,6 @@ ys_resolve_type(yang_stmt *ys,
uint8_t fraction = 0;
yang_stmt *resolved = NULL;
- if (ys->ys_keyword != Y_TYPE)
- return 0;
if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
&options, &mincv, &maxcv, &pattern, &fraction) < 0)
goto done;
diff --git a/test/test1.sh b/test/test1.sh
index e7f34af4..d60031ed 100755
--- a/test/test1.sh
+++ b/test/test1.sh
@@ -87,7 +87,7 @@ expectfn "$clixon_cli -1f $clixon_cf -l o debug level 1" ""
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 0" ""
new "cli downcall"
-expectfn "$clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$"
+expectfn "$clixon_cli -1f $clixon_cf -l o rpc ipv4" "^"
new "Kill backend"
# Check if still alive
diff --git a/test/test4.sh b/test/test4.sh
index 4ce09d69..78121d3f 100755
--- a/test/test4.sh
+++ b/test/test4.sh
@@ -10,7 +10,7 @@ clixon_netconf=clixon_netconf
clixon_cli=clixon_cli
cat < /tmp/test.yang
-module ietf-ip{
+module example{
container x {
list y {
key "a b";
diff --git a/test/test6.sh b/test/test6.sh
index e180e7b9..e3cf7b07 100755
--- a/test/test6.sh
+++ b/test/test6.sh
@@ -9,47 +9,21 @@
clixon_netconf=clixon_netconf
clixon_cli=clixon_cli
-cat < /tmp/rpc.yang
-module ietf-ip{
- rpc fib-route {
- input {
- leaf name {
- type string;
- mandatory "true";
- }
- leaf destination-address {
- type string;
- }
- }
- output {
- container route {
- leaf address{
- type string;
- }
- leaf address{
- type string;
- }
- }
- }
- }
-}
-EOF
-
# kill old backend (if any)
new "kill old backend"
-sudo clixon_backend -zf $clixon_cf -y /tmp/rpc
+sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "start backend"
# start new backend
-sudo clixon_backend -If $clixon_cf -y /tmp/rpc
+sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
-new "netconf rpc (notyet)"
-#expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/rpc" "]]>]]>" "^]]>]]>$"
+new "netconf rpc"
+expecteof "$clixon_netconf -qf $clixon_cf" "ipv4]]>]]>" "^]]>]]>$"
new "Kill backend"
# Check if still alive
diff --git a/test/test7.sh b/test/test7.sh
index d53a8f9f..34389b7c 100755
--- a/test/test7.sh
+++ b/test/test7.sh
@@ -10,7 +10,7 @@ clixon_netconf=clixon_netconf
clixon_cli=clixon_cli
cat < /tmp/leafref.yang
-module ietf-ip{
+module example{
typedef admin-status{
type string;
}