diff --git a/CHANGELOG.md b/CHANGELOG.md
index 375bdb39..e6772a75 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,18 @@ Expected: September, 2021
### New features
+* List pageing for Netconf and Restconf
+ * Experimental, work-in-progress
+ * Enable with LIST_PAGINATION compile-time option
+ * According to:
+ * draft-wwlh-netconf-list-pagination-00.txt
+ * draft-wwlh-netconf-list-pagination-rc-01
+ * Added yangs:
+ * ietf-restconf-list-pagination@2015-01-30.yang
+ * clixon-netconf-list-pagination@2021-08-27.yang
+ * ietf-origin@2018-02-14.yang
+ * ietf-yang-metadata@2016-08-05.yang
+ * ietf-netconf-with-defaults@2011-06-01.yang
* YANG Leafref feature update
* Closer adherence to RFC 7950. Some of this is changed behavior, some is new feature.
* Essentially instead of looking at the referring leaf, context is referred(target) node
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index e28647cd..44b335a1 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -1396,12 +1396,6 @@ backend_rpc_init(clicon_handle h)
if (rpc_callback_register(h, from_client_validate, NULL,
NETCONF_BASE_NAMESPACE, "validate") < 0)
goto done;
-#ifdef LIST_PAGINATION
- /* draft-ietf-netconf-restconf-collection-00 */
- if (rpc_callback_register(h, from_client_get_pageable_list, NULL,
- NETCONF_COLLECTION_NAMESPACE, "get-pageable-list") < 0)
- goto done;
-#endif
/* In backend_client.? RPC from RFC 5277 */
if (rpc_callback_register(h, from_client_create_subscription, NULL,
EVENT_RFC5277_NAMESPACE, "create-subscription") < 0)
diff --git a/apps/backend/backend_get.c b/apps/backend/backend_get.c
index 57d36e2a..f1c132f3 100644
--- a/apps/backend/backend_get.c
+++ b/apps/backend/backend_get.c
@@ -272,7 +272,7 @@ client_statedata(clicon_handle h,
goto done;
}
-#if defined(LIST_PAGINATION) || defined(CLIXON_PAGINATION)
+#ifdef LIST_PAGINATION
/*! Help function for parsing restconf query parameter and setting netconf attribute
*
* If not "unbounded", parse and set a numeric value
@@ -364,7 +364,7 @@ get_common(clicon_handle h,
int ret;
char *reason = NULL;
cbuf *cbmsg = NULL; /* For error msg */
-#ifdef CLIXON_PAGINATION
+#ifdef LIST_PAGINATION
uint32_t limit = 0;
uint32_t offset = 0;
uint32_t total = 0;
@@ -379,7 +379,7 @@ get_common(clicon_handle h,
char *valstr;
yang_stmt *ylist;
cxobj *xcache = NULL;
-#endif /* CLIXON_PAGINATION */
+#endif /* LIST_PAGINATION */
clicon_debug(1, "%s", __FUNCTION__);
username = clicon_username_get(h);
@@ -418,7 +418,7 @@ get_common(clicon_handle h,
goto ok;
}
}
-#ifdef CLIXON_PAGINATION
+#ifdef LIST_PAGINATION
/* Check if list pagination */
if ((x = xml_find_type(xe, NULL, "list-pagination", CX_ELMNT)) != NULL &&
(valstr = xml_body(x)) != NULL &&
@@ -525,7 +525,7 @@ get_common(clicon_handle h,
}
} /* list_pagination */
-#endif /* CLIXON_PAGINATION */
+#endif /* LIST_PAGINATION */
/* Read config
* XXX This seems unnecessary complex
*/
@@ -661,7 +661,7 @@ get_common(clicon_handle h,
if (xml_default_recurse(xret, 0) < 0)
goto done;
-#ifdef CLIXON_PAGINATION
+#ifdef LIST_PAGINATION
/* Add remaining attribute */
if (list_pagination && remaining && xlen){
cxobj *xa;
@@ -685,7 +685,7 @@ get_common(clicon_handle h,
if (cba)
cbuf_free(cba);
}
-#endif /* CLIXON_PAGINATION */
+#endif /* LIST_PAGINATION */
/* Pre-NACM access step */
xnacm = clicon_nacm_cache(h);
if (xnacm != NULL){ /* Do NACM validation */
@@ -710,7 +710,7 @@ get_common(clicon_handle h,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
-#ifdef CLIXON_PAGINATION
+#ifdef LIST_PAGINATION
if (cbpath)
cbuf_free(cbpath);
#endif
@@ -790,319 +790,3 @@ from_client_get(clicon_handle h,
content = netconf_content_str2int(attr);
return get_common(h, xe, content, "running", cbret);
}
-
-#ifdef LIST_PAGINATION
-
-/*! Retrieve collection configuration and device state information
- *
- * @param[in] h Clicon handle
- * @param[in] xe Request:
- * @param[out] cbret Return xml tree, eg ..., stuctured path tree */
- if ((ret = clixon_instance_id_parse(yspec, &path_tree, &xerr, "%s", xpath)) < 0)
- goto done;
- if (ret == 0){
- if (xerr && clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
- goto done;
- goto ok;
- }
- /* get last element of path, eg /a/b/c, get c */
- if ((cp = PREVQ(clixon_path *, path_tree)) == NULL){
- if (netconf_bad_element(cbret, "application", "list-target", "path invalid") < 0)
- goto done;
- goto ok;
- }
- /* get yang of last element */
- if ((y = cp->cp_yang) == NULL){
- if (netconf_bad_element(cbret, "application", "list-target", "No yang associated with path") < 0)
- goto done;
- goto ok;
- }
- if (yang_keyword_get(y) != Y_LIST && yang_keyword_get(y) != Y_LEAF_LIST){
- if (netconf_bad_element(cbret, "application", "list-target", "path invalid") < 0)
- goto done;
- goto ok;
- }
- /* 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);
-
- /* Split into CT or CF */
- if (yang_config_ancestor(y) == 1){ /* CT */
- if (content == CONTENT_CONFIG || content == CONTENT_ALL){
- if ((ret = xmldb_get0(h, datastore, YB_MODULE, nsc, xpath, 1, &xret, NULL, &xerr)) < 0) {
- if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
- goto done;
- goto ok;
- }
- if (ret == 0){
- if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
- goto done;
- goto ok;
- }
- /* First get number of hits (ALL entries: note not optimized) */
- if (xpath_count(xret, nsc, xpath, &total) < 0)
- goto done;
- if (total < (offset + limit))
- remaining = 0;
- else
- remaining = total - (offset + limit);
- }
- /* There may be CF data in a CT collection */
- if (content == CONTENT_ALL){
- if ((ret = client_statedata(h, cbuf_get(cbpath), nsc, &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(cbpath), nsc, &xret)) < 0)
- goto done;
- if (ret == 0){ /* Error from callback (error in xret) */
- if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
- goto done;
- goto ok;
- }
- }
- }
- if (0 && clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){
- /* XXX: subset in collection may not have mandatory variables */
- /* 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)
- goto done;
- if (ret > 0 &&
- (ret = xml_yang_validate_add(h, xret, &xerr)) < 0)
- goto done;
- if (ret == 0){
- if (clicon_debug_get())
- clicon_log_xml(LOG_DEBUG, xret, "VALIDATE_STATE");
- if (clixon_netconf_internal_error(xerr,
- ". Internal error, state callback returned invalid XML",
- NULL) < 0)
- goto done;
- if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
- goto done;
- goto ok;
- }
- } /* CLICON_VALIDATE_STATE_XML */
-
- if (content == CONTENT_NONCONFIG){ /* state only, all config should be removed now */
- /* Keep state data only, remove everything that is not config. Note that state data
- * may be a sub-part in a config tree, we need to traverse to find all
- */
- if (xml_non_config_data(xret, NULL) < 0)
- goto done;
- if (xml_tree_prune_flagged_sub(xret, XML_FLAG_MARK, 1, NULL) < 0)
- goto done;
- if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
- goto done;
- }
- /* 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.
- */
- if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, cbuf_get(cbpath)) < 0)
- goto done;
-
- /* Pre-NACM access step */
- 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)
- goto done;
- }
- cprintf(cbret, "",
- NETCONF_BASE_NAMESPACE, NETCONF_COLLECTION_NAMESPACE); /* OK */
- if ((ns = yang_find_mynamespace(y)) != NULL) {
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- for (i=0; i0?depth+1:depth) < 0)
- goto done;
- }
- }
- cprintf(cbret, "");
- ok:
- retval = 0;
- done:
- clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
- if (datastore)
- free(datastore);
- if (path_tree)
- clixon_path_free(path_tree);
- if (xtop)
- xml_free(xtop);
- if (cbpath)
- cbuf_free(cbpath);
- if (cb)
- cbuf_free(cb);
- if (reason)
- free(reason);
- if (xerr)
- xml_free(xerr);
- if (xvec)
- free(xvec);
- if (nsc)
- xml_nsctx_free(nsc);
- if (xret)
- xml_free(xret);
- return retval;
-}
-#endif /* LIST_PAGINATION */
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 3eaa1954..471aafb1 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -1348,7 +1348,7 @@ cli_pagination(clicon_handle h, cvec *vars, cvec *argv)
}
if ((nsc = xml_nsctx_init(prefix, namespace)) == NULL)
goto done;
- if (clicon_rpc_get_pageable_list(h, "running", xpath, NULL, nsc, CONTENT_CONFIG, NULL,
+ if (clicon_rpc_get_pageable_list(h, "running", xpath, NULL, nsc, CONTENT_CONFIG, -1,
"2", "0",
NULL, NULL, NULL,
&xret) < 0){
diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h
index c7d6a368..4ba23cf5 100644
--- a/apps/restconf/clixon_restconf.h
+++ b/apps/restconf/clixon_restconf.h
@@ -53,8 +53,8 @@ enum restconf_media{
YANG_DATA_XML, /* "application/yang-data+xml" */
YANG_PATCH_JSON, /* "application/yang-patch+json" */
YANG_PATCH_XML, /* "application/yang-patch+xml" */
- YANG_COLLECTION_XML, /* draft-ietf-netconf-restconf-collection-00.txt */
- YANG_COLLECTION_JSON /* draft-ietf-netconf-restconf-collection-00.txt */
+ YANG_COLLECTION_XML, /* draft-wwlh-netconf-list-pagination-rc-01.txt */
+ YANG_COLLECTION_JSON /* draft-wwlh-netconf-list-pagination-rc-01.txt */
};
typedef enum restconf_media restconf_media;
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index a3e7be81..b29f50f4 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -207,7 +207,7 @@ static const map_str2int http_media_map[] = {
{"application/yang-data+json", YANG_DATA_JSON},
{"application/yang-patch+xml", YANG_PATCH_XML},
{"application/yang-patch+json", YANG_PATCH_JSON},
- {"application/yang-collection+xml", YANG_COLLECTION_XML},
+ {"application/yang-collection+xml", YANG_COLLECTION_XML}, /* XXX -data+xml-list?? */
{"application/yang-collection+json", YANG_COLLECTION_JSON},
{NULL, -1}
};
diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c
index 7541e110..392c3f01 100644
--- a/apps/restconf/restconf_methods_get.c
+++ b/apps/restconf/restconf_methods_get.c
@@ -149,7 +149,6 @@ api_data_get2(clicon_handle h,
goto ok;
}
}
-
/* Check for content attribute */
if ((attr = cvec_find_str(qvec, "content")) != NULL){
clicon_debug(1, "%s content=%s", __FUNCTION__, attr);
@@ -181,19 +180,9 @@ api_data_get2(clicon_handle h,
}
}
}
-
clicon_debug(1, "%s path:%s", __FUNCTION__, xpath);
- switch (content){
- case CONTENT_CONFIG:
- case CONTENT_NONCONFIG:
- case CONTENT_ALL:
- ret = clicon_rpc_get(h, xpath, nsc, content, depth, &xret);
- break;
- default:
- clicon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
- goto done;
- break;
- }
+ ret = clicon_rpc_get(h, xpath, nsc, content, depth, &xret);
+
if (ret < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
@@ -356,9 +345,11 @@ api_data_collection(clicon_handle h,
netconf_content content = CONTENT_ALL;
cxobj *xtop = NULL;
cxobj *xbot = NULL;
+ cxobj *xp;
+ cxobj *xpr;
yang_stmt *y = NULL;
cbuf *cbrpc = NULL;
- char *depth;
+ int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
char *limit;
char *offset;
char *direction;
@@ -434,13 +425,30 @@ api_data_collection(clicon_handle h,
goto done;
}
/* Clixon extensions and collection attributes */
- depth = cvec_find_str(qvec, "depth");
+ /* Check for depth attribute */
+ if ((attr = cvec_find_str(qvec, "depth")) != NULL){
+ clicon_debug(1, "%s depth=%s", __FUNCTION__, attr);
+ if (strcmp(attr, "unbounded") != 0){
+ char *reason = NULL;
+ if ((ret = parse_int32(attr, &depth, &reason)) < 0){
+ clicon_err(OE_XML, errno, "parse_int32");
+ goto done;
+ }
+ if (ret==0){
+ if (netconf_bad_attribute_xml(&xerr, "application",
+ "depth", "Unrecognized value of depth attribute") < 0)
+ goto done;
+ if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
+ goto done;
+ goto ok;
+ }
+ }
+ }
limit = cvec_find_str(qvec, "limit");
offset = cvec_find_str(qvec, "offset");
direction = cvec_find_str(qvec, "direction");
sort = cvec_find_str(qvec, "sort");
where = cvec_find_str(qvec, "where");
-
if (clicon_rpc_get_pageable_list(h, "running", xpath, y, nsc, content,
depth, limit, offset, direction, sort, where,
&xret) < 0){
@@ -454,7 +462,6 @@ api_data_collection(clicon_handle h,
goto done;
goto ok;
}
-
/* We get return via netconf which is complete tree from root
* We need to cut that tree to only the object.
*/
@@ -468,16 +475,34 @@ api_data_collection(clicon_handle h,
goto done;
goto ok;
}
+ if ((xpr = xml_new("yang-collection", NULL, CX_ELMNT)) == NULL)
+ goto done;
+ if (xmlns_set(xpr, NULL, RESTCONF_PAGINATON_NAMESPACE) < 0)
+ goto done;
+ if ((xp = xpath_first(xret, nsc, "%s", xpath)) != NULL){
+ char *ns=NULL;
+ if (xml2ns(xp, NULL, &ns) < 0)
+ goto done;
+ if (ns != NULL){
+ if (xmlns_set(xp, NULL, ns) < 0)
+ goto done;
+ }
+ if (xml_rm(xp) < 0)
+ goto done;
+ if (xml_insert(xpr, xp, INS_LAST, NULL, NULL) < 0)
+ goto done;
+ }
+
/* Normal return, no error */
if ((cbx = cbuf_new()) == NULL)
goto done;
switch (media_out){
case YANG_COLLECTION_XML:
- if (clicon_xml2cbuf(cbx, xret, 0, pretty, -1) < 0) /* Dont print top object? */
+ if (clicon_xml2cbuf(cbx, xpr, 0, pretty, -1) < 0) /* Dont print top object? */
goto done;
break;
case YANG_COLLECTION_JSON:
- if (xml2json_cbuf(cbx, xret, pretty) < 0)
+ if (xml2json_cbuf(cbx, xpr, pretty) < 0)
goto done;
break;
default:
@@ -518,6 +543,8 @@ api_data_collection(clicon_handle h,
free(xpath);
if (nsc)
xml_nsctx_free(nsc);
+ if (xpr)
+ xml_free(xpr);
if (xtop)
xml_free(xtop);
if (cbx)
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index 669268b9..3f7f6aa9 100644
--- a/include/clixon_custom.h
+++ b/include/clixon_custom.h
@@ -124,7 +124,3 @@
* draft-wwlh-netconf-list-pagination-rc-01
*/
#define LIST_PAGINATION
-
-/*! Clixon netconf pagination
- */
-#define CLIXON_PAGINATION
diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h
index c27d929b..39130a3b 100644
--- a/lib/clixon/clixon_proto_client.h
+++ b/lib/clixon/clixon_proto_client.h
@@ -55,7 +55,10 @@ int clicon_rpc_delete_config(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_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 *yco, cvec *nsc, netconf_content content, char *depth, char *count, char *skip, char *direction, char *sort, char *where, cxobj **xt);
+int clicon_rpc_get_pageable_list(clicon_handle h, char *datastore, char *xpath, yang_stmt *yli,
+ cvec *nsc, netconf_content content, int32_t depth,
+ char *limit, char *offset, char *direction, char *sort, char *where,
+ cxobj **xt);
int clicon_rpc_close_session(clicon_handle h);
int clicon_rpc_kill_session(clicon_handle h, uint32_t session_id);
int clicon_rpc_validate(clicon_handle h, char *db);
diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h
index ac232095..dab22508 100644
--- a/lib/clixon/clixon_xml.h
+++ b/lib/clixon/clixon_xml.h
@@ -55,13 +55,17 @@
#define NETCONF_INPUT_CONFIG "config"
/* Collections namespace from draft-ietf-netconf-restconf-collection-00.txt
+ * XXX: Obsolete but may come back in style
*/
#define NETCONF_COLLECTION_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination"
-/* See RFC 7950 Sec 5.3.1: YANG defines an XML namespace for NETCONF
- * operations, content, and the element.
+/* Collections namespace for Clixon
*/
-#define NETCONF_COLLECTION_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination"
+#define CLIXON_PAGINATON_NAMESPACE "http://clicon.org/clixon-netconf-list-pagination"
+
+/* Collections namespace for restconf
+ */
+#define RESTCONF_PAGINATON_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination"
/* Output symbol for netconf get/get-config
* ietf-netconf.yang defines it as output:
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 17db459a..21aadd8f 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -1531,9 +1531,13 @@ netconf_module_load(clicon_handle h)
xml_bind_netconf_message_id_optional(1);
#endif
#ifdef LIST_PAGINATION
- /* Load netconf list pagination */
- if (yang_spec_parse_module(h, "ietf-netconf-list-pagination", NULL, yspec)< 0)
+ /* Load clixon netconf list pagination */
+ if (yang_spec_parse_module(h, "clixon-netconf-list-pagination", NULL, yspec)< 0)
goto done;
+ /* Load restconf list pagination */
+ if (yang_spec_parse_module(h, "ietf-restconf-list-pagination", NULL, yspec)< 0)
+ goto done;
+
#if 0
/* XXX Clixon test harness problem: when loading ietf-list-pagination, it loads
* ietf-system-capabilities which in turn loads ietf-netconf-acm. As this is a
@@ -1543,10 +1547,8 @@ 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;
+#ifdef LIST_PAGINATION
+
#endif
#endif
retval = 0;
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index f952293f..80ff1eda 100644
--- a/lib/src/clixon_proto_client.c
+++ b/lib/src/clixon_proto_client.c
@@ -822,7 +822,7 @@ clicon_rpc_get(clicon_handle h,
/* Clixon extension, depth= */
if (depth != -1)
cprintf(cb, " depth=\"%d\"", depth);
- cprintf(cb, ">");
+ cprintf(cb, ">"); /* get */
/* If xpath, add a filter */
if (xpath && strlen(xpath)) {
cprintf(cb, "<%s:filter %s:type=\"xpath\" %s:select=\"%s\"",
@@ -882,7 +882,8 @@ clicon_rpc_get(clicon_handle h,
/*! Get database configuration and state data collection
* @param[in] h Clicon handle
* @param[in] xpath To identify a list/leaf-list
- * @param[in] yli Yang-stmt of list/leaf-list of collection, if given make sanity check
+ * @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] nsc Namespace context for filter
* @param[in] content Clixon extension: all, config, noconfig. -1 means all
@@ -896,7 +897,6 @@ clicon_rpc_get(clicon_handle h,
* Either or .
* @retval 0 OK
* @retval -1 Error, fatal or xml
-
* @see clicon_rpc_get
* @see draft-ietf-netconf-restconf-collection-00
* @note the netconf return message is yang populated, as well as the return data
@@ -908,7 +908,7 @@ clicon_rpc_get_pageable_list(clicon_handle h,
yang_stmt *yli,
cvec *nsc, /* namespace context for xpath */
netconf_content content,
- char *depth,
+ int32_t depth,
char *limit,
char *offset,
char *direction,
@@ -921,15 +921,16 @@ clicon_rpc_get_pageable_list(clicon_handle h,
cbuf *cb = NULL;
cxobj *xret = NULL;
cxobj *xerr = NULL;
- cxobj *xr;
+ cxobj *xr; /* return msg */
+ cxobj *xd; /* return data */
char *username;
uint32_t session_id;
int ret;
yang_stmt *yspec;
- cxobj *x;
if (datastore == NULL){
clicon_err(OE_XML, EINVAL, "datastore not given");
+ goto done;
}
if (session_id_check(h, &session_id) < 0)
goto done;
@@ -941,31 +942,39 @@ clicon_rpc_get_pageable_list(clicon_handle h,
cprintf(cb, " xmlns:%s=\"%s\"",
NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE);
cprintf(cb, " %s", NETCONF_MESSAGE_ID_ATTR);
- cprintf(cb, ">");
- cprintf(cb, "ds:%s", datastore);
- if (xpath){
- cprintf(cb, " */
+ if (depth != -1)
+ cprintf(cb, " depth=\"%d\"", depth);
+ /* declare cp prefix in get, so sub-elements dont need to */
+ cprintf(cb, " xmlns:cp=\"%s\"", CLIXON_PAGINATON_NAMESPACE);
+ cprintf(cb, ">"); /* get */
+ /* If xpath, add a filter */
+ if (xpath && strlen(xpath)) {
+ cprintf(cb, "<%s:filter %s:type=\"xpath\" %s:select=\"%s\"",
+ NETCONF_BASE_PREFIX, NETCONF_BASE_PREFIX, NETCONF_BASE_PREFIX,
+ xpath);
if (xml_nsctx_cbuf(cb, nsc) < 0)
goto done;
- cprintf(cb, ">%s", xpath);
+ cprintf(cb, "/>");
}
+ /* Explicit use of list-pagination */
+ cprintf(cb, "true");
if (limit)
- cprintf(cb, "%s", limit);
+ cprintf(cb, "%s", limit);
if (offset)
- cprintf(cb, "%s", offset);
+ cprintf(cb, "%s", offset);
if (direction)
- cprintf(cb, "%s", direction);
+ cprintf(cb, "%s", direction);
if (sort)
- cprintf(cb, "%s", sort);
+ cprintf(cb, "%s", sort);
if (where)
- cprintf(cb, "%s", where);
- cprintf(cb, "");
+ cprintf(cb, "%s", where);
+ cprintf(cb, "");
+ cprintf(cb, "");
if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
@@ -973,34 +982,29 @@ clicon_rpc_get_pageable_list(clicon_handle h,
/* Send xml error back: first check error, then ok */
if ((xr = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
xr = xml_parent(xr); /* point to rpc-reply */
- else if ((xr = xpath_first(xret, NULL, "/rpc-reply/pageable-list")) == NULL){
- if ((xr = xml_new("pageable_list", NULL, CX_ELMNT)) == NULL)
+ else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL){
+ if ((xd = xml_new(NETCONF_OUTPUT_DATA, NULL, CX_ELMNT)) == NULL)
goto done;
}
- else if (yli != NULL) {
+ else{
yspec = clicon_dbspec_yang(h);
- /* Populate all children with y */
- x = NULL;
- while ((x = xml_child_each(xr, x, CX_ELMNT)) != NULL){
- xml_spec_set(x, yli);
- if ((ret = xml_bind_yang(x, YB_PARENT, yspec, &xerr)) < 0)
+ if ((ret = xml_bind_yang(xd, YB_MODULE, yspec, &xerr)) < 0)
+ goto done;
+ if (ret == 0){
+ if (clixon_netconf_internal_error(xerr,
+ ". Internal error, backend returned invalid XML.",
+ NULL) < 0)
+ goto done;
+ if ((xd = xpath_first(xerr, NULL, "rpc-error")) == NULL){
+ clicon_err(OE_XML, ENOENT, "Expected rpc-error tag but none found(internal)");
goto done;
- if (ret == 0){
- if (clixon_netconf_internal_error(xerr,
- ". Internal error, backend returned XML tat doid not match given YANG.",
- yli?yang_argument_get(yli):NULL) < 0)
- goto done;
- if ((xr = xpath_first(xerr, NULL, "rpc-error")) == NULL){
- clicon_err(OE_XML, ENOENT, "Expected rpc-error tag but none found(internal)");
- goto done;
- }
}
}
}
- if (xr){
- if (xml_rm(xr) < 0)
+ if (xt){
+ if (xml_rm(xd) < 0)
goto done;
- *xt = xr;
+ *xt = xd;
}
retval = 0;
done:
diff --git a/test/test_pagination.sh b/test/test_pagination.sh
index 66ac0ecf..2aea412f 100755
--- a/test/test_pagination.sh
+++ b/test/test_pagination.sh
@@ -3,7 +3,6 @@
# Backlog items:
# 1. "remaining" annotation RFC 7952
# 2. pattern '.*[\n].*' { modifier invert-match;
-# XXX: augment Netconf GET instead, not RPC
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@@ -277,16 +276,12 @@ function testlimit()
new "clixon limit=$limit NETCONF get"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOtrue$limit]]>]]>" "alicepublic17]]>]]>$"
- # "old: ietf"
- new "ietf limit=$limit NETCONF"
- expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOds:running/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers$limit]]>]]>" "17]]>]]>$"
-
new "limit=$limit 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?limit=$limit)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+xml" "17"
+ expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+xml" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers?limit=$limit)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+xml" "17"
# XXX [17]
new "limit=$limit 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?limit=$limit)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+json" "{\"pageable-list\":{\"example-social:uint8-numbers\":17,\"@example-social:uint8-numbers\": \[{\"ietf-netconf-list-pagination:remaining\": $remaining}\]}}"
+ expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-collection+json" $RCPROTO://localhost/restconf/data/example-social:members/member=alice/favorites/uint8-numbers?limit=$limit)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+json" '{"yang-collection":{"example-social:uint8-numbers":17,"@example-social:uint8-numbers": \[{"clixon-netconf-list-pagination:remaining": 5}\]}}'
}
new "test params: -f $cfg -s startup -- -sS $fstate"
diff --git a/yang/mandatory/Makefile.in b/yang/mandatory/Makefile.in
index fc31d905..cc481cab 100644
--- a/yang/mandatory/Makefile.in
+++ b/yang/mandatory/Makefile.in
@@ -55,7 +55,9 @@ YANGSPECS += ietf-origin@2018-02-14.yang
YANGSPECS += ietf-yang-metadata@2016-08-05.yang
YANGSPECS += ietf-netconf-with-defaults@2011-06-01.yang
# in draft-wwlh-netconf-list-pagination:
-YANGSPECS += ietf-netconf-list-pagination@2020-10-30.yang
+YANGSPECS += ietf-netconf-list-pagination@2020-10-30.yang
+# in draft-wwlh-netconf-list-pagination-rc:
+YANGSPECS += ietf-restconf-list-pagination@2015-01-30.yang
all:
diff --git a/yang/mandatory/ietf-restconf-list-pagination@2015-01-30.yang b/yang/mandatory/ietf-restconf-list-pagination@2015-01-30.yang
new file mode 100644
index 00000000..9e9e340d
--- /dev/null
+++ b/yang/mandatory/ietf-restconf-list-pagination@2015-01-30.yang
@@ -0,0 +1,54 @@
+module ietf-restconf-list-pagination {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination";
+ prefix rlpg;
+ import ietf-restconf {
+ prefix rc;
+ }
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web:
+ WG List: ";
+
+ description
+ "This module contains conceptual YANG specifications
+ for the RESTCONF Collection resource type.
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The YANG grouping statements provide a normative syntax
+ for XML and JSON message encoding purposes.
+
+ Copyright (c) 2015 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
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC XXXX; see
+ the RFC itself for full legal notices.";
+
+ revision 2015-01-30 {
+ description
+ "Initial revision.";
+ reference
+ "RFC XXXX: RESTCONF Collection Resource.";
+ }
+ rc:yang-data yang-collection {
+ uses collection;
+ }
+
+ grouping collection {
+ description
+ "Conceptual container representing the
+ yang-collection resource type.";
+ container yang-collection {
+ description
+ "Container representing the yang-collection
+ resource type.";
+ }
+ }
+}