diff --git a/CHANGELOG.md b/CHANGELOG.md
index b49318fa..c361c0fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -533,6 +533,14 @@ Developers may need to change their code
* See [namespace prefix nc is not supported in full #154](https://github.com/clicon/clixon/issues/154)
* Fixed [Clixon backend generates wrong XML on empty string value #144](https://github.com/clicon/clixon/issues/144)
+### New features
+
+* Prototype of collection draft
+ * This is prototype work for ietf netconf work
+ * See draft-ietf-netconf-restconf-collection-00.txt
+ * New yang: ietf-restconf-collection@2020-10-22.yang
+ * New http media: application/yang.collection+xml/json
+
## 4.8.0
18 October 2020
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index be7b9377..d4f63a82 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -1335,6 +1335,305 @@ from_client_kill_session(clicon_handle h,
return retval;
}
+/*! 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,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,
+ 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, "unbounded") != 0){
+ if ((ret = parse_uint32(valstr, value, &reason)) < 0){
+ clicon_err(OE_XML, errno, "parse_uint32");
+ goto done;
+ }
+ if (ret == 0){
+ if (netconf_bad_attribute(cbret, "application",
+ "count", "Unrecognized value of count attribute") < 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
+ *
+ * @param[in] h Clicon handle
+ * @param[in] xe Request:
+ * @param[out] cbret Return xml tree, eg ..., 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, xpath?xpath:"/") < 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)
+ 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 (xtop)
+ xml_free(xtop);
+ 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;
+}
+
/*! Create a notification subscription
* @param[in] h Clicon handle
* @param[in] xe Request:
@@ -1960,7 +2259,10 @@ backend_rpc_init(clicon_handle h)
if (rpc_callback_register(h, from_client_validate, NULL,
NETCONF_BASE_NAMESPACE, "validate") < 0)
goto done;
-
+ /* draft-ietf-netconf-restconf-collection-00 */
+ if (rpc_callback_register(h, from_client_get_collection, NULL,
+ NETCONF_COLLECTION_NAMESPACE, "get-collection") < 0)
+ goto done;
/* 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/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h
index 069d37a7..c7d6a368 100644
--- a/apps/restconf/clixon_restconf.h
+++ b/apps/restconf/clixon_restconf.h
@@ -42,13 +42,19 @@ extern "C" {
#define _CLIXON_RESTCONF_H_
/*
- * Types (also in restconf_lib.h)
+ * Types
+ */
+/*! RESTCONF media types
+ * @see http_media_map
+ * @note DUPLICATED in clixon_lib.h
*/
enum restconf_media{
- YANG_DATA_JSON, /* "application/yang-data+json" */
- YANG_DATA_XML, /* "application/yang-data+xml" */
- YANG_PATCH_JSON, /* "application/yang-patch+json" */
- YANG_PATCH_XML /* "application/yang-patch+xml" */
+ YANG_DATA_JSON, /* "application/yang-data+json" */
+ 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 */
};
typedef enum restconf_media restconf_media;
diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c
index 29c87ceb..553fc16e 100644
--- a/apps/restconf/restconf_err.c
+++ b/apps/restconf/restconf_err.c
@@ -275,6 +275,7 @@ api_return_err(clicon_handle h,
switch (media){
case YANG_DATA_XML:
case YANG_PATCH_XML:
+ case YANG_COLLECTION_XML:
clicon_debug(1, "%s code:%d", __FUNCTION__, code);
if (pretty){
cprintf(cb, " \n");
@@ -291,6 +292,7 @@ api_return_err(clicon_handle h,
break;
case YANG_DATA_JSON:
case YANG_PATCH_JSON:
+ case YANG_COLLECTION_JSON:
clicon_debug(1, "%s code:%d", __FUNCTION__, code);
if (pretty){
cprintf(cb, "{\n\"ietf-restconf:errors\" : ");
@@ -306,9 +308,7 @@ api_return_err(clicon_handle h,
cprintf(cb, "}\r\n");
}
break;
- default:
- clicon_err(OE_YANG, EINVAL, "Invalid media type %d", media);
- goto done;
+ default: /* Just ignore the body so that there is a reply */
break;
} /* switch media */
assert(cbuf_len(cb));
diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c
index 1384aee0..50779512 100644
--- a/apps/restconf/restconf_lib.c
+++ b/apps/restconf/restconf_lib.c
@@ -203,10 +203,12 @@ static const map_str2int http_reason_phrase_map[] = {
* @see restconf_media_str2int
*/
static const map_str2int http_media_map[] = {
- {"application/yang-data+xml", YANG_DATA_XML},
- {"application/yang-data+json", YANG_DATA_JSON},
- {"application/yang-patch+xml", YANG_PATCH_XML},
- {"application/yang-patch+json", YANG_PATCH_JSON},
+ {"application/yang-data+xml", YANG_DATA_XML},
+ {"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+json", YANG_COLLECTION_JSON},
{NULL, -1}
};
diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h
index 53292f5c..8f5418f7 100644
--- a/apps/restconf/restconf_lib.h
+++ b/apps/restconf/restconf_lib.h
@@ -46,13 +46,15 @@ extern "C" {
*/
/*! RESTCONF media types
* @see http_media_map
- * (also in clixon_restconf.h)
+ * @note DUPLICATED in clixon_restconf.h
*/
enum restconf_media{
YANG_DATA_JSON, /* "application/yang-data+json" */
YANG_DATA_XML, /* "application/yang-data+xml" */
YANG_PATCH_JSON, /* "application/yang-patch+json" */
- YANG_PATCH_XML /* "application/yang-patch+xml" */
+ 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 */
};
typedef enum restconf_media restconf_media;
diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c
index 881884e5..5ff22dd6 100644
--- a/apps/restconf/restconf_methods_get.c
+++ b/apps/restconf/restconf_methods_get.c
@@ -75,7 +75,7 @@
* @param[in] media_out Output media
* @param[in] head If 1 is HEAD, otherwise GET
* @code
- * curl -G http://localhost/restconf/data/interfaces/interface=eth0
+ * curl -X GET http://localhost/restconf/data/interfaces/interface=eth0
* @endcode
* See RFC8040 Sec 4.2 and 4.3
* XXX: cant find a way to use Accept request field to choose Content-Type
@@ -215,7 +215,8 @@ api_data_get2(clicon_handle h,
goto ok;
}
/* Normal return, no error */
- if ((cbx = cbuf_new()) == NULL)
+ if ((cbx = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
switch (media_out){
@@ -228,6 +229,9 @@ api_data_get2(clicon_handle h,
goto done;
break;
default:
+ if (restconf_unsupported_media(req) < 0)
+ goto done;
+ goto ok;
break;
}
}
@@ -309,6 +313,227 @@ api_data_get2(clicon_handle h,
return retval;
}
+/*! GET Collection
+ * According to restconf collection draft. Lists, work in progress
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
+ * @param[in] pcvec Vector of path ie DOCUMENT_URI element
+ * @param[in] pi Offset, where path starts
+ * @param[in] qvec Vector of query string (QUERY_STRING)
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * @param[in] head If 1 is HEAD, otherwise GET
+ * @code
+ * curl -X GET http://localhost/restconf/data/interfaces
+ * @endcode
+ * A collection resource contains a set of data resources. It is used
+ * to represent a all instances or a subset of all instances in a YANG
+ * list or leaf-list.
+ * @see draft-ietf-netconf-restconf-collection-00.txt
+ */
+static int
+api_data_collection(clicon_handle h,
+ void *req,
+ char *api_path,
+ cvec *pcvec, /* XXX remove? */
+ int pi,
+ cvec *qvec,
+ int pretty,
+ restconf_media media_out)
+{
+ int retval = -1;
+ char *xpath = NULL;
+ cbuf *cbx = NULL;
+ yang_stmt *yspec;
+ cxobj *xret = NULL;
+ cxobj *xerr = NULL; /* malloced */
+ cxobj *xe = NULL; /* not malloced */
+ cxobj **xvec = NULL;
+ int i;
+ int ret;
+ cvec *nsc = NULL;
+ char *attr; /* attribute value string */
+ netconf_content content = CONTENT_ALL;
+ cxobj *xtop = NULL;
+ cxobj *xbot = NULL;
+ yang_stmt *y = NULL;
+ cbuf *cbrpc = NULL;
+ char *depth;
+ char *count;
+ char *skip;
+ char *direction;
+ char *sort;
+ char *where;
+
+ clicon_debug(1, "%s", __FUNCTION__);
+ if ((yspec = clicon_dbspec_yang(h)) == NULL){
+ clicon_err(OE_FATAL, 0, "No DB_SPEC");
+ goto done;
+ }
+ /* strip /... from start */
+ for (i=0; i");
+ /* 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,
@@ -877,7 +878,134 @@ clicon_rpc_get(clicon_handle h,
return retval;
}
+/*! Get database configuration and state data collection
+ * @param[in] h Clicon handle
+ * @param[in] apipath To identify a list/leaf-list
+ * @param[in] yli Yang-stmt of list/leaf-list of collection
+ * @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
+ * @param[in] depth Nr of XML levels to get, -1 is all, 0 is none
+ * @param[in] count Collection/clixon extension
+ * @param[in] skip Collection/clixon extension
+ * @param[in] direction Collection/clixon extension
+ * @param[in] sort Collection/clixon extension
+ * @param[in] where Collection/clixon extension
+ * @param[out] xt XML tree. Free with xml_free.
+ * 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
+ */
+int
+clicon_rpc_get_collection(clicon_handle h,
+ char *apipath,
+ yang_stmt *yli,
+ cvec *nsc, /* namespace context for filter */
+ netconf_content content,
+ char *depth,
+ char *count,
+ char *skip,
+ char *direction,
+ char *sort,
+ char *where,
+ cxobj **xt)
+{
+ int retval = -1;
+ struct clicon_msg *msg = NULL;
+ cbuf *cb = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr = NULL;
+ cxobj *xr;
+ char *username;
+ uint32_t session_id;
+ int ret;
+ yang_stmt *yspec;
+ cxobj *x;
+
+ if (session_id_check(h, &session_id) < 0)
+ goto done;
+ if ((cb = cbuf_new()) == NULL)
+ goto done;
+ cprintf(cb, "");
+ if (count)
+ cprintf(cb, "%s", apipath);
+ if (count)
+ cprintf(cb, "%s", count);
+ if (skip)
+ cprintf(cb, "%s", skip);
+ if (direction)
+ cprintf(cb, "%s", direction);
+ if (sort)
+ cprintf(cb, "%s", sort);
+ if (where)
+ cprintf(cb, "%s", where);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_encode(session_id, "%s", cbuf_get(cb))) == NULL)
+ goto done;
+ if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
+ goto done;
+ /* 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/collection")) == NULL){
+ if ((xr = xml_new("collection", NULL, CX_ELMNT)) == NULL)
+ goto done;
+ }
+ else{
+ yspec = clicon_dbspec_yang(h);
+ /* Populate all children with yco */
+ 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)
+ goto done;
+ if (ret == 0){
+ if (clixon_netconf_internal_error(xerr,
+ ". Internal error, backend returned invalid XML.",
+ 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)
+ goto done;
+ *xt = xr;
+ }
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ if (xerr)
+ xml_free(xerr);
+ if (xret)
+ xml_free(xret);
+ if (msg)
+ free(msg);
+ return retval;
+}
+
/*! Send a close a netconf user session. Socket is also closed if still open
+ *
* @param[in] h CLICON handle
* @retval 0 OK
* @retval -1 Error and logged to syslog
diff --git a/lib/src/clixon_xml_bind.c b/lib/src/clixon_xml_bind.c
index 7d38bff6..c625e93d 100644
--- a/lib/src/clixon_xml_bind.c
+++ b/lib/src/clixon_xml_bind.c
@@ -743,4 +743,3 @@ xml_bind_yang_rpc_reply(cxobj *xrpc,
retval = 0;
goto done;
}
-
diff --git a/test/test_collection.sh b/test/test_collection.sh
new file mode 100755
index 00000000..5debe9a3
--- /dev/null
+++ b/test/test_collection.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# Restconf RFC8040 Appendix A and B "jukebox" example
+# For collection / scaling activity
+# Magic line must be first in script (see README.md)
+s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
+
+APPNAME=example
+
+cfg=$dir/conf.xml
+fjukebox=$dir/example-jukebox.yang
+
+cat < $cfg
+
+ $cfg
+ ietf-netconf:startup
+ /usr/local/share/clixon
+ $IETFRFC
+ $dir
+ false
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/lib/$APPNAME/backend
+ $dir/restconf.pidfile
+ $dir
+ true
+
+EOF
+
+cat < $dir/startup_db
+
+
+
+
+ Foo Fighters
+
+ Crime and Punishment
+ 1995
+
+
+ One by One
+ 2002
+
+
+ The Color and the Shape
+ 1997
+
+
+ There is Nothing Left to Loose
+ 1999
+
+
+ White and Black
+ 1998
+
+
+
+
+
+EOF
+
+# Common Jukebox spec (fjukebox must be set)
+. ./jukebox.sh
+
+new "test params: -f $cfg -- -s" # XXX: -sS state file
+
+if [ $BE -ne 0 ]; then
+ new "kill old backend"
+ sudo clixon_backend -zf $cfg
+ if [ $? -ne 0 ]; then
+ err
+ fi
+ sudo pkill -f clixon_backend # to be sure
+ new "start backend -s startup -f $cfg"
+ start_backend -s startup -f "$cfg"
+fi
+
+new "waiting"
+wait_backend
+
+if [ $RC -ne 0 ]; then
+ new "kill old restconf daemon"
+ stop_restconf_pre
+
+ new "start restconf daemon"
+ start_restconf -f $cfg
+
+ new "waiting"
+ wait_restconf
+fi
+
+new "C.1. 'count' Parameter RESTCONF"
+expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang.collection+xml" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album/?count=2)" 0 "HTTP/1.1 200 OK" "application/yang.collection+xml" 'Crime and Punishment1995One by One2002'
+
+new "C.1. 'count' Parameter NETCONF"
+expecteof "$clixon_netconf -qf $cfg" 0 "runningexample-jukebox/example-jukebox:jukebox/library/artist=Foo Fighters/album2]]>]]>" '^Crime and Punishment1995One by One2002]]>]]>$'
+
+if [ $RC -ne 0 ]; then
+ new "Kill restconf daemon"
+ stop_restconf
+fi
+
+if [ $BE -eq 0 ]; then
+ exit # BE
+fi
+
+new "Kill backend"
+# Check if premature kill
+pid=$(pgrep -u root -f clixon_backend)
+if [ -z "$pid" ]; then
+ err "backend already dead"
+fi
+# kill backend
+stop_backend -f $cfg
+
+rm -rf $dir
diff --git a/yang/clixon/ietf-netconf-collection@2020-10-22.yang b/yang/clixon/ietf-netconf-collection@2020-10-22.yang
new file mode 100644
index 00000000..f9fc125a
--- /dev/null
+++ b/yang/clixon/ietf-netconf-collection@2020-10-22.yang
@@ -0,0 +1,125 @@
+module ietf-netconf-collection {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-collection";
+ prefix "rcoll";
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ WG Chair: Mehmet Ersue
+
+
+ WG Chair: Mahesh Jethanandani
+
+
+ Editor: Andy Bierman
+
+
+ Editor: Martin Bjorklund
+
+
+ Editor: Kent Watsen
+ ";
+
+ 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.";
+ /*
+ module: ietf-netconf-collection
+ rpcs:
+
+ +---x get-collection
+ +--w input
+ +--w module-name string
+ +--w datastore string
+ +--w list-target string
+ +--w count uint32
+ +--w skip uint32
+ +--w direction enum
+ +--w sort string
+ +--w where string
+ +--ro output
+ +-ro collection anydata
+ */
+
+
+ revision 2020-10-22 {
+ description
+ "Draft by Olof Hagsand / Clixon.";
+ }
+ rpc get-collection {
+ input {
+ leaf module-name {
+ /* Not needed with proper list-target */
+ type string;
+ }
+ leaf datastore {
+ type string;
+ default "running";
+ }
+ leaf list-target {
+ description "api-path";
+ mandatory true;
+ type string;
+ }
+ leaf count {
+ type union {
+ type uint32;
+ type string {
+ pattern 'unbounded';
+ }
+ }
+ }
+ leaf skip {
+ type union {
+ type uint32;
+ type string {
+ pattern 'unbounded';
+ }
+ }
+ }
+ leaf direction {
+ type enumeration {
+ enum forward;
+ enum reverse;
+ }
+ }
+ leaf sort {
+ type string;
+ }
+ leaf where {
+ type string;
+ }
+ }
+ output {
+ anyxml collection {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+}