- New netconf-specific uint32 parse functions
- Added failure handling to xpath traverse_canonical - Started pagination cli code
This commit is contained in:
parent
390b0886ed
commit
a046306270
16 changed files with 410 additions and 241 deletions
|
|
@ -732,7 +732,7 @@ from_client_kill_session(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
uint32_t id; /* session id */
|
uint32_t id; /* session id */
|
||||||
char *str;
|
char *str = NULL;
|
||||||
struct client_entry *ce;
|
struct client_entry *ce;
|
||||||
char *db = "running"; /* XXX */
|
char *db = "running"; /* XXX */
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
@ -745,15 +745,10 @@ from_client_kill_session(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((ret = parse_uint32(str, &id, &reason)) < 0){
|
if ((ret = netconf_parse_uint32("session-id", str, NULL, 0, cbret, &id)) < 0)
|
||||||
clicon_err(OE_XML, errno, "parse_uint32");
|
goto done;
|
||||||
goto done;
|
if (ret == 0)
|
||||||
}
|
goto ok;
|
||||||
if (ret == 0){
|
|
||||||
if (netconf_bad_element(cbret, "protocol", "session-id", reason) < 0)
|
|
||||||
goto done;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* may or may not be in active client list, probably not */
|
/* may or may not be in active client list, probably not */
|
||||||
if ((ce = ce_find_byid(backend_client_list(h), id)) != NULL){
|
if ((ce = ce_find_byid(backend_client_list(h), id)) != NULL){
|
||||||
xmldb_unlock_all(h, id); /* Removes locks on all databases */
|
xmldb_unlock_all(h, id); /* Removes locks on all databases */
|
||||||
|
|
|
||||||
|
|
@ -273,17 +273,18 @@ client_statedata(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LIST_PAGINATION
|
#ifdef LIST_PAGINATION
|
||||||
|
|
||||||
/*! Help function for parsing restconf query parameter and setting netconf attribute
|
/*! Help function for parsing restconf query parameter and setting netconf attribute
|
||||||
*
|
*
|
||||||
* If not "unbounded", parse and set a numeric value
|
* If not "unbounded", parse and set a numeric value
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] name Name of attribute
|
* @param[in] name Name of attribute
|
||||||
* @param[in] defaultstr Default string which is accepted and sets value to 0
|
* @param[in] defaultstr Default string which is accepted and sets value to 0
|
||||||
* @param[in,out] cbret Output buffer for internal RPC message
|
* @param[in,out] cbret Output buffer for internal RPC message if invalid
|
||||||
* @param[out] value Value
|
* @param[out] value Value
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 Invalid, cbret set
|
* @retval 0 Invalid, cbret set
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
element2value(clicon_handle h,
|
element2value(clicon_handle h,
|
||||||
|
|
@ -293,35 +294,15 @@ element2value(clicon_handle h,
|
||||||
cbuf *cbret,
|
cbuf *cbret,
|
||||||
uint32_t *value)
|
uint32_t *value)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
|
||||||
char *valstr;
|
char *valstr;
|
||||||
int ret;
|
|
||||||
char *reason = NULL;
|
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
||||||
*value = 0;
|
*value = 0;
|
||||||
if ((x = xml_find_type(xe, NULL, name, CX_ELMNT)) != NULL &&
|
if ((x = xml_find_type(xe, NULL, name, CX_ELMNT)) != NULL &&
|
||||||
(valstr = xml_body(x)) != NULL &&
|
(valstr = xml_body(x)) != NULL){
|
||||||
strcmp(valstr, defaultstr) != 0){
|
return netconf_parse_uint32(name, valstr, defaultstr, 0, cbret, value);
|
||||||
if ((ret = parse_uint32(valstr, value, &reason)) < 0){
|
|
||||||
clicon_err(OE_XML, errno, "parse_uint32");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (ret == 0){
|
|
||||||
if (netconf_bad_element(cbret, "application",
|
|
||||||
name, "Unrecognized value") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
retval = 1;
|
return 1;
|
||||||
done:
|
|
||||||
if (reason)
|
|
||||||
free(reason);
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -368,8 +349,8 @@ get_common(clicon_handle h,
|
||||||
char *reason = NULL;
|
char *reason = NULL;
|
||||||
cbuf *cbmsg = NULL; /* For error msg */
|
cbuf *cbmsg = NULL; /* For error msg */
|
||||||
#ifdef LIST_PAGINATION
|
#ifdef LIST_PAGINATION
|
||||||
uint32_t limit = 0;
|
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
|
uint32_t limit = 0;
|
||||||
uint32_t total = 0;
|
uint32_t total = 0;
|
||||||
uint32_t remaining = 0;
|
uint32_t remaining = 0;
|
||||||
char *direction = NULL;
|
char *direction = NULL;
|
||||||
|
|
@ -393,6 +374,8 @@ get_common(clicon_handle h,
|
||||||
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
if ((xfilter = xml_find(xe, "filter")) != NULL){
|
||||||
char *xpath0;
|
char *xpath0;
|
||||||
cvec *nsc1 = NULL;
|
cvec *nsc1 = NULL;
|
||||||
|
cbuf *cbreason = NULL;
|
||||||
|
|
||||||
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
|
if ((xpath0 = xml_find_value(xfilter, "select"))==NULL)
|
||||||
xpath0 = "/";
|
xpath0 = "/";
|
||||||
/* Create namespace context for xpath from <filter>
|
/* Create namespace context for xpath from <filter>
|
||||||
|
|
@ -402,8 +385,16 @@ get_common(clicon_handle h,
|
||||||
else
|
else
|
||||||
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
if (xml_nsctx_node(xfilter, &nsc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1) < 0)
|
if ((ret = xpath2canonical(xpath0, nsc, yspec, &xpath, &nsc1, &cbreason)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
if (netconf_bad_attribute(cbret, "application",
|
||||||
|
"select", cbuf_get(cbreason)) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
if (cbreason)
|
||||||
|
cbuf_free(cbreason);
|
||||||
if (nsc)
|
if (nsc)
|
||||||
xml_nsctx_free(nsc);
|
xml_nsctx_free(nsc);
|
||||||
nsc = nsc1;
|
nsc = nsc1;
|
||||||
|
|
@ -435,36 +426,42 @@ get_common(clicon_handle h,
|
||||||
if (yang_path_arg(yspec, xpath, &ylist) < 0)
|
if (yang_path_arg(yspec, xpath, &ylist) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ylist == NULL){
|
if (ylist == NULL){
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "list-pagination is enabled but target is not found") < 0)
|
if ((cbmsg = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* error reason should be in clicon_err_reason */
|
||||||
|
cprintf(cbmsg, "Netconf get list-pagination: \"%s\" not found", xpath);
|
||||||
|
if (netconf_invalid_value(cbret, "application", cbuf_get(cbmsg)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (yang_keyword_get(ylist) != Y_LIST &&
|
if (yang_keyword_get(ylist) != Y_LIST &&
|
||||||
yang_keyword_get(ylist) != Y_LEAF_LIST){
|
yang_keyword_get(ylist) != Y_LEAF_LIST){
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "list-pagination is enabled but target is not leaf or leaf-list") < 0)
|
if (netconf_invalid_value(cbret, "application", "list-pagination is enabled but target is not leaf or leaf-list") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((list_config = yang_config_ancestor(ylist)) != 0){ /* config list */
|
if ((list_config = yang_config_ancestor(ylist)) != 0){ /* config list */
|
||||||
if (content == CONTENT_NONCONFIG){
|
if (content == CONTENT_NONCONFIG){
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "list-pagination targets a config list but content request is nonconfig") < 0)
|
if (netconf_invalid_value(cbret, "application", "list-pagination targets a config list but content request is nonconfig") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { /* state list */
|
else { /* state list */
|
||||||
if (content == CONTENT_CONFIG){
|
if (content == CONTENT_CONFIG){
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "list-pagination targets a state list but content request is config") < 0)
|
if (netconf_invalid_value(cbret, "application", "list-pagination targets a state list but content request is config") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* limit */
|
|
||||||
if ((ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
|
|
||||||
goto done;
|
|
||||||
/* offset */
|
/* offset */
|
||||||
if (ret && (ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
|
if (ret && (ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* limit */
|
||||||
|
if ((ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
|
||||||
|
goto done;
|
||||||
/* direction */
|
/* direction */
|
||||||
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
|
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
|
||||||
direction = xml_body(x);
|
direction = xml_body(x);
|
||||||
|
|
@ -517,7 +514,11 @@ get_common(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
/* XXX remaining of state list??*/
|
/* Remaining of state list. Two strategies:
|
||||||
|
* 1. New api where state callback is registered, lock, iterative, unlock
|
||||||
|
* 2. Read all here and count (fallback)
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
/* Append predicate to original xpath and replace it */
|
/* Append predicate to original xpath and replace it */
|
||||||
xpath2 = cbuf_get(cbpath);
|
xpath2 = cbuf_get(cbpath);
|
||||||
|
|
@ -525,9 +526,7 @@ get_common(clicon_handle h,
|
||||||
else
|
else
|
||||||
xpath2 = xpath;
|
xpath2 = xpath;
|
||||||
#endif /* LIST_PAGINATION */
|
#endif /* LIST_PAGINATION */
|
||||||
/* Read config
|
/* Read configuration */
|
||||||
* XXX This seems unnecessary complex
|
|
||||||
*/
|
|
||||||
switch (content){
|
switch (content){
|
||||||
case CONTENT_CONFIG: /* config data only */
|
case CONTENT_CONFIG: /* config data only */
|
||||||
/* specific xpath */
|
/* specific xpath */
|
||||||
|
|
|
||||||
|
|
@ -1317,28 +1317,38 @@ cli_help(clicon_handle h, cvec *vars, cvec *argv)
|
||||||
* XXX: This is hardcoded only for test_pagination.sh
|
* XXX: This is hardcoded only for test_pagination.sh
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv Vector of cli string and instantiated variables
|
* @param[in] cvv Vector of cli string and instantiated variables
|
||||||
* @param[in] argv Vector. Format: <xpath> <prefix> <namespace>
|
* @param[in] argv Vector. Format: <xpath> <prefix> <namespace> <format>
|
||||||
|
* Also, if there is a cligen variable called "xpath" it will override argv xpath arg
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_pagination(clicon_handle h, cvec *vars, cvec *argv)
|
cli_pagination(clicon_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
char *namespace = NULL;
|
char *namespace = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
char *formatstr;
|
char *formatstr;
|
||||||
enum format_enum format;
|
enum format_enum format;
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
cg_var *cv;
|
||||||
|
int i;
|
||||||
|
const int win = 20;
|
||||||
|
|
||||||
if (cvec_len(argv) != 4){
|
if (cvec_len(argv) != 4){
|
||||||
clicon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format>");
|
clicon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format>");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
xpath = cvec_i_str(argv, 0);
|
/* prefix:variable overrides argv */
|
||||||
|
if ((cv = cvec_find(cvv, "xpath")) != NULL)
|
||||||
|
xpath = cv_string_get(cv);
|
||||||
|
else
|
||||||
|
xpath = cvec_i_str(argv, 0);
|
||||||
prefix = cvec_i_str(argv, 1);
|
prefix = cvec_i_str(argv, 1);
|
||||||
namespace = cvec_i_str(argv, 2);
|
namespace = cvec_i_str(argv, 2);
|
||||||
formatstr = cv_string_get(cvec_i(argv, 3)); /* Fourthformat: output format */
|
formatstr = cv_string_get(cvec_i(argv, 3)); /* Fourthformat: output format */
|
||||||
|
|
@ -1348,34 +1358,39 @@ cli_pagination(clicon_handle h, cvec *vars, cvec *argv)
|
||||||
}
|
}
|
||||||
if ((nsc = xml_nsctx_init(prefix, namespace)) == NULL)
|
if ((nsc = xml_nsctx_init(prefix, namespace)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_get_pageable_list(h, "running", xpath, NULL, nsc, CONTENT_CONFIG, -1,
|
for (i = 0; i < 10; i++){
|
||||||
"2", "0",
|
if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc,
|
||||||
NULL, NULL, NULL,
|
CONTENT_NONCONFIG,
|
||||||
&xret) < 0){
|
-1, /* depth */
|
||||||
goto done;
|
win*i, /* offset */
|
||||||
}
|
win*(i+1), /* limit */
|
||||||
if ((xerr = xpath_first(xret, NULL, "/rpc-error")) != NULL){
|
NULL, NULL, NULL, /* nyi */
|
||||||
clixon_netconf_error(xerr, "Get configuration", NULL);
|
&xret) < 0){
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
xc = NULL;
|
|
||||||
while ((xc = xml_child_each(xret, xc, CX_ELMNT)) != NULL)
|
|
||||||
switch (format){
|
|
||||||
case FORMAT_XML:
|
|
||||||
clicon_xml2file_cb(stdout, xc, 0, 1, cligen_output);
|
|
||||||
break;
|
|
||||||
case FORMAT_JSON:
|
|
||||||
xml2json_cb(stdout, xc, 1, cligen_output);
|
|
||||||
break;
|
|
||||||
case FORMAT_TEXT:
|
|
||||||
xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */
|
|
||||||
break;
|
|
||||||
case FORMAT_CLI:
|
|
||||||
xml2cli_cb(stdout, xc, NULL, GT_HIDE, cligen_output); /* cli syntax */
|
|
||||||
break;
|
|
||||||
case FORMAT_NETCONF:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if ((xerr = xpath_first(xret, NULL, "/rpc-error")) != NULL){
|
||||||
|
clixon_netconf_error(xerr, "Get configuration", NULL);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(xret, xc, CX_ELMNT)) != NULL)
|
||||||
|
switch (format){
|
||||||
|
case FORMAT_XML:
|
||||||
|
clicon_xml2file_cb(stdout, xc, 0, 1, cligen_output);
|
||||||
|
break;
|
||||||
|
case FORMAT_JSON:
|
||||||
|
xml2json_cb(stdout, xc, 1, cligen_output);
|
||||||
|
break;
|
||||||
|
case FORMAT_TEXT:
|
||||||
|
xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */
|
||||||
|
break;
|
||||||
|
case FORMAT_CLI:
|
||||||
|
xml2cli_cb(stdout, xc, NULL, GT_HIDE, cligen_output); /* cli syntax */
|
||||||
|
break;
|
||||||
|
case FORMAT_NETCONF:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xret)
|
if (xret)
|
||||||
|
|
@ -1386,7 +1401,7 @@ cli_pagination(clicon_handle h, cvec *vars, cvec *argv)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int
|
int
|
||||||
cli_pagination(clicon_handle h, cvec *vars, cvec *argv)
|
cli_pagination(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Not yet implemented\n");
|
fprintf(stderr, "Not yet implemented\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,6 @@ expand_dbvar(void *h,
|
||||||
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
cxobj *xbot = NULL; /* xpath, NULL if datastore */
|
||||||
yang_stmt *y = NULL; /* yang spec of xpath */
|
yang_stmt *y = NULL; /* yang spec of xpath */
|
||||||
yang_stmt *yp;
|
yang_stmt *yp;
|
||||||
char *reason = NULL;
|
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
int cvvi = 0;
|
int cvvi = 0;
|
||||||
|
|
@ -368,8 +367,6 @@ expand_dbvar(void *h,
|
||||||
xml_free(xerr);
|
xml_free(xerr);
|
||||||
if (nsc)
|
if (nsc)
|
||||||
xml_nsctx_free(nsc);
|
xml_nsctx_free(nsc);
|
||||||
if (reason)
|
|
||||||
free(reason);
|
|
||||||
if (api_path)
|
if (api_path)
|
||||||
free(api_path);
|
free(api_path);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
|
|
|
||||||
|
|
@ -351,8 +351,8 @@ api_data_collection(clicon_handle h,
|
||||||
yang_stmt *y = NULL;
|
yang_stmt *y = NULL;
|
||||||
cbuf *cbrpc = NULL;
|
cbuf *cbrpc = NULL;
|
||||||
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
||||||
char *limit;
|
uint32_t limit = 0;
|
||||||
char *offset;
|
uint32_t offset = 0;
|
||||||
char *direction;
|
char *direction;
|
||||||
char *sort;
|
char *sort;
|
||||||
char *where;
|
char *where;
|
||||||
|
|
@ -446,13 +446,29 @@ api_data_collection(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
limit = cvec_find_str(qvec, "limit");
|
if ((attr = cvec_find_str(qvec, "limit")) != NULL){ /* 1-uint32 or "unbounded" */
|
||||||
offset = cvec_find_str(qvec, "offset");
|
if ((ret = netconf_parse_uint32_xml("limit", attr, "unbounded", 0, &xerr, &limit)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((attr = cvec_find_str(qvec, "offset")) != NULL){ /* 1-uint32 or "none" */
|
||||||
|
if ((ret = netconf_parse_uint32_xml("offset", attr, "none", 0, &xerr, &offset)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
direction = cvec_find_str(qvec, "direction");
|
direction = cvec_find_str(qvec, "direction");
|
||||||
sort = cvec_find_str(qvec, "sort");
|
sort = cvec_find_str(qvec, "sort");
|
||||||
where = cvec_find_str(qvec, "where");
|
where = cvec_find_str(qvec, "where");
|
||||||
if (clicon_rpc_get_pageable_list(h, "running", xpath, y, nsc, content,
|
if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc, content,
|
||||||
depth, limit, offset, direction, sort, where,
|
depth, offset, limit, direction, sort, where,
|
||||||
&xret) < 0){
|
&xret) < 0){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -483,23 +499,12 @@ api_data_collection(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#if 0
|
/* Note, the netconf GET pageable list can not distinguish between:
|
||||||
/* Check if not exists */
|
* - not-exists, ie there are no entries
|
||||||
if (xlen == 0){
|
* - no matching entries, eg there are entries, just not that match
|
||||||
/* 4.3: If a retrieval request for a data resource represents an
|
* Here we take the latter approach to return an empty list and do not
|
||||||
instance that does not exist, then an error response containing
|
* handle the non-exist case differently.
|
||||||
a "404 Not Found" status-line MUST be returned by the server.
|
*/
|
||||||
The error-tag value "invalid-value" is used in this case. */
|
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
|
||||||
goto done;
|
|
||||||
/* override invalid-value default 400 with 404 */
|
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) != NULL){
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 404) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (i=0; i<xlen; i++){
|
for (i=0; i<xlen; i++){
|
||||||
xp = xvec[i];
|
xp = xvec[i];
|
||||||
ns = NULL;
|
ns = NULL;
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,7 @@ example_statedata(clicon_handle h,
|
||||||
}
|
}
|
||||||
if ((xt = xml_new("config", NULL, CX_ELMNT)) == NULL)
|
if ((xt = xml_new("config", NULL, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Note, does not care about xpath / list-pagination */
|
||||||
if (clixon_xml_parse_file(fp, YB_MODULE, yspec, &xt, NULL) < 0)
|
if (clixon_xml_parse_file(fp, YB_MODULE, yspec, &xt, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
||||||
|
|
|
||||||
|
|
@ -140,5 +140,8 @@ int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id);
|
||||||
int netconf_hello_req(clicon_handle h, cbuf *cb);
|
int netconf_hello_req(clicon_handle h, cbuf *cb);
|
||||||
int clixon_netconf_error_fn(const char *fn, const int line, cxobj *xerr, const char *fmt, const char *arg);
|
int clixon_netconf_error_fn(const char *fn, const int line, cxobj *xerr, const char *fmt, const char *arg);
|
||||||
int clixon_netconf_internal_error(cxobj *xerr, char *msg, char *arg);
|
int clixon_netconf_internal_error(cxobj *xerr, char *msg, char *arg);
|
||||||
|
int netconf_parse_uint32(char *name, char *valstr, char *defaultstr, uint32_t defaultval, cbuf *cbret, uint32_t *value);
|
||||||
|
int netconf_parse_uint32_xml(char *name, char *valstr, char *defaultstr, uint32_t defaultval, cxobj **xerr, uint32_t *value);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _CLIXON_NETCONF_LIB_H */
|
#endif /* _CLIXON_NETCONF_LIB_H */
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,10 @@ int clicon_rpc_delete_config(clicon_handle h, char *db);
|
||||||
int clicon_rpc_lock(clicon_handle h, char *db);
|
int clicon_rpc_lock(clicon_handle h, char *db);
|
||||||
int clicon_rpc_unlock(clicon_handle h, char *db);
|
int clicon_rpc_unlock(clicon_handle h, char *db);
|
||||||
int clicon_rpc_get(clicon_handle h, char *xpath, cvec *nsc, netconf_content content, int32_t depth, cxobj **xret);
|
int clicon_rpc_get(clicon_handle h, char *xpath, cvec *nsc, netconf_content content, int32_t depth, cxobj **xret);
|
||||||
int clicon_rpc_get_pageable_list(clicon_handle h, char *datastore, char *xpath, yang_stmt *yli,
|
int clicon_rpc_get_pageable_list(clicon_handle h, char *datastore, char *xpath,
|
||||||
cvec *nsc, netconf_content content, int32_t depth,
|
cvec *nsc, netconf_content content, int32_t depth,
|
||||||
char *limit, char *offset, char *direction, char *sort, char *where,
|
uint32_t offset, uint32_t limit,
|
||||||
|
char *direction, char *sort, char *where,
|
||||||
cxobj **xt);
|
cxobj **xt);
|
||||||
int clicon_rpc_close_session(clicon_handle h);
|
int clicon_rpc_close_session(clicon_handle h);
|
||||||
int clicon_rpc_kill_session(clicon_handle h, uint32_t session_id);
|
int clicon_rpc_kill_session(clicon_handle h, uint32_t session_id);
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ cxobj *xpath_first_localonly(cxobj *xcur, const char *xpformat, ...);
|
||||||
int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, size_t *veclen, ...);
|
int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, size_t *veclen, ...);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1);
|
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1, cbuf **cbreason);
|
||||||
int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count);
|
int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count);
|
||||||
|
|
||||||
#endif /* _CLIXON_XPATH_H */
|
#endif /* _CLIXON_XPATH_H */
|
||||||
|
|
|
||||||
|
|
@ -1841,3 +1841,111 @@ clixon_netconf_internal_error(cxobj *xerr,
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Parse string into uint32 and return netconf bad-element msg on error
|
||||||
|
*
|
||||||
|
* @param[in] name Name of attribute (for error msg)
|
||||||
|
* @param[in] valstr Value string to be parsed
|
||||||
|
* @param[in] defaultstr If given, default string which is accepted and sets value to 0
|
||||||
|
* @param[in,out] cbret Output buffer for internal RPC message if invalid
|
||||||
|
* @param[out] value Value if valid
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 Invalid, cbret set
|
||||||
|
* @retval 1 OK
|
||||||
|
* @code
|
||||||
|
* char *name = "a";
|
||||||
|
* char *valstr = "322";
|
||||||
|
* char *defaultstr = "unbounded";
|
||||||
|
* cbuf *cbret = cbuf_new();
|
||||||
|
* uint32_t value = 0;
|
||||||
|
*
|
||||||
|
* if ((ret = netconf_parse_uint32(name, valstr, defaultstr, cbret, &value) < 0)
|
||||||
|
* goto err;
|
||||||
|
* if (ret == 0)
|
||||||
|
* // cbret contains netconf errmsg
|
||||||
|
* else
|
||||||
|
* // value contains uin32
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_parse_uint32(char *name,
|
||||||
|
char *valstr,
|
||||||
|
char *defaultstr,
|
||||||
|
uint32_t defaultvalue,
|
||||||
|
cbuf *cbret,
|
||||||
|
uint32_t *value)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int ret;
|
||||||
|
char *reason = NULL;
|
||||||
|
|
||||||
|
if (valstr == NULL){
|
||||||
|
clicon_err(OE_NETCONF, EINVAL, "valstr is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (defaultstr && strcmp(valstr, defaultstr) == 0)
|
||||||
|
*value = defaultvalue;
|
||||||
|
else {
|
||||||
|
if ((ret = parse_uint32(valstr, value, &reason)) < 0){
|
||||||
|
clicon_err(OE_XML, errno, "parse_uint32");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (ret == 0){
|
||||||
|
if (netconf_bad_element(cbret, "application",
|
||||||
|
name, "Unrecognized value") < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Parse string into uint32 and return netconf bad-element msg on error xml variant
|
||||||
|
* @see netconf_parse_uint32_xml
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_parse_uint32_xml(char *name,
|
||||||
|
char *valstr,
|
||||||
|
char *defaultstr,
|
||||||
|
uint32_t defaultvalue,
|
||||||
|
cxobj **xerr,
|
||||||
|
uint32_t *value)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int ret;
|
||||||
|
char *reason = NULL;
|
||||||
|
|
||||||
|
if (valstr == NULL){
|
||||||
|
clicon_err(OE_NETCONF, EINVAL, "valstr is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (defaultstr && strcmp(valstr, defaultstr) == 0)
|
||||||
|
*value = defaultvalue;
|
||||||
|
else {
|
||||||
|
if ((ret = parse_uint32(valstr, value, &reason)) < 0){
|
||||||
|
clicon_err(OE_XML, errno, "parse_uint32");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (ret == 0){
|
||||||
|
if (netconf_bad_element_xml(xerr, "application",
|
||||||
|
name, "Unrecognized value") < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,7 @@ clicon_rpc_get_config(clicon_handle h,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
cxobj *xd;
|
cxobj *xd = NULL;
|
||||||
uint32_t session_id;
|
uint32_t session_id;
|
||||||
int ret;
|
int ret;
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
|
|
@ -488,7 +488,7 @@ clicon_rpc_get_config(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xt){
|
if (xt && xd){
|
||||||
if (xml_rm(xd) < 0)
|
if (xml_rm(xd) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
*xt = xd;
|
*xt = xd;
|
||||||
|
|
@ -799,7 +799,7 @@ clicon_rpc_get(clicon_handle h,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
cxobj *xd;
|
cxobj *xd = NULL;
|
||||||
char *username;
|
char *username;
|
||||||
uint32_t session_id;
|
uint32_t session_id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -860,7 +860,7 @@ clicon_rpc_get(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xt){
|
if (xt && xd){
|
||||||
if (xml_rm(xd) < 0)
|
if (xml_rm(xd) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
*xt = xd;
|
*xt = xd;
|
||||||
|
|
@ -882,14 +882,12 @@ clicon_rpc_get(clicon_handle h,
|
||||||
/*! Get database configuration and state data collection
|
/*! Get database configuration and state data collection
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] xpath To identify a list/leaf-list
|
* @param[in] xpath To identify a list/leaf-list
|
||||||
* @param[in] yli Yang-stmt of list/leaf-list of collection, if given yang populate return
|
|
||||||
* xt and make sanity check
|
|
||||||
* @param[in] namespace Namespace associated w xpath
|
* @param[in] namespace Namespace associated w xpath
|
||||||
* @param[in] nsc Namespace context for filter
|
* @param[in] nsc Namespace context for filter
|
||||||
* @param[in] content Clixon extension: all, config, noconfig. -1 means all
|
* @param[in] content Clixon extension: all, config, noconfig. -1 means all
|
||||||
* @param[in] depth Nr of XML levels to get, -1 is all, 0 is none
|
* @param[in] depth Nr of XML levels to get, -1 is all, 0 is none
|
||||||
* @param[in] limit Collection/clixon extension
|
* @param[in] offset uint32, 0 means none
|
||||||
* @param[in] offset Collection/clixon extension
|
* @param[in] limit uint32, 0 means unbounded
|
||||||
* @param[in] direction Collection/clixon extension
|
* @param[in] direction Collection/clixon extension
|
||||||
* @param[in] sort Collection/clixon extension
|
* @param[in] sort Collection/clixon extension
|
||||||
* @param[in] where Collection/clixon extension
|
* @param[in] where Collection/clixon extension
|
||||||
|
|
@ -905,12 +903,11 @@ int
|
||||||
clicon_rpc_get_pageable_list(clicon_handle h,
|
clicon_rpc_get_pageable_list(clicon_handle h,
|
||||||
char *datastore,
|
char *datastore,
|
||||||
char *xpath,
|
char *xpath,
|
||||||
yang_stmt *yli,
|
|
||||||
cvec *nsc, /* namespace context for xpath */
|
cvec *nsc, /* namespace context for xpath */
|
||||||
netconf_content content,
|
netconf_content content,
|
||||||
int32_t depth,
|
int32_t depth,
|
||||||
char *limit,
|
uint32_t offset,
|
||||||
char *offset,
|
uint32_t limit,
|
||||||
char *direction,
|
char *direction,
|
||||||
char *sort,
|
char *sort,
|
||||||
char *where,
|
char *where,
|
||||||
|
|
@ -921,8 +918,7 @@ clicon_rpc_get_pageable_list(clicon_handle h,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
cxobj *xr; /* return msg */
|
cxobj *xd = NULL; /* return data */
|
||||||
cxobj *xd; /* return data */
|
|
||||||
char *username;
|
char *username;
|
||||||
uint32_t session_id;
|
uint32_t session_id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -963,10 +959,10 @@ clicon_rpc_get_pageable_list(clicon_handle h,
|
||||||
}
|
}
|
||||||
/* Explicit use of list-pagination */
|
/* Explicit use of list-pagination */
|
||||||
cprintf(cb, "<cp:list-pagination>true</cp:list-pagination>");
|
cprintf(cb, "<cp:list-pagination>true</cp:list-pagination>");
|
||||||
if (limit)
|
if (offset != 0)
|
||||||
cprintf(cb, "<cp:limit>%s</cp:limit>", limit);
|
cprintf(cb, "<cp:offset>%u</cp:offset>", offset);
|
||||||
if (offset)
|
if (limit != 0)
|
||||||
cprintf(cb, "<cp:offset>%s</cp:offset>", offset);
|
cprintf(cb, "<cp:limit>%u</cp:limit>", limit);
|
||||||
if (direction)
|
if (direction)
|
||||||
cprintf(cb, "<cp:direction>%s</cp:direction>", direction);
|
cprintf(cb, "<cp:direction>%s</cp:direction>", direction);
|
||||||
if (sort)
|
if (sort)
|
||||||
|
|
@ -980,8 +976,8 @@ clicon_rpc_get_pageable_list(clicon_handle h,
|
||||||
if (clicon_rpc_msg(h, msg, &xret) < 0)
|
if (clicon_rpc_msg(h, msg, &xret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Send xml error back: first check error, then ok */
|
/* Send xml error back: first check error, then ok */
|
||||||
if ((xr = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
|
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
|
||||||
xr = xml_parent(xr); /* point to rpc-reply */
|
xd = xml_parent(xd); /* point to rpc-reply */
|
||||||
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){
|
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){
|
||||||
if ((xd = xml_new(NETCONF_OUTPUT_DATA, NULL, CX_ELMNT)) == NULL)
|
if ((xd = xml_new(NETCONF_OUTPUT_DATA, NULL, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1001,7 +997,7 @@ clicon_rpc_get_pageable_list(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xt){
|
if (xt && xd){
|
||||||
if (xml_rm(xd) < 0)
|
if (xml_rm(xd) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
*xt = xd;
|
*xt = xd;
|
||||||
|
|
|
||||||
|
|
@ -930,37 +930,64 @@ xpath_vec_bool(cxobj *xcur,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*! Translate an xpath/nsc pair to a "canonical" form using yang prefixes
|
||||||
|
*
|
||||||
|
* @param[in] xs Parsed xpath - xpath_tree
|
||||||
|
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
||||||
|
* @param[in] nsc0 Input namespace context
|
||||||
|
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
||||||
|
* @param[out] reason Error reason if result is 0 - failed
|
||||||
|
* @retval 1 OK with nsc1 containing the transformed nsc
|
||||||
|
* @retval 0 XPath failure with reason set to why
|
||||||
|
* @retval -1 Fatal Error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
traverse_canonical(xpath_tree *xs,
|
traverse_canonical(xpath_tree *xs,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
cvec *nsc0,
|
cvec *nsc0,
|
||||||
cvec *nsc1)
|
cvec *nsc1,
|
||||||
|
cbuf **reason)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *prefix0;
|
char *prefix0;
|
||||||
char *prefix1;
|
char *prefix1;
|
||||||
char *namespace;
|
char *namespace;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
switch (xs->xs_type){
|
switch (xs->xs_type){
|
||||||
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
||||||
prefix0 = xs->xs_s0;
|
prefix0 = xs->xs_s0;
|
||||||
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
|
if ((namespace = xml_nsctx_get(nsc0, prefix0)) == NULL){
|
||||||
clicon_err(OE_XML, ENOENT, "No namespace found for prefix: %s",
|
if ((cb = cbuf_new()) == NULL){
|
||||||
prefix0);
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "No namespace found for prefix: %s", prefix0);
|
||||||
|
if (reason)
|
||||||
|
*reason = cb;
|
||||||
|
goto failed;
|
||||||
}
|
}
|
||||||
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
|
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) == NULL){
|
||||||
clicon_err(OE_XML, ENOENT, "No modules found for namespace: %s",
|
if ((cb = cbuf_new()) == NULL){
|
||||||
namespace);
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "No modules found for namespace: %s", namespace);
|
||||||
|
if (reason)
|
||||||
|
*reason = cb;
|
||||||
|
goto failed;
|
||||||
}
|
}
|
||||||
if ((prefix1 = yang_find_myprefix(ymod)) == NULL){
|
if ((prefix1 = yang_find_myprefix(ymod)) == NULL){
|
||||||
clicon_err(OE_XML, ENOENT, "No prefix found in module: %s",
|
if ((cb = cbuf_new()) == NULL){
|
||||||
yang_argument_get(ymod));
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "No prefix found in module: %s", yang_argument_get(ymod));
|
||||||
|
if (reason)
|
||||||
|
*reason = cb;
|
||||||
|
goto failed;
|
||||||
}
|
}
|
||||||
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
||||||
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
||||||
|
|
@ -977,25 +1004,36 @@ traverse_canonical(xpath_tree *xs,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (xs->xs_c0)
|
if (xs->xs_c0){
|
||||||
if (traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1) < 0)
|
if ((ret = traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1, reason)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xs->xs_c1)
|
if (ret == 0)
|
||||||
if (traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1) < 0)
|
goto failed;
|
||||||
|
}
|
||||||
|
if (xs->xs_c1){
|
||||||
|
if ((ret = traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1, reason)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
if (ret == 0)
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
failed:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate an xpath/nsc pair to a "canonical" form using yang prefixes
|
/*! Translate an xpath/nsc pair to a "canonical" form using yang prefixes
|
||||||
|
*
|
||||||
* @param[in] xpath0 Input xpath
|
* @param[in] xpath0 Input xpath
|
||||||
* @param[in] nsc0 Input namespace context
|
* @param[in] nsc0 Input namespace context
|
||||||
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
||||||
* @param[out] xpath1 Output xpath. Free after use with free
|
* @param[out] xpath1 Output xpath. Free after use with free
|
||||||
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
||||||
* @retval 0 OK, xpath1 and nsc1 allocated
|
* @retval 1 OK, xpath1 and nsc1 allocated
|
||||||
* @retval -1 Error
|
* @retval 0 XPath failure with reason set to why
|
||||||
|
* @retval -1 Fatal Error
|
||||||
* Example:
|
* Example:
|
||||||
* Module A has prefix a and namespace urn:example:a and symbols x
|
* Module A has prefix a and namespace urn:example:a and symbols x
|
||||||
* Module B with prefix b and namespace urn:example:b and symbols y
|
* Module B with prefix b and namespace urn:example:b and symbols y
|
||||||
|
|
@ -1008,7 +1046,8 @@ traverse_canonical(xpath_tree *xs,
|
||||||
* @code
|
* @code
|
||||||
* char *xpath1 = NULL;
|
* char *xpath1 = NULL;
|
||||||
* cvec *nsc1 = NULL;
|
* cvec *nsc1 = NULL;
|
||||||
* if (xpath2canonical(xpath0, nsc0, yspec, &xpath1, &nsc1) < 0)
|
* cbuf *reason = NULL;
|
||||||
|
* if ((ret = xpath2canonical(xpath0, nsc0, yspec, &xpath1, &nsc1, &reason)) < 0)
|
||||||
* err;
|
* err;
|
||||||
* ...
|
* ...
|
||||||
* if (xpath1) free(xpath1);
|
* if (xpath1) free(xpath1);
|
||||||
|
|
@ -1017,15 +1056,17 @@ traverse_canonical(xpath_tree *xs,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xpath2canonical(const char *xpath0,
|
xpath2canonical(const char *xpath0,
|
||||||
cvec *nsc0,
|
cvec *nsc0,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
char **xpath1,
|
char **xpath1,
|
||||||
cvec **nsc1p)
|
cvec **nsc1p,
|
||||||
|
cbuf **cbreason)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
xpath_tree *xpt = NULL;
|
xpath_tree *xpt = NULL;
|
||||||
cvec *nsc1 = NULL;
|
cvec *nsc1 = NULL;
|
||||||
cbuf *xcb = NULL;
|
cbuf *xcb = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Parse input xpath into an xpath-tree */
|
/* Parse input xpath into an xpath-tree */
|
||||||
if (xpath_parse(xpath0, &xpt) < 0)
|
if (xpath_parse(xpath0, &xpt) < 0)
|
||||||
|
|
@ -1036,8 +1077,10 @@ xpath2canonical(const char *xpath0,
|
||||||
/* Traverse tree to find prefixes, transform them to canonical form and
|
/* Traverse tree to find prefixes, transform them to canonical form and
|
||||||
* create a canonical network namespace
|
* create a canonical network namespace
|
||||||
*/
|
*/
|
||||||
if (traverse_canonical(xpt, yspec, nsc0, nsc1) < 0)
|
if ((ret = traverse_canonical(xpt, yspec, nsc0, nsc1, cbreason)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto failed;
|
||||||
/* Print tree with new prefixes */
|
/* Print tree with new prefixes */
|
||||||
if ((xcb = cbuf_new()) == NULL){
|
if ((xcb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
|
@ -1055,7 +1098,7 @@ xpath2canonical(const char *xpath0,
|
||||||
*nsc1p = nsc1;
|
*nsc1p = nsc1;
|
||||||
nsc1 = NULL;
|
nsc1 = NULL;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
if (xcb)
|
if (xcb)
|
||||||
cbuf_free(xcb);
|
cbuf_free(xcb);
|
||||||
|
|
@ -1064,6 +1107,9 @@ xpath2canonical(const char *xpath0,
|
||||||
if (xpt)
|
if (xpt)
|
||||||
xpath_tree_free(xpt);
|
xpath_tree_free(xpt);
|
||||||
return retval;
|
return retval;
|
||||||
|
failed:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Return a count(xpath)
|
/*! Return a count(xpath)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
# Backlog items:
|
# Backlog items:
|
||||||
# 1. "remaining" annotation RFC 7952
|
# 1. "remaining" annotation RFC 7952
|
||||||
# 2. pattern '.*[\n].*' { modifier invert-match;
|
# 2. pattern '.*[\n].*' { modifier invert-match;
|
||||||
|
# XXX: handling of empty return ?
|
||||||
|
|
||||||
# Magic line must be first in script (see README.md)
|
# Magic line must be first in script (see README.md)
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
@ -22,6 +23,10 @@ RESTCONFIG=$(restconf_config none false)
|
||||||
# Validate internal state xml
|
# Validate internal state xml
|
||||||
: ${validatexml:=false}
|
: ${validatexml:=false}
|
||||||
|
|
||||||
|
# Number of list/leaf-list entries in file
|
||||||
|
#: ${perfnr:=20000}
|
||||||
|
: ${perfnr:=200}
|
||||||
|
|
||||||
cat <<EOF > $cfg
|
cat <<EOF > $cfg
|
||||||
<clixon-config xmlns="http://clicon.org/config">
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
|
@ -206,59 +211,27 @@ cat<<EOF > $fstate
|
||||||
</stats>
|
</stats>
|
||||||
</member>
|
</member>
|
||||||
</members>
|
</members>
|
||||||
<audit-logs xmlns="http://example.com/ns/example-social">
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>": "2020-10-11T06:47:59Z",</timestamp>
|
|
||||||
<member-id>alice</member-id>
|
|
||||||
<source-ip>192.168.0.92</source-ip>
|
|
||||||
<request>POST /groups/group/2043</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2020-11-01T15:22:01Z</timestamp>
|
|
||||||
<member-id>bob</member-id>
|
|
||||||
<source-ip>192.168.2.16</source-ip>
|
|
||||||
<request>POST /groups/group/123</request>
|
|
||||||
<outcome>false</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2020-12-12T21:00:28Z</timestamp>
|
|
||||||
<member-id>eric</member-id>
|
|
||||||
<source-ip>192.168.254.1</source-ip>
|
|
||||||
<request>POST /groups/group/10</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2021-01-03T06:47:59Z</timestamp>
|
|
||||||
<member-id>alice</member-id>
|
|
||||||
<source-ip>192.168.0.92</source-ip>
|
|
||||||
<request>POST /groups/group/333</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2021-01-21T10:00:00Z</timestamp>
|
|
||||||
<member-id>bob</member-id>
|
|
||||||
<source-ip>192.168.2.16</source-ip>
|
|
||||||
<request>POST /groups/group/42</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2020-02-07T09:06:21Z</timestamp>
|
|
||||||
<member-id>alice</member-id>
|
|
||||||
<source-ip>192.168.0.92</source-ip>
|
|
||||||
<request>POST /groups/group/1202</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
<audit-log>
|
|
||||||
<timestamp>2020-02-28T02:48:11Z</timestamp>
|
|
||||||
<member-id>bob</member-id>
|
|
||||||
<source-ip>192.168.2.16</source-ip>
|
|
||||||
<request>POST /groups/group/345</request>
|
|
||||||
<outcome>true</outcome>
|
|
||||||
</audit-log>
|
|
||||||
</audit-logs>
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Check this later with committed data
|
||||||
|
new "generate state with $perfnr list entries"
|
||||||
|
echo "<audit-logs xmlns=\"http://example.com/ns/example-social\">" >> $fstate
|
||||||
|
for (( i=0; i<$perfnr; i++ )); do
|
||||||
|
echo " <audit-log>" >> $fstate
|
||||||
|
mon=$(( ( RANDOM % 10 ) ))
|
||||||
|
day=$(( ( RANDOM % 10 ) ))
|
||||||
|
hour=$(( ( RANDOM % 10 ) ))
|
||||||
|
echo " <timestamp>2020-0$mon-0$dayT0$hour:48:11Z</timestamp>" >> $fstate
|
||||||
|
echo " <member-id>bob</member-id>" >> $fstate
|
||||||
|
ip1=$(( ( RANDOM % 255 ) ))
|
||||||
|
ip2=$(( ( RANDOM % 255 ) ))
|
||||||
|
echo " <source-ip>192.168.$ip1.$ip2</source-ip>" >> $fstate
|
||||||
|
echo " <request>POST</request>" >> $fstate
|
||||||
|
echo " <outcome>true</outcome>" >> $fstate
|
||||||
|
echo " </audit-log>" >> $fstate
|
||||||
|
done
|
||||||
|
echo -n "</audit-logs>" >> $fstate # No CR
|
||||||
|
|
||||||
# Run limit-only test with netconf, restconf+xml and restconf+json
|
# Run limit-only test with netconf, restconf+xml and restconf+json
|
||||||
# Args:
|
# Args:
|
||||||
# 1. offset
|
# 1. offset
|
||||||
|
|
@ -278,6 +251,7 @@ function testlimit()
|
||||||
jsonlist="" # for restconf json
|
jsonlist="" # for restconf json
|
||||||
jsonmeta=""
|
jsonmeta=""
|
||||||
let i=0
|
let i=0
|
||||||
|
|
||||||
for li in $list; do
|
for li in $list; do
|
||||||
if [ $i = 0 ]; then
|
if [ $i = 0 ]; then
|
||||||
if [ $limit == 0 ]; then
|
if [ $limit == 0 ]; then
|
||||||
|
|
@ -315,19 +289,40 @@ function testlimit()
|
||||||
jsonstr="${jsonstr}&offset=$offset"
|
jsonstr="${jsonstr}&offset=$offset"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
new "limit=$limit NETCONF get-config"
|
|
||||||
# expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination>$limitxmlstr$offsetxmlstr</get-config></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><favorites>$xmllist</favorites></member></members></data></rpc-reply>]]>]]>$"
|
|
||||||
|
|
||||||
new "limit=$limit NETCONF get"
|
if [ -z "$list" ]; then
|
||||||
# expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination>$limitxmlstr$offsetxmlstr</get></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><favorites>$xmllist</favorites></member></members></data></rpc-reply>]]>]]>$"
|
reply="<rpc-reply $DEFAULTNS><data/></rpc-reply>]]>]]>$"
|
||||||
|
else
|
||||||
|
reply="<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><favorites>$xmllist</favorites></member></members></data></rpc-reply>]]>]]>$"
|
||||||
|
fi
|
||||||
|
new "limit=$limit offset=$offset NETCONF get-config"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination>$limitxmlstr$offsetxmlstr</get-config></rpc>]]>]]>" "$reply"
|
||||||
|
|
||||||
new "limit=$limit Parameter RESTCONF xml"
|
if [ -z "$list" ]; then
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+xml" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers${jsonstr})" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+xml" "<yang-collection xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination\">$xmllist2</yang-collection>"
|
reply="<rpc-reply $DEFAULTNS><data/></rpc-reply>]]>]]>$"
|
||||||
|
else
|
||||||
|
reply="<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><favorites>$xmllist</favorites></member></members></data></rpc-reply>]]>]]>$"
|
||||||
|
fi
|
||||||
|
new "limit=$limit offset=$offset NETCONF get"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination>$limitxmlstr$offsetxmlstr</get></rpc>]]>]]>" "$reply"
|
||||||
|
|
||||||
new "limit=$limit Parameter RESTCONF json"
|
if [ -z "$list" ]; then
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+json" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers${jsonstr})" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+json" "{\"yang-collection\":{\"example-social:uint8-numbers\":\[$jsonlist\]$jsonmeta}"
|
reply="<yang-collection xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination\"/>"
|
||||||
|
else
|
||||||
|
reply="<yang-collection xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination\">$xmllist2</yang-collection>"
|
||||||
|
fi
|
||||||
|
new "limit=$limit offset=$offset Parameter RESTCONF xml"
|
||||||
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+xml" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers${jsonstr})" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+xml" "$reply"
|
||||||
|
|
||||||
} # testrunf
|
if [ -z "$list" ]; then
|
||||||
|
reply="{\"yang-collection\":{}}"
|
||||||
|
else
|
||||||
|
reply="{\"yang-collection\":{\"example-social:uint8-numbers\":\[$jsonlist\]$jsonmeta}"
|
||||||
|
fi
|
||||||
|
new "limit=$limit offset=$offset Parameter RESTCONF json"
|
||||||
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+json" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers${jsonstr})" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+json" "$reply"
|
||||||
|
|
||||||
|
} # testlimit
|
||||||
|
|
||||||
new "test params: -f $cfg -s startup -- -sS $fstate"
|
new "test params: -f $cfg -s startup -- -sS $fstate"
|
||||||
|
|
||||||
|
|
@ -357,6 +352,7 @@ fi
|
||||||
new "wait restconf"
|
new "wait restconf"
|
||||||
wait_restconf
|
wait_restconf
|
||||||
|
|
||||||
|
|
||||||
new "A.3.1.1. limit=1"
|
new "A.3.1.1. limit=1"
|
||||||
testlimit 0 1 5 "17"
|
testlimit 0 1 5 "17"
|
||||||
|
|
||||||
|
|
@ -381,12 +377,12 @@ testlimit 2 0 0 "11 7 5 3"
|
||||||
new "A.3.2.3. offset=5"
|
new "A.3.2.3. offset=5"
|
||||||
testlimit 5 0 0 "3"
|
testlimit 5 0 0 "3"
|
||||||
|
|
||||||
#new "A.3.2.4. offset=6"
|
new "A.3.2.4. offset=6"
|
||||||
#testlimit 6 0 0 ""
|
testlimit 6 0 0 ""
|
||||||
|
|
||||||
# This is incomplete wrt the draft
|
# This is incomplete wrt the draft
|
||||||
new "A.3.7. limit=2 offset=2"
|
new "A.3.7. limit=2 offset=2"
|
||||||
testlimit 2 2 2 "11 7"
|
testlimitn 2 2 2 "11 7"
|
||||||
|
|
||||||
# CLI
|
# CLI
|
||||||
# XXX This relies on a very specific clispec command: need a more generic test
|
# XXX This relies on a very specific clispec command: need a more generic test
|
||||||
|
|
@ -415,6 +411,7 @@ fi
|
||||||
|
|
||||||
unset RESTCONFIG
|
unset RESTCONFIG
|
||||||
unset validatexml
|
unset validatexml
|
||||||
|
unset perfnr
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,10 +57,10 @@ new "xpath canonical form descendants"
|
||||||
expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"'
|
expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"'
|
||||||
|
|
||||||
new "xpath canonical form (no default should fail)"
|
new "xpath canonical form (no default should fail)"
|
||||||
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b 2> /dev/null)" 255
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b 2>&1)" 0 "/x/j:y: No namespace found for prefix"
|
||||||
|
|
||||||
new "xpath canonical form (wrong namespace should fail)"
|
new "xpath canonical form (wrong namespace should fail)"
|
||||||
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b 2>/dev/null)" 255
|
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:c -n j:urn:example:b 2>&1)" 0 "/i:x/j:y: No modules found for namespace"
|
||||||
|
|
||||||
rm -rf $dir
|
rm -rf $dir
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -281,8 +281,14 @@ main(int argc,
|
||||||
if (canonical){
|
if (canonical){
|
||||||
char *xpath1 = NULL;
|
char *xpath1 = NULL;
|
||||||
cvec *nsc1 = NULL;
|
cvec *nsc1 = NULL;
|
||||||
if (xpath2canonical(xpath, nsc, yspec, &xpath1, &nsc1) < 0)
|
cbuf *cbreason = NULL;
|
||||||
|
|
||||||
|
if ((ret = xpath2canonical(xpath, nsc, yspec, &xpath1, &nsc1, &cbreason)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
fprintf(stderr, "Error with %s: %s", xpath, cbuf_get(cbreason));
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
xpath = xpath1;
|
xpath = xpath1;
|
||||||
if (xpath)
|
if (xpath)
|
||||||
fprintf(stdout, "%s\n", xpath);
|
fprintf(stdout, "%s\n", xpath);
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,9 @@ module clixon-netconf-list-pagination {
|
||||||
default "unbounded";
|
default "unbounded";
|
||||||
description
|
description
|
||||||
"The maximum number of list entries to return. The
|
"The maximum number of list entries to return. The
|
||||||
value of the 'limit' parameter is either an integer
|
value of the 'limit' parameter is either an integer
|
||||||
greater than or equal to 1, or the string 'unbounded'.
|
greater than or equal to 1, or the string 'unbounded'.
|
||||||
The string 'unbounded' is the default value.";
|
The string 'unbounded' is the default value.";
|
||||||
}
|
}
|
||||||
leaf offset {
|
leaf offset {
|
||||||
type union {
|
type union {
|
||||||
|
|
@ -119,9 +119,9 @@ module clixon-netconf-list-pagination {
|
||||||
default "none";
|
default "none";
|
||||||
description
|
description
|
||||||
"The first list item to return.
|
"The first list item to return.
|
||||||
the 'offset' parameter is either an integer greater than
|
the 'offset' parameter is either an integer greater than
|
||||||
or equal to 1, or the string 'unbounded'. The string
|
or equal to 1, or the string 'unbounded'. The string
|
||||||
'unbounded' is the default value.";
|
'none' is the default value.";
|
||||||
}
|
}
|
||||||
leaf direction {
|
leaf direction {
|
||||||
type enumeration {
|
type enumeration {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue