- Moved restrconf code from pageing rpc to get
This commit is contained in:
parent
c9843b34a6
commit
fb0b9409f3
15 changed files with 192 additions and 415 deletions
12
CHANGELOG.md
12
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: <rpc><xn></rpc>
|
||||
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||
* @param[in] arg client-entry
|
||||
* @param[in] regarg User argument given at rpc_callback_register()
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @see from_client_get
|
||||
*/
|
||||
int
|
||||
from_client_get_pageable_list(clicon_handle h,
|
||||
cxobj *xe,
|
||||
cbuf *cbret,
|
||||
void *arg,
|
||||
void *regarg)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
char *xpath = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen;
|
||||
cxobj *xnacm = NULL;
|
||||
char *username;
|
||||
cvec *nsc = NULL; /* Create a netconf namespace context from filter */
|
||||
char *attr;
|
||||
netconf_content content = CONTENT_ALL;
|
||||
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
||||
yang_stmt *yspec;
|
||||
int i;
|
||||
cxobj *xerr = NULL;
|
||||
int ret;
|
||||
cbuf *cb = NULL;
|
||||
uint32_t limit = 0;
|
||||
uint32_t offset = 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 *cbpath = NULL;
|
||||
cxobj *xtop = NULL;
|
||||
yang_stmt *y;
|
||||
char *ns;
|
||||
clixon_path *path_tree = NULL;
|
||||
clixon_path *cp;
|
||||
cxobj *xa; /* attribute */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
username = clicon_username_get(h);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||
goto done;
|
||||
}
|
||||
/* Clixon extensions: content */
|
||||
if ((attr = xml_find_value(xe, "content")) != NULL)
|
||||
content = netconf_content_str2int(attr);
|
||||
/* Clixon extensions: depth */
|
||||
if ((attr = xml_find_value(xe, "depth")) != 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(cbret, "application",
|
||||
"depth", "Unrecognized value of depth attribute") < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
/* datastore */
|
||||
if (ret && (x = xml_find_type(xe, NULL, "datastore", CX_ELMNT)) != NULL){
|
||||
if (nodeid_split(xml_body(x), NULL, &datastore) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0)
|
||||
goto ok;
|
||||
/* list-target, create (xml and) yang to check if is state (CF) or config (CT)
|
||||
* is mandatory
|
||||
*/
|
||||
if (ret && (x = xml_find_type(xe, NULL, "list-target", CX_ELMNT)) != NULL){
|
||||
xpath = xml_body(x);
|
||||
if (xml_nsctx_node(x, &nsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xpath == NULL){
|
||||
clicon_err(OE_NETCONF, 0, "Missing list-target/xpath, is mandatory");
|
||||
goto done;
|
||||
}
|
||||
if ((xtop = xml_new("top", NULL, CX_ELMNT)) == NULL)
|
||||
goto done;
|
||||
/* Parse xpath -> 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, "<rpc-reply xmlns=\"%s\"><pageable-list xmlns=\"%s\">",
|
||||
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; i<xlen; i++){
|
||||
x = xvec[i];
|
||||
/* Add namespace */
|
||||
if (xmlns_set(x, NULL, ns) < 0)
|
||||
goto done;
|
||||
if (i == 0 && remaining != total){
|
||||
/* Add remaining annotation to first element
|
||||
* If no elements were removed, this annotation MUST NOT appear
|
||||
*/
|
||||
if ((xa = xml_new("remaining", x, CX_ATTR)) == NULL)
|
||||
goto done;
|
||||
cbuf_reset(cb);
|
||||
cprintf(cb, "%u", remaining);
|
||||
if (xml_value_set(xa, cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
if (xml_prefix_set(xa, "ycoll") < 0)
|
||||
goto done;
|
||||
if (xmlns_set(x, "ycoll", "urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination") < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Top level is data, so add 1 to depth if significant */
|
||||
if (clicon_xml2cbuf(cbret, x, 0, 0, depth>0?depth+1:depth) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
cprintf(cbret, "</pageable-list></rpc-reply>");
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -124,7 +124,3 @@
|
|||
* draft-wwlh-netconf-list-pagination-rc-01
|
||||
*/
|
||||
#define LIST_PAGINATION
|
||||
|
||||
/*! Clixon netconf pagination
|
||||
*/
|
||||
#define CLIXON_PAGINATION
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <edit-config>
|
||||
* operations, <error-info> content, and the <action> 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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -822,7 +822,7 @@ clicon_rpc_get(clicon_handle h,
|
|||
/* Clixon extension, depth=<level> */
|
||||
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 <config> or <rpc-error>.
|
||||
* @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, "><get-pageable-list xmlns=\"%s\"", NETCONF_COLLECTION_NAMESPACE);
|
||||
cprintf(cb, "><get ");
|
||||
/* Clixon extension, content=all,config, or nonconfig */
|
||||
if ((int)content != -1)
|
||||
cprintf(cb, " content=\"%s\"", netconf_content_int2str(content));
|
||||
if (depth)
|
||||
cprintf(cb, " depth=\"%s\"", depth);
|
||||
cprintf(cb, ">");
|
||||
cprintf(cb, "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:%s</datastore>", datastore);
|
||||
if (xpath){
|
||||
cprintf(cb, "<list-target");
|
||||
/* Clixon extension, depth=<level> */
|
||||
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</list-target>", xpath);
|
||||
cprintf(cb, "/>");
|
||||
}
|
||||
/* Explicit use of list-pagination */
|
||||
cprintf(cb, "<cp:list-pagination>true</cp:list-pagination>");
|
||||
if (limit)
|
||||
cprintf(cb, "<limit>%s</limit>", limit);
|
||||
cprintf(cb, "<cp:limit>%s</cp:limit>", limit);
|
||||
if (offset)
|
||||
cprintf(cb, "<offset>%s</offset>", offset);
|
||||
cprintf(cb, "<cp:offset>%s</cp:offset>", offset);
|
||||
if (direction)
|
||||
cprintf(cb, "<direction>%s</direction>", direction);
|
||||
cprintf(cb, "<cp:direction>%s</cp:direction>", direction);
|
||||
if (sort)
|
||||
cprintf(cb, "<sort>%s</sort>", sort);
|
||||
cprintf(cb, "<cp:sort>%s</cp:sort>", sort);
|
||||
if (where)
|
||||
cprintf(cb, "<where>%s</where>", where);
|
||||
cprintf(cb, "</get-pageable-list></rpc>");
|
||||
cprintf(cb, "<cp:where>%s</cp:where>", where);
|
||||
cprintf(cb, "</get>");
|
||||
cprintf(cb, "</rpc>");
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><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"
|
||||
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" "<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>"
|
||||
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" "<yang-collection xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf-list-pagination\"><uint8-numbers cp:remaining=\"$remaining\" xmlns:cp=\"http://clicon.org/clixon-netconf-list-pagination\" xmlns=\"http://example.com/ns/example-social\">17</uint8-numbers></yang-collection>"
|
||||
|
||||
# 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"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
54
yang/mandatory/ietf-restconf-list-pagination@2015-01-30.yang
Normal file
54
yang/mandatory/ietf-restconf-list-pagination@2015-01-30.yang
Normal file
|
|
@ -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: <http://tools.ietf.org/wg/netconf/>
|
||||
WG List: <mailto:netconf@ietf.org>";
|
||||
|
||||
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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue