* Yang "min-element" and "max-element" feature supported
* New Clixon Yang RPC: ping. To check if backup is running. * Fixed support for multiple datanodes in a choice/case statement.
This commit is contained in:
parent
2fc37d2470
commit
c529847790
20 changed files with 776 additions and 286 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -3,8 +3,15 @@
|
||||||
## 3.10.0/4.0.0 (Upcoming)
|
## 3.10.0/4.0.0 (Upcoming)
|
||||||
|
|
||||||
### Major New features
|
### Major New features
|
||||||
|
* Yang "min-element" and "max-element" feature supported
|
||||||
|
* According to RFC 7950 7.7.4 and 7.7.5
|
||||||
|
* See (tests)[test/test_minmax.sh]
|
||||||
|
* The following cornercases are not supported:
|
||||||
|
* Check for min-elements>0 for empty lists on top-level
|
||||||
|
* Check for min-elements>0 for empty lists in choice/case
|
||||||
* Yang "unique" feature supported
|
* Yang "unique" feature supported
|
||||||
* See RFC 7950 7.8.3
|
* According to RFC 7950 7.8.3
|
||||||
|
* See (tests)[test/test_unique.sh]
|
||||||
* Persistent CLI history: [Preserve CLI command history across sessions. The up/down arrows](https://github.com/clicon/clixon/issues/79)
|
* Persistent CLI history: [Preserve CLI command history across sessions. The up/down arrows](https://github.com/clicon/clixon/issues/79)
|
||||||
* The design is similar to bash history:
|
* The design is similar to bash history:
|
||||||
* The CLI loads/saves its complete history to a file on entry and exit, respectively
|
* The CLI loads/saves its complete history to a file on entry and exit, respectively
|
||||||
|
|
@ -46,6 +53,8 @@
|
||||||
|
|
||||||
### API changes on existing features (you may need to change your code)
|
### API changes on existing features (you may need to change your code)
|
||||||
|
|
||||||
|
* New Clixon Yang RPC: ping. To check if backup is running.
|
||||||
|
* Try with `<rpc xmlns="http://clicon.org/lib"><ping/></rpc>]]>]]>`
|
||||||
* Restconf with startup feature will now copy all edit changes to startup db (as it should according to RFC 8040)
|
* Restconf with startup feature will now copy all edit changes to startup db (as it should according to RFC 8040)
|
||||||
* Netconf Startup feature is no longer hardcoded, you need to explicitly enable it (See RFC 6241, Section 8.7)
|
* Netconf Startup feature is no longer hardcoded, you need to explicitly enable it (See RFC 6241, Section 8.7)
|
||||||
* Enable in config file with: `<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>`, or use `*:*`
|
* Enable in config file with: `<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>`, or use `*:*`
|
||||||
|
|
@ -116,6 +125,8 @@
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
|
||||||
|
* Netconf error handling modified
|
||||||
|
* New option -e added. If set, the netconf client returns -1 on error.
|
||||||
* A new minimal "hello world" example has been added
|
* A new minimal "hello world" example has been added
|
||||||
* Experimental customized error output strings, see [lib/clixon/clixon_err_string.h]
|
* Experimental customized error output strings, see [lib/clixon/clixon_err_string.h]
|
||||||
* Empty leaf values, eg <a></a> are now checked at validation.
|
* Empty leaf values, eg <a></a> are now checked at validation.
|
||||||
|
|
@ -143,6 +154,7 @@
|
||||||
* Added libgen.h for baseline()
|
* Added libgen.h for baseline()
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
* Fixed support for multiple datanodes in a choice/case statement. Only single datanode was supported.
|
||||||
* Fixed an ordering problem showing up in validate/commit callbacks. If two new items following each order (yang-wise), only the first showed up in the new-list. Thanks achernavin!
|
* Fixed an ordering problem showing up in validate/commit callbacks. If two new items following each order (yang-wise), only the first showed up in the new-list. Thanks achernavin!
|
||||||
* Fixed a problem caused by recent sorting patches that made "ordered-by user" lists fail in some cases, causing multiple list entries with same keys. NACM being one example. Thanks vratnikov!
|
* Fixed a problem caused by recent sorting patches that made "ordered-by user" lists fail in some cases, causing multiple list entries with same keys. NACM being one example. Thanks vratnikov!
|
||||||
* [Restconf does not handle startup datastore according to the RFC](https://github.com/clicon/clixon/issues/74)
|
* [Restconf does not handle startup datastore according to the RFC](https://github.com/clicon/clixon/issues/74)
|
||||||
|
|
|
||||||
|
|
@ -111,12 +111,11 @@ Clixon follows:
|
||||||
|
|
||||||
However, the following YANG syntax modules are not implemented:
|
However, the following YANG syntax modules are not implemented:
|
||||||
- deviation
|
- deviation
|
||||||
- min/max-elements
|
|
||||||
- action
|
- action
|
||||||
- refine
|
- refine
|
||||||
- Yang extended Xpath functions: re-match, deref, derived-from, derived-from-or-self, enum-value, bit-is-set
|
|
||||||
|
|
||||||
Restrictions on Yang types are as follows:
|
The following restrictions on Yang exists:
|
||||||
|
- Yang extended Xpath functions: re-match, deref, derived-from, derived-from-or-self, enum-value, bit-is-set
|
||||||
- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60)
|
- Submodules cannot re-use a prefix in an import statement that is already used for another imported module in the module that the submodule belongs to. (see https://github.com/clicon/clixon/issues/60)
|
||||||
- Default values on leaf-lists are not supported (RFC7950 7.7.2)
|
- Default values on leaf-lists are not supported (RFC7950 7.7.2)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1039,6 +1039,26 @@ from_client_debug(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Check liveness of backend daemon, just send a reply
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
|
* @param[in] arg client-entry
|
||||||
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
from_client_ping(clicon_handle h,
|
||||||
|
cxobj *xe,
|
||||||
|
cbuf *cbret,
|
||||||
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
|
{
|
||||||
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! An internal clicon message has arrived from a client. Receive and dispatch.
|
/*! An internal clicon message has arrived from a client. Receive and dispatch.
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] s Socket where message arrived. read from this.
|
* @param[in] s Socket where message arrived. read from this.
|
||||||
|
|
@ -1271,10 +1291,13 @@ backend_rpc_init(clicon_handle h)
|
||||||
if (rpc_callback_register(h, from_client_create_subscription, NULL,
|
if (rpc_callback_register(h, from_client_create_subscription, NULL,
|
||||||
"urn:ietf:params:xml:ns:netmod:notification", "create-subscription") < 0)
|
"urn:ietf:params:xml:ns:netmod:notification", "create-subscription") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* In backend_client.? Clixon RPC */
|
/* Clixon RPC */
|
||||||
if (rpc_callback_register(h, from_client_debug, NULL,
|
if (rpc_callback_register(h, from_client_debug, NULL,
|
||||||
"http://clicon.org/lib", "debug") < 0)
|
"http://clicon.org/lib", "debug") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (rpc_callback_register(h, from_client_ping, NULL,
|
||||||
|
"http://clicon.org/lib", "ping") < 0)
|
||||||
|
goto done;
|
||||||
retval =0;
|
retval =0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,13 @@
|
||||||
#include "netconf_rpc.h"
|
#include "netconf_rpc.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define NETCONF_OPTS "hD:f:l:qa:u:d:p:y:U:t:o:"
|
#define NETCONF_OPTS "hD:f:l:qa:u:d:p:y:U:t:eo:"
|
||||||
|
|
||||||
#define NETCONF_LOGFILE "/tmp/clixon_netconf.log"
|
#define NETCONF_LOGFILE "/tmp/clixon_netconf.log"
|
||||||
|
|
||||||
|
/*! Ignore errors on packet errors: continue */
|
||||||
|
static int ignore_packet_errors = 1;
|
||||||
|
|
||||||
/*! Process incoming packet
|
/*! Process incoming packet
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cb Packet buffer
|
* @param[in] cb Packet buffer
|
||||||
|
|
@ -235,8 +238,9 @@ netconf_input_cb(int s,
|
||||||
/* OK, we have an xml string from a client */
|
/* OK, we have an xml string from a client */
|
||||||
/* Remove trailer */
|
/* Remove trailer */
|
||||||
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
||||||
if (netconf_input_packet(h, cb) < 0)
|
if (netconf_input_packet(h, cb) < 0 &&
|
||||||
; //goto done; // ignore errors
|
!ignore_packet_errors) // default is to ignore errors
|
||||||
|
goto done;
|
||||||
if (cc_closed)
|
if (cc_closed)
|
||||||
break;
|
break;
|
||||||
cbuf_reset(cb);
|
cbuf_reset(cb);
|
||||||
|
|
@ -338,6 +342,7 @@ usage(clicon_handle h,
|
||||||
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
|
"\t-y <file>\tLoad yang spec file (override yang main module)\n"
|
||||||
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
|
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
|
||||||
"\t-t <sec>\tTimeout in seconds. Quit after this time.\n"
|
"\t-t <sec>\tTimeout in seconds. Quit after this time.\n"
|
||||||
|
"\t-e \tDont ignore errors on packet input.\n"
|
||||||
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
|
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
|
||||||
argv0,
|
argv0,
|
||||||
clicon_netconf_dir(h)
|
clicon_netconf_dir(h)
|
||||||
|
|
@ -349,6 +354,7 @@ int
|
||||||
main(int argc,
|
main(int argc,
|
||||||
char **argv)
|
char **argv)
|
||||||
{
|
{
|
||||||
|
int retval = -1;
|
||||||
int c;
|
int c;
|
||||||
char *argv0 = argv[0];
|
char *argv0 = argv[0];
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
|
|
@ -455,6 +461,9 @@ main(int argc,
|
||||||
case 't': /* timeout in seconds */
|
case 't': /* timeout in seconds */
|
||||||
tv.tv_sec = atoi(optarg);
|
tv.tv_sec = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'e': /* dont ignore packet errors */
|
||||||
|
ignore_packet_errors = 0;
|
||||||
|
break;
|
||||||
case 'o':{ /* Configuration option */
|
case 'o':{ /* Configuration option */
|
||||||
char *val;
|
char *val;
|
||||||
if ((val = index(optarg, '=')) == NULL)
|
if ((val = index(optarg, '=')) == NULL)
|
||||||
|
|
@ -528,9 +537,12 @@ main(int argc,
|
||||||
}
|
}
|
||||||
if (event_loop() < 0)
|
if (event_loop() < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (ignore_packet_errors)
|
||||||
|
retval = 0;
|
||||||
netconf_terminate(h);
|
netconf_terminate(h);
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
|
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
|
||||||
return 0;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,10 @@ api_data_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
if (xretcom){ /* Clear: can be reused again below */
|
||||||
|
xml_free(xretcom);
|
||||||
|
xretcom = NULL;
|
||||||
|
}
|
||||||
if (if_feature(yspec, "ietf-netconf", "startup")){
|
if (if_feature(yspec, "ietf-netconf", "startup")){
|
||||||
/* RFC8040 Sec 1.4:
|
/* RFC8040 Sec 1.4:
|
||||||
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
||||||
|
|
@ -933,6 +937,10 @@ api_data_put(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
if (xretcom){ /* Clear: can be reused again below */
|
||||||
|
xml_free(xretcom);
|
||||||
|
xretcom = NULL;
|
||||||
|
}
|
||||||
if (if_feature(yspec, "ietf-netconf", "startup")){
|
if (if_feature(yspec, "ietf-netconf", "startup")){
|
||||||
/* RFC8040 Sec 1.4:
|
/* RFC8040 Sec 1.4:
|
||||||
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
||||||
|
|
@ -1108,6 +1116,10 @@ api_data_delete(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
if (xretcom){ /* Clear: can be reused again below */
|
||||||
|
xml_free(xretcom);
|
||||||
|
xretcom = NULL;
|
||||||
|
}
|
||||||
if (if_feature(yspec, "ietf-netconf", "startup")){
|
if (if_feature(yspec, "ietf-netconf", "startup")){
|
||||||
/* RFC8040 Sec 1.4:
|
/* RFC8040 Sec 1.4:
|
||||||
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
* If the NETCONF server supports :startup, the RESTCONF server MUST
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
|
||||||
int netconf_malformed_message(cbuf *cb, char *message);
|
int netconf_malformed_message(cbuf *cb, char *message);
|
||||||
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
||||||
int netconf_data_not_unique(cbuf *cb, cxobj *x, cvec *cvk);
|
int netconf_data_not_unique(cbuf *cb, cxobj *x, cvec *cvk);
|
||||||
|
int netconf_minmax_elements(cbuf *cb, cxobj *x, int max);
|
||||||
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
|
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
|
||||||
int netconf_module_load(clicon_handle h);
|
int netconf_module_load(clicon_handle h);
|
||||||
char *netconf_db_find(cxobj *xn, char *name);
|
char *netconf_db_find(cxobj *xn, char *name);
|
||||||
|
|
|
||||||
|
|
@ -1008,6 +1008,41 @@ netconf_data_not_unique(cbuf *cb,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create Netconf too-many/few-elements err msg according to RFC 7950 15.2/15.3
|
||||||
|
*
|
||||||
|
* A NETCONF operation would result in configuration data where a
|
||||||
|
list or a leaf-list would have too many entries, the following error
|
||||||
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
||||||
|
* @param[in] x List element containing duplicate
|
||||||
|
* @param[in] max If set, return too-many, otherwise too-few
|
||||||
|
* @see RFC7950 Sec 15.1
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_minmax_elements(cbuf *cb,
|
||||||
|
cxobj *x,
|
||||||
|
int max)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
if (cprintf(cb, "<rpc-reply><rpc-error>"
|
||||||
|
"<error-type>protocol</error-type>"
|
||||||
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
"<error-app-tag>too-%s-elements</error-app-tag>"
|
||||||
|
"<error-severity>error</error-severity>"
|
||||||
|
"<error-path>%s</error-path>"
|
||||||
|
"</rpc-error></rpc-reply>",
|
||||||
|
max?"many":"few",
|
||||||
|
xml_name(x)) < 0) /* XXX should be xml2xpath */
|
||||||
|
goto err;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
err:
|
||||||
|
clicon_err(OE_XML, errno, "cprintf");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Help function: merge - check yang - if error make netconf errmsg
|
/*! Help function: merge - check yang - if error make netconf errmsg
|
||||||
* @param[in] x XML tree
|
* @param[in] x XML tree
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
|
|
|
||||||
|
|
@ -503,7 +503,6 @@ rpc_callback_call(clicon_handle h,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int ret;
|
|
||||||
rpc_callback_t *rc;
|
rpc_callback_t *rc;
|
||||||
char *name;
|
char *name;
|
||||||
char *prefix;
|
char *prefix;
|
||||||
|
|
@ -520,7 +519,7 @@ rpc_callback_call(clicon_handle h,
|
||||||
if (strcmp(rc->rc_name, name) == 0 &&
|
if (strcmp(rc->rc_name, name) == 0 &&
|
||||||
namespace && rc->rc_namespace &&
|
namespace && rc->rc_namespace &&
|
||||||
strcmp(rc->rc_namespace, namespace) == 0){
|
strcmp(rc->rc_namespace, namespace) == 0){
|
||||||
if ((ret = rc->rc_callback(h, xe, cbret, arg, rc->rc_arg)) < 0){
|
if (rc->rc_callback(h, xe, cbret, arg, rc->rc_arg) < 0){
|
||||||
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name);
|
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -495,6 +495,7 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (cbret set)
|
* @retval 0 Validation failed (cbret set)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
* Check if xt is part of valid choice
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_choice(cxobj *xt,
|
check_choice(cxobj *xt,
|
||||||
|
|
@ -502,27 +503,59 @@ check_choice(cxobj *xt,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *yc;
|
|
||||||
yang_stmt *y;
|
yang_stmt *y;
|
||||||
|
yang_stmt *ytp; /* yt:s parent */
|
||||||
|
yang_stmt *ytcase = NULL; /* yt:s parent case if any */
|
||||||
|
yang_stmt *ytchoice = NULL;
|
||||||
yang_stmt *yp;
|
yang_stmt *yp;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xp;
|
cxobj *xp;
|
||||||
|
|
||||||
if ((yc = yang_choice(yt)) == NULL)
|
if ((ytp = yang_parent_get(yt)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
|
/* Return OK if xt is not choice */
|
||||||
|
switch (yang_keyword_get(ytp)){
|
||||||
|
case Y_CASE:
|
||||||
|
ytcase = ytp;
|
||||||
|
ytchoice = yang_parent_get(ytp);
|
||||||
|
break;
|
||||||
|
case Y_CHOICE:
|
||||||
|
ytchoice = ytp;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto ok; /* Not choice */
|
||||||
|
break;
|
||||||
|
}
|
||||||
if ((xp = xml_parent(xt)) == NULL)
|
if ((xp = xml_parent(xt)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
x = NULL; /* Find a child with same yang spec */
|
x = NULL; /* Find a child with same yang spec */
|
||||||
while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL) {
|
while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL) {
|
||||||
if ((x != xt) &&
|
if (x == xt)
|
||||||
(y = xml_spec(x)) != NULL &&
|
continue;
|
||||||
(yp = yang_choice(y)) != NULL &&
|
y = xml_spec(x);
|
||||||
yp == yc){
|
if (y == yt) /* eg same list */
|
||||||
if (netconf_bad_element(cbret, "application", xml_name(x), "Element in choice statement already exists") < 0)
|
continue;
|
||||||
goto done;
|
yp = yang_parent_get(y);
|
||||||
goto fail;
|
switch (yang_keyword_get(yp)){
|
||||||
|
case Y_CASE:
|
||||||
|
if (yang_parent_get(yp) != ytchoice) /* Not same choice (not releveant) */
|
||||||
|
continue;
|
||||||
|
if (yp == ytcase) /* same choice but different case */
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case Y_CHOICE:
|
||||||
|
if (yp != ytcase) /* Not same choice (not relevant) */
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue; /* not choice */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
if (netconf_bad_element(cbret, "application", xml_name(x), "Element in choice statement already exists") < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
} /* while */
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
|
@ -731,46 +764,168 @@ check_unique_list(cxobj *x,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Detect unique constraint for duplicates from parent node
|
/*! Given a list, check if any min/max-elemants constraints apply
|
||||||
|
* @param[in] x One x (the last) of a specific lis
|
||||||
|
* @param[in] y Yang spec of x
|
||||||
|
* @param[in] nr Number of elements (like x) in thlist
|
||||||
|
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||||
|
* @retval 1 Validation OK
|
||||||
|
* @retval 0 Validation failed (cbret set)
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see RFC7950 7.7.5
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
check_min_max(cxobj *x,
|
||||||
|
yang_stmt *y,
|
||||||
|
int nr,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_stmt *ymin; /* yang min */
|
||||||
|
yang_stmt *ymax; /* yang max */
|
||||||
|
cg_var *cv;
|
||||||
|
|
||||||
|
if ((ymin = yang_find(y, Y_MIN_ELEMENTS, NULL)) != NULL){
|
||||||
|
cv = yang_cv_get(ymin);
|
||||||
|
if (nr < cv_uint32_get(cv)){
|
||||||
|
if (netconf_minmax_elements(cbret, x, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((ymax = yang_find(y, Y_MAX_ELEMENTS, NULL)) != NULL){
|
||||||
|
cv = yang_cv_get(ymax);
|
||||||
|
if (cv_uint32_get(cv) > 0 && /* 0 means unbounded */
|
||||||
|
nr > cv_uint32_get(cv)){
|
||||||
|
if (netconf_minmax_elements(cbret, x, 1) < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Detect unique constraint for duplicates from parent node and minmax
|
||||||
* @param[in] xt XML parent (may have lists w unique constraints as child)
|
* @param[in] xt XML parent (may have lists w unique constraints as child)
|
||||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||||
* @retval 1 Validation OK
|
* @retval 1 Validation OK
|
||||||
* @retval 0 Validation failed (cbret set)
|
* @retval 0 Validation failed (cbret set)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* Assume xt:s children are sorted and yang populated.
|
* Assume xt:s children are sorted and yang populated.
|
||||||
* The routine finds the lists: ie xt may have several lists in the children,
|
* The function does two different things of the children of an XML node:
|
||||||
* example
|
* (1) Check min/max element constraints
|
||||||
* shows two lists [x1,..] and [x2,..].
|
* (2) Check unique constraints
|
||||||
|
*
|
||||||
|
* The routine uses a node traversing mechanism as the following example, where
|
||||||
|
* two lists [x1,..] and [x2,..] are embedded:
|
||||||
* xt: {a, b, [x1, x1, x1], d, e, f, [x2, x2, x2], g}
|
* xt: {a, b, [x1, x1, x1], d, e, f, [x2, x2, x2], g}
|
||||||
* Then call check_unique_list on each list.
|
|
||||||
* The function does this using a single iteration and uses the fact that the
|
* The function does this using a single iteration and uses the fact that the
|
||||||
* xml symbols share yang symbols: ie [x1..] has yang y1 and d has yd.
|
* xml symbols share yang symbols: ie [x1..] has yang y1 and d has yd.
|
||||||
|
*
|
||||||
|
* Unique constraints:
|
||||||
|
* Lists are identified, then check_unique_list is called on each list.
|
||||||
* Example, x has an associated yang list node with list of unique constraints
|
* Example, x has an associated yang list node with list of unique constraints
|
||||||
* y-list->y-unique - "a"
|
* y-list->y-unique - "a"
|
||||||
* xt->x -> ab
|
* xt->x -> ab
|
||||||
* x -> bc
|
* x -> bc
|
||||||
* x -> ab
|
* x -> ab
|
||||||
|
*
|
||||||
|
* Min-max constraints:
|
||||||
|
* Find upper and lower bound of existing lists and report violations
|
||||||
|
* Somewhat tricky to find violation of min-elements of empty
|
||||||
|
* lists, but this is done by a "gap-detection" mechanism, which detects
|
||||||
|
* gaps in the xml nodes given the ancestor Yang structure.
|
||||||
|
* But no gap analysis is done if the yang spec of the top-level xml is unknown
|
||||||
|
* Example:
|
||||||
|
* Yang structure:y1, y2, y3,
|
||||||
|
* XML structure: [x1, x1], [x3, x3] where [x2] list is missing
|
||||||
|
* @note min-element constraints on empty lists are not detected on top-level.
|
||||||
|
* Or more specifically, if no yang spec if associated with the top-level
|
||||||
|
* XML node. This may not be a large problem since it would mean empty configs
|
||||||
|
* are not allowed.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_unique_parent(cxobj *xt,
|
check_list_unique_minmax(cxobj *xt,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x = NULL;
|
cxobj *x = NULL;
|
||||||
yang_stmt *y;
|
yang_stmt *y;
|
||||||
|
yang_stmt *yt;
|
||||||
yang_stmt *yp = NULL; /* previous in list */
|
yang_stmt *yp = NULL; /* previous in list */
|
||||||
yang_stmt *yu;
|
yang_stmt *ye = NULL; /* yang each list to catch emtpy */
|
||||||
|
yang_stmt *ych; /* y:s parent node (if choice that ye can compare to) */
|
||||||
|
cxobj *xp = NULL; /* previous in list */
|
||||||
|
yang_stmt *yu; /* yang unique */
|
||||||
int ret;
|
int ret;
|
||||||
|
int nr=0; /* Nr of list elements for min/max check */
|
||||||
|
enum rfc_6020 keyw;
|
||||||
|
|
||||||
/* */
|
/* RFC 7950 7.7.5: regarding min-max elements check
|
||||||
|
* The behavior of the constraint depends on the type of the
|
||||||
|
* leaf-list's or list's closest ancestor node in the schema tree
|
||||||
|
* that is not a non-presence container (see Section 7.5.1):
|
||||||
|
* o If no such ancestor exists in the schema tree, the constraint
|
||||||
|
* is enforced.
|
||||||
|
* o Otherwise, if this ancestor is a case node, the constraint is
|
||||||
|
* enforced if any other node from the case exists.
|
||||||
|
* o Otherwise, it is enforced if the ancestor node exists.
|
||||||
|
*/
|
||||||
|
yt = xml_spec(xt); /* If yt == NULL, then no gap-analysis is done */
|
||||||
|
/* Traverse all elemenents */
|
||||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||||
if ((y = xml_spec(x)) == NULL)
|
if ((y = xml_spec(x)) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if (y == yp) /* If same yang as previous x, then skip (eg same list) */
|
if ((ych=yang_choice(y)) == NULL)
|
||||||
|
ych = y;
|
||||||
|
keyw = yang_keyword_get(y);
|
||||||
|
if (keyw != Y_LIST && keyw != Y_LEAF_LIST)
|
||||||
continue;
|
continue;
|
||||||
yp = y;
|
if (yp != NULL){ /* There exists a previous (leaf)list */
|
||||||
if (yang_keyword_get(y) != Y_LIST)
|
if (y == yp){ /* If same yang as previous x, then skip (eg same list) */
|
||||||
|
nr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Check if the list length violates min/max */
|
||||||
|
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yp = y; /* Restart min/max count */
|
||||||
|
xp = x; /* Need a reference to the XML as well */
|
||||||
|
nr = 1;
|
||||||
|
/* Gap analysis: Check if there is any empty list between y and yp
|
||||||
|
* Note, does not detect empty choice list (too complicated)
|
||||||
|
*/
|
||||||
|
if (yt != NULL && ych != ye){
|
||||||
|
/* Skip analysis if Yang spec is unknown OR
|
||||||
|
* if we are still iterating the same Y_CASE w multiple lists
|
||||||
|
*/
|
||||||
|
ye = yn_each(yt, ye);
|
||||||
|
if (ye && ych != ye)
|
||||||
|
do {
|
||||||
|
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
|
||||||
|
/* Check if the list length violates min/max */
|
||||||
|
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ye = yn_each(yt, ye);
|
||||||
|
} while(ye != NULL && /* to avoid livelock (shouldnt happen) */
|
||||||
|
ye != ych);
|
||||||
|
}
|
||||||
|
if (keyw != Y_LIST)
|
||||||
continue;
|
continue;
|
||||||
|
/* Here only lists. test unique constraints */
|
||||||
yu = NULL;
|
yu = NULL;
|
||||||
while ((yu = yn_each(y, yu)) != NULL) {
|
while ((yu = yn_each(y, yu)) != NULL) {
|
||||||
if (yang_keyword_get(yu) != Y_UNIQUE)
|
if (yang_keyword_get(yu) != Y_UNIQUE)
|
||||||
|
|
@ -785,6 +940,29 @@ check_unique_parent(cxobj *xt,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* yp if set, is a list that has been traversed
|
||||||
|
* This check is made in the loop as well - this is for the last list
|
||||||
|
*/
|
||||||
|
if (yp){
|
||||||
|
/* Check if the list length violates min/max */
|
||||||
|
if ((ret = check_min_max(xp, yp, nr, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
/* Check if there is any empty list between after last non-empty list
|
||||||
|
* Note, does not detect empty lists within choice/case (too complicated)
|
||||||
|
*/
|
||||||
|
if ((ye = yn_each(yt, ye)) != NULL)
|
||||||
|
do {
|
||||||
|
if (yang_keyword_get(ye) == Y_LIST || yang_keyword_get(ye) == Y_LEAF_LIST){
|
||||||
|
/* Check if the list length violates min/max */
|
||||||
|
if ((ret = check_min_max(xt, ye, 0, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} while((ye = yn_each(yt, ye)) != NULL);
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -793,7 +971,6 @@ check_unique_parent(cxobj *xt,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Validate a single XML node with yang specification for added entry
|
/*! Validate a single XML node with yang specification for added entry
|
||||||
* 1. Check if mandatory leafs present as subs.
|
* 1. Check if mandatory leafs present as subs.
|
||||||
* 2. Check leaf values, eg int ranges and string regexps.
|
* 2. Check leaf values, eg int ranges and string regexps.
|
||||||
|
|
@ -938,9 +1115,9 @@ xml_yang_validate_all(cxobj *xt,
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (ys != NULL && yang_config(ys) != 0){
|
if (yang_config(ys) != 0){
|
||||||
/* Node-specific validation */
|
/* Node-specific validation */
|
||||||
switch (ys->ys_keyword){
|
switch (yang_keyword_get(ys)){
|
||||||
case Y_ANYXML:
|
case Y_ANYXML:
|
||||||
case Y_ANYDATA:
|
case Y_ANYDATA:
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
@ -960,42 +1137,11 @@ xml_yang_validate_all(cxobj *xt,
|
||||||
if (validate_identityref(xt, ys, yc, cbret) < 0)
|
if (validate_identityref(xt, ys, yc, cbret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ((yc = yang_find(ys, Y_MIN_ELEMENTS, NULL)) != NULL){
|
|
||||||
/* The behavior of the constraint depends on the type of the
|
|
||||||
* leaf-list's or list's closest ancestor node in the schema tree
|
|
||||||
* that is not a non-presence container (see Section 7.5.1):
|
|
||||||
* o If no such ancestor exists in the schema tree, the constraint
|
|
||||||
* is enforced.
|
|
||||||
* o Otherwise, if this ancestor is a case node, the constraint is
|
|
||||||
* enforced if any other node from the case exists.
|
|
||||||
* o Otherwise, it is enforced if the ancestor node exists.
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
cxobj *xp;
|
|
||||||
cxobj *x;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ((xp = xml_parent(xt)) != NULL){
|
|
||||||
nr = atoi(yc->ys_argument);
|
|
||||||
x = NULL;
|
|
||||||
i = 0;
|
|
||||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL)
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if ((yc = yang_find(ys, Y_MAX_ELEMENTS, NULL)) != NULL){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ((ret = check_unique_parent(xt, cbret)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
|
||||||
/* must sub-node RFC 7950 Sec 7.5.3. Can be several.
|
/* must sub-node RFC 7950 Sec 7.5.3. Can be several.
|
||||||
* XXX. use yang path instead? */
|
* XXX. use yang path instead? */
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
|
|
@ -1033,6 +1179,14 @@ xml_yang_validate_all(cxobj *xt,
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
/* Check unique and min-max after choice test for example*/
|
||||||
|
if (yang_config(ys) != 0){
|
||||||
|
/* Checks if next level contains any unique list constraints */
|
||||||
|
if ((ret = check_list_unique_minmax(xt, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
ok:
|
ok:
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
|
@ -1059,6 +1213,8 @@ xml_yang_validate_all_top(cxobj *xt,
|
||||||
if ((ret = xml_yang_validate_all(x, cbret)) < 1)
|
if ((ret = xml_yang_validate_all(x, cbret)) < 1)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
if ((ret = check_list_unique_minmax(xt, cbret)) < 1)
|
||||||
|
return ret;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -759,12 +759,12 @@ yang_choice(yang_stmt *y)
|
||||||
yang_stmt *yp;
|
yang_stmt *yp;
|
||||||
|
|
||||||
if ((yp = y->ys_parent) != NULL){
|
if ((yp = y->ys_parent) != NULL){
|
||||||
switch (yp->ys_keyword){
|
switch (yang_keyword_get(yp)){
|
||||||
case Y_CHOICE:
|
case Y_CHOICE:
|
||||||
return yp;
|
return yp;
|
||||||
break;
|
break;
|
||||||
case Y_CASE:
|
case Y_CASE:
|
||||||
return yp->ys_parent;
|
return yang_parent_get(yp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -773,31 +773,57 @@ yang_choice(yang_stmt *y)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Find matching y in yp:s children, "yang order" of y when y is choice
|
||||||
|
* @param[in] yp Choice node
|
||||||
|
* @param[in] y Yang datanode to find
|
||||||
|
* @param[out] index Index of y in yp:s list of children
|
||||||
|
* @retval 0 not found (must be datanode)
|
||||||
|
* @retval 1 found
|
||||||
|
* @see order1 the main function
|
||||||
|
* There are two distinct cases, either (1) the choice has case statements, or
|
||||||
|
* (2) it uses shortcut mode without case statements.
|
||||||
|
* In (1) we need to count how many sub-statements and keep a max
|
||||||
|
* In (2) we increment with only 1.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
order1_choice(yang_stmt *yp,
|
order1_choice(yang_stmt *yp,
|
||||||
yang_stmt *y)
|
yang_stmt *y,
|
||||||
|
int *index)
|
||||||
{
|
{
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
|
int shortcut=0;
|
||||||
|
int max=0;
|
||||||
|
|
||||||
for (i=0; i<yp->ys_len; i++){
|
for (i=0; i<yp->ys_len; i++){ /* Loop through choice */
|
||||||
ys = yp->ys_stmt[i];
|
ys = yp->ys_stmt[i];
|
||||||
if (ys->ys_keyword == Y_CASE){
|
if (ys->ys_keyword == Y_CASE){ /* Loop through case */
|
||||||
for (j=0; j<ys->ys_len; j++){
|
for (j=0; j<ys->ys_len; j++){
|
||||||
yc = ys->ys_stmt[j];
|
yc = ys->ys_stmt[j];
|
||||||
if (yang_datanode(yc) && yc == y)
|
if (yang_datanode(yc) && yc == y){
|
||||||
|
*index += j;
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (j>max)
|
||||||
|
max = j;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
shortcut = 1; /* Shortcut, no case */
|
||||||
|
if (yang_datanode(ys) && ys == y)
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
else if (yang_datanode(ys) && ys == y)
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
if (shortcut)
|
||||||
|
(*index)++;
|
||||||
|
else
|
||||||
|
*index += max;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
/*! Find matching y in yp:s children, return "yang order" of y or -1 if not found
|
||||||
* @param[in] yp Parent
|
* @param[in] yp Parent
|
||||||
* @param[in] y Yang datanode to find
|
* @param[in] y Yang datanode to find
|
||||||
* @param[out] index Index of y in yp:s list of children
|
* @param[out] index Index of y in yp:s list of children
|
||||||
|
|
@ -815,7 +841,7 @@ order1(yang_stmt *yp,
|
||||||
for (i=0; i<yp->ys_len; i++){
|
for (i=0; i<yp->ys_len; i++){
|
||||||
ys = yp->ys_stmt[i];
|
ys = yp->ys_stmt[i];
|
||||||
if (ys->ys_keyword == Y_CHOICE){
|
if (ys->ys_keyword == Y_CHOICE){
|
||||||
if (order1_choice(ys, y) == 1) /* If one of the choices is "y" */
|
if (order1_choice(ys, y, index) == 1) /* If one of the choices is "y" */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -823,8 +849,8 @@ order1(yang_stmt *yp,
|
||||||
continue;
|
continue;
|
||||||
if (ys==y)
|
if (ys==y)
|
||||||
return 1;
|
return 1;
|
||||||
|
(*index)++;
|
||||||
}
|
}
|
||||||
(*index)++;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +874,7 @@ yang_order(yang_stmt *y)
|
||||||
|
|
||||||
if (y == NULL)
|
if (y == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
/* Some special handling if yp is choice (or case) and maybe union?
|
/* Some special handling if yp is choice (or case)
|
||||||
* if so, the real parent (from an xml point of view) is the parents
|
* if so, the real parent (from an xml point of view) is the parents
|
||||||
* parent.
|
* parent.
|
||||||
*/
|
*/
|
||||||
|
|
@ -3218,7 +3244,9 @@ ys_parse(yang_stmt *ys,
|
||||||
*
|
*
|
||||||
* The cv:s created in parse-tree as follows:
|
* The cv:s created in parse-tree as follows:
|
||||||
* fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
|
* fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
|
||||||
*
|
* revision: cv as uint32 date: Integer version as YYYYMMDD
|
||||||
|
* min-elements: cv as uint32
|
||||||
|
* max-elements: cv as uint32, '0' means unbounded
|
||||||
* @see ys_populate
|
* @see ys_populate
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -3228,8 +3256,15 @@ ys_parse_sub(yang_stmt *ys,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
uint8_t fd;
|
uint8_t fd;
|
||||||
uint32_t date = 0;
|
uint32_t date = 0;
|
||||||
|
char *arg;
|
||||||
|
enum rfc_6020 keyword;
|
||||||
|
char *reason = NULL;
|
||||||
|
int ret;
|
||||||
|
uint32_t minmax;
|
||||||
|
|
||||||
switch (ys->ys_keyword){
|
arg = yang_argument_get(ys);
|
||||||
|
keyword = yang_keyword_get(ys);
|
||||||
|
switch (keyword){
|
||||||
case Y_FRACTION_DIGITS:
|
case Y_FRACTION_DIGITS:
|
||||||
if (ys_parse(ys, CGV_UINT8) == NULL)
|
if (ys_parse(ys, CGV_UINT8) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -3241,7 +3276,7 @@ ys_parse_sub(yang_stmt *ys,
|
||||||
break;
|
break;
|
||||||
case Y_REVISION:
|
case Y_REVISION:
|
||||||
case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
|
case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
|
||||||
if (ys_parse_date_arg(ys->ys_argument, &date) < 0)
|
if (ys_parse_date_arg(arg, &date) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
|
if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "cv_new");
|
clicon_err(OE_YANG, errno, "cv_new");
|
||||||
|
|
@ -3250,14 +3285,37 @@ ys_parse_sub(yang_stmt *ys,
|
||||||
cv_uint32_set(ys->ys_cv, date);
|
cv_uint32_set(ys->ys_cv, date);
|
||||||
break;
|
break;
|
||||||
case Y_STATUS: /* RFC7950 7.21.2: "current", "deprecated", or "obsolete". */
|
case Y_STATUS: /* RFC7950 7.21.2: "current", "deprecated", or "obsolete". */
|
||||||
if (strcmp(ys->ys_argument, "current") &&
|
if (strcmp(arg, "current") &&
|
||||||
strcmp(ys->ys_argument, "deprecated") &&
|
strcmp(arg, "deprecated") &&
|
||||||
strcmp(ys->ys_argument, "obsolete")){
|
strcmp(arg, "obsolete")){
|
||||||
clicon_err(OE_YANG, errno, "Invalid status: \"%s\", expected current, deprecated, or obsolete", ys->ys_argument);
|
clicon_err(OE_YANG, errno, "Invalid status: \"%s\", expected current, deprecated, or obsolete", arg);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Y_MAX_ELEMENTS:
|
||||||
|
case Y_MIN_ELEMENTS:
|
||||||
|
if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "cv_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (keyword == Y_MAX_ELEMENTS &&
|
||||||
|
strcmp(arg, "unbounded") == 0)
|
||||||
|
cv_uint32_set(ys->ys_cv, 0); /* 0 means unbounded for max */
|
||||||
|
else{
|
||||||
|
if ((ret = parse_uint32(arg, &minmax, &reason)) < 0){
|
||||||
|
clicon_err(OE_YANG, errno, "parse_uint32");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (ret == 0){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "element-min/max parse error: %s", reason);
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cv_uint32_set(ys->ys_cv, minmax);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Y_UNKNOWN: /* XXX This code assumes ymod already loaded
|
case Y_UNKNOWN: /* XXX This code assumes ymod already loaded
|
||||||
but it may not be */
|
but it may not be */
|
||||||
if (extra == NULL)
|
if (extra == NULL)
|
||||||
|
|
@ -3289,8 +3347,7 @@ int
|
||||||
yang_mandatory(yang_stmt *ys)
|
yang_mandatory(yang_stmt *ys)
|
||||||
{
|
{
|
||||||
yang_stmt *ym;
|
yang_stmt *ym;
|
||||||
char *reason = NULL;
|
cg_var *cv;
|
||||||
uint8_t min_elements; /* XXX change to 32 (need new cligen version) */
|
|
||||||
|
|
||||||
/* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
|
/* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
|
||||||
* statement with the value "true". */
|
* statement with the value "true". */
|
||||||
|
|
@ -3305,12 +3362,8 @@ yang_mandatory(yang_stmt *ys)
|
||||||
* value greater than zero. */
|
* value greater than zero. */
|
||||||
else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){
|
else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){
|
||||||
if ((ym = yang_find(ys, Y_MIN_ELEMENTS, NULL)) != NULL){
|
if ((ym = yang_find(ys, Y_MIN_ELEMENTS, NULL)) != NULL){
|
||||||
/* XXX change to 32 (need new cligen version) */
|
cv = yang_cv_get(ym);
|
||||||
if (parse_uint8(ym->ys_argument, &min_elements, &reason) != 1){
|
return cv_uint32_get(cv) > 0;
|
||||||
clicon_err(OE_YANG, EINVAL, "%s", reason?reason:"parse_uint8");
|
|
||||||
return 0; /* XXX ignore error */
|
|
||||||
}
|
|
||||||
return min_elements > 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 3) A container node without a "presence" statement and that has at
|
/* 3) A container node without a "presence" statement and that has at
|
||||||
|
|
|
||||||
34
test/lib.sh
34
test/lib.sh
|
|
@ -72,8 +72,8 @@ testname=
|
||||||
# eg logging to a file: RCLOG="-l f/www-data/restconf.log"
|
# eg logging to a file: RCLOG="-l f/www-data/restconf.log"
|
||||||
: ${RCLOG:=}
|
: ${RCLOG:=}
|
||||||
|
|
||||||
# Wait after daemons (backend/restconf) start. Set to 10 if valgrind
|
# Wait after daemons (backend/restconf) start. See mem.sh for valgrind
|
||||||
: ${RCWAIT:=1}
|
: ${RCWAIT:=2}
|
||||||
|
|
||||||
# Parse yangmodels from https://github.com/YangModels/yang
|
# Parse yangmodels from https://github.com/YangModels/yang
|
||||||
# Recommended: checkout yangmodels elsewhere in the tree and set the env
|
# Recommended: checkout yangmodels elsewhere in the tree and set the env
|
||||||
|
|
@ -168,6 +168,21 @@ stop_backend(){
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Wait for restconf to stop sending 502 Bad Gateway
|
||||||
|
wait_backend(){
|
||||||
|
reply=$(echo '<rpc message-id="101" xmlns="http://clicon.org/lib"><ping/></rpc>]]>]]>' | clixon_netconf -qef $cfg 2> /dev/null)
|
||||||
|
let i=0;
|
||||||
|
while [[ $reply != "<rpc-reply"* ]]; do
|
||||||
|
sleep 1
|
||||||
|
reply=$(echo '<rpc message-id="101" xmlns="http://clicon.org/lib"><ping/></rpc>]]>]]>' | clixon_netconf -qef $cfg 2> /dev/null)
|
||||||
|
let i++;
|
||||||
|
# echo "wait_backend $i"
|
||||||
|
if [ $i -ge $RCWAIT ]; then
|
||||||
|
err "backend timeout $RCWAIT seconds"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
start_restconf(){
|
start_restconf(){
|
||||||
# Start in background
|
# Start in background
|
||||||
sudo su -c "$clixon_restconf $RCLOG -D $DBG $*" -s /bin/sh www-data &
|
sudo su -c "$clixon_restconf $RCLOG -D $DBG $*" -s /bin/sh www-data &
|
||||||
|
|
@ -184,6 +199,21 @@ stop_restconf(){
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Wait for restconf to stop sending 502 Bad Gateway
|
||||||
|
wait_restconf(){
|
||||||
|
hdr=$(curl --head -sS http://localhost/restconf)
|
||||||
|
let i=0;
|
||||||
|
while [[ $hdr == "HTTP/1.1 502 Bad Gateway"* ]]; do
|
||||||
|
sleep 1
|
||||||
|
hdr=$(curl --head -sS http://localhost/restconf)
|
||||||
|
let i++;
|
||||||
|
# echo "wait_restconf $i"
|
||||||
|
if [ $i -ge $RCWAIT ]; then
|
||||||
|
err "restconf timeout $RCWAIT seconds"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Increment test number and print a nice string
|
# Increment test number and print a nice string
|
||||||
new(){
|
new(){
|
||||||
if [ $valgrindtest -eq 1 ]; then
|
if [ $valgrindtest -eq 1 ]; then
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ memonce(){
|
||||||
valgrindtest=3 # This means backend valgrind test
|
valgrindtest=3 # This means backend valgrind test
|
||||||
sudo chmod 660 $valgrindfile
|
sudo chmod 660 $valgrindfile
|
||||||
sudo chown www-data $valgrindfile
|
sudo chown www-data $valgrindfile
|
||||||
: ${RCWAIT:=5} # valgrind backend needs some time to get up
|
: ${RCWAIT:=10} # valgrind backend needs some time to get up
|
||||||
clixon_restconf="/usr/bin/valgrind --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile /www-data/clixon_restconf"
|
clixon_restconf="/usr/bin/valgrind --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile /www-data/clixon_restconf"
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_MODULE_MAIN>system</CLICON_YANG_MODULE_MAIN>
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
|
@ -83,11 +83,27 @@ module system{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Choice with multiple items */
|
||||||
|
choice mch {
|
||||||
|
case first{
|
||||||
|
leaf-list ma {
|
||||||
|
type int32;
|
||||||
|
}
|
||||||
|
leaf mb {
|
||||||
|
type int32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case second{
|
||||||
|
leaf-list mc {
|
||||||
|
type int32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
new "test params: -f $cfg -y $fyang"
|
new "test params: -f $cfg"
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
|
|
@ -97,55 +113,55 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
sudo pkill clixon_backend # to be sure
|
sudo pkill clixon_backend # to be sure
|
||||||
|
|
||||||
new "start backend -s init -f $cfg -y $fyang"
|
new "start backend -s init -f $cfg"
|
||||||
start_backend -s init -f $cfg -y $fyang
|
start_backend -s init -f $cfg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "kill old restconf daemon"
|
new "kill old restconf daemon"
|
||||||
sudo pkill -u www-data clixon_restconf
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
new "start restconf daemon"
|
new "start restconf daemon"
|
||||||
start_restconf -f $cfg -y $fyang
|
start_restconf -f $cfg
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
sleep $RCWAIT
|
||||||
|
|
||||||
# First vanilla (protocol) case
|
# First vanilla (protocol) case
|
||||||
new "netconf validate empty"
|
new "netconf validate empty"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf set protocol both udp and tcp"
|
new "netconf set protocol both udp and tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/><udp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/><udp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate both udp and tcp fail"
|
new "netconf validate both udp and tcp fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>udp</bad-element></error-info><error-severity>error</error-severity><error-message>Element in choice statement already exists</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>udp</bad-element></error-info><error-severity>error</error-severity><error-message>Element in choice statement already exists</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf set empty protocol"
|
new "netconf set empty protocol"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate protocol"
|
new "netconf validate protocol"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf set protocol tcp"
|
new "netconf set protocol tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get protocol tcp"
|
new "netconf get protocol tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></data></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf commit protocol tcp"
|
new "netconf commit protocol tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><udp nc:operation="create"/></protocol></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><udp nc:operation="create"/></protocol></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get protocol udp"
|
new "netconf get protocol udp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><udp/></protocol></system></data></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><udp/></protocol></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf commit protocol udp"
|
new "netconf commit protocol udp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
#-- restconf
|
#-- restconf
|
||||||
|
|
||||||
|
|
@ -166,66 +182,100 @@ new "restconf set protocol tcp+udp fail"
|
||||||
expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": null, "udp": null}}')" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "bad-element","error-info": {"bad-element": "udp"},"error-severity": "error","error-message": "Element in choice statement already exists"}}}
'
|
expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": null, "udp": null}}')" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "bad-element","error-info": {"bad-element": "udp"},"error-severity": "error","error-message": "Element in choice statement already exists"}}}
'
|
||||||
|
|
||||||
new "cli set protocol udp"
|
new "cli set protocol udp"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol udp" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg -l o set system protocol udp" 0 "^$"
|
||||||
|
|
||||||
new "cli get protocol udp"
|
new "cli get protocol udp"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol udp$"
|
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^system protocol udp$"
|
||||||
|
|
||||||
new "cli change protocol to tcp"
|
new "cli change protocol to tcp"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol tcp" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg -l o set system protocol tcp" 0 "^$"
|
||||||
|
|
||||||
new "cli get protocol tcp"
|
new "cli get protocol tcp"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol tcp$"
|
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^system protocol tcp$"
|
||||||
|
|
||||||
new "cli delete all"
|
new "cli delete all"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o delete all" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg -l o delete all" 0 "^$"
|
||||||
|
|
||||||
new "commit"
|
new "commit"
|
||||||
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o commit" 0 "^$"
|
expectfn "$clixon_cli -1 -f $cfg -l o commit" 0 "^$"
|
||||||
|
|
||||||
# Second shorthand (no case)
|
# Second shorthand (no case)
|
||||||
new "netconf set protocol both udp and tcp"
|
new "netconf set protocol both udp and tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/><udp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/><udp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf validate both udp and tcp fail"
|
new "netconf validate both udp and tcp fail"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>udp</bad-element></error-info><error-severity>error</error-severity><error-message>Element in choice statement already exists</error-message></rpc-error></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>udp</bad-element></error-info><error-severity>error</error-severity><error-message>Element in choice statement already exists</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf set shorthand tcp"
|
new "netconf set shorthand tcp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><tcp/></shorthand></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><tcp/></shorthand></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf get shorthand udp"
|
new "netconf get shorthand udp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></data></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf validate shorthand"
|
new "netconf validate shorthand"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf discard-changes"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
# Third check mandatory
|
# Third check mandatory
|
||||||
new "netconf set empty mandatory"
|
new "netconf set empty mandatory"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get mandatory empty"
|
new "netconf get mandatory empty"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory/></system></data></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory/></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf validate mandatory"
|
new "netconf validate mandatory"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>data-missing</error-tag><error-app-tag>missing-choice</error-app-tag><error-info><missing-choice>name</missing-choice></error-info><error-severity>error</error-severity></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>data-missing</error-tag><error-app-tag>missing-choice</error-app-tag><error-info><missing-choice>name</missing-choice></error-info><error-severity>error</error-severity></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf set mandatory udp"
|
new "netconf set mandatory udp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf get mandatory udp"
|
new "netconf get mandatory udp"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></data></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "netconf validate mandatory"
|
new "netconf validate mandatory"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
# Choice with multiple items
|
||||||
|
|
||||||
|
new "netconf choice multiple items first"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config" op="replace">
|
||||||
|
<ma>0</ma>
|
||||||
|
<ma>1</ma>
|
||||||
|
<mb>2</mb>
|
||||||
|
</system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf validate multiple ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf choice multiple items second"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config" op="replace">
|
||||||
|
<mb>0</mb>
|
||||||
|
<mb>1</mb>
|
||||||
|
</system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf validate multiple ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf choice multiple items mix"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config" op="replace">
|
||||||
|
<ma>0</ma>
|
||||||
|
<mb>2</mb>
|
||||||
|
<mc>2</mc>
|
||||||
|
</system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf validate multiple error"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>mc</bad-element></error-info><error-severity>error</error-severity><error-message>Element in choice statement already exists</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
stop_restconf
|
stop_restconf
|
||||||
|
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Yang list / leaf-list operations. min/max-elements
|
|
||||||
|
|
||||||
# Magic line must be first in script (see README.md)
|
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
|
||||||
|
|
||||||
APPNAME=example
|
|
||||||
|
|
||||||
cfg=$dir/conf_yang.xml
|
|
||||||
fyang=$dir/test.yang
|
|
||||||
|
|
||||||
cat <<EOF > $cfg
|
|
||||||
<clixon-config xmlns="http://clicon.org/config">
|
|
||||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
|
||||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
|
||||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
|
||||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
|
||||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
|
||||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
|
||||||
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
|
||||||
</clixon-config>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat <<EOF > $fyang
|
|
||||||
module $APPNAME{
|
|
||||||
yang-version 1.1;
|
|
||||||
namespace "urn:example:clixon";
|
|
||||||
prefix ex;
|
|
||||||
container c{
|
|
||||||
presence true;
|
|
||||||
list a0{
|
|
||||||
key k;
|
|
||||||
leaf k {
|
|
||||||
type string;
|
|
||||||
}
|
|
||||||
min-elements 0;
|
|
||||||
max-elements "unbounded";
|
|
||||||
}
|
|
||||||
list a1{
|
|
||||||
key k;
|
|
||||||
leaf k {
|
|
||||||
type string;
|
|
||||||
}
|
|
||||||
description "There must be at least one of these";
|
|
||||||
min-elements 1;
|
|
||||||
max-elements 2;
|
|
||||||
}
|
|
||||||
leaf-list b0{
|
|
||||||
type string;
|
|
||||||
min-elements 0;
|
|
||||||
max-elements "unbounded";
|
|
||||||
}
|
|
||||||
leaf-list b1{
|
|
||||||
description "There must be at least one of these";
|
|
||||||
type string;
|
|
||||||
min-elements 1;
|
|
||||||
max-elements 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
new "test params: -f $cfg"
|
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
|
||||||
new "kill old backend"
|
|
||||||
sudo clixon_backend -zf $cfg
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
err
|
|
||||||
fi
|
|
||||||
new "start backend -s init -f $cfg"
|
|
||||||
# start new backend
|
|
||||||
start_backend -s init -f $cfg
|
|
||||||
|
|
||||||
new "waiting"
|
|
||||||
sleep $RCWAIT
|
|
||||||
fi
|
|
||||||
|
|
||||||
new "minmax: minimal"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: minimal validate ok"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: maximal"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>0</b1><b0>1</b0></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
new "minmax: validate ok"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: empty"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"/></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
# NYI
|
|
||||||
if false; then # nyi
|
|
||||||
new "minmax: validate should fail"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: no list"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: no leaf-list"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: Too large list"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><a1><k>1</k></a1><a1><k>2</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "minmax: Too large leaf-list"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><b1>0</b1><b1>1</b1><b1>2</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
|
||||||
|
|
||||||
new "minmax: validate should fail"
|
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error/></rpc-reply>]]>]]>$"
|
|
||||||
fi # NYI
|
|
||||||
|
|
||||||
if [ $BE -eq 0 ]; then
|
|
||||||
exit # BE
|
|
||||||
fi
|
|
||||||
|
|
||||||
new "Kill backend"
|
|
||||||
# Check if premature kill
|
|
||||||
pid=`pgrep -u root -f clixon_backend`
|
|
||||||
if [ -z "$pid" ]; then
|
|
||||||
err "backend already dead"
|
|
||||||
fi
|
|
||||||
# kill backend
|
|
||||||
stop_backend -f $cfg
|
|
||||||
|
|
||||||
rm -rf $dir
|
|
||||||
241
test/test_minmax.sh
Executable file
241
test/test_minmax.sh
Executable file
|
|
@ -0,0 +1,241 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Yang list / leaf-list min/max-element tests.
|
||||||
|
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
cfg=$dir/conf_yang.xml
|
||||||
|
fyang=$dir/test.yang
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module $APPNAME{
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:clixon";
|
||||||
|
prefix ex;
|
||||||
|
container c{
|
||||||
|
presence true;
|
||||||
|
list a0{
|
||||||
|
key k;
|
||||||
|
leaf k {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
min-elements 0;
|
||||||
|
max-elements "unbounded";
|
||||||
|
}
|
||||||
|
list a1{
|
||||||
|
key k;
|
||||||
|
leaf k {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
description "There must be at least one of these";
|
||||||
|
min-elements 1;
|
||||||
|
max-elements 2;
|
||||||
|
}
|
||||||
|
leaf-list b0{
|
||||||
|
type string;
|
||||||
|
min-elements 0;
|
||||||
|
max-elements "unbounded";
|
||||||
|
}
|
||||||
|
leaf-list b1{
|
||||||
|
description "There must be at least one of these";
|
||||||
|
type string;
|
||||||
|
min-elements 1;
|
||||||
|
max-elements 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container c2{
|
||||||
|
description "with choices";
|
||||||
|
choice ch1{
|
||||||
|
case x{
|
||||||
|
list a1{
|
||||||
|
key k;
|
||||||
|
leaf k {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
min-elements 1;
|
||||||
|
max-elements 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case y{
|
||||||
|
list a2{
|
||||||
|
key k;
|
||||||
|
leaf k {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
min-elements 0;
|
||||||
|
max-elements 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
choice ch2{
|
||||||
|
leaf-list b1{
|
||||||
|
type string;
|
||||||
|
min-elements 1;
|
||||||
|
max-elements 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf-list b3{
|
||||||
|
description "top-level";
|
||||||
|
type int32;
|
||||||
|
min-elements 1;
|
||||||
|
max-elements 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg"
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s init -f $cfg"
|
||||||
|
# start new backend
|
||||||
|
start_backend -s init -f $cfg
|
||||||
|
|
||||||
|
new "waiting"
|
||||||
|
sleep $RCWAIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "minmax: minimal"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon">
|
||||||
|
<a1><k>0</k></a1>
|
||||||
|
<b1>0</b1>
|
||||||
|
</c></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax: minimal validate ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax: maximal"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a0><k>0</k></a0><a0><k>1</k></a0><a0><k>unbounded</k></a0><a1><k>0</k></a1><a1><k>1</k></a1><b0>0</b0><b0>1</b0><b0>unbounded</b0><b1>0</b1><b0>1</b0></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: validate ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax: empty"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"/></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
#XXX echo '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' |$clixon_netconf -qf $cfg
|
||||||
|
new "minmax: validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-few-elements</error-app-tag><error-severity>error</error-severity><error-path>c</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: no list"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-few-elements</error-app-tag><error-severity>error</error-severity><error-path>c</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: no leaf-list"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-few-elements</error-app-tag><error-severity>error</error-severity><error-path>c</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: Too large list"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><a1><k>1</k></a1><a1><k>2</k></a1><b1>0</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag><error-severity>error</error-severity><error-path>a1</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: Too large leaf-list"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c xmlns="urn:example:clixon"><a1><k>0</k></a1><b1>0</b1><b1>1</b1><b1>2</b1></c></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "minmax: validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag><error-severity>error</error-severity><error-path>b1</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
# With choice c1
|
||||||
|
new "minmax choice: minimal"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c2 xmlns="urn:example:clixon">
|
||||||
|
<a1><k>0</k></a1>
|
||||||
|
<b1>0</b1>
|
||||||
|
</c2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax choice: minimal validate ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax choice: many a2"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c2 xmlns="urn:example:clixon">
|
||||||
|
<a2><k>0</k></a2>
|
||||||
|
<a2><k>1</k></a2>
|
||||||
|
<a2><k>2</k></a2>
|
||||||
|
<b1>0</b1>
|
||||||
|
</c2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax choice: many a2 validate ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax choice: too many a1"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><c2 xmlns="urn:example:clixon">
|
||||||
|
<a1><k>0</k></a1>
|
||||||
|
<a1><k>1</k></a1>
|
||||||
|
<a1><k>2</k></a1>
|
||||||
|
<b1>0</b1>
|
||||||
|
</c2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax choice: too many validate should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag><error-severity>error</error-severity><error-path>a1</error-path></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
# top config level
|
||||||
|
new "minmax top level"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config>
|
||||||
|
<b3 xmlns="urn:example:clixon">0</b3>
|
||||||
|
<b3 xmlns="urn:example:clixon">1</b3>
|
||||||
|
</config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax top level ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax top level too many"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config>
|
||||||
|
<b3 xmlns="urn:example:clixon">0</b3>
|
||||||
|
<b3 xmlns="urn:example:clixon">1</b3>
|
||||||
|
<b3 xmlns="urn:example:clixon">2</b3>
|
||||||
|
</config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "minmax top level too many should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-app-tag>too-many-elements</error-app-tag><error-severity>error</error-severity><error-path>b3</error-path></rpc-error></rpc-reply>]]>]]>'
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
if [ $BE -eq 0 ]; then
|
||||||
|
exit # BE
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=`pgrep -u root -f clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
stop_backend -f $cfg
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
@ -102,7 +102,9 @@ EOF
|
||||||
start_restconf -f $cfg -- -a
|
start_restconf -f $cfg -- -a
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
wait_backend
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
|
|
||||||
#----------- First get
|
#----------- First get
|
||||||
case "$ret1" in
|
case "$ret1" in
|
||||||
|
|
@ -115,6 +117,7 @@ EOF
|
||||||
'
|
'
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
new "get startup 42"
|
new "get startup 42"
|
||||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 "$ret"
|
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 "$ret"
|
||||||
|
|
||||||
|
|
@ -190,5 +193,4 @@ testrun true permit deny permit 0 1 3
|
||||||
new "nacm enabled, exec default permit, write permit (expect fail)"
|
new "nacm enabled, exec default permit, write permit (expect fail)"
|
||||||
testrun true deny permit permit 2 0 2
|
testrun true deny permit permit 2 0 2
|
||||||
|
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ new "start restconf daemon"
|
||||||
start_restconf -f $cfg
|
start_restconf -f $cfg
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
wait_backend
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
|
|
@ -73,12 +74,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r
|
||||||
|
|
||||||
# Should be alphabetically ordered
|
# Should be alphabetically ordered
|
||||||
new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
|
new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
|
||||||
expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": null}}
|
expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null,"clixon-lib:ping": null,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": null}}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
||||||
expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>'
|
expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><ping xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>'
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,8 @@ new "start restconf daemon"
|
||||||
start_restconf -f $cfg -y $fyang
|
start_restconf -f $cfg -y $fyang
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
wait_backend
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
: ${clixon_util_stream:=clixon_util_stream}
|
: ${clixon_util_stream:=clixon_util_stream}
|
||||||
NCWAIT=5 # Wait (netconf valgrind may need more time)
|
NCWAIT=10 # Wait (netconf valgrind may need more time)
|
||||||
|
|
||||||
DATE=$(date +"%Y-%m-%d")
|
DATE=$(date +"%Y-%m-%d")
|
||||||
|
|
||||||
|
|
@ -120,7 +120,8 @@ new "start restconf daemon"
|
||||||
start_restconf -f $cfg -y $fyang
|
start_restconf -f $cfg -y $fyang
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
wait_backend
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
#
|
#
|
||||||
# 1. Netconf RFC5277 stream testing
|
# 1. Netconf RFC5277 stream testing
|
||||||
|
|
|
||||||
|
|
@ -52,4 +52,7 @@ module clixon-lib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rpc ping {
|
||||||
|
description "Check aliveness of backend daemon.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue