Removed list-pagination-partial-state extension

Refactored pagination code
Reverted clixon-lib.yang to 2024-04-01 revision
This commit is contained in:
Olof hagsand 2024-10-16 10:32:37 +02:00
parent b9ff2d083a
commit f87ff859a6
19 changed files with 286 additions and 799 deletions

View file

@ -1,7 +1,7 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
@ -25,7 +25,7 @@
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
@ -93,7 +93,7 @@ restconf_client_get_capabilities(clixon_handle h,
int retval = -1;
cxobj *xrstate = NULL; /* xml restconf-state node */
cbuf *cb = NULL;
if ((xrstate = xpath_first(*xret, NULL, "restconf-state")) == NULL){
clixon_err(OE_YANG, ENOENT, "restconf-state not found in config node");
goto done;
@ -206,7 +206,7 @@ get_statedata(clixon_handle h,
int ret;
cbuf *cb = NULL;
cxobj *xerr = NULL;
clixon_debug(CLIXON_DBG_BACKEND, "");
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clixon_err(OE_YANG, ENOENT, "No yang spec");
@ -271,7 +271,7 @@ get_statedata(clixon_handle h,
xerr = NULL;
goto fail;
}
/* Some state, client state, is avaliable in backend only, not in lib
/* Some state, client state, is avaliable in backend only, not in lib
* Needs merge since same subtree as previous lib state
*/
if ((ret = backend_monitoring_state_get(h, yspec, xpath, nsc, &x1, &xerr)) < 0)
@ -325,16 +325,16 @@ get_statedata(clixon_handle h,
goto done;
}
/*! Help function to filter out anything that is outside of xpath
/*! Help function to filter out anything that is outside of xpath
*
* Code complex to filter out anything that is outside of xpath
* Code complex to filter out anything that is outside of xpath
* Actually this is a safety catch, should really be done in plugins
* and modules_state functions.
* But it is problematic, because defaults, at least of config data, is in place
* and we need to re-add it.
* Note original xpath
*
* @param[in] h Clixon handle
* @param[in] h Clixon handle
* @param[in] yspec Yang spec
* @param[in] xret Result XML tree
* @param[in] xvec xpath lookup result on xret
@ -382,7 +382,7 @@ filter_xpath_again(clixon_handle h,
/*! Help function for NACM access and return message
*
* @param[in] h Clixon handle
* @param[in] h Clixon handle
* @param[in] xret Result XML tree
* @param[in] xvec xpath lookup result on xret
* @param[in] xlen length of xvec
@ -391,7 +391,7 @@ filter_xpath_again(clixon_handle h,
* @param[in] username User name for NACM access
* @param[in] depth Nr of levels to print, -1 is all, 0 is none
* @param[in] wdef With-defaults parameter
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 0 OK
* @retval -1 Error
*/
@ -414,7 +414,7 @@ get_nacm_and_reply(clixon_handle h,
xnacm = clicon_nacm_cache(h);
if (xnacm != NULL){ /* Do NACM validation */
/* NACM datanode/module read validation */
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
goto done;
}
cprintf(cbret, "<rpc-reply xmlns=\"%s\">", NETCONF_BASE_NAMESPACE); /* OK */
@ -456,7 +456,7 @@ element2value(clixon_handle h,
{
char *valstr;
cxobj *x;
*value = 0;
if ((x = xml_find_type(xe, NULL, name, CX_ELMNT)) != NULL &&
(valstr = xml_body(x)) != NULL){
@ -467,11 +467,11 @@ element2value(clixon_handle h,
/*! Extract offset and limit from get/list-pagination
*
* @param[in] h Clixon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] h Clixon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[out] offset Number of entries in the working result-set that should be skipped
* @param[out] limit Limits the number of entries returned from the working result-set
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 1 OK
* @retval 0 Invalid, netconf bad-element error cbret set
* @retval -1 Error
@ -484,7 +484,7 @@ list_pagination_hdr(clixon_handle h,
cbuf *cbret)
{
int retval = -1;
/* offset */
if ((retval = element2value(h, xe, "offset", "none", cbret, offset)) < 0)
goto done;
@ -497,58 +497,72 @@ list_pagination_hdr(clixon_handle h,
/*! Special handling of state data for partial reading
*
* Only if extension list-pagination-partial-state is enabled on the list
* @param[in] h Clixon handle
* @param[in] ce Client entry, for locking
* @param[in] yspec (Top-level) yang spec
* @param[in] xpath XPath point to object to get
* @param[in] offset Start of pagination interval
* @param[in] limit Number of elements (limit)
* @param[out] xret Returned xml state tree
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 1 OK
* @retval 0 Fail, cbret contains error message
* @retval -1 Error
* @param[in] ce Client entry, for locking
* @param[in] yspec (Top-level) yang spec
* @param[in] xpath XPath point to object to get
* @param[in] where
* @param[in] sort_by
* @param[in] direction NULL means forward
* @param[in] offset Start of pagination interval
* @param[in] limit Number of elements (limit)
* @param[out] xret Returned xml state tree
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 1 OK
* @retval 0 Fail, cbret contains error message
* @retval -1 Error
*/
static int
get_pagination_partial(clixon_handle h,
struct client_entry *ce,
yang_stmt *yspec,
char *xpath,
uint32_t offset,
uint32_t limit,
cxobj *xret,
cbuf *cbret)
get_pagination_state_partial(clixon_handle h,
struct client_entry *ce,
yang_stmt *yspec,
char *xpath,
char *where,
char *sort_by,
char *direction,
uint32_t offset,
uint32_t limit,
cxobj *xret,
cbuf *cbret)
{
int retval = -1;
int locked;
cbuf *cberr = NULL;
uint32_t iddb; /* DBs lock, if any */
cxobj *xerr = NULL;
int ret;
int retval = -1;
int locked;
cbuf *cberr = NULL;
uint32_t iddb; /* DBs lock, if any */
cxobj *xerr = NULL;
pagination_data_t pd = {0,};
dispatcher_entry_t *htable = NULL;
int ret;
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
if ((iddb = xmldb_islocked(h, "running")) != 0 &&
iddb == ce->ce_id)
locked = 1;
else
locked = 0;
if ((ret = clixon_pagination_cb_call(h, xpath, locked,
offset, limit,
xret)) < 0)
goto done;
if (ret == 0){
if ((cberr = cbuf_new()) == NULL){
clixon_err(OE_UNIX, errno, "cbuf_new");
goto done;
pd.pd_where = where;
pd.pd_sort_by = sort_by;
pd.pd_direction = direction;
pd.pd_offset = offset;
pd.pd_limit = limit;
pd.pd_locked = locked;
pd.pd_xstate = xret;
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
if (htable){
if (dispatcher_call_handlers(htable, h, xpath, &pd) < 0){
if ((cberr = cbuf_new()) == NULL){
clixon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* error reason should be in clixon_err_reason */
cprintf(cberr, "Internal error, pagination state callback invalid return : %s",
clixon_err_reason());
if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0)
goto done;
if (clixon_xml2cbuf(cbret, xerr, 0, 0, NULL, -1, 0) < 0)
goto done;
goto fail;
}
/* error reason should be in clixon_err_reason */
cprintf(cberr, "Internal error, pagination state callback invalid return : %s",
clixon_err_reason());
if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0)
goto done;
if (clixon_xml2cbuf(cbret, xerr, 0, 0, NULL, -1, 0) < 0)
goto done;
goto fail;
}
/* System makes the binding */
@ -580,9 +594,9 @@ get_pagination_partial(clixon_handle h,
*
* It is specialized enough to have its own function. Specifically, extra attributes as well
* as the list-paginaiton API
* @param[in] h Clixon handle
* @param[in] h Clixon handle
* @param[in] ce Client entry, for locking
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] content Get config/state/both
* @param[in] db Database name
* @param[in] depth Depth attribute
@ -591,10 +605,10 @@ get_pagination_partial(clixon_handle h,
* @param[in] nsc Namespace context of xpath
* @param[in] username
* @param[in] wdef With-defaults parameter, see RFC 6243
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 0 OK
* @retval -1 Error
* @note pagination uses appending xpath with predicate, eg [position()<limit], this may not work
* @note pagination uses appending xpath with predicate, eg [position()<limit], this may not work
* if there is an existing predicate
* XXX Reuse code with get_common
* From draft-ietf-netconf-list-pagination-04.txt 3.1:
@ -637,16 +651,18 @@ get_list_pagination(clixon_handle h,
char *sort_by = NULL;
char *direction = NULL;
char *where = NULL;
int extflag = 0;
int i;
int j;
int ret;
dispatcher_entry_t *htable = NULL;
// int extflag = 0;
#ifdef LIST_PAGINATION_REMAINING
cxobj *xcache;
uint32_t total;
uint32_t remaining = 0;
#endif
clixon_debug(CLIXON_DBG_BACKEND | CLIXON_DBG_DETAIL, "");
if (cbret == NULL){
clixon_err(OE_PLUGIN, EINVAL, "cbret is NULL");
goto done;
@ -685,13 +701,14 @@ get_list_pagination(clixon_handle h,
goto done;
goto ok;
}
if (yang_extension_value(ylist, "list-pagination-partial-state", CLIXON_LIB_NS, &extflag, NULL) < 0)
goto done;
if (extflag){ /* pagination_cb / partial state API */
partial_pagination_cb = 1;
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
if (htable) {
if ((ret = dispatcher_match_exact(htable, xpath)) < 0)
goto done;
if (ret > 0)
partial_pagination_cb = 1;
}
}
/* first processes the "where" parameter (see Section 3.1.1) */
if ((x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL &&
(where = xml_body(x)) != NULL){
@ -749,35 +766,32 @@ get_list_pagination(clixon_handle h,
goto done;
break;
}/* switch content */
switch (content){ /* Read state */
case CONTENT_CONFIG: /* config data only */
break;
case CONTENT_ALL: /* both config and state */
case CONTENT_NONCONFIG: /* state data only */
if (partial_pagination_cb) /* Partial reads, special handling */
break;
if ((ret = get_statedata(h, xpath?xpath:"/", nsc, &xret)) < 0)
goto done;
if (ret == 0){ /* Error from callback (error in xret) */
if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0)
goto done;
goto ok;
}
/* Add defaults to state data. This consumes some cycles */
/* Ensure all state-data is report-all */
if (xml_global_defaults(h, xret, nsc, xpath, yspec, 1) < 0)
goto done;
/* Apply default values */
if (xml_default_recurse(xret, 1, 0) < 0)
goto done;
}
if (partial_pagination_cb) {
if ((ret = get_pagination_partial(h, ce, yspec, xpath, offset, limit, xret, cbret)) < 0)
if ((ret = get_pagination_state_partial(h, ce, yspec, xpath,
where, sort_by, direction,
offset, limit,
xret, cbret)) < 0)
goto done;
if (ret == 0)
goto ok;
}
else {
if (content != CONTENT_CONFIG){
if ((ret = get_statedata(h, xpath?xpath:"/", nsc, &xret)) < 0)
goto done;
if (ret == 0){ /* Error from callback (error in xret) */
if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0)
goto done;
goto ok;
}
/* Add defaults to state data. This consumes some cycles */
/* Ensure all state-data is report-all */
if (xml_global_defaults(h, xret, nsc, xpath, yspec, 1) < 0)
goto done;
/* Apply default values */
if (xml_default_recurse(xret, 1, 0) < 0)
goto done;
}
/* first processes the "where" parameter (see Section 3.1.1) */
if (where){
if (xpath_vec(xret, nsc, "%s[%s]", &xvec, &xlen, xpath?xpath:"/", where) < 0)
@ -864,10 +878,10 @@ get_list_pagination(clixon_handle h,
if (filter_xpath_again(h, yspec, xret, xvec, xlen, xpath, nsc) < 0)
goto done;
#ifdef LIST_PAGINATION_REMAINING
/* Add remaining attribute Sec 3.1.5:
Any list or leaf-list that is limited includes, on the first element in the result set,
/* Add remaining attribute Sec 3.1.5:
Any list or leaf-list that is limited includes, on the first element in the result set,
a metadata value [RFC7952] called "remaining"*/
if (limit && x1){
if (limit && x1){
cxobj *xa;
cbuf *cba = NULL;
@ -901,16 +915,16 @@ get_list_pagination(clixon_handle h,
/*! Common get/get-config code for retrieving configuration and state information.
*
* @param[in] h Clixon handle
* @param[in] h Clixon handle
* @param[in] ce Client entry, for locking
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] xe Request: <rpc><xn></rpc>
* @param[in] content Get config/state/both
* @param[in] db Database name
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @retval 0 OK
* @retval -1 Error
* @see from_client_get
* @see from_client_get_config
* @see from_client_get_config
*/
static int
get_common(clixon_handle h,
@ -986,7 +1000,7 @@ get_common(clixon_handle h,
goto ok;
}
}
if ((wdefstr = xml_find_body(xe, "with-defaults")) != NULL)
if ((wdefstr = xml_find_body(xe, "with-defaults")) != NULL)
wdef = withdefaults_str2int(wdefstr);
/* How to check if list-pagination?
* Problem is clixon expands messages on entry and pagination default values + non-presence cont
@ -1073,7 +1087,7 @@ get_common(clixon_handle h,
break;
}/* switch content */
/* If not only config,
* get state data from plugins as defined by plugin_statedata(), if any
* get state data from plugins as defined by plugin_statedata(), if any
*/
/* Read state */
switch (content){
@ -1099,11 +1113,11 @@ get_common(clixon_handle h,
}
if (content != CONTENT_CONFIG &&
clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){
/* Check XML by validating it. return internal error with error cause
/* Check XML by validating it. return internal error with error cause
* Primarily intended for user-supplied state-data.
* The whole config tree must be present in case the state data references config data
*/
if ((ret = xml_yang_validate_all_top(h, xret, &xerr)) < 0)
if ((ret = xml_yang_validate_all_top(h, xret, &xerr)) < 0)
goto done;
if (ret > 0 &&
(ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
@ -1197,8 +1211,8 @@ from_client_get_config(clixon_handle h,
return retval;
}
/*! Retrieve running configuration and device state information.
*
/*! Retrieve running configuration and device state
*
* @param[in] h Clixon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
@ -1207,7 +1221,7 @@ from_client_get_config(clixon_handle h,
* @retval 0 OK
* @retval -1 Error
*
* @see from_client_get_config
* @see from_client_get_config
*/
int
from_client_get(clixon_handle h,

View file

@ -485,42 +485,6 @@ clixon_plugin_lockdb_all(clixon_handle h,
return retval;
}
/*! Traverse state data callbacks for partial pagination state callbacks
*
* Only if list-pagination-partial-state extension is set
* @param[in] h Clixon handle
* @param[in] xpath Registered XPath using canonical prefixes
* @param[in] locked Running datastore is locked by this caller
* @param[in] offset Start of pagination interval
* @param[in] limit Number of elements (limit)
* @param[out] xstate Returned xml state tree
* @retval 1 OK
* @retval -1 Error
*/
int
clixon_pagination_cb_call(clixon_handle h,
char *xpath,
int locked,
uint32_t offset,
uint32_t limit,
cxobj *xstate)
{
int retval = -1;
pagination_data_t pd;
dispatcher_entry_t *htable = NULL;
pd.pd_offset = offset;
pd.pd_limit = limit;
pd.pd_locked = locked;
pd.pd_xstate = xstate;
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
if (htable && dispatcher_call_handlers(htable, h, xpath, &pd) < 0)
goto done;
retval = 1; // XXX 0?
done:
return retval;
}
/*! Register a state data callback
*
* @param[in] h Clixon handle

View file

@ -85,6 +85,9 @@ typedef struct {
* @see pagination_offset() and other accessor functions
*/
typedef struct {
char *pd_where;
char *pd_sort_by;
char *pd_direction;
uint32_t pd_offset; /* Start of pagination interval */
uint32_t pd_limit; /* Number of elements (limit) */
int pd_locked; /* Running datastore is locked by this caller */
@ -104,9 +107,6 @@ int clixon_plugin_statedata_all(clixon_handle h, yang_stmt *yspec, cvec *nsc, ch
int clixon_plugin_lockdb_all(clixon_handle h, char *db, int lock, int id);
int clixon_pagination_cb_register(clixon_handle h, handler_function fn, char *path, void *arg);
int clixon_pagination_cb_call(clixon_handle h, char *xpath, int locked,
uint32_t offset, uint32_t limit,
cxobj *xstate);
int clixon_pagination_free(clixon_handle h);
transaction_data_t * transaction_new(void);