- New netconf-specific uint32 parse functions

- Added failure handling to xpath traverse_canonical
- Started pagination cli code
This commit is contained in:
Olof hagsand 2021-08-30 14:43:24 +02:00
parent 390b0886ed
commit a046306270
16 changed files with 410 additions and 241 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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)

View file

@ -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 */

View file

@ -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);

View file

@ -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 */

View file

@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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 {