diff --git a/CHANGELOG.md b/CHANGELOG.md index b53a59d3..d3a67f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,11 @@ Users may have to change how they access the system * Added linenumbers to all YANG symbols for better debug and errors * Improved error messages for YANG identityref:s and leafref:s by adding original line numbers +### Minor features + +* ietf-yang-metadata RFC 7952 support, placeholder parsing and extension + * No actual json/xml semantics + ### Corrected Bugs * Partly Fixed: [String concatenation in YANG model leads to syntax error](https://github.com/clicon/clixon/issues/265) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 300431eb..a8d6da12 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -359,6 +359,7 @@ client_statedata(clicon_handle h, goto done; } cbuf_reset(cb); + /* XXX This code does not filter state data with xpath */ cprintf(cb, "", namespace); if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) goto done; @@ -1422,8 +1423,10 @@ from_client_get_pageable_list(clicon_handle h, int i; cxobj *xerr = NULL; int ret; - uint32_t count = 0; - uint32_t skip = 0; + uint32_t limit = 0; + uint32_t offset = 0; + size_t total = 0; + uint32_t remaining = 0; char *direction = NULL; char *sort = NULL; char *where = NULL; @@ -1435,6 +1438,7 @@ from_client_get_pageable_list(clicon_handle h, char *ns; clixon_path *path_tree = NULL; clixon_path *cp; + cxobj *xa; /* attribute */ clicon_debug(1, "%s", __FUNCTION__); username = clicon_username_get(h); @@ -1458,11 +1462,11 @@ from_client_get_pageable_list(clicon_handle h, goto ok; } } - /* count */ - if ((ret = element2value(h, xe, "count", "unbounded", cbret, &count)) < 0) + /* limit */ + if ((ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0) goto done; - /* skip */ - if (ret && (ret = element2value(h, xe, "skip", "none", cbret, &skip)) < 0) + /* 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){ @@ -1496,13 +1500,17 @@ from_client_get_pageable_list(clicon_handle h, 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, "%s", xpath)) < 0) + if ((ret = clixon_instance_id_parse(yspec, &path_tree, &xerr, "%s", xpath)) < 0) goto done; if (ret == 0){ - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (xerr && clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) goto done; goto ok; } @@ -1524,39 +1532,50 @@ from_client_get_pageable_list(clicon_handle h, goto ok; } /* Build a "predicate" cbuf - * This solution uses xpath predicates to translate "count" and "skip" to + * This solution uses xpath predicates to translate "limit" and "offset" to * relational operators <>. */ if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - /* This uses xpath. Maybe count/limit should use parameters */ + /* This uses xpath. Maybe limit should use parameters */ cprintf(cb, "%s", xpath); if (where) cprintf(cb, "[%s]", where); - if (skip){ - cprintf(cb, "[%u <= position()", skip); - if (count) - cprintf(cb, " and position() < %u", count+skip); + if (offset){ + cprintf(cb, "[%u <= position()", offset); + if (limit) + cprintf(cb, " and position() < %u", limit+offset); cprintf(cb, "]"); } - else if (count) - cprintf(cb, "[position() < %u]", count); + else if (limit) + cprintf(cb, "[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, cbuf_get(cb), 1, &xret, NULL, &xerr)) < 0) { + if ((ret = xmldb_get0(h, datastore, YB_MODULE, nsc, xpath, 1, &xret, NULL, &xerr)) < 0) { if (netconf_operation_failed(cbret, "application", "read registry")< 0) goto done; goto ok; } - } - if (ret == 0){ - if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + if (ret == 0){ + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + goto done; + goto ok; + } + /* First get number of hits */ + if (xpath_vec(xret, nsc, "%s", &xvec, &total, xpath) < 0) goto done; - goto ok; + if (xvec){ + free(xvec); + xvec = NULL; + } + if (total < (offset + limit)) + remaining = 0; + else + remaining = total - (offset + limit); } /* There may be CF data in a CT collection */ if (content == CONTENT_ALL){ @@ -1576,7 +1595,8 @@ from_client_get_pageable_list(clicon_handle h, } } } - if (clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){ + 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 @@ -1614,7 +1634,7 @@ from_client_get_pageable_list(clicon_handle h, * Actually this is a safety catch, should really be done in plugins * and modules_state functions. */ - if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, cbuf_get(cb)) < 0) goto done; /* Pre-NACM access step */ @@ -1632,6 +1652,21 @@ from_client_get_pageable_list(clicon_handle h, /* 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); /* reuse */ + cprintf(cb, "%u", remaining); + if (xml_value_set(xa, cbuf_get(cb)) < 0) + goto done; + if (xml_prefix_set(xa, "lpg") < 0) + goto done; + if (xmlns_set(x, "lpg", "urn:ietf:params:xml:ns:yang:ietf-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; @@ -2291,7 +2326,7 @@ backend_rpc_init(clicon_handle h) #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) + NETCONF_COLLECTION_NAMESPACE, "get-pagable-list") < 0) goto done; #endif /* In backend_client.? RPC from RFC 5277 */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 9c10bd16..d494f6bc 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -767,6 +767,9 @@ main(int argc, if (netconf_module_features(h) < 0) goto done; + /* In case ietf-yang-metadata is loaded by application, handle annotation extension */ + if (yang_metadata_init(h) < 0) + goto done; /* External NACM file? * Note, loads yang -> extensions -> plugins */ diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index d045dd42..f55d7c39 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -237,23 +237,25 @@ clixon_plugin_daemon_all(clicon_handle h) * * @param[in] cp Plugin handle * @param[in] h clicon handle + * @param[in] nsc namespace context for xpath * @param[in] xpath String with XPATH syntax. or NULL for all - * @param[out] xret If retval=1, state tree created and returned: ... + * @param[out] xp If retval=1, state tree created and returned: ... * @retval -1 Fatal error * @retval 0 Statedata callback failed. no XML tree returned * @retval 1 OK if callback found (and called) xret is set */ static int -clixon_plugin_statedata_one(clixon_plugin_t *cp, - clicon_handle h, - cvec *nsc, - char *xpath, - cxobj **xp) +clixon_plugin_statedata_one(clixon_plugin_t *cp, + clicon_handle h, + cvec *nsc, + char *xpath, + cxobj **xp) { int retval = -1; plgstatedata_t *fn; /* Plugin statedata fn */ cxobj *x = NULL; + clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp)); if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){ if ((x = xml_new(XML_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) goto done; @@ -263,7 +265,6 @@ clixon_plugin_statedata_one(clixon_plugin_t *cp, __FUNCTION__, clixon_plugin_name_get(cp)); goto fail; /* Dont quit here on user callbacks */ } - } if (xp && x) *xp = x; @@ -295,12 +296,12 @@ clixon_plugin_statedata_all(clicon_handle h, char *xpath, cxobj **xret) { - int retval = -1; - int ret; - cxobj *x = NULL; - clixon_plugin_t *cp = NULL; - cbuf *cberr = NULL; - cxobj *xerr = NULL; + int retval = -1; + int ret; + cxobj *x = NULL; + clixon_plugin_t *cp = NULL; + cbuf *cberr = NULL; + cxobj *xerr = NULL; clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { @@ -328,10 +329,8 @@ clixon_plugin_statedata_all(clicon_handle h, x = NULL; continue; } -#if 1 if (clicon_debug_get()) - clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__); -#endif + clicon_log_xml(LOG_DEBUG, x, "%s %s STATE:", __FUNCTION__, clixon_plugin_name_get(cp)); /* XXX: ret == 0 invalid yang binding should be handled as internal error */ if ((ret = xml_bind_yang(x, YB_MODULE, yspec, &xerr)) < 0) goto done; diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index e965868f..498e6ac9 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -633,6 +633,9 @@ main(int argc, */ if (netconf_module_features(h) < 0) goto done; + /* In case ietf-yang-metadata is loaded by application, handle annotation extension */ + if (yang_metadata_init(h) < 0) + goto done; /* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */ xml_nsctx_namespace_netconf_default(h); /* Create top-level and store as option */ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 7aa8c9da..ca49f26a 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -796,11 +796,13 @@ main(int argc, */ if (netconf_module_features(h) < 0) goto done; - + /* Initialize plugin module by creating a handle holding plugin and callback lists */ if (clixon_plugin_module_init(h) < 0) goto done; - + /* In case ietf-yang-metadata is loaded by application, handle annotation extension */ + if (yang_metadata_init(h) < 0) + goto done; /* Create top-level yang spec and store as option */ if ((yspec = yspec_new()) == NULL) goto done; diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index b8508102..c085d30a 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -370,7 +370,9 @@ main(int argc, */ if (netconf_module_features(h) < 0) goto done; - + /* In case ietf-yang-metadata is loaded by application, handle annotation extension */ + if (yang_metadata_init(h) < 0) + goto done; /* Create top-level yang spec and store as option */ if ((yspec = yspec_new()) == NULL) goto done; diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c index 213e0480..fdfdc630 100644 --- a/apps/restconf/restconf_main_native.c +++ b/apps/restconf/restconf_main_native.c @@ -1724,7 +1724,9 @@ restconf_clixon_init(clicon_handle h, */ if (netconf_module_features(h) < 0) goto done; - + /* In case ietf-yang-metadata is loaded by application, handle annotation extension */ + if (yang_metadata_init(h) < 0) + goto done; /* Create top-level yang spec and store as option */ if ((yspec = yspec_new()) == NULL) goto done; diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 85239f18..c2a46bcf 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -359,8 +359,8 @@ api_data_collection(clicon_handle h, yang_stmt *y = NULL; cbuf *cbrpc = NULL; char *depth; - char *count; - char *skip; + char *limit; + char *offset; char *direction; char *sort; char *where; @@ -435,14 +435,14 @@ api_data_collection(clicon_handle h, } /* Clixon extensions and collection attributes */ depth = cvec_find_str(qvec, "depth"); - count = cvec_find_str(qvec, "count"); - skip = cvec_find_str(qvec, "skip"); + 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, count, skip, direction, sort, where, + depth, limit, offset, direction, sort, where, &xret) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md index ec67fbba..b944a2ea 100644 --- a/doc/DEVELOP.md +++ b/doc/DEVELOP.md @@ -149,7 +149,7 @@ How to debug Send debug level in run-time to backend: ``` - echo "1]]>]]>" | clixon_netconf -q + echo "1]]>]]>" | clixon_netconf -q -o CLICON_NETCONF_HELLO_OPTIONAL=true ``` ### Set backend debug diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 32b75f88..3f7f6aa9 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -120,7 +120,7 @@ /*! Enable list pagination drafts * draft-wwlh-netconf-list-pagination-00, - * draft-wwlh-netconf-list-pagination-nc-00 - * draft-wwlh-netconf-list-pagination-rc-00 + * draft-wwlh-netconf-list-pagination-nc-01 + * draft-wwlh-netconf-list-pagination-rc-01 */ #define LIST_PAGINATION diff --git a/lib/clixon/clixon_path.h b/lib/clixon/clixon_path.h index 06b7d8fb..6baa2b6c 100644 --- a/lib/clixon/clixon_path.h +++ b/lib/clixon/clixon_path.h @@ -91,12 +91,12 @@ int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, int clixon_xml_find_instance_id(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...) __attribute__ ((format (printf, 5, 6)));; int clixon_instance_id_bind(yang_stmt *yt, cvec *nsctx, const char *format, ...) __attribute__ ((format (printf, 3, 4))); -int clixon_instance_id_parse(yang_stmt *yt, clixon_path **cplistp, const char *format, ...) __attribute__ ((format (printf, 3, 4))); +int clixon_instance_id_parse(yang_stmt *yt, clixon_path **cplistp, cxobj **xerr, const char *format, ...) __attribute__ ((format (printf, 4, 5))); #else int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...); int clixon_xml_find_instance_id(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format, ...); int clixon_instance_id_bind(yang_stmt *yt, cvec *nsctx, const char *format, ...); -int clixon_instance_id_parse(yang_stmt *yt, clixon_path **cplistp, const char *format, ...); +int clixon_instance_id_parse(yang_stmt *yt, clixon_path **cplistp, cxobj **xerr, const char *format, ...); #endif #endif /* _CLIXON_PATH_H_ */ diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h index e0fe4dfa..d1431f4b 100644 --- a/lib/clixon/clixon_yang_module.h +++ b/lib/clixon/clixon_yang_module.h @@ -76,5 +76,6 @@ yang_stmt *yang_find_module_by_namespace(yang_stmt *yspec, char *ns); yang_stmt *yang_find_module_by_namespace_revision(yang_stmt *yspec, const char *ns, const char *revision); yang_stmt *yang_find_module_by_name_revision(yang_stmt *yspec, const char *name, const char *revision); yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name); +int yang_metadata_init(clicon_handle h); #endif /* _CLIXON_YANG_MODULE_H_ */ diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 41d0d65a..820f6cfc 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -1256,9 +1256,9 @@ _json_parse(char *str, /* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all * members of a top-level JSON object */ - if (yspec && xml_prefix(x) == NULL - /* && yb != YB_MODULE_NEXT XXX Dont know what this is for */ - ){ + if (yspec && xml_prefix(x) == NULL && + /* XXX: For top-level config file: */ + (yb != YB_NONE || strcmp(xml_name(x),DATASTORE_TOP_SYMBOL)!=0)){ if ((cberr = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index d8df2fc4..dc3a8861 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -50,6 +50,7 @@ #include #include #include +#include /* cligen */ #include @@ -72,6 +73,8 @@ #include "clixon_xpath.h" #include "clixon_yang_module.h" #include "clixon_yang_parse_lib.h" +#include "clixon_plugin.h" + #include "clixon_netconf_lib.h" /*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A @@ -1485,6 +1488,7 @@ netconf_module_features(clicon_handle h) return retval; } + /*! Load generic yang specs, ie ietf netconf yang module and set enabled features * @param[in] h Clixon handle * @retval 0 OK @@ -1494,8 +1498,8 @@ netconf_module_features(clicon_handle h) int netconf_module_load(clicon_handle h) { - int retval = -1; - yang_stmt *yspec; + int retval = -1; + yang_stmt *yspec; yspec = clicon_dbspec_yang(h); /* Load yang spec */ @@ -1530,6 +1534,15 @@ netconf_module_load(clicon_handle h) /* Load netconf list pagination */ if (yang_spec_parse_module(h, "ietf-netconf-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 + * system module (always loaded) it means all test-cases + */ + /* Load list pagination */ + if (yang_spec_parse_module(h, "ietf-list-pagination", NULL, yspec)< 0) + goto done; +#endif #endif retval = 0; done: diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 4c23cb03..86c50743 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -1403,9 +1403,8 @@ api_path_resolve(clixon_path *cplist, /*! Resolve instance-id prefix:names to yang statements * @param[in] cplist Lisp of clixon-path * @param[in] yt Yang statement of top symbol (can be yang-spec if top-level) - * @param[in] xt XML statement for prefix context * @retval -1 Error - * @retval 0 Fail + * @retval 0 Fail error in xerr * @retval 1 OK * @note: The spec says: prefixes depend on the XML context in which the value occurs. * However, canonical prefixes/namespaces are used based on loaded yang modules. @@ -1440,7 +1439,7 @@ instance_id_resolve(clixon_path *cplist, } if (yang_keyword_get(yt) == Y_SPEC){ if ((yt = yang_find_module_by_prefix_yspec(yspec, cp->cp_prefix)) == NULL){ - clicon_err(OE_YANG, ENOENT, "Prefix does not correspond to existing module."); + clicon_err(OE_YANG, ENOENT, "Prefix \"%s\" does not correspond to any existing module", cp->cp_prefix); goto fail; } } @@ -1857,6 +1856,7 @@ clixon_instance_id_bind(yang_stmt *yt, * example. * @param[in] yt Yang statement of top symbol (can be yang-spec if top-level) * @param[out] cplistp Path parse-tree + * @param[out] xerr Contains error if retval=0 * @param[in] format Format string for xpath syntax * @retval -1 Error * @retval 0 Non-fatal failure, yang bind failures, etc, @@ -1865,6 +1865,7 @@ clixon_instance_id_bind(yang_stmt *yt, int clixon_instance_id_parse(yang_stmt *yt, clixon_path **cplistp, + cxobj **xerr, const char *format, ...) { @@ -1898,8 +1899,11 @@ clixon_instance_id_parse(yang_stmt *yt, /* Resolve module:name to pointer to yang-stmt, fail if not successful */ if ((ret = instance_id_resolve(cplist, yt)) < 0) goto done; - if (ret == 0) + if (ret == 0){ + if (xerr && netconf_invalid_value_xml(xerr, "application", clicon_err_reason) < 0) + goto done; goto fail; + } if (cplistp){ *cplistp = cplist; cplist = NULL; diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 61a23a0e..666b91fd 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -159,7 +159,6 @@ plugin_module_struct_set(clicon_handle h, return 0; } - /* Access functions */ /*! Get plugin api diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 4e80ffe1..037d1eae 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -887,8 +887,8 @@ clicon_rpc_get(clicon_handle h, * @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] limit Collection/clixon extension + * @param[in] offset Collection/clixon extension * @param[in] direction Collection/clixon extension * @param[in] sort Collection/clixon extension * @param[in] where Collection/clixon extension @@ -909,8 +909,8 @@ clicon_rpc_get_pageable_list(clicon_handle h, cvec *nsc, /* namespace context for xpath */ netconf_content content, char *depth, - char *count, - char *skip, + char *limit, + char *offset, char *direction, char *sort, char *where, @@ -941,7 +941,7 @@ 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, ">%s", xpath); } - if (count) - cprintf(cb, "%s", count); - if (skip) - cprintf(cb, "%s", skip); + if (limit) + cprintf(cb, "%s", limit); + if (offset) + cprintf(cb, "%s", offset); if (direction) cprintf(cb, "%s", direction); if (sort) cprintf(cb, "%s", sort); if (where) 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) diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 29084fb6..cdf11c2e 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -736,7 +736,7 @@ xpath_first_localonly(cxobj *xcur, * If result is not nodeset, return empty nodeset * @param[in] xcur xml-tree where to search * @param[in] nsc External XML namespace context, or NULL - * @param[in] xpformat Format string for XPATH syntax + * @param[in] xpformat Format string for XPATH syntax * @param[out] vec vector of xml-trees. Vector must be free():d after use * @param[out] veclen returns length of vector in return value * @retval 0 OK diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 6d783602..f8ac5bdf 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1604,8 +1604,8 @@ yang_print(FILE *f, } /* Log/debug info about top-level (sub)modules no recursion - * @param[in] f File to print to. * @param[in] yspec Yang spec + * @param[in] dbglevel Debug level */ int yang_spec_dump(yang_stmt *yspec, @@ -2402,7 +2402,6 @@ ys_populate_unknown(clicon_handle h, */ if (clixon_plugin_extension_all(h, yext, ys) < 0) goto done; - retval = 0; done: if (prefix) diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 3718ceb8..dc069571 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -679,3 +679,60 @@ yang_find_module_by_name(yang_stmt *yspec, return ymod; return NULL; } + +/*! Callback for handling RFC 7952 annotations + * + * a server indicates that it is prepared to handle that annotation according to the + * annotation's definition. That is, an annotation advertised by the + * server may be attached to an instance of a data node defined in any + * YANG module that is implemented by the server. + * Possibly add them to yang parsing, cardinality, etc? + * as described in Section 3. + * Note this is called by the module using the extension md:annotate, not by + * ietf-yang-metadata.yang + */ +static int +ietf_yang_metadata_extension_cb(clicon_handle h, + yang_stmt *yext, + yang_stmt *ys) +{ + int retval = -1; + char *extname; + char *modname; + yang_stmt *ymod; + + ymod = ys_module(yext); + modname = yang_argument_get(ymod); + extname = yang_argument_get(yext); + if (strcmp(modname, "ietf-yang-metadata") != 0 || strcmp(extname, "annotation") != 0) + goto ok; + clicon_debug(1, "%s Enabled extension:%s:%s", __FUNCTION__, modname, extname); + /* XXX Nothing yet - this should signal that xml attribute annotations are allowed + * Possibly, add an "annotation" YANG node. + */ + ok: + retval = 0; + // done: + return retval; +} + +/*! In case ietf-yang-metadata is loaded by application, handle annotation extension + * Consider moving fn + */ +int +yang_metadata_init(clicon_handle h) +{ + int retval = -1; + clixon_plugin_t *cp = NULL; + + + /* Create a pseudo-plugin to create extension callback to set the ietf-yang-meta + * yang-data extension for api-root top-level restconf function. + */ + if (clixon_pseudo_plugin(h, "pseudo yang metadata", &cp) < 0) + goto done; + clixon_plugin_api_get(cp)->ca_extension = ietf_yang_metadata_extension_cb; + retval = 0; + done: + return retval; +} diff --git a/test/example_module.sh b/test/example_module.sh deleted file mode 100755 index dbfdd1ca..00000000 --- a/test/example_module.sh +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env bash -# Example-module from draft-netconf-list-pagination-nc-00.txt -# Assumes variable fexample is set to name of yang file -# An extra leaf-list is added (clixon) - -cat < $fexample -module example-module { - yang-version 1.1; - namespace "http://example.com/ns/example-module"; - prefix exm; - - import iana-crypt-hash { - prefix ianach; - } - import ietf-inet-types { - prefix inet; - } - import ietf-yang-types { - prefix yang; - } - - organization - "Example, Inc."; - contact - "support at example.com"; - description - "Example Data Model Module."; - - revision 2020-10-06 { - description - "Initial version."; - reference - "example.com document 1-4673."; - } - - container admins { - description - "Admin Group configuration."; - list admin { - key "name"; - description - "List of admins for admin group configuration."; - ordered-by system; - leaf name { - type string { - length "1 .. max"; - } - description - "The name of the admin."; - } - leaf access { - type enumeration { - enum permit { - description - "Permit access privilege."; - } - enum deny { - description - "Deny access privilege."; - } - enum limited { - description - "Limited access privilege."; - } - } - default "permit"; - description - "The Access privilege type for this admin."; - } - leaf email-address { - type inet:email-address; - description - "Contact email of the admin."; - } - leaf password { - type ianach:crypt-hash; - description - "The password for this entry."; - } - leaf-list status { - type string; - config false; - description - "The status for this entry."; - } - container preference { - leaf-list number { - type uint8; - description - "Defines the perference numbers for the admin."; - } - description - "Preference parameters."; - } - list skill { - key "name"; - description - "Represents one 'sill' resource within one - 'admin' resource."; - leaf name { - type string { - length "1 .. max"; - } - description - "The name of the skill."; - } - leaf rank { - type uint16; - description - "The rank identifying the rank on - the skill."; - } - } - } - } - container rulebase { - description - "Rule base configuration"; - list rule { - key "name"; - description - "List of rules for rulebase."; - ordered-by user; - leaf name { - type string { - length "1 .. max"; - } - description - "The name of the rule."; - } - leaf match { - type string { - length "1 .. max"; - } - description - "The rules in this rulebase determine what fields will be - matched upon before any action is taken on them."; - } - leaf action { - type enumeration { - enum forwarding { - description - "Specify forwarding behavior per rule entry."; - } - enum logging { - description - "Specify logging behavior per rule entry."; - } - } - default "logging"; - description - "Defintion of the action for this rule entry."; - } - } - } - container device-logs { - description - "Device log configuration"; - list device-log { - description - "List of device logs."; - config false; - leaf device-id { - type string; - description - "The device id of the device log."; - } - leaf time-received { - type yang:date-and-time; - description - "The timestamp value at the time this - log was received."; - } - leaf time-generated { - type yang:date-and-time; - description - "The timestamp value at the time this - log was generated."; - } - leaf message { - type string; - description - "Message given at start of login session."; - } - } - } - container audit-logs { - description - "Audit log configuration"; - list audit-log { - key "log-creation"; - description - "List of audit logs."; - config false; - leaf source-ip { - type inet:ip-address; - description - "The IP address of the targeted object."; - } - leaf log-creation { - type yang:date-and-time; - description - "The timestamp value at the time this - log was created."; - } - leaf request { - type string; - description - "Request type of audit log."; - } - leaf outcome { - type boolean; - default "true"; - description - "Indicate the audit log is retrieved sucessfully or not."; - } - } - } - container prefixes { - description - "Enclosing container for the list of prefixes in a policy - prefix list"; - list prefix-list { - key "ip-prefix masklength-lower masklength-upper"; - description - "List of prefixes in the prefix set"; - leaf ip-prefix { - type inet:ip-prefix; - mandatory true; - description - "The prefix member in CIDR notation -- while the - prefix may be either IPv4 or IPv6, most - implementations require all members of the prefix set - to be the same address family. Mixing address types in - the same prefix set is likely to cause an error."; - } - leaf masklength-lower { - type uint8; - description - "Masklength range lower bound."; - } - leaf masklength-upper { - type uint8 { - range "1..128"; - } - must '../masklength-upper >= ../masklength-lower' { - error-message "The upper bound should not be lessthan lower bound."; - } - description - "Masklength range upper bound. - - The combination of masklength-lower and masklength-upper - define a range for the mask length, or single 'exact' - length if masklength-lower and masklenght-upper are equal. - - Example: 10.3.192.0/21 through 10.3.192.0/24 would be - expressed as prefix: 10.3.192.0/21, - masklength-lower=21, - masklength-upper=24 - - Example: 10.3.192.0/21 (an exact match) would be - expressed as prefix: 10.3.192.0/21, - masklength-lower=21, - masklength-upper=21"; - } - } - } -} -EOF - - diff --git a/test/example_social.sh b/test/example_social.sh new file mode 100755 index 00000000..6895a66a --- /dev/null +++ b/test/example_social.sh @@ -0,0 +1,299 @@ +#!/usr/bin/env bash +# Example-social from draft-netconf-list-pagination-00.txt appendix A.1 +# Assumes variable fexample is set to name of yang file +# Note inverted pattern is commented + +cat < $fexample + module example-social { + yang-version 1.1; + namespace "http://example.com/ns/example-social"; + prefix es; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + + import iana-crypt-hash { + prefix ianach; + reference + "RFC 7317: A YANG Data Model for System Management"; + } + + organization "Example, Inc."; + contact "support@example.com"; + description "Example Social Data Model."; + + revision 2021-07-21 { /* clixon edit */ + description + "Initial version."; + reference + "RFC XXXX: Example social module."; + } + + container members { + description + "Container for list of members."; + list member { + key "member-id"; + description + "List of members."; + + leaf member-id { + type string { + length "1..80"; +/* + pattern '.*[\n].*' { + modifier invert-match; + } +*/ + } + description + "The member's identifier."; + } + + leaf email-address { + type inet:email-address; + mandatory true; + description + "The member's email address."; + } + leaf password { + type ianach:crypt-hash; + mandatory true; + description + "The member's hashed-password."; + } + + leaf avatar { + type binary; + description + "An binary image file."; + } + + leaf tagline { + type string { + length "1..80"; +/* + pattern '.*[\n].*' { + modifier invert-match; + } +*/ + } + description + "The member's tagline."; + } + + container privacy-settings { + leaf hide-network { + type boolean; + description + "Hide who you follow and who follows you."; + } + leaf post-visibility { + type enumeration { + enum public { + description + "Posts are public."; + } + enum unlisted { + description + "Posts are unlisted, though visable to all."; + } + enum followers-only { + description + "Posts only visible to followers."; + } + } + default public; + description + "The post privacy setting."; + } + description + "Preferences for the member."; + } + + leaf-list following { + type leafref { + path "/members/member/member-id"; + } + description + "Other members this members is following."; + } + + container posts { + description + "The member's posts."; + list post { + key timestamp; + leaf timestamp { + type yang:date-and-time; + description + "The timestamp for the member's post."; + } + leaf title { + type string { + length "1..80"; +/* + pattern '.*[\n].*' { + modifier invert-match; + } +*/ + } + description + "A one-line title."; + } + leaf body { + type string; + mandatory true; + description + "The body of the post."; + } + description + "A list of posts."; + } + } + + container favorites { + description + "The member's favorites."; + leaf-list uint8-numbers { + type uint8; + ordered-by user; + description + "The member's favorite uint8 numbers."; + } + leaf-list uint64-numbers { + type uint64; + ordered-by user; + description + "The member's favorite uint64 numbers."; + } + leaf-list int8-numbers { + type int8; + ordered-by user; + description + "The member's favorite int8 numbers."; + } + leaf-list int64-numbers { + type int64; + ordered-by user; + description + "The member's favorite uint64 numbers."; + } + leaf-list decimal64-numbers { + type decimal64 { + fraction-digits 5; + } + ordered-by user; + description + "The member's favorite decimal64 numbers."; + } + leaf-list bits { + type bits { + bit zero { + position 0; + description "zero"; + } + bit one { + position 1; + description "one"; + } + bit two { + position 2; + description "two"; + } + } + ordered-by user; + description + "The member's favorite bits."; + } + } + + container stats { + config false; + description + "Operational state members values."; + leaf joined { + type yang:date-and-time; + mandatory true; + description + "Timestamp when member joined."; + } + leaf membership-level { + type enumeration { + enum admin { + description + "Site administrator."; + } + enum standard { + description + "Standard membership level."; + } + enum pro { + description + "Professional membership level."; + } + } + mandatory true; + description + "The membership level for this member."; + } + leaf last-activity { + type yang:date-and-time; + description + "Timestamp of member's last activity."; + } + } + } + } + + container audit-logs { + config false; + description + "Audit log configuration"; + list audit-log { + description + "List of audit logs."; + leaf timestamp { + type yang:date-and-time; + mandatory true; + description + "The timestamp for the event."; + } + leaf member-id { + type string; + mandatory true; + description + "The 'member-id' of the member."; + } + leaf source-ip { + type inet:ip-address; + mandatory true; + description + "The apparent IP address the member used."; + } + leaf request { + type string; + mandatory true; + description + "The member's request."; + } + leaf outcome { + type boolean; + mandatory true; + description + "Indicate if request was permitted."; + } + } + } + } +EOF + + diff --git a/test/test_pagination.sh b/test/test_pagination.sh index c056637e..18edc1ac 100755 --- a/test/test_pagination.sh +++ b/test/test_pagination.sh @@ -1,15 +1,21 @@ #!/usr/bin/env bash -# Restconf RFC8040 Appendix A and B "jukebox" example -# For pagination / scaling I-D activity +# List pagination tests according to draft-wwlh-netconf-list-pagination-00 +# Backlog items: +# 1. "remaining" annotation RFC 7952 +# 2. pattern '.*[\n].*' { modifier invert-match; + # 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 -fexample=$dir/example-module.yang +fexample=$dir/example-social.yang fstate=$dir/mystate.xml +# Common example-module spec (fexample must be set) +. ./example_social.sh + # Define default restconfig config: RESTCONFIG RESTCONFIG=$(restconf_config none false) @@ -26,265 +32,230 @@ cat < $cfg /usr/local/lib/$APPNAME/backend $dir/restconf.pidfile $dir + json true $APPNAME /usr/local/lib/$APPNAME/cli /usr/local/lib/$APPNAME/clispec + true $RESTCONFIG EOF +# See draft-wwlh-netconf-list-pagination-00 A.2 (except stats and audit-log) cat <<'EOF' > $dir/startup_db - - - - Alice - permit - alice@example.com - $0$1543 - - 1 - 2 - - - Customer Service - 99 - - - Problem Solving - 90 - - - - Bob - limited - bob@example.com - $0$2789 - - 2 - 3 - - - Conflict Resolution - 93 - - - Management - 23 - - - Organization - 44 - - - Problem Solving - 98 - - - - Joe - permit - joe@example.com - $0$6523 - - 1 - 4 - - - Management - 96 - - - Collaboration - 92 - - - - Frank - deny - frank@example.com - $0$4030 - - 5 - 9 - - - Organization - 90 - - - Negotiation - 80 - - - - Tom - permit - tom@example.com - $0$2376 - - 2 - 5 - - - Adaptability. - 98 - - - Active Listening - 85 - - - - - - SvrA-http - 92.0.2.0/24 - forwarding - - - SvrA-ftp - 203.0.113.1/32 - forwarding - - - p2p - p2p - logging - - - any - any - logging - - - SvrA-tcp - 80 - forwarding - - - - - 10.0.0.0/8 - 17 - 18 - - - 2000:1::/48 - 48 - 48 - - - 2000:2::/48 - 48 - 48 - - - 2000:3::/48 - 16 - 16 - - - ::/0 - 0 - 128 - - - +{"config": + { + "example-social:members": { + "member": [ + { + "member-id": "alice", + "email-address": "alice@example.com", + "password": "$0$1543", + "avatar": "BASE64VALUE=", + "tagline": "Every day is a new day", + "privacy-settings": { + "hide-network": "false", + "post-visibility": "public" + }, + "following": ["bob", "eric", "lin"], + "posts": { + "post": [ + { + "timestamp": "2020-07-08T13:12:45Z", + "title": "My first post", + "body": "Hiya all!" + }, + { + "timestamp": "2020-07-09T01:32:23Z", + "title": "Sleepy...", + "body": "Catch y'all tomorrow." + } + ] + }, + "favorites": { + "uint8-numbers": [17, 13, 11, 7, 5, 3], + "int8-numbers": [-5, -3, -1, 1, 3, 5] + } + }, + { + "member-id": "bob", + "email-address": "bob@example.com", + "password": "$0$1543", + "avatar": "BASE64VALUE=", + "tagline": "Here and now, like never before.", + "posts": { + "post": [ + { + "timestamp": "2020-08-14T03:32:25Z", + "body": "Just got in." + }, + { + "timestamp": "2020-08-14T03:33:55Z", + "body": "What's new?" + }, + { + "timestamp": "2020-08-14T03:34:30Z", + "body": "I'm bored..." + } + ] + }, + "favorites": { + "decimal64-numbers": ["3.14159", "2.71828"] + } + }, + { + "member-id": "eric", + "email-address": "eric@example.com", + "password": "$0$1543", + "avatar": "BASE64VALUE=", + "tagline": "Go to bed with dreams; wake up with a purpose.", + "following": ["alice"], + "posts": { + "post": [ + { + "timestamp": "2020-09-17T18:02:04Z", + "title": "Son, brother, husband, father", + "body": "What's your story?" + } + ] + }, + "favorites": { + "bits": ["two", "one", "zero"] + } + }, + { + "member-id": "lin", + "email-address": "lin@example.com", + "password": "$0$1543", + "privacy-settings": { + "hide-network": "true", + "post-visibility": "followers-only" + }, + "following": ["joe", "eric", "alice"] + }, + { + "member-id": "joe", + "email-address": "joe@example.com", + "password": "$0$1543", + "avatar": "BASE64VALUE=", + "tagline": "Greatness is measured by courage and heart.", + "privacy-settings": { + "post-visibility": "unlisted" + }, + "following": ["bob"], + "posts": { + "post": [ + { + "timestamp": "2020-10-17T18:02:04Z", + "body": "What's your status?" + } + ] + } + } + ] + } + } +} EOF +# See draft-wwlh-netconf-list-pagination-00 A.2 (only stats and audit-log) cat< $fstate - - - Alice - Available - - - Bob - Busy - - - Joe - Do Not Disturb - - - Frank - Offline - - - Tom - Do Not Disturb - - - - - Cloud-IoT-Device-A - 2020-07-08T12:38:32Z - 2020-07-08T12:37:12Z - Upload contains 6 datapoints - - - Cloud-IoT-Device-B - 2020-07-08T16:20:54Z - 2020-07-08T16:20:14Z - Upload successful - - - Cloud-IoT-Device-C - 2020-07-08T17:30:34Z - 2020-07-08T17:30:12Z - Receive a configuration update - - - Cloud-IoT-Device-D - 2020-07-08T18:40:13Z - 2020-07-08T18:40:00Z - Keep-alive ping sent to server - - - Cloud-IoT-Device-E - 2020-07-08T19:48:34Z - 2020-07-08T19:48:00Z - Uploading data to DataPoint - - - - - 192.168.0.92 - 2020-11-01T06:47:59Z - User-logged-out - true - - - 192.168.0.92 - 2020-11-01T06:49:03Z - User-logged-in - true - - - 192.168.0.92 - 2020-11-01T06:51:34Z - Patron-card-viewed - false - - - 192.168.0.92 - 2020-11-01T06:53:01Z - User-logged-out - true - - - 192.168.0.92 - 2020-11-01T06:56:22Z - User-logged-in - false - - + + + alice + + 2020-07-08T12:38:32Z + admin + 2021-04-01T02:51:11Z + + + + bob + + 2020-08-14T03:30:00Z + standard + 2020-08-14T03:34:30Z + + + + eric + + 2020-09-17T19:38:32Z + pro + 2020-09-17T18:02:04Z + + + + lin + + 2020-07-09T12:38:32Z + standard + 2021-04-01T02:51:11Z + + + + joe + + 2020-10-08T12:38:32Z + pro + 2021-04-01T02:51:11Z + + + + + + ": "2020-10-11T06:47:59Z", + alice + 192.168.0.92 + POST /groups/group/2043 + true + + + 2020-11-01T15:22:01Z + bob + 192.168.2.16 + POST /groups/group/123 + false + + + 2020-12-12T21:00:28Z + eric + 192.168.254.1 + POST /groups/group/10 + true + + + 2021-01-03T06:47:59Z + alice + 192.168.0.92 + POST /groups/group/333 + true + + + 2021-01-21T10:00:00Z + bob + 192.168.2.16 + POST /groups/group/42 + true + + + 2020-02-07T09:06:21Z + alice + 192.168.0.92 + POST /groups/group/1202 + true + + + 2020-02-28T02:48:11Z + bob + 192.168.2.16 + POST /groups/group/345 + true + + EOF -# Common example-module spec (fexample must be set) -. ./example_module.sh - new "test params: -f $cfg -s startup -- -sS $fstate" if [ $BE -ne 0 ]; then @@ -295,7 +266,7 @@ if [ $BE -ne 0 ]; then fi sudo pkill -f clixon_backend # to be sure - new "start backend -s startup -f $cfg -- -sS $mystate" + new "start backend -s startup -f $cfg -- -sS $fstate" start_backend -s startup -f $cfg -- -sS $fstate fi @@ -313,18 +284,24 @@ fi new "wait restconf" wait_restconf -# draft-wwlh-netconf-list-pagination-nc-00.txt -new "C.1. 'count' Parameter NETCONF" -expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOds:running/exm:admins/exm:admin[exm:name='Bob']/exm:skill2]]>]]>" "Conflict Resolution93Management23]]>]]>$" +new "A.3.1.1. 'limit=1' NETCONF" +# XXX: augment GET instead, not RPC +expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOds:running/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers1]]>]]>" "17]]>]]>$" + +#new "A.3.1.1. 'limit' Parameter RESTCONF" +#expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang.collection+xml" $RCPROTO://localhost/restconf/data/ietf-netconf-list-pagination:get-pagable-list -d "ds:running/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers1")" 0 "HTTP/$HVER 200" "Content-Type: application/yang-collection+xml" foo + +new "A.3.1.2. 'limit=2' NETCONF" +expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOds:running/es:members/es:member[es:member-id='alice']/es:favorites/es:uint8-numbers2]]>]]>" "1713]]>]]>$" +exit + + -new "C.2. 'skip' Parameter NETCONF" -expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOds:running/exm:admins/exm:admin[exm:name='Bob']/exm:skill22]]>]]>" "Organization44Problem Solving98]]>]]>$" # CLI # XXX This relies on a very specific clispec command: need a more generic test -new "cli show" -echo "$clixon_cli -1 -f $cfg -l o show pagination" -expectpart "$($clixon_cli -1 -f $cfg -l o show pagination)" 0 "" "Conflict Resolution" "93" "Management" "23" --not-- "Organization" "44" +#new "cli show" +#expectpart "$($clixon_cli -1 -f $cfg -l o show pagination)" 0 "" "Conflict Resolution" "93" "Management" "23" --not-- "Organization" "44" # draft-wwlh-netconf-list-pagination-rc-00.txt #new "A.1. 'count' Parameter RESTCONF" diff --git a/yang/clixon/clixon-config@2021-07-11.yang b/yang/clixon/clixon-config@2021-07-11.yang index 55839147..3de7cd7b 100644 --- a/yang/clixon/clixon-config@2021-07-11.yang +++ b/yang/clixon/clixon-config@2021-07-11.yang @@ -204,7 +204,10 @@ module clixon-config { "Datastore format."; type enumeration{ enum xml{ - description "Save and load xmldb as XML"; + description + "Save and load xmldb as XML + More specifically, such a file looks like: ... provided + DATASTORE_TOP_SYMBOL is 'config'"; } enum json{ description "Save and load xmldb as JSON"; diff --git a/yang/mandatory/ietf-netconf-list-pagination@2020-10-30.yang b/yang/mandatory/ietf-netconf-list-pagination@2020-10-30.yang index d7e421aa..4a13d0cb 100644 --- a/yang/mandatory/ietf-netconf-list-pagination@2020-10-30.yang +++ b/yang/mandatory/ietf-netconf-list-pagination@2020-10-30.yang @@ -3,6 +3,16 @@ module ietf-netconf-list-pagination { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-list-pagination"; prefix ycoll; + import ietf-netconf { + prefix nc; + reference + "RFC 6241: Network Configuration Protocol (NETCONF)"; + } + import ietf-netconf-with-defaults { + prefix ncwd; + reference + "RFC 6243: With-defaults Capability for NETCONF"; + } import ietf-yang-types { prefix yang; reference @@ -20,16 +30,6 @@ module ietf-netconf-list-pagination { "RFC 8342: Network Management Datastore Architecture (NMDA)"; } - import ietf-netconf { - prefix nc; - reference - "RFC 6241: Network Configuration Protocol (NETCONF)"; - } - import ietf-netconf-with-defaults { - prefix ncwd; - reference - "RFC 6243: With-defaults Capability for NETCONF"; - } organization "IETF NETCONF (Network Configuration) Working Group"; @@ -64,7 +64,6 @@ module ietf-netconf-list-pagination { This version of this YANG module is part of RFC 8526; see the RFC itself for full legal notices."; - revision 2020-10-30 { description "Initial revision."; @@ -91,7 +90,7 @@ module ietf-netconf-list-pagination { Datastore Architecture, Section 3.1.1.2"; } - rpc get-pageable-list { + rpc get-pagable-list { description "Use enhanced filtering features to retrieve data from a specific NMDA datastore. The content returned by get-data @@ -240,7 +239,7 @@ module ietf-netconf-list-pagination { mandatory true; type string; } - leaf count { + leaf limit { type union { type uint32; type string { @@ -254,7 +253,7 @@ module ietf-netconf-list-pagination { greater than or equal to 1, or the string 'unbounded'. The string 'unbounded' is the default value."; } - leaf skip { + leaf offset { type union { type uint32; type string {