* Protyped netconf native pagination
This commit is contained in:
parent
6bf3112fe7
commit
b03cf426a4
8 changed files with 493 additions and 85 deletions
|
|
@ -412,37 +412,89 @@ client_get_config_only(clicon_handle h,
|
|||
yang_stmt *yspec,
|
||||
char *db,
|
||||
char *xpath,
|
||||
#ifdef CLIXON_PAGINATION
|
||||
uint32_t limit,
|
||||
uint32_t offset,
|
||||
char *xpathpred,
|
||||
#endif /* CLIXON_PAGINATION */
|
||||
char *username,
|
||||
int32_t depth,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xnacm = NULL;
|
||||
cxobj **xvec = NULL;
|
||||
cxobj *xerr = NULL;
|
||||
size_t xlen;
|
||||
int ret;
|
||||
int retval = -1;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xnacm = NULL;
|
||||
cxobj **xvec = NULL;
|
||||
cxobj *xerr = NULL;
|
||||
size_t xlen;
|
||||
int ret;
|
||||
#ifdef CLIXON_PAGINATION
|
||||
cxobj *xcache = NULL;
|
||||
uint32_t total = 0;
|
||||
cbuf *cb = NULL;
|
||||
uint32_t remaining = 0;
|
||||
|
||||
#endif
|
||||
|
||||
/* Note xret can be pruned by nacm below (and change name),
|
||||
* so zero-copy cant be used
|
||||
* Also, must use external namespace context here due to <filter stmt
|
||||
*/
|
||||
if ((ret = xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, &xret, NULL, &xerr)) < 0) {
|
||||
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
|
||||
if ((ret = xmldb_get0(h, db, YB_MODULE, nsc,
|
||||
#ifdef CLIXON_PAGINATION
|
||||
xpathpred,
|
||||
#else
|
||||
xpath,
|
||||
#endif
|
||||
1, &xret, NULL, &xerr)) < 0){
|
||||
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
if (ret == 0){
|
||||
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
#ifdef CLIXON_PAGINATION
|
||||
/* First get number of hits (ALL entries: note not optimized)
|
||||
*/
|
||||
if ((xcache = xmldb_cache_get(h, db)) != NULL){
|
||||
if (xpath_count(xcache, nsc, xpath, &total) < 0)
|
||||
goto done;
|
||||
if (total >= (offset + limit))
|
||||
remaining = total - (offset + limit);
|
||||
}
|
||||
/* XXX need only first for remaining, but all for NACM */
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* Add remain attribute to first returning element */
|
||||
if (xlen){
|
||||
cxobj *x;
|
||||
cxobj *xa;
|
||||
x = xvec[0];
|
||||
if ((xa = xml_new("remaining", x, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%u", remaining);
|
||||
if (xml_value_set(xa, cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "cp") < 0)
|
||||
goto done;
|
||||
if (xmlns_set(x, "cp", "http://clicon.org/clixon-netconf-list-pagination") < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
/* Pre-NACM access step */
|
||||
xnacm = clicon_nacm_cache(h);
|
||||
if (xnacm != NULL){ /* Do NACM validation */
|
||||
#ifndef CLIXON_PAGINATION
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
#endif
|
||||
/* NACM datanode/module read validation */
|
||||
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
|
||||
goto done;
|
||||
|
|
@ -460,6 +512,8 @@ client_get_config_only(clicon_handle h,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (xvec)
|
||||
|
|
@ -469,6 +523,59 @@ client_get_config_only(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
#if defined(LIST_PAGINATION) || defined(CLIXON_PAGINATION)
|
||||
/*! Help function for parsing restconf query parameter and setting netconf attribute
|
||||
*
|
||||
* If not "unbounded", parse and set a numeric value
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] name Name of attribute
|
||||
* @param[in] defaultstr Default string which is accepted and sets value to 0
|
||||
* @param[in,out] cbret Output buffer for internal RPC message
|
||||
* @param[out] value Value
|
||||
* @retval -1 Error
|
||||
* @retval 0 Invalid, cbret set
|
||||
* @retval 1 OK
|
||||
*/
|
||||
static int
|
||||
element2value(clicon_handle h,
|
||||
cxobj *xe,
|
||||
char *name,
|
||||
char *defaultstr,
|
||||
cbuf *cbret,
|
||||
uint32_t *value)
|
||||
{
|
||||
int retval = -1;
|
||||
char *valstr;
|
||||
int ret;
|
||||
char *reason = NULL;
|
||||
cxobj *x;
|
||||
|
||||
*value = 0;
|
||||
if ((x = xml_find_type(xe, NULL, name, CX_ELMNT)) != NULL &&
|
||||
(valstr = xml_body(x)) != NULL &&
|
||||
strcmp(valstr, defaultstr) != 0){
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Retrieve all or part of a specified configuration.
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
|
|
@ -500,6 +607,15 @@ from_client_get_config(clicon_handle h,
|
|||
char *attr;
|
||||
char *xpath0;
|
||||
cvec *nsc1 = NULL;
|
||||
#ifdef CLIXON_PAGINATION
|
||||
uint32_t limit = 0;
|
||||
uint32_t offset = 0;
|
||||
char *direction = NULL;
|
||||
char *sort = NULL;
|
||||
char *where = NULL;
|
||||
cbuf *cbpath = NULL;
|
||||
cxobj *x;
|
||||
#endif /* CLIXON_PAGINATION */
|
||||
|
||||
username = clicon_username_get(h);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
|
|
@ -551,7 +667,57 @@ from_client_get_config(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
}
|
||||
if ((ret = client_get_config_only(h, nsc, yspec, db, xpath, username, -1, cbret)) < 0)
|
||||
#ifdef CLIXON_PAGINATION
|
||||
/* limit */
|
||||
if ((ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
|
||||
goto done;
|
||||
/* offset */
|
||||
if (ret && (ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
|
||||
goto done;
|
||||
/* direction */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
|
||||
direction = xml_body(x);
|
||||
if (strcmp(direction, "forward") != 0 && strcmp(direction, "reverse") != 0){
|
||||
if (netconf_bad_attribute(cbret, "application",
|
||||
"direction", "Unrecognized value of direction attribute") < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
/* sort */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "sort", CX_ELMNT)) != NULL)
|
||||
sort = xml_body(x);
|
||||
if (sort) ; /* XXX */
|
||||
/* where */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL)
|
||||
where = xml_body(x);
|
||||
/* Build a "predicate" cbuf
|
||||
* This solution uses xpath predicates to translate "limit" and "offset" to
|
||||
* relational operators <>.
|
||||
*/
|
||||
if ((cbpath = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* This uses xpath. Maybe limit should use parameters */
|
||||
cprintf(cbpath, "%s", xpath);
|
||||
if (where)
|
||||
cprintf(cbpath, "[%s]", where);
|
||||
if (offset){
|
||||
cprintf(cbpath, "[%u <= position()", offset);
|
||||
if (limit)
|
||||
cprintf(cbpath, " and position() < %u", limit+offset);
|
||||
cprintf(cbpath, "]");
|
||||
}
|
||||
else if (limit)
|
||||
cprintf(cbpath, "[position() < %u]", limit);
|
||||
#endif /* CLIXON_PAGINATION */
|
||||
if ((ret = client_get_config_only(h, nsc, yspec, db, xpath,
|
||||
#ifdef CLIXON_PAGINATION
|
||||
limit, offset,
|
||||
cbuf_get(cbpath),
|
||||
#endif
|
||||
username, -1, cbret)) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1078,6 +1244,15 @@ from_client_get(clicon_handle h,
|
|||
cxobj *xerr = NULL;
|
||||
int ret;
|
||||
char *reason = NULL;
|
||||
#ifdef CLIXON_PAGINATION
|
||||
uint32_t limit = 0;
|
||||
uint32_t offset = 0;
|
||||
char *direction = NULL;
|
||||
char *sort = NULL;
|
||||
char *where = NULL;
|
||||
cbuf *cbpath = NULL;
|
||||
cxobj *x;
|
||||
#endif /* CLIXON_PAGINATION */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
username = clicon_username_get(h);
|
||||
|
|
@ -1119,8 +1294,58 @@ from_client_get(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
}
|
||||
#ifdef CLIXON_PAGINATION
|
||||
/* limit */
|
||||
if ((ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
|
||||
goto done;
|
||||
/* offset */
|
||||
if (ret && (ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
|
||||
goto done;
|
||||
/* direction */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
|
||||
direction = xml_body(x);
|
||||
if (strcmp(direction, "forward") != 0 && strcmp(direction, "reverse") != 0){
|
||||
if (netconf_bad_attribute(cbret, "application",
|
||||
"direction", "Unrecognized value of direction attribute") < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
/* sort */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "sort", CX_ELMNT)) != NULL)
|
||||
sort = xml_body(x);
|
||||
if (sort) ; /* XXX */
|
||||
/* where */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL)
|
||||
where = xml_body(x);
|
||||
/* Build a "predicate" cbuf
|
||||
* This solution uses xpath predicates to translate "limit" and "offset" to
|
||||
* relational operators <>.
|
||||
*/
|
||||
if ((cbpath = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* This uses xpath. Maybe limit should use parameters */
|
||||
cprintf(cbpath, "%s", xpath);
|
||||
if (where)
|
||||
cprintf(cbpath, "[%s]", where);
|
||||
if (offset){
|
||||
cprintf(cbpath, "[%u <= position()", offset);
|
||||
if (limit)
|
||||
cprintf(cbpath, " and position() < %u", limit+offset);
|
||||
cprintf(cbpath, "]");
|
||||
}
|
||||
else if (limit)
|
||||
cprintf(cbpath, "[position() < %u]", limit);
|
||||
#endif /* CLIXON_PAGINATION */
|
||||
if (content == CONTENT_CONFIG){ /* config only, no state */
|
||||
if (client_get_config_only(h, nsc, yspec, "running", xpath, username, depth, cbret) < 0)
|
||||
if (client_get_config_only(h, nsc, yspec, "running", xpath,
|
||||
#ifdef CLIXON_PAGINATION
|
||||
limit, offset,
|
||||
cbuf_get(cbpath),
|
||||
#endif
|
||||
username, depth, cbret) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
|
@ -1239,6 +1464,9 @@ from_client_get(clicon_handle h,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
|
||||
if (cbpath)
|
||||
cbuf_free(cbpath);
|
||||
if (reason)
|
||||
free(reason);
|
||||
if (xerr)
|
||||
|
|
@ -1337,56 +1565,6 @@ from_client_kill_session(clicon_handle h,
|
|||
}
|
||||
|
||||
#ifdef LIST_PAGINATION
|
||||
/*! Help function for parsing restconf query parameter and setting netconf attribute
|
||||
*
|
||||
* If not "unbounded", parse and set a numeric value
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] name Name of attribute
|
||||
* @param[in] defaultstr Default string which is accepted and sets value to 0
|
||||
* @param[in,out] cbret Output buffer for internal RPC message
|
||||
* @param[out] value Value
|
||||
* @retval -1 Error
|
||||
* @retval 0 Invalid, cbret set
|
||||
* @retval 1 OK
|
||||
*/
|
||||
static int
|
||||
element2value(clicon_handle h,
|
||||
cxobj *xe,
|
||||
char *name,
|
||||
char *defaultstr,
|
||||
cbuf *cbret,
|
||||
uint32_t *value)
|
||||
{
|
||||
int retval = -1;
|
||||
char *valstr;
|
||||
int ret;
|
||||
char *reason = NULL;
|
||||
cxobj *x;
|
||||
|
||||
*value = 0;
|
||||
if ((x = xml_find_type(xe, NULL, name, CX_ELMNT)) != NULL &&
|
||||
(valstr = xml_body(x)) != NULL &&
|
||||
strcmp(valstr, defaultstr) != 0){
|
||||
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;
|
||||
}
|
||||
|
||||
/*! Retrieve collection configuration and device state information
|
||||
*
|
||||
|
|
@ -1423,16 +1601,17 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
int i;
|
||||
cxobj *xerr = NULL;
|
||||
int ret;
|
||||
cbuf *cb = NULL;
|
||||
uint32_t limit = 0;
|
||||
uint32_t offset = 0;
|
||||
size_t total = 0;
|
||||
uint32_t total = 0;
|
||||
uint32_t remaining = 0;
|
||||
char *direction = NULL;
|
||||
char *sort = NULL;
|
||||
char *where = NULL;
|
||||
char *datastore = NULL;
|
||||
char *reason = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cbuf *cbpath = NULL;
|
||||
cxobj *xtop = NULL;
|
||||
yang_stmt *y;
|
||||
char *ns;
|
||||
|
|
@ -1535,22 +1714,22 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
* This solution uses xpath predicates to translate "limit" and "offset" to
|
||||
* relational operators <>.
|
||||
*/
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
if ((cbpath = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* This uses xpath. Maybe limit should use parameters */
|
||||
cprintf(cb, "%s", xpath);
|
||||
cprintf(cbpath, "%s", xpath);
|
||||
if (where)
|
||||
cprintf(cb, "[%s]", where);
|
||||
cprintf(cbpath, "[%s]", where);
|
||||
if (offset){
|
||||
cprintf(cb, "[%u <= position()", offset);
|
||||
cprintf(cbpath, "[%u <= position()", offset);
|
||||
if (limit)
|
||||
cprintf(cb, " and position() < %u", limit+offset);
|
||||
cprintf(cb, "]");
|
||||
cprintf(cbpath, " and position() < %u", limit+offset);
|
||||
cprintf(cbpath, "]");
|
||||
}
|
||||
else if (limit)
|
||||
cprintf(cb, "[position() < %u]", limit);
|
||||
cprintf(cbpath, "[position() < %u]", limit);
|
||||
|
||||
/* Split into CT or CF */
|
||||
if (yang_config_ancestor(y) == 1){ /* CT */
|
||||
|
|
@ -1565,13 +1744,9 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* First get number of hits */
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &total, xpath) < 0)
|
||||
/* First get number of hits (ALL entries: note not optimized) */
|
||||
if (xpath_count(xret, nsc, xpath, &total) < 0)
|
||||
goto done;
|
||||
if (xvec){
|
||||
free(xvec);
|
||||
xvec = NULL;
|
||||
}
|
||||
if (total < (offset + limit))
|
||||
remaining = 0;
|
||||
else
|
||||
|
|
@ -1579,14 +1754,14 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
}
|
||||
/* There may be CF data in a CT collection */
|
||||
if (content == CONTENT_ALL){
|
||||
if ((ret = client_statedata(h, cbuf_get(cb), nsc, content, &xret)) < 0)
|
||||
if ((ret = client_statedata(h, cbuf_get(cbpath), nsc, content, &xret)) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else { /* CF */
|
||||
/* There can be no CT data in a CF collection */
|
||||
if (content == CONTENT_NONCONFIG || content == CONTENT_ALL){
|
||||
if ((ret = client_statedata(h, cbuf_get(cb), nsc, content, &xret)) < 0)
|
||||
if ((ret = client_statedata(h, cbuf_get(cbpath), nsc, content, &xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* Error from callback (error in xret) */
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
|
||||
|
|
@ -1634,7 +1809,7 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
* Actually this is a safety catch, should really be done in plugins
|
||||
* and modules_state functions.
|
||||
*/
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, cbuf_get(cb)) < 0)
|
||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, cbuf_get(cbpath)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Pre-NACM access step */
|
||||
|
|
@ -1646,7 +1821,11 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
}
|
||||
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><pageable-list xmlns=\"%s\">",
|
||||
NETCONF_BASE_NAMESPACE, NETCONF_COLLECTION_NAMESPACE); /* OK */
|
||||
if ((ns = yang_find_mynamespace(y)) != NULL)
|
||||
if ((ns = yang_find_mynamespace(y)) != NULL) {
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<xlen; i++){
|
||||
x = xvec[i];
|
||||
/* Add namespace */
|
||||
|
|
@ -1658,7 +1837,7 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
*/
|
||||
if ((xa = xml_new("remaining", x, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
cbuf_reset(cb); /* reuse */
|
||||
cbuf_reset(cb);
|
||||
cprintf(cb, "%u", remaining);
|
||||
if (xml_value_set(xa, cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
|
|
@ -1671,6 +1850,7 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
if (clicon_xml2cbuf(cbret, x, 0, 0, depth>0?depth+1:depth) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
cprintf(cbret, "</pageable-list></rpc-reply>");
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1682,6 +1862,8 @@ from_client_get_pageable_list(clicon_handle h,
|
|||
clixon_path_free(path_tree);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
if (cbpath)
|
||||
cbuf_free(cbpath);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (reason)
|
||||
|
|
|
|||
|
|
@ -124,3 +124,7 @@
|
|||
* draft-wwlh-netconf-list-pagination-rc-01
|
||||
*/
|
||||
#define LIST_PAGINATION
|
||||
|
||||
/*! Clixon netconf pagination
|
||||
*/
|
||||
#define CLIXON_PAGINATION
|
||||
|
|
|
|||
|
|
@ -161,5 +161,6 @@ int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, si
|
|||
#endif
|
||||
|
||||
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1);
|
||||
int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count);
|
||||
|
||||
#endif /* _CLIXON_XPATH_H */
|
||||
|
|
|
|||
|
|
@ -1543,6 +1543,11 @@ netconf_module_load(clicon_handle h)
|
|||
if (yang_spec_parse_module(h, "ietf-list-pagination", NULL, yspec)< 0)
|
||||
goto done;
|
||||
#endif
|
||||
#ifdef CLIXON_PAGINATION
|
||||
/* Load clixon netconf list pagination */
|
||||
if (yang_spec_parse_module(h, "clixon-netconf-list-pagination", NULL, yspec)< 0)
|
||||
goto done;
|
||||
#endif
|
||||
#endif
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h> /* NaN */
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -1064,3 +1065,41 @@ xpath2canonical(const char *xpath0,
|
|||
xpath_tree_free(xpt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Return a count(xpath)
|
||||
*
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath XPATH syntax
|
||||
* @param[oit] count Nr of elements of xpath
|
||||
* @note This function is made for making optimizations in certain circumstances, such as a list
|
||||
*/
|
||||
int
|
||||
xpath_count(cxobj *xcur,
|
||||
cvec *nsc,
|
||||
const char *xpath,
|
||||
uint32_t *count)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xc = NULL;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "count(%s)", xpath);
|
||||
if (xpath_vec_ctx(xcur, nsc, cbuf_get(cb), 0, &xc) < 0)
|
||||
goto done;
|
||||
if (xc && xc->xc_type == XT_NUMBER && xc->xc_number != NAN)
|
||||
*count = (uint32_t)xc->xc_number;
|
||||
else
|
||||
*count = 0;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xc)
|
||||
ctx_free(xc);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
|
||||
<CLICON_VALIDATE_STATE_XML>false</CLICON_VALIDATE_STATE_XML>
|
||||
$RESTCONFIG
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
|
@ -257,7 +257,7 @@ cat<<EOF > $fstate
|
|||
</audit-logs>
|
||||
EOF
|
||||
|
||||
# Run limitonly test with netconf, restconf+xml and restconf+json
|
||||
# Run limit-only test with netconf, restconf+xml and restconf+json
|
||||
# Args:
|
||||
# 1. limit
|
||||
# 2. remaining
|
||||
|
|
@ -267,7 +267,15 @@ function testlimit()
|
|||
limit=$1
|
||||
remaining=$2
|
||||
|
||||
new "limit=$limit NETCONF"
|
||||
# "clixon get"
|
||||
new "clixon 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\"/><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">$limit</limit></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><uint8-numbers cp:remaining=\"$remaining\" xmlns:cp=\"http://clicon.org/clixon-netconf-list-pagination\">17</uint8-numbers></favorites></member></members></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "clixon limit=$limit 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\"/><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">$limit</limit></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><uint8-numbers cp:remaining=\"$remaining\" xmlns:cp=\"http://clicon.org/clixon-netconf-list-pagination\">17</uint8-numbers></favorites></member></members></data></rpc-reply>]]>]]>$"
|
||||
|
||||
# "old: ietf"
|
||||
new "ietf limit=$limit NETCONF"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-pageable-list xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination\"><datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore><list-target xmlns:es=\"http://example.com/ns/example-social\">/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers</list-target><limit>$limit</limit></get-pageable-list></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><pageable-list xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination\"><uint8-numbers xmlns=\"http://example.com/ns/example-social\" ycoll:remaining=\"$remaining\" xmlns:ycoll=\"urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination\">17</uint8-numbers></pageable-list></rpc-reply>]]>]]>$"
|
||||
|
||||
new "limit=$limit Parameter RESTCONF xml"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ YANGSPECS += clixon-lib@2021-03-08.yang # 5.1
|
|||
YANGSPECS += clixon-rfc5277@2008-07-01.yang
|
||||
YANGSPECS += clixon-xml-changelog@2019-03-21.yang
|
||||
YANGSPECS += clixon-restconf@2021-05-20.yang # 5.2
|
||||
YANGSPECS += clixon-netconf-list-pagination@2021-08-27.yang
|
||||
|
||||
APPNAME = clixon # subdir ehere these files are installed
|
||||
|
||||
|
|
|
|||
168
yang/clixon/clixon-netconf-list-pagination@2021-08-27.yang
Normal file
168
yang/clixon/clixon-netconf-list-pagination@2021-08-27.yang
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
module clixon-netconf-list-pagination {
|
||||
yang-version 1.1;
|
||||
namespace "http://clicon.org/clixon-netconf-list-pagination";
|
||||
prefix cp;
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
reference
|
||||
"RFC 6991: Common YANG Data Types";
|
||||
}
|
||||
import ietf-yang-metadata {
|
||||
prefix "md";
|
||||
reference
|
||||
"RFC 7952: Defining and Using Metadata with YANG";
|
||||
}
|
||||
import ietf-netconf {
|
||||
prefix nc;
|
||||
reference
|
||||
"RFC 6241: Network Configuration Protocol (NETCONF)";
|
||||
}
|
||||
|
||||
organization
|
||||
"IETF NETCONF (Network Configuration) Working Group";
|
||||
contact
|
||||
"WG Web: <http://tools.ietf.org/wg/netconf/>
|
||||
WG List: <mailto:netconf@ietf.org>
|
||||
|
||||
Editor:
|
||||
|
||||
Editor:
|
||||
|
||||
Editor: ";
|
||||
description
|
||||
"This module define a new operation -- <get-collection>
|
||||
to support YANG based pagination.
|
||||
|
||||
The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
|
||||
NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
|
||||
'MAY', and 'OPTIONAL' in this document are to be interpreted as
|
||||
described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
|
||||
they appear in all capitals, as shown here.
|
||||
|
||||
Copyright (c) 2019 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, is permitted pursuant to, and subject to
|
||||
the license terms contained in, the Simplified BSD License set
|
||||
forth in Section 4.c of the IETF Trust's Legal Provisions
|
||||
Relating to IETF Documents
|
||||
(https://trustee.ietf.org/license-info).
|
||||
|
||||
This version of this YANG module is part of RFC 8526; see
|
||||
the RFC itself for full legal notices.
|
||||
|
||||
Clixon:
|
||||
- changed get-pagable -> get-pageable
|
||||
- renamed count -> limit
|
||||
- renamed skip -> offset
|
||||
- added import ietf-yang-metadata
|
||||
- added md:annotation remaining
|
||||
";
|
||||
revision 2021-08-27 {
|
||||
description
|
||||
"Dervied from ietf-netconf-list-pagination@2020-10-30.";
|
||||
reference
|
||||
"RFC XXXX: YANG Based Pagination.";
|
||||
}
|
||||
|
||||
// Annotations
|
||||
md:annotation remaining {
|
||||
type uint32;
|
||||
description
|
||||
"This annotation contains the number of elements removed
|
||||
from a result set after a 'limit' or 'sublist-limit'
|
||||
operation. If no elements were removed, this annotation
|
||||
MUST NOT appear. The minimum value (0), which never
|
||||
occurs in normal operation, is reserved to represent
|
||||
'unknown'. The maximum value (2^32-1) is reserved to
|
||||
represent any value greater than or equal to 2^32-1
|
||||
elements.";
|
||||
}
|
||||
grouping pageing-parameters {
|
||||
leaf limit {
|
||||
type union {
|
||||
type uint32;
|
||||
type string {
|
||||
pattern 'unbounded';
|
||||
}
|
||||
}
|
||||
default "unbounded";
|
||||
description
|
||||
"The maximum number of list entries to return. The
|
||||
value of the 'count' parameter is either an integer
|
||||
greater than or equal to 1, or the string 'unbounded'.
|
||||
The string 'unbounded' is the default value.";
|
||||
}
|
||||
leaf offset {
|
||||
type union {
|
||||
type uint32;
|
||||
type string {
|
||||
pattern 'none';
|
||||
}
|
||||
}
|
||||
default "none";
|
||||
description
|
||||
"The first list item to return.
|
||||
the 'skip' parameter is either an integer greater than
|
||||
or equal to 1, or the string 'unbounded'. The string
|
||||
'unbounded' is the default value.";
|
||||
}
|
||||
leaf direction {
|
||||
type enumeration {
|
||||
enum forward;
|
||||
enum reverse;
|
||||
}
|
||||
default "forward";
|
||||
description
|
||||
"Direction relative to the 'sort' order through list
|
||||
or leaf-list. It can be forward direction or reverse
|
||||
direction.";
|
||||
}
|
||||
leaf sort {
|
||||
type union {
|
||||
type string {
|
||||
length "1..max" {
|
||||
description
|
||||
"The name of a descendent node to sort on. For
|
||||
'Config false' lists and leaf-lists, the node SHOULD
|
||||
have the 'TBD' extension indicating that it has been
|
||||
indexed, enabling efficient sorts.";
|
||||
}
|
||||
}
|
||||
type enumeration {
|
||||
enum default {
|
||||
description
|
||||
"Indicates that the 'default' order is assumed. For
|
||||
'ordered-by user' lists and leaf-lists, the default order
|
||||
is the user-configured order. For 'ordered-by system'
|
||||
lists and leaf-lists, the default order is specified by the
|
||||
system.";
|
||||
}
|
||||
}
|
||||
}
|
||||
default "default";
|
||||
description
|
||||
"Indicates how the entries in a list are to be sorted.";
|
||||
}
|
||||
leaf where {
|
||||
type yang:xpath1.0;
|
||||
description
|
||||
"The boolean filter to select data instances to return from
|
||||
the list or leaf-list target. The Xpath expression MAY be
|
||||
constrained either server-wide, by datastore, by 'config'
|
||||
status, or per list or leaf-list. Details regarding how
|
||||
constraints are communicated are TBD. This parameter
|
||||
is optional; no filtering is applied when it is not
|
||||
specified.";
|
||||
}
|
||||
}
|
||||
augment /nc:get-config/nc:input {
|
||||
uses pageing-parameters;
|
||||
}
|
||||
// extending the get operation
|
||||
augment /nc:get/nc:input {
|
||||
uses pageing-parameters;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue