From 65c44b41e8da5562b14069b9b0adcf0c76a15b6c Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 12 Oct 2018 18:39:33 +0200 Subject: [PATCH] RFC 7895: * Changed Netconf hello to single capabilty urn:ietf:params:netconf:capability:yang-library:1.0 according to YANG 1.1 RFC7950 Sec 5.6.4. * Set by option: CLICON_MODULE_LIBRARY_RFC7895 - enabled by default * Option CLICON_MODULE_SET_ID is set and changed when modules change. --- CHANGELOG.md | 9 +- apps/backend/backend_client.c | 72 +-------- apps/backend/backend_main.c | 14 +- apps/netconf/netconf_hello.c | 47 +++--- apps/netconf/netconf_hello.h | 2 +- apps/netconf/netconf_main.c | 25 +-- apps/restconf/restconf_main.c | 9 +- doc/FAQ.md | 3 + lib/clixon/clixon.h.in | 1 + lib/clixon/clixon_yang_module.h | 57 +++++++ lib/src/Makefile.in | 2 +- lib/src/clixon_options.c | 7 +- lib/src/clixon_yang_module.c | 240 +++++++++++++++++++++++++++++ test/test_feature.sh | 5 +- test/test_netconf.sh | 4 + yang/clixon-config@2018-04-30.yang | 10 ++ 16 files changed, 385 insertions(+), 122 deletions(-) create mode 100644 lib/clixon/clixon_yang_module.h create mode 100644 lib/src/clixon_yang_module.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec9d819..ee8a544b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,10 @@ * logical combination of features not implemented, eg if-feature "not foo or bar and baz"; * YANG Module Library support * According to RFC 7895 and implemented by ietf-yang-library.yang - * Supported: module, name, revision, namespace - * Not supported: notification, deviation, module-set-id, etc. - * Enabled by default, disable by resetting CLICON_MODULE_LIBRARY_RFC7895 + * Changed Netconf hello to single capabilty urn:ietf:params:netconf:capability:yang-library:1.0 according to YANG 1.1 RFC7950 Sec 5.6.4. + * Set by option: CLICON_MODULE_LIBRARY_RFC7895 - enabled by default + * Option CLICON_MODULE_SET_ID is set and changed when modules change. + * Notification not supported * Yang 1.1 notification support (RFC 7950: Sec 7.16) * Major rewrite of event streams * See clicon_stream.[ch] for details @@ -25,6 +26,8 @@ * Enabled by CLICON_STREAM_DISCOVERY_RFC5277 and CLICON_STREAM_DISCOVERY_RFC8040. ### API changes on existing features (you may need to change your code) +* Netconf hello capability updated to YANG 1.1 RFC7950 Sec 5.6.4 + * A single capability is announced instead of many. * Major rewrite of event streams * If you used old event callbacks API, you need to switch to the streams API * See clixon_stream.[ch] diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 8dac8c7d..18f3f942 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -274,76 +274,6 @@ client_get_streams(clicon_handle h, return retval; } -/*! Get modules state according to RFC 7895 - * @param[in] h Clicon handle - * @param[in] yspec Yang spec - * @param[in] xpath Xpath selection, not used but may be to filter early - * @param[in] module Name of yang module - * @param[in,out] xret Existing XML tree, merge x into this - * @retval -1 Error (fatal) - * @retval 0 OK - * @retval 1 Statedata callback failed - */ -static int -client_get_modules(clicon_handle h, - yang_spec *yspec, - char *xpath, - char *module, - cxobj **xret) -{ - int retval = -1; - cxobj *x = NULL; - cbuf *cb = NULL; - yang_stmt *ylib = NULL; /* ietf-yang-library */ - yang_stmt *yns = NULL; /* namespace */ - yang_stmt *ymod; /* generic module */ - yang_stmt *ys; - - if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){ - clicon_err(OE_YANG, 0, "%s not found", module); - goto done; - } - if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){ - clicon_err(OE_YANG, 0, "%s yang namespace not found", module); - goto done; - } - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, 0, "clicon buffer"); - goto done; - } - cprintf(cb,"", yns->ys_argument); - cprintf(cb,"1"); /* NYI */ - - ymod = NULL; - while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { - cprintf(cb,""); - cprintf(cb,"%s", ymod->ys_argument); - if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL) - cprintf(cb,"%s", ys->ys_argument); - else - cprintf(cb,""); - if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL) - cprintf(cb,"%s", ys->ys_argument); - else - cprintf(cb,""); - cprintf(cb,""); - } - cprintf(cb,""); - - if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ - if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) - goto done; - retval = 1; - goto done; - } - retval = netconf_trymerge(x, yspec, xret); - done: - if (cb) - cbuf_free(cb); - if (x) - xml_free(x); - return retval; -} /*! Get system state-data, including streams and plugins * @param[in] h Clicon handle @@ -375,7 +305,7 @@ client_statedata(clicon_handle h, (retval = client_get_streams(h, yspec, xpath, "ietf-restconf-monitoring", "restconf-state", xret)) != 0) goto done; if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && - (retval = client_get_modules(h, yspec, xpath, "ietf-yang-library", xret)) != 0) + (retval = yang_modules_state_get(h, yspec, xret)) != 0) goto done; if ((retval = clixon_plugin_statedata(h, yspec, xpath, xret)) != 0) goto done; diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 2cc110f2..3d71c2fd 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -735,8 +735,7 @@ main(int argc, if ((yspec = yspec_new()) == NULL) goto done; clicon_dbspec_yang_set(h, yspec); - /* Read and parse application yang specification: either a module and search - * in dir, or a specific file + /* Load main application yang specification: either module or specific file */ if (yang_filename){ if (yang_spec_parse_file(h, yang_filename, clicon_yang_dir(h), yspec) < 0) @@ -747,17 +746,18 @@ main(int argc, clicon_yang_module_revision(h), yspec) < 0) goto done; - - /* Add system modules */ + + /* Load yang module library, RFC7895 */ + if (yang_modules_init(h) < 0) + goto done; + /* Load yang Restconf stream discovery */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") && yang_spec_parse_module(h, "ietf-restconf-monitoring", CLIXON_DATADIR, NULL, yspec)< 0) goto done; + /* Load yang Netconf stream discovery */ if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec)< 0) goto done; - if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && - yang_spec_parse_module(h, "ietf-yang-library", CLIXON_DATADIR, NULL, yspec)< 0) - goto done; /* Set options: database dir and yangspec (could be hidden in connect?)*/ if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0) goto done; diff --git a/apps/netconf/netconf_hello.c b/apps/netconf/netconf_hello.c index 9fc0c2c2..6343a299 100644 --- a/apps/netconf/netconf_hello.c +++ b/apps/netconf/netconf_hello.c @@ -105,28 +105,37 @@ netconf_hello_dispatch(cxobj *xn) return retval; } -/* - * netconf_create_hello - * create capability string (once) +/*! Create Netconf hello. Single cap and defer individual to querying modules + * This follows YANG 1.1 RFC7950 Sec 5.6.4, where a single capability announced + * and a client may query supported modules using RFC 7895 (Yang Module + * Library). + * @param[in] h Clicon handle + * @param[in] cb Msg buffer + * @param[in] session_id Id of client session */ int -netconf_create_hello(cbuf *xf, /* msg buffer */ - int session_id) +netconf_create_hello(clicon_handle h, + cbuf *cb, + int session_id) { - int retval = 0; + int retval = -1; + char *module_set_id; + char *ietf_yang_library_revision; - add_preamble(xf); - cprintf(xf, ""); - cprintf(xf, ""); - cprintf(xf, "urn:ietf:params:xml:ns:netconf:base:1.0\n"); - cprintf(xf, "urn:ietf:params:xml:ns:netconf:capability:candidate:1:0\n"); - cprintf(xf, "urn:ietf:params:xml:ns:netconf:capability:validate:1.0\n"); - cprintf(xf, "urn:ietf:params:netconf:capability:xpath:1.0\n"); - cprintf(xf, "urn:ietf:params:netconf:capability:notification:1.0\n"); - cprintf(xf, "urn:ietf:params:netconf:capability:startup:1.0\n"); - cprintf(xf, ""); - cprintf(xf, "%lu", (long unsigned int)42+session_id); - cprintf(xf, ""); - add_postamble(xf); + module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID"); + if ((ietf_yang_library_revision = yang_modules_revision(h)) == NULL) + goto done; + add_preamble(cb); + cprintf(cb, ""); + cprintf(cb, ""); + cprintf(cb, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=\"%s\"&module-set-id=%s", + ietf_yang_library_revision, + module_set_id); + cprintf(cb, ""); + cprintf(cb, "%lu", (long unsigned int)42+session_id); + cprintf(cb, ""); + add_postamble(cb); + retval = 0; + done: return retval; } diff --git a/apps/netconf/netconf_hello.h b/apps/netconf/netconf_hello.h index 82e235f7..4f877b32 100644 --- a/apps/netconf/netconf_hello.h +++ b/apps/netconf/netconf_hello.h @@ -40,7 +40,7 @@ /* * Prototypes */ -int netconf_create_hello(cbuf *xf, int session_id); +int netconf_create_hello(clicon_handle h, cbuf *cb, int session_id); int netconf_hello_dispatch(cxobj *xn); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 7617beac..7e7a279c 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -241,28 +241,29 @@ netconf_input_cb(int s, return retval; } -/* - * send_hello - * args: s file descriptor to write on (eg 1 - stdout) +/*! Send netconf hello message + * @param[in] h Clicon handle + * @param[in] s File descriptor to write on (eg 1 - stdout) */ static int -send_hello(int s) +send_hello(clicon_handle h, + int s) { - cbuf *xf; - int retval = -1; + int retval = -1; + cbuf *cb; - if ((xf = cbuf_new()) == NULL){ + if ((cb = cbuf_new()) == NULL){ clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__); goto done; } - if (netconf_create_hello(xf, getpid()) < 0) + if (netconf_create_hello(h, cb, getpid()) < 0) goto done; - if (netconf_output(s, xf, "hello") < 0) + if (netconf_output(s, cb, "hello") < 0) goto done; retval = 0; done: - if (xf) - cbuf_free(xf); + if (cb) + cbuf_free(cb); return retval; } @@ -461,7 +462,7 @@ main(int argc, *(argv-1) = tmp; if (!quiet) - send_hello(1); + send_hello(h, 1); if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0) goto done; if (debug) diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index a38cf164..3eaf0681 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -266,6 +266,7 @@ api_yang_library_version(clicon_handle h, cxobj *xt = NULL; cbuf *cb = NULL; int pretty; + char *ietf_yang_library_revision = "2016-06-21"; /* XXX */ clicon_debug(1, "%s", __FUNCTION__); pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); @@ -275,7 +276,7 @@ api_yang_library_version(clicon_handle h, FCGX_SetExitStatus(200, r->out); /* OK */ FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json"); FCGX_FPrintF(r->out, "\r\n"); - if (xml_parse_string("2016-06-21", NULL, &xt) < 0) + if (xml_parse_va(&xt, NULL, "%s", ietf_yang_library_revision) < 0) goto done; if (xml_rootchild(xt, 0, &xt) < 0) goto done; @@ -641,9 +642,9 @@ main(int argc, if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && yang_spec_parse_module(h, "ietf-netconf-notification", CLIXON_DATADIR, NULL, yspec)< 0) goto done; - if (clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895") && - yang_spec_parse_module(h, "ietf-yang-library", CLIXON_DATADIR, NULL, yspec)< 0) - goto done; + /* Load yang module library, RFC7895 */ + if (yang_modules_init(h) < 0) + goto done; if (stream_register(h, "NETCONF", "default NETCONF event stream") < 0) goto done; diff --git a/doc/FAQ.md b/doc/FAQ.md index 11e68214..f5f5ba9f 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -113,6 +113,9 @@ The example below shows enabling a specific feature; enabling all features in mo *:* ``` +Features can be probed by using RFC 7895 Yang module library which provides +information on all modules and which features are enabled. + ## Can I run Clixon as docker containers? Yes, the example works as docker containers as well. There should be a diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index 448387c4..c454835f 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h new file mode 100644 index 00000000..0ca4760c --- /dev/null +++ b/lib/clixon/clixon_yang_module.h @@ -0,0 +1,57 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * Yang module and feature handling + * @see https://tools.ietf.org/html/rfc7895 + */ + +#ifndef _CLIXON_YANG_MODULE_H_ +#define _CLIXON_YANG_MODULE_H_ + +/* + * Constants + */ + +/* + * Types + */ + + +/* + * Prototypes + */ +int yang_modules_init(clicon_handle h); +char *yang_modules_revision(clicon_handle h); +int yang_modules_state_get(clicon_handle h, yang_spec *yspec, cxobj **xret); + +#endif /* _CLIXON_YANG_MODULE_H_ */ diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 8e80f61a..787c19c8 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -69,7 +69,7 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \ clixon_string.c clixon_handle.c \ clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \ - clixon_json.c clixon_yang.c clixon_yang_type.c \ + clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_client.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 2070cda2..1c0584d9 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -255,9 +255,10 @@ clicon_options_main(clicon_handle h) } /*! Check if a clicon option has a value - * @param[in] h clicon_handle - * @param[in] name option name - * @retval + * @param[in] h clicon_handle + * @param[in] name option name + * @retval !=0 option exists + * @retval 0 option does not exists */ int clicon_option_exists(clicon_handle h, diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c new file mode 100644 index 00000000..4acf1f34 --- /dev/null +++ b/lib/src/clixon_yang_module.c @@ -0,0 +1,240 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * Yang module and feature handling + * @see https://tools.ietf.org/html/rfc7895 + */ + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include "clixon_log.h" +#include "clixon_err.h" +#include "clixon_string.h" +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_handle.h" +#include "clixon_file.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_options.h" +#include "clixon_netconf_lib.h" +#include "clixon_yang_module.h" + +/*! Init the Yang module library + * + * Load RFC7895 yang spec, module-set-id, etc. + * @param[in] h Clicon handle + * @note CLIXON_DATADIR is hardcoded + */ +int +yang_modules_init(clicon_handle h) +{ + int retval = -1; + yang_spec *yspec; + + yspec = clicon_dbspec_yang(h); + if (!clicon_option_bool(h, "CLICON_MODULE_LIBRARY_RFC7895")) + goto ok; + /* Ensure module-set-id is set */ + if (!clicon_option_exists(h, "CLICON_MODULE_SET_ID")){ + clicon_err(OE_CFG, ENOENT, "CLICON_MODULE_SET_ID must be defined when CLICON_MODULE_LIBRARY_RFC7895 is enabled"); + goto done; + } + /* Ensure revision exists is set */ + if (yang_spec_parse_module(h, "ietf-yang-library", CLIXON_DATADIR, NULL, yspec)< 0) + goto done; + /* Find revision */ + if (yang_modules_revision(h) == NULL){ + clicon_err(OE_CFG, ENOENT, "Yang client library yang spec has no revision"); + goto done; + } + ok: + retval = 0; + done: + return retval; +} + +/*! Return RFC7895 revision (if parsed) + * @param[in] h Clicon handle + * @retval revision String (dont free) + * @retval NULL Error: RFC7895 not loaded or revision not found + */ +char * +yang_modules_revision(clicon_handle h) +{ + yang_spec *yspec; + yang_stmt *ymod; + yang_stmt *yrev; + char *revision = NULL; + + yspec = clicon_dbspec_yang(h); + if ((ymod = yang_find((yang_node*)yspec, Y_MODULE, "ietf-yang-library")) != NULL){ + if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL){ + revision = yrev->ys_argument; + } + } + return revision; +} + +/*! Get modules state according to RFC 7895 + * @param[in] h Clicon handle + * @param[in] yspec Yang spec + * @param[in,out] xret Existing XML tree, merge x into this + * @retval -1 Error (fatal) + * @retval 0 OK + * @retval 1 Statedata callback failed + * @notes NYI: schema, deviation +x +--ro modules-state +x +--ro module-set-id string +x +--ro module* [name revision] +x +--ro name yang:yang-identifier +x +--ro revision union + +--ro schema? inet:uri +x +--ro namespace inet:uri + +--ro feature* yang:yang-identifier + +--ro deviation* [name revision] + | +--ro name yang:yang-identifier + | +--ro revision union + +--ro conformance-type enumeration + +--ro submodule* [name revision] + +--ro name yang:yang-identifier + +--ro revision union + +--ro schema? inet:uri + */ +int +yang_modules_state_get(clicon_handle h, + yang_spec *yspec, + cxobj **xret) +{ + int retval = -1; + cxobj *x = NULL; + cbuf *cb = NULL; + yang_stmt *ylib = NULL; /* ietf-yang-library */ + yang_stmt *yns = NULL; /* namespace */ + yang_stmt *ymod; /* generic module */ + yang_stmt *ys; + yang_stmt *yc; + char *module_set_id; + char *module = "ietf-yang-library"; + + module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID"); + if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL){ + clicon_err(OE_YANG, 0, "%s not found", module); + goto done; + } + if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){ + clicon_err(OE_YANG, 0, "%s yang namespace not found", module); + goto done; + } + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, 0, "clicon buffer"); + goto done; + } + cprintf(cb,"", yns->ys_argument); + cprintf(cb,"%s", module_set_id); + + ymod = NULL; + while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) { + if (ymod->ys_keyword != Y_MODULE) + continue; + cprintf(cb,""); + cprintf(cb,"%s", ymod->ys_argument); + if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL) + cprintf(cb,"%s", ys->ys_argument); + else + cprintf(cb,""); + if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL) + cprintf(cb,"%s", ys->ys_argument); + else + cprintf(cb,""); + yc = NULL; + while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) { + switch(yc->ys_keyword){ + case Y_FEATURE: + if (yc->ys_cv && cv_bool_get(yc->ys_cv)) + cprintf(cb,"%s", yc->ys_argument); + break; + case Y_SUBMODULE: + cprintf(cb,""); + cprintf(cb,"%s", yc->ys_argument); + if ((ys = yang_find((yang_node*)yc, Y_REVISION, NULL)) != NULL) + cprintf(cb,"%s", ys->ys_argument); + else + cprintf(cb,""); + cprintf(cb,""); + break; + default: + break; + } + } + cprintf(cb,""); + } + cprintf(cb,""); + + if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ + if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) + goto done; + retval = 1; + goto done; + } + retval = netconf_trymerge(x, yspec, xret); + done: + if (cb) + cbuf_free(cb); + if (x) + xml_free(x); + return retval; +} diff --git a/test/test_feature.sh b/test/test_feature.sh index afcb05bb..892c3dbc 100755 --- a/test/test_feature.sh +++ b/test/test_feature.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Yang features. if-feature +# Yang features. if-feature. and schema resources according to RFC7895 APPNAME=example # include err() and new() functions and creates $dir . ./lib.sh @@ -93,6 +93,9 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "foo]]>]]>" '^operation-failedprotocolerrorXML node config/A has no corresponding yang specification \(Invalid XML or wrong Yang spec?' +new "netconf schema resource, RFC 7895" +expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^exampleAietf-inet-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-inet-typesietf-interfaces2014-05-08urn:ietf:params:xml:ns:yang:ietf-interfacesietf-routing2014-10-26urn:ietf:params:xml:ns:yang:ietf-routingrouter-idietf-yang-library2016-06-21urn:ietf:params:xml:ns:yang:ietf-yang-libraryietf-yang-types2013-07-15urn:ietf:params:xml:ns:yang:ietf-yang-types]]>]]>$' + new "Kill backend" # kill backend sudo clixon_backend -zf $cfg diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 26fa1ef3..df5ac83d 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -11,6 +11,7 @@ tmp=$dir/tmp.x cat < $cfg $cfg + 42 /usr/local/share/$APPNAME/yang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend @@ -96,6 +97,9 @@ if [ $? -ne 0 ]; then err fi +new "netconf hello" +expecteof "$clixon_netconf -f $cfg -y $fyang" 0 ']]>]]>' '^urn:ietf:params:netconf:capability:yang-library:1.0\?revision="2016-06-21"\&module-set-id=42' +exit new "netconf get-config" expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 ']]>]]>' '^]]>]]>$' diff --git a/yang/clixon-config@2018-04-30.yang b/yang/clixon-config@2018-04-30.yang index 1535a156..1d39ffb8 100644 --- a/yang/clixon-config@2018-04-30.yang +++ b/yang/clixon-config@2018-04-30.yang @@ -366,6 +366,16 @@ module clixon-config { data. If enabled, module info will appear when doing netconf get or restconf GET"; } + leaf CLICON_MODULE_SET_ID { + type string; + default "0"; + description "If RFC 7895 YANG Module library enabled: + Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } leaf CLICON_STREAM_DISCOVERY_RFC5277 { type boolean; default false;