Added YANG RPC support, with example rpc documentation and testcase (test7.sh); Extended example with ietf-routing (not only ietf-ip).
This commit is contained in:
parent
f995f1e268
commit
e56cf607a3
17 changed files with 177 additions and 112 deletions
|
|
@ -1,5 +1,9 @@
|
||||||
# Clixon CHANGELOG
|
# 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 completion for generated cli leafrefs for both absolute and relatve paths.
|
||||||
|
|
||||||
- Added validation for leafref forward and backward references.
|
- Added validation for leafref forward and backward references.
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ The [YANG RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) is implemented w
|
||||||
- object-references
|
- object-references
|
||||||
- if-feature
|
- if-feature
|
||||||
- unique
|
- unique
|
||||||
- rpc
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,26 +196,28 @@ catch:
|
||||||
return -1;
|
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
|
static int
|
||||||
ys_find_rpc(yang_stmt *ys,
|
ys_find_rpc(yang_stmt *ys,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
cxobj *xn = (cxobj*)arg;
|
find_rpc_arg *fra = (find_rpc_arg*)arg;
|
||||||
char *name = xml_name(xn);
|
|
||||||
|
|
||||||
if (ys->ys_keyword == Y_RPC && strcmp(name, ys->ys_argument) == 0){
|
if (strcmp(fra->name, ys->ys_argument) == 0){
|
||||||
/*
|
fra->yrpc = ys;
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
return 1; /* handled */
|
return 1; /* handled */
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! See if there is any callback registered for this tag
|
/*! See if there is any callback registered for this tag
|
||||||
*
|
*
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
|
|
@ -234,6 +236,10 @@ netconf_plugin_callbacks(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
netconf_reg_t *nreg;
|
netconf_reg_t *nreg;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
yang_stmt *yrpc;
|
||||||
|
yang_stmt *yinput;
|
||||||
|
find_rpc_arg fra = {0,0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (deps != NULL){
|
if (deps != NULL){
|
||||||
nreg = deps;
|
nreg = deps;
|
||||||
|
|
@ -249,8 +255,33 @@ netconf_plugin_callbacks(clicon_handle h,
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
goto done;
|
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;
|
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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ YANGSPECS += ietf-routing@2014-10-26.yang
|
||||||
YANGSPECS += ietf-ipv4-unicast-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-ipv6-unicast-routing@2014-10-26.yang
|
||||||
YANGSPECS += ietf-ipsec@2016-03-09.yang
|
YANGSPECS += ietf-ipsec@2016-03-09.yang
|
||||||
|
YANGSPECS += example.yang
|
||||||
|
|
||||||
# Backend plugin
|
# Backend plugin
|
||||||
BE_SRC = routing_backend.c
|
BE_SRC = routing_backend.c
|
||||||
|
|
|
||||||
|
|
@ -65,22 +65,45 @@ Routing notification
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending
|
## Operation data
|
||||||
|
|
||||||
Clixon has an extension mechanism which can be used to make extended internal
|
Clixon implements Yang RPC operations by an extension mechanism. The
|
||||||
netconf messages to the backend configuration engine. You may need this to
|
extension mechanism enables you to add application-specific
|
||||||
make some special operation that is not covered by standard
|
operations. It works by adding user-defined callbacks for added
|
||||||
netconf functions. The example has a simple "echo" downcall
|
netconf operations. It is possible to use the extension mechanism
|
||||||
mechanism that simply echoes what is sent down and is included for
|
independent of the yang rpc construct, but it is recommended to use
|
||||||
reference. A more realistic downcall would perform some action, such as
|
that, and the example includes such an example:
|
||||||
reading some status.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```
|
```
|
||||||
cli> downcall "This is a string"
|
cli> rpc ipv4
|
||||||
This is a string
|
<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>
|
||||||
|
```
|
||||||
|
|
||||||
|
The backend in turn registers a callback (fib_route()) which handles the RPC.
|
||||||
|
```
|
||||||
|
static int
|
||||||
|
fib_route(clicon_handle h,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
## State data
|
## State data
|
||||||
|
|
||||||
Netconf <get> and restconf GET also returns state data, in contrast to
|
Netconf <get> and restconf GET also returns state data, in contrast to
|
||||||
|
|
|
||||||
10
example/example.yang
Normal file
10
example/example.yang
Normal file
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,12 @@ CLICON_CLI_MODE routing
|
||||||
|
|
||||||
# Option used to construct initial yang file:
|
# Option used to construct initial yang file:
|
||||||
# <module>[@<revision>]
|
# <module>[@<revision>]
|
||||||
CLICON_YANG_MODULE_MAIN ietf-ip
|
#CLICON_YANG_MODULE_MAIN ietf-ip
|
||||||
|
CLICON_YANG_MODULE_MAIN example
|
||||||
|
|
||||||
# Option used to construct initial yang file:
|
# Option used to construct initial yang file:
|
||||||
# <module>[@<revision>]
|
# <module>[@<revision>]
|
||||||
CLICON_YANG_MODULE_REVISION 2014-06-16
|
#CLICON_YANG_MODULE_REVISION 2014-06-16
|
||||||
|
|
||||||
# Generate code for CLI completion of existing db symbols
|
# Generate code for CLI completion of existing db symbols
|
||||||
# CLICON_CLI_GENMODEL_COMPLETION 0
|
# CLICON_CLI_GENMODEL_COMPLETION 0
|
||||||
|
|
|
||||||
|
|
@ -119,14 +119,27 @@ notification_timer_setup(clicon_handle h)
|
||||||
return event_reg_timeout(t, notification_timer, h, "notification timer");
|
return event_reg_timeout(t, notification_timer, h, "notification timer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! IETF Routing fib-route rpc */
|
||||||
static int
|
static int
|
||||||
routing_downcall(clicon_handle h,
|
fib_route(clicon_handle h,
|
||||||
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>%s</ok></rpc-reply>", xml_body(xe));
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! IETF Routing route-count rpc */
|
||||||
|
static int
|
||||||
|
route_count(clicon_handle h,
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,10 +186,15 @@ plugin_init(clicon_handle h)
|
||||||
|
|
||||||
if (notification_timer_setup(h) < 0)
|
if (notification_timer_setup(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Register callback for netconf application-specific rpc call */
|
/* Register callback for routing rpc calls */
|
||||||
if (backend_rpc_cb_register(h, routing_downcall,
|
if (backend_rpc_cb_register(h, fib_route,
|
||||||
NULL,
|
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)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -92,40 +92,36 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! get argument and send as string to backend as RPC (which returns the string)
|
/*! Example "downcall": ietf-routing fib-route RPC */
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
downcall(clicon_handle h,
|
fib_route_rpc(clicon_handle h,
|
||||||
cvec *vars,
|
cvec *cvv,
|
||||||
cvec *argv)
|
cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg = NULL;
|
cg_var *instance;
|
||||||
char *str="";
|
cxobj *xtop = NULL;
|
||||||
cg_var *cv;
|
cxobj *xrpc;
|
||||||
cxobj *xret=NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
|
||||||
cxobj *xdata;
|
|
||||||
|
|
||||||
if (cvec_len(vars)==2){
|
/* User supplied variable in CLI command */
|
||||||
if ((cv = cvec_i(vars, 1)) != NULL)
|
instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
|
||||||
str = cv_string_get(cv);
|
/* Create XML for fib-route netconf RPC */
|
||||||
}
|
if (clicon_xml_parse(&xtop, "<rpc><fib-route><routing-instance-name>%s</routing-instance-name></fib-route></rpc>", instance) < 0)
|
||||||
if ((msg = clicon_msg_encode("<rpc><myrouting>%s</myrouting></rpc>", str)) == NULL)
|
|
||||||
goto done;
|
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;
|
goto done;
|
||||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
/* Print result */
|
||||||
clicon_rpc_generate_error(xerr);
|
xml_print(stdout, xml_child_i(xret, 0));
|
||||||
goto done;
|
retval = 0;
|
||||||
}
|
done:
|
||||||
if ((xdata = xpath_first(xret, "//ok")) != NULL)
|
|
||||||
cli_output(stdout, "%s\n", xml_body(xdata));
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (xret)
|
if (xret)
|
||||||
xml_free(xret);
|
xml_free(xret);
|
||||||
if (msg)
|
if (xtop)
|
||||||
free(msg);
|
xml_free(xtop);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ CLICON_PROMPT="%U@%H> ";
|
||||||
CLICON_PLUGIN="routing_cli";
|
CLICON_PLUGIN="routing_cli";
|
||||||
|
|
||||||
# Note, when switching to PT, change datamodel to only @datamodel
|
# Note, when switching to PT, change datamodel to only @datamodel
|
||||||
set @datamodel:ietf-ip, cli_set();
|
set @datamodel:example, cli_set();
|
||||||
merge @datamodel:ietf-ip, cli_merge();
|
merge @datamodel:example, cli_merge();
|
||||||
create @datamodel:ietf-ip, cli_create();
|
create @datamodel:example, cli_create();
|
||||||
delete("Delete a configuration item") @datamodel:ietf-ip, cli_del();
|
delete("Delete a configuration item") @datamodel:example, cli_del();
|
||||||
|
|
||||||
validate("Validate changes"), cli_validate();
|
validate("Validate changes"), cli_validate();
|
||||||
commit("Commit the changes"), cli_commit();
|
commit("Commit the changes"), cli_commit();
|
||||||
|
|
@ -49,7 +49,7 @@ load("Load configuration from XML file") <filename:string>("Filename (local file
|
||||||
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
|
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
|
||||||
}
|
}
|
||||||
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
|
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
|
||||||
downcall("This is a downcall") <str:rest>, downcall();
|
rpc("fib-route rpc") <instance:string>("routing instance"), fib_route_rpc("myarg");
|
||||||
notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text");
|
notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text");
|
||||||
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
|
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
|
||||||
lock,cli_lock("candidate");
|
lock,cli_lock("candidate");
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
* - Cant use the symbols in this file because yacc needs token definitions
|
* - Cant use the symbols in this file because yacc needs token definitions
|
||||||
*/
|
*/
|
||||||
enum rfc_6020{
|
enum rfc_6020{
|
||||||
Y_ANYXML,
|
Y_ANYXML = 0,
|
||||||
Y_ARGUMENT,
|
Y_ARGUMENT,
|
||||||
Y_AUGMENT,
|
Y_AUGMENT,
|
||||||
Y_BASE,
|
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_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, 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_abs(yang_node *yn, char *xpath);
|
||||||
yang_node *yang_xpath(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);
|
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,7 @@ yn_each(yang_node *yn,
|
||||||
*
|
*
|
||||||
* @param[in] yn Yang node, current context node.
|
* @param[in] yn Yang node, current context node.
|
||||||
* @param[in] keyword if 0 match any keyword
|
* @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
|
* 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.
|
* argument==NULL you cannot, but I have not seen any such examples.
|
||||||
* @see yang_find_syntax
|
* @see yang_find_syntax
|
||||||
|
|
@ -1587,15 +1587,15 @@ yang_parse(clicon_handle h,
|
||||||
clicon_dbspec_name_set(h, ymod->ys_argument);
|
clicon_dbspec_name_set(h, ymod->ys_argument);
|
||||||
|
|
||||||
/* Resolve all types */
|
/* 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 */
|
/* Step 2: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
||||||
if (yang_expand((yang_node*)ysp) < 0)
|
if (yang_expand((yang_node*)ysp) < 0)
|
||||||
goto done;
|
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 */
|
/* 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;
|
goto done;
|
||||||
|
|
||||||
/* Step 4: Top-level augmentation of all modules */
|
/* 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
|
* The tree is traversed depth-first, which at least guarantees that a parent is
|
||||||
* traversed before a child.
|
* traversed before a child.
|
||||||
* @param[in] yn yang node
|
* @param[in] yn yang node
|
||||||
|
* @param[in] key yang keyword to use as filer or -1 for all
|
||||||
* @param[in] fn Callback
|
* @param[in] fn Callback
|
||||||
* @param[in] arg Argument
|
* @param[in] arg Argument
|
||||||
* @retval -1 Error, aborted at first error encounter
|
* @retval -1 Error, aborted at first error encounter
|
||||||
|
|
@ -1625,12 +1626,13 @@ yang_parse(clicon_handle h,
|
||||||
* {
|
* {
|
||||||
* return 0;
|
* return 0;
|
||||||
* }
|
* }
|
||||||
* yang_apply((yang_node*)ys, ys_fn, NULL);
|
* yang_apply((yang_node*)ys, Y_TYPE, ys_fn, NULL);
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note do not delete or move around any children during this function
|
* @note do not delete or move around any children during this function
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang_apply(yang_node *yn,
|
yang_apply(yang_node *yn,
|
||||||
|
enum rfc_6020 keyword,
|
||||||
yang_applyfn_t fn,
|
yang_applyfn_t fn,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
|
|
@ -1641,9 +1643,15 @@ yang_apply(yang_node *yn,
|
||||||
|
|
||||||
for (i=0; i<yn->yn_len; i++){
|
for (i=0; i<yn->yn_len; i++){
|
||||||
ys = yn->yn_stmt[i];
|
ys = yn->yn_stmt[i];
|
||||||
if (fn(ys, arg) < 0)
|
if (keyword == -1 || keyword == ys->ys_keyword){
|
||||||
goto done;
|
if ((ret = fn(ys, arg)) < 0)
|
||||||
if ((ret = yang_apply((yang_node*)ys, fn, arg)) < 0)
|
goto done;
|
||||||
|
if (ret > 0){
|
||||||
|
retval = ret;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((ret = yang_apply((yang_node*)ys, keyword, fn, arg)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret > 0){
|
if (ret > 0){
|
||||||
retval = ret;
|
retval = ret;
|
||||||
|
|
|
||||||
|
|
@ -218,8 +218,6 @@ ys_resolve_type(yang_stmt *ys,
|
||||||
uint8_t fraction = 0;
|
uint8_t fraction = 0;
|
||||||
yang_stmt *resolved = NULL;
|
yang_stmt *resolved = NULL;
|
||||||
|
|
||||||
if (ys->ys_keyword != Y_TYPE)
|
|
||||||
return 0;
|
|
||||||
if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
|
if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
|
||||||
&options, &mincv, &maxcv, &pattern, &fraction) < 0)
|
&options, &mincv, &maxcv, &pattern, &fraction) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -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" ""
|
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 0" ""
|
||||||
|
|
||||||
new "cli downcall"
|
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" "^<rpc-reply>"
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ clixon_netconf=clixon_netconf
|
||||||
clixon_cli=clixon_cli
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
cat <<EOF > /tmp/test.yang
|
cat <<EOF > /tmp/test.yang
|
||||||
module ietf-ip{
|
module example{
|
||||||
container x {
|
container x {
|
||||||
list y {
|
list y {
|
||||||
key "a b";
|
key "a b";
|
||||||
|
|
|
||||||
|
|
@ -9,47 +9,21 @@
|
||||||
clixon_netconf=clixon_netconf
|
clixon_netconf=clixon_netconf
|
||||||
clixon_cli=clixon_cli
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
cat <<EOF > /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)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
sudo clixon_backend -zf $clixon_cf -y /tmp/rpc
|
sudo clixon_backend -zf $clixon_cf
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "start backend"
|
new "start backend"
|
||||||
# start new backend
|
# start new backend
|
||||||
sudo clixon_backend -If $clixon_cf -y /tmp/rpc
|
sudo clixon_backend -If $clixon_cf
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
new "netconf rpc (notyet)"
|
new "netconf rpc"
|
||||||
#expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/rpc" "<rpc><fib-route><name></name></fib-route></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
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"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ clixon_netconf=clixon_netconf
|
||||||
clixon_cli=clixon_cli
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
cat <<EOF > /tmp/leafref.yang
|
cat <<EOF > /tmp/leafref.yang
|
||||||
module ietf-ip{
|
module example{
|
||||||
typedef admin-status{
|
typedef admin-status{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue