Feature Request: Support RFC 6022 (NETCONF Monitoring)

* Added capabilities and schema state, and get-schema rpc
* New `clixon-config@2022-11-01.yang` revision
  * Added option:
    * `CLICON_NETCONF_MONITORING`
    * `CLICON_NETCONF_MONITORING_LOCATION`
This commit is contained in:
Olof hagsand 2022-11-22 13:56:20 +01:00
parent 8ebab16c4c
commit c94e9dad67
20 changed files with 1988 additions and 64 deletions

View file

@ -1,6 +1,6 @@
# Clixon Changelog
* [6.0.0](#600) Expected: End of 2022
* [6.0.0](#600) Expected: Nov 2022
* [5.9.0](#590) 24 September 2022
* [5.8.0](#580) 28 July 2022
* [5.7.0](#570) 17 May 2022
@ -38,10 +38,16 @@
* [3.3.1](#331) June 7 2017
## 6.0.0
Expected: End of 2022
Expected: Nov 2022
### New features
* Netconf monitoring
* First part: Capabilities and schema state and get-schema
* Remains: Datastore, sessions and statistics state
* Standards
* RFC 6022 "YANG Module for NETCONF Monitoring"
* See [Feature Request: Support RFC 6022 (NETCONF Monitoring)](https://github.com/clicon/clixon/issues/370)
* Confirmed-commit capability
* Standards
* RFC 4741 "NETCONF Configuration Protocol": Section 8.4
@ -57,6 +63,10 @@ Expected: End of 2022
Users may have to change how they access the system
* New `clixon-config@2022-11-01.yang` revision
* Added option:
* `CLICON_NETCONF_MONITORING`
* `CLICON_NETCONF_MONITORING_LOCATION`
* Added `PRETTYPRINT_INDENT` compile-time option controlling indentation level for XML,JSON and TEXT
* Default value is `3`
* NETCONF: Removed `message-id` from hello protocol following RFC 6241

View file

@ -722,7 +722,7 @@ from_client_lock(clicon_handle h,
yang_stmt *yspec;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9");
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if ((db = netconf_db_find(xe, "target")) == NULL){
@ -1046,6 +1046,109 @@ from_client_create_subscription(clicon_handle h,
return retval;
}
/*! Retrieve a schema from the NETCONF server.
*
* @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 RFC6022, ietf-netconf-monitoring.yang
*/
static int
from_client_get_schema(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
int retval = -1;
cxobj *x; /* Generic xml tree */
cvec *nsc = NULL;
char *identifier = NULL;
char *version = NULL;
char *format = NULL;
yang_stmt *yspec;
yang_stmt *ymod;
yang_stmt *ymatch;
yang_stmt *yrev;
cbuf *cbyang = NULL;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if ((nsc = xml_nsctx_init(NULL, NETCONF_MONITORING_NAMESPACE)) == NULL)
goto done;
if ((x = xpath_first(xe, nsc, "identifier")) == NULL){
if (netconf_missing_element(cbret, "protocol", "identifier", NULL) < 0)
goto done;
goto ok;
}
identifier = xml_body(x);
if ((x = xpath_first(xe, nsc, "version")) != NULL)
version = xml_body(x);
if ((x = xpath_first(xe, nsc, "format")) != NULL)
format = xml_body(x);
ymatch = NULL;
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (yang_keyword_get(ymod) != Y_MODULE &&
yang_keyword_get(ymod) != Y_SUBMODULE)
continue;
if (strcmp(identifier, yang_argument_get(ymod)) != 0)
continue;
if (version){
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) == NULL)
continue;
if (strcmp(version, yang_argument_get(yrev)) != 0)
continue;
ymatch = ymod;
break;
}
else if (ymatch){
/* If more than one schema matches the requested parameters, the
* <error-tag> is 'operation-failed', and <error-app-tag> is
* 'data-not-unique'.
*/
if (netconf_data_not_unique(cbret, NULL, NULL)< 0)
goto done;
goto ok;
}
else
ymatch = ymod;
}
if (ymatch == NULL){
if (netconf_invalid_value(cbret, "protocol", "No such schema") < 0)
goto done;
goto ok;
}
if (format && strcmp(format, "yang") != 0){
if (netconf_invalid_value(cbret, "protocol", "Format not supported") < 0)
goto done;
goto ok;
}
cprintf(cbret, "<rpc-reply xmlns=\"%s\"><data xmlns=\"%s\">",
NETCONF_BASE_NAMESPACE, NETCONF_MONITORING_NAMESPACE);
if ((cbyang = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
yang_print_cbuf(cbyang, ymatch, 0, 0);
xml_chardata_cbuf_append(cbret, cbuf_get(cbyang));
cprintf(cbret, "</data></rpc-reply>");
ok:
retval = 0;
done:
if (cbyang)
cbuf_free(cbyang);
if (nsc)
xml_nsctx_free(nsc);
return retval;
}
/*! Set debug level.
* @param[in] h Clicon handle
* @param[in] xe Request: <rpc><xn></rpc>
@ -1388,6 +1491,10 @@ from_client_msg(clicon_handle h,
goto reply;
}
ce->ce_id = id;
/* As a side-effect, this expands xt with default values according to "report-all"
* This may not be correct, the RFC does not mention expanding default values for
* input RPC
*/
if ((ret = xml_yang_validate_rpc(h, x, 1, &xret)) < 0)
goto done;
if (ret == 0){
@ -1604,6 +1711,10 @@ backend_rpc_init(clicon_handle h)
if (rpc_callback_register(h, from_client_create_subscription, NULL,
EVENT_RFC5277_NAMESPACE, "create-subscription") < 0)
goto done;
/* RFC 6022 */
if (rpc_callback_register(h, from_client_get_schema, NULL,
NETCONF_MONITORING_NAMESPACE, "get-schema") < 0)
goto done;
/* Clixon RPC */
if (rpc_callback_register(h, from_client_debug, NULL,
CLIXON_LIB_NS, "debug") < 0)

View file

@ -251,6 +251,12 @@ get_client_statedata(clicon_handle h,
if (ret == 0)
goto fail;
}
if (clicon_option_bool(h, "CLICON_NETCONF_MONITORING")){
if ((ret = netconf_monitoring_state_get(h, yspec, xpath, nsc, 0, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
/* Use plugin state callbacks */
if ((ret = clixon_plugin_statedata_all(h, yspec, nsc, xpath, xret)) < 0)
goto done;

View file

@ -470,7 +470,7 @@ netconf_notification_cb(int s,
clicon_err(OE_NETCONF, EFAULT, "Notification malformed");
goto done;
}
if ((nsc = xml_nsctx_init(NULL, NOTIFICATION_RFC5277_NAMESPACE)) == NULL)
if ((nsc = xml_nsctx_init(NULL, NETCONF_NOTIFICATION_NAMESPACE)) == NULL)
goto done;
if ((xn = xpath_first(xt, nsc, "notification")) == NULL)
goto ok;

View file

@ -84,6 +84,7 @@ extern "C" {
#include <clixon/clixon_xml_sort.h>
#include <clixon/clixon_yang_parse_lib.h>
#include <clixon/clixon_yang_module.h>
#include <clixon/clixon_netconf_monitoring.h>
#include <clixon/clixon_stream.h>
#include <clixon/clixon_proto.h>
#include <clixon/clixon_netconf_lib.h>

View file

@ -69,6 +69,28 @@
*/
#define YANG_XML_NAMESPACE "urn:ietf:params:xml:ns:yang:1"
/* RFC 6022 YANG Module for NETCONF Monitoring
*/
#define NETCONF_MONITORING_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
/* Default STREAM namespace (see rfc5277 3.1)
* From RFC8040:
* The structure of the event data is based on the <notification>
* element definition in Section 4 of [RFC5277]. It MUST conform to the
* schema for the <notification> element in Section 4 of [RFC5277],
* using the XML namespace as defined in the XSD as follows:
* urn:ietf:params:xml:ns:netconf:notification:1.0
* It is used everywhere in yangmodels, but not in openconfig
*/
#define NETCONF_NOTIFICATION_NAMESPACE "urn:ietf:params:xml:ns:netconf:notification:1.0"
#define NETCONF_NOTIFICATION_CAPABILITY "urn:ietf:params:netconf:capability:notification:1.0"
/*
* Then there is also this namespace that is only used in RFC5277 seems to be for "netconf"
* events. The usage seems wrong here,...
*/
#define EVENT_RFC5277_NAMESPACE "urn:ietf:params:xml:ns:netmod:notification"
/*
* Types
*/
@ -152,6 +174,7 @@ int netconf_operation_failed(cbuf *cb, char *type, char *message);
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
int netconf_malformed_message(cbuf *cb, char *message);
int netconf_malformed_message_xml(cxobj **xret, char *message);
int netconf_data_not_unique(cbuf *cb, cxobj *x, cvec *cvk);
int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
int netconf_minmax_elements_xml(cxobj **xret, cxobj *xp, char *name, int max);
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
@ -161,6 +184,7 @@ char *netconf_db_find(cxobj *xn, char *name);
int netconf_err2cb(cxobj *xerr, cbuf *cberr);
const netconf_content netconf_content_str2int(char *str);
const char *netconf_content_int2str(netconf_content nr);
int netconf_capabilites(clicon_handle h, cbuf *cb);
int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id);
int netconf_hello_req(clicon_handle h, cbuf *cb);
int clixon_netconf_error_fn(const char *fn, const int line, cxobj *xerr, const char *fmt, const char *arg);

View file

@ -0,0 +1,46 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
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 *****
* RFC 6022 YANG Module for NETCONF Monitoring
*/
#ifndef _CLIXON_NETCONF_MONITORING_H_
#define _CLIXON_NETCONF_MONITORING_H_
/*
* Prototypes
*/
int netconf_monitoring_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, int brief, cxobj **xret);
#endif /* _CLIXON_NETCONF_MONITORING_H_ */

View file

@ -41,22 +41,6 @@
/*
* Constants
*/
/* Default STREAM namespace (see rfc5277 3.1)
* From RFC8040:
* The structure of the event data is based on the <notification>
* element definition in Section 4 of [RFC5277]. It MUST conform to the
* schema for the <notification> element in Section 4 of [RFC5277],
* using the XML namespace as defined in the XSD as follows:
* urn:ietf:params:xml:ns:netconf:notification:1.0
* It is used everywhere in yangmodels, but not in openconfig
*/
#define NOTIFICATION_RFC5277_NAMESPACE "urn:ietf:params:xml:ns:netconf:notification:1.0"
/*
* Then there is also this namespace that is only used in RFC5277 seems to be for "netconf"
* events. The usage seems wrong here,...
*/
#define EVENT_RFC5277_NAMESPACE "urn:ietf:params:xml:ns:netmod:notification"
/*
* Types

View file

@ -82,7 +82,7 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_regex.c clixon_handle.c clixon_file.c \
clixon_xml.c clixon_xml_io.c clixon_xml_sort.c clixon_xml_map.c clixon_xml_vec.c \
clixon_xml_bind.c clixon_json.c clixon_proc.c \
clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_netconf_monitoring.c \
clixon_yang_parse_lib.c clixon_yang_sub_parse.c \
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
clixon_path.c clixon_validate.c clixon_validate_minmax.c \

View file

@ -1245,7 +1245,7 @@ netconf_operation_failed_xml(cxobj **xret,
return retval;
}
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
/*! Create Netconf malformed-message error cbuf according to RFC 6241 App A
*
* A message could not be handled because it failed to be parsed correctly.
* For example, the message is not well-formed XML or it uses an
@ -1332,6 +1332,35 @@ netconf_malformed_message_xml(cxobj **xret,
return retval;
}
/*! Create Netconf data-not-unique error message according to RFC 7950 15.1
*
* A NETCONF operation would result in configuration data where a
* "unique" constraint is invalidated.
* @param[out] xret Error XML tree. Free with xml_free after use
* @param[in] x List element containing duplicate
* @param[in] cvk List of components in x that are non-unique
* @see RFC7950 Sec 15.1
* @see netconf_data_not_unique_xml Same but returns XML tree
*/
int
netconf_data_not_unique(cbuf *cb,
cxobj *x,
cvec *cvk)
{
int retval = -1;
cxobj *xret = NULL;
if (netconf_data_not_unique_xml(&xret, x, cvk) < 0)
goto done;
if (clixon_xml2cbuf(cb, xret, 0, 0, -1, 0) < 0)
goto done;
retval = 0;
done:
if (xret)
xml_free(xret);
return retval;
}
/*! Create Netconf data-not-unique error message according to RFC 7950 15.1
*
* A NETCONF operation would result in configuration data where a
@ -1714,22 +1743,8 @@ netconf_content_int2str(netconf_content nr)
return clicon_int2str(netconf_content_map, nr);
}
/*! Create Netconf server hello. Single cap and defer individual to querying modules
* @param[in] h Clicon handle
* @param[in] cb Msg buffer
* @param[in] session_id Id of client session
* Lots of dependencies here. regarding the hello protocol.
* RFC6241 NETCONF Protocol says: (8.1)
* MUST send a <hello> element containing a list of that peer's capabilities
* MUST send at least the base NETCONF capability, urn:ietf:params:netconf:base:1.1
* MAY include capabilities for previous NETCONF versions
* A server MUST include a <session-id>
* A client MUST NOT include a <session-id>
* A server receiving <session-id> MUST terminate the NETCONF session.
* A client not receiving <session-id> MUST terminate w/o sending<close-session>
* the example shows urn:ietf:params:netconf:capability:startup:1.0
/*! List capabilities
*
* RFC5277 NETCONF Event Notifications
* urn:ietf:params:netconf:capability:notification:1.0 is advertised during the capability exchange
*
@ -1750,29 +1765,18 @@ netconf_content_int2str(netconf_content nr)
* urn:ietf:params:netconf:capability:startup:1.0 (8.7)
* urn:ietf:params:netconf:capability:xpath:1.0 (8.9)
* urn:ietf:params:netconf:capability:notification:1.0 (RFC5277)
*
* @note the hello message is created bythe netconf application, not the
* backend, and backend may implement more modules - please consider if using
* library routines for detecting capabilities here. In contrast, yang module
* list (RFC7895) is processed by the backend.
* @note If you add new, remember to encode bodies if needed, see xml_chardata_encode()
* @note
* @see yang_modules_state_get
* @see netconf_module_load
*/
int
netconf_hello_server(clicon_handle h,
cbuf *cb,
uint32_t session_id)
netconf_capabilites(clicon_handle h,
cbuf *cb)
{
int retval = -1;
char *module_set_id;
char *ietf_yang_library_revision;
char *encstr = NULL;
char *ietf_yang_library_revision;
yang_stmt *yspec = clicon_dbspec_yang(h);
char *module_set_id;
module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID");
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(cb, "<capabilities>");
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0){
/* Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1"
@ -1810,20 +1814,59 @@ netconf_hello_server(clicon_handle h,
xml_chardata_cbuf_append(cb, "urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all,trim,report-all-tagged");
cprintf(cb, "</capability>");
/* RFC5277 Notification Capability */
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>");
cprintf(cb, "<capability>%s</capability>", NETCONF_NOTIFICATION_CAPABILITY);
/* It is somewhat arbitrary why some features/capabilities are hardocded and why some are not
* rfc 6241 Sec 8.4 confirmed-commit capabilities */
if (if_feature(yspec, "ietf-netconf", "confirmed-commit"))
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>");
cprintf(cb, "</capabilities>");
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
/*! Create Netconf server hello. Single cap and defer individual to querying modules
* @param[in] h Clicon handle
* @param[in] cb Msg buffer
* @param[in] session_id Id of client session
* Lots of dependencies here. regarding the hello protocol.
* RFC6241 NETCONF Protocol says: (8.1)
* MUST send a <hello> element containing a list of that peer's capabilities
* MUST send at least the base NETCONF capability, urn:ietf:params:netconf:base:1.1
* MAY include capabilities for previous NETCONF versions
* A server MUST include a <session-id>
* A client MUST NOT include a <session-id>
* A server receiving <session-id> MUST terminate the NETCONF session.
* A client not receiving <session-id> MUST terminate w/o sending<close-session>
* the example shows urn:ietf:params:netconf:capability:startup:1.0
*
* @note the hello message is created bythe netconf application, not the
* backend, and backend may implement more modules - please consider if using
* library routines for detecting capabilities here. In contrast, yang module
* list (RFC7895) is processed by the backend.
* @note If you add new, remember to encode bodies if needed, see xml_chardata_encode()
* @note
* @see yang_modules_state_get
* @see netconf_module_load
*/
int
netconf_hello_server(clicon_handle h,
cbuf *cb,
uint32_t session_id)
{
int retval = -1;
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
if (netconf_capabilites(h, cb) < 0)
goto done;
if (session_id)
cprintf(cb, "<session-id>%lu</session-id>", (long unsigned int)session_id);
cprintf(cb, "</hello>");
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}

View file

@ -0,0 +1,179 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
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 *****
* RFC 6022 YANG Module for NETCONF Monitoring
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_io.h"
#include "clixon_netconf_lib.h"
#include "clixon_options.h"
#include "clixon_err.h"
#include "clixon_netconf_monitoring.h"
/*!
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in,out] cb CLIgen buffer
* @retval -1 Error (fatal)
* @retval 0 OK
*/
static int
yang_modules(clicon_handle h,
yang_stmt *yspec,
cbuf *cb)
{
int retval = -1;
yang_stmt *ym = NULL;
yang_stmt *y1;
char *identifier;
char *revision;
char *dir;
cprintf(cb, "<schemas>");
while ((ym = yn_each(yspec, ym)) != NULL) {
cprintf(cb, "<schema>");
identifier = yang_argument_get(ym);
cprintf(cb, "<identifier>%s</identifier>", identifier);
cprintf(cb, "<version>");
revision = NULL;
if ((y1 = yang_find(ym, Y_REVISION, NULL)) != NULL){
revision = yang_argument_get(y1);
cprintf(cb, "%s", revision);
}
cprintf(cb, "</version>");
cprintf(cb, "<format>yang</format>");
if ((y1 = yang_find(ym, Y_NAMESPACE, NULL)) != NULL){
cprintf(cb, "<namespace>%s</namespace>", yang_argument_get(y1));
}
/* A local implementation may have other locations, how to configure? */
cprintf(cb, "<location>NETCONF</location>");
if ((dir = clicon_option_str(h,"CLICON_NETCONF_MONITORING_LOCATION")) != NULL){
if (revision)
cprintf(cb, "<location>%s/%s@%s.yang</location>", dir, identifier, revision);
else
cprintf(cb, "<location>%s/%s.yang</location>", dir, identifier);
}
cprintf(cb, "</schema>");
}
cprintf(cb, "</schemas>");
retval = 0;
//done:
return retval;
}
/*! Get modules state according to RFC 7895
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath
* @param[in] brief Just name, revision and uri (no cache)
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 Statedata callback failed
* @retval 1 OK
* 2.1
* netconf-state
* /capabilities
* /datastores
* /schemas
* /sessions
* /statistics
*/
int
netconf_monitoring_state_get(clicon_handle h,
yang_stmt *yspec,
char *xpath,
cvec *nsc,
int brief,
cxobj **xret)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* capabilities 2.1.1 */
cprintf(cb, "<netconf-state xmlns=\"%s\">", NETCONF_MONITORING_NAMESPACE);
if (netconf_capabilites(h, cb) < 0)
goto done;
/* datastores 2.1.2 */
// XXX
/* schemas 2.1.3 */
if (yang_modules(h, yspec, cb) < 0)
goto done;
/* sessions 2.1.4 */
// XXX
/* statistics 2.1.5 */
// XXX
cprintf(cb, "</netconf-state>");
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0)
goto done;
retval = 1;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
return retval;
// fail:
// retval = 0;
// goto done;
}

View file

@ -80,6 +80,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_io.h"
#include "clixon_netconf_lib.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
@ -585,7 +586,7 @@ stream_notify(clicon_handle h,
}
/* From RFC5277 */
cprintf(cb, "<notification xmlns=\"%s\"><eventTime>%s</eventTime>%s</notification>",
NOTIFICATION_RFC5277_NAMESPACE, timestr, str);
NETCONF_NOTIFICATION_NAMESPACE, timestr, str);
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &xev, NULL) < 0)
goto done;
if (xml_rootchild(xev, 0, &xev) < 0)
@ -649,7 +650,7 @@ stream_notify_xml(clicon_handle h,
goto done;
}
cprintf(cb, "<notification xmlns=\"%s\"><eventTime>%s</eventTime>NULL</notification>",
NOTIFICATION_RFC5277_NAMESPACE,
NETCONF_NOTIFICATION_NAMESPACE,
timestr); /* XXX str is always NULL */
if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, yspec, &xev, NULL) < 0)
goto done;

View file

@ -28,6 +28,7 @@ cat <<EOF > $cfg
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
<CLICON_STREAM_DISCOVERY_RFC8040>false</CLICON_STREAM_DISCOVERY_RFC8040>
<CLICON_NETCONF_MONITORING>false</CLICON_NETCONF_MONITORING>
</clixon-config>
EOF

View file

@ -41,6 +41,7 @@ cat <<EOF > $cfg
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_STREAM_DISCOVERY_RFC8040>false</CLICON_STREAM_DISCOVERY_RFC8040>
<CLICON_NETCONF_MONITORING>false</CLICON_NETCONF_MONITORING>
</clixon-config>
EOF

105
test/test_netconf_monitoring.sh Executable file
View file

@ -0,0 +1,105 @@
#!/usr/bin/env bash
# Test for RFC6022 YANG Module for NETCONF Monitoring
# See eg Examples:
# 4.1. Retrieving Schema List via <get> Operation
# 4.2. Retrieving Schema Instances
# 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_yang.xml
fyang=$dir/clixon-example@2022-01-01.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2022-01-01;
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
new "wait backend"
wait_backend
new "Retrieving all state via <get> operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get/></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability>.*</capabilities><schemas>.*</schemas></netconf-state></data></rpc-reply>" ""
# 4.1. Retrieving Schema List via <get> Operation
new "Retrieving Schema List via <get> Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas/></netconf-state></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema><identifier>clixon-example</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location></schema>.*</schemas></netconf-state></data></rpc-reply>"
# 4.2. Retrieving Schema Instances
# From 2b. bar, version 2008-06-1 in YANG format, via get-schema
new "Retrieving clixon-example schema instance using id, version, format"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2022-01-01</version><format>yang</format></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module clixon-example {yang-version 1.1;namespace urn:example:clixon;prefix ex;revision 2022-01-01;}</data></rpc-reply>"
new "Retrieving clixon-example schema instance using id, version only"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2022-01-01</version></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module clixon-example {yang-version 1.1;namespace urn:example:clixon;prefix ex;revision 2022-01-01;}</data></rpc-reply>"
new "Retrieving clixon-example schema instance using id only"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module clixon-example {yang-version 1.1;namespace urn:example:clixon;prefix ex;revision 2022-01-01;}</data></rpc-reply>"
new "Retrieving ietf-inet-types schema"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>ietf-inet-types</identifier></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module ietf-inet-types {namespace urn:ietf:params:xml:ns:yang:ietf-
inet-types;prefix inet"
# Negative tests
new "get-schema: no id"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"></get-schema></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>identifier</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable of get-schema in module ietf-netconf-monitoring</error-message></rpc-error></rpc-reply>"
new "get-schema: non-existing schema"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>not-found</identifier></get-schema></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such schema</error-message></rpc-error></rpc-reply>"
new "get-schema: non-existing format"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2022-01-01</version><format>xsd</format></get-schema></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>Format not supported</error-message></rpc-error></rpc-reply>"
new "get-schema: non-existing date"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2013-01-01</version><format>yang</format></get-schema></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>No such schema</error-message></rpc-error></rpc-reply>"
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
fi
rm -rf $dir
new "endtest"
endtest

View file

@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Test for RFC6022 YANG Module for NETCONF Monitoring
# Tests the location scheme by using the clixon http-data feature
# 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_yang.xml
fyang=$dir/clixon-example@2022-01-01.yang
# Proper test setup
datapath=/data
wdir=$dir/www
RESTCONFIG=$(restconf_config none false $RCPROTO $enable)
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
<CLICON_FEATURE>clixon-restconf:http-data</CLICON_FEATURE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_HTTP_DATA_PATH>$datapath</CLICON_HTTP_DATA_PATH>
<CLICON_HTTP_DATA_ROOT>$wdir</CLICON_HTTP_DATA_ROOT>
<CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING>
<CLICON_NETCONF_MONITORING_LOCATION>$RCPROTO://localhost/www</CLICON_NETCONF_MONITORING_LOCATION>
$RESTCONFIG
</clixon-config>
EOF
cat <<EOF > $dir/clixon-example@2022-01-01.yang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2022-01-01;
}
EOF
# Same via HTTP
cat <<EOF > $dir/www/data/clixon-example@2022-01-01.yang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2022-01-01;
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
new "wait backend"
wait_backend
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
stop_restconf_pre
new "start restconf daemon"
start_restconf -f $cfg
fi
new "wait restconf"
wait_restconf $RCPROTO
new "Retrieving Schema List via <get> Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas/></netconf-state></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><schemas><schema><identifier>clixon-example</identifier><version>2022-01-01</version><format>yang</format><namespace>urn:example:clixon</namespace><location>NETCONF</location><location>$RCPROTO://localhost/www/clixon-example@2022-01-01.yang</location></schema>.*</schemas></netconf-state></data></rpc-reply>"
# 4.2. Retrieving Schema Instances
# From 2b. bar, version 2008-06-1 in YANG format, via get-schema
new "Get clixon-example schema via http location"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $RCPROTO://localhost/www/clixon-example@2022-01-01.yang)" 0 "HTTP/$HVER 404"
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
fi
rm -rf $dir
new "endtest"
endtest

View file

@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Test for RFC6022 YANG Module for NETCONF Monitoring for multiple schemas
# Clixon supports multiple schemas only in the case of specific upgrade scenarios
# The following is made to check multipel schemas:
# 1. Two revisions of clixon-example.yang in MAIN_DIR
# 2. MODSTATE and CHECKOLD is true and STARTUP enabled
# 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_yang.xml
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_XMLDB_UPGRADE_CHECKOLD>true</CLICON_XMLDB_UPGRADE_CHECKOLD>
<CLICON_XMLDB_MODSTATE>true</CLICON_XMLDB_MODSTATE>
<CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING>
</clixon-config>
EOF
# Double yang specs to get two revisions
cat <<EOF > $dir/clixon-example@2000-01-01.yang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2000-01-01;
}
EOF
cat <<EOF > $dir/clixon-example@2022-01-01.yang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2022-01-01;
}
EOF
# Just to get multi
cat <<EOF > $dir/startup_db
<${DATASTORE_TOP}>
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
<module-set>
<name>default</name>
<content-id>42</content-id>
<module>
<name>clixon-example</name>
<revision>2000-01-01</revision>
<namespace>urn:example:clixon</namespace>
</module>
</module-set>
</yang-library>
</${DATASTORE_TOP}>
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg
fi
new "wait backend"
wait_backend
new "get-schema: multiple schemas, fail"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier></get-schema></rpc>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-app-tag>data-not-unique</error-app-tag><error-severity>error</error-severity></rpc-error></rpc-reply>"
new "get-schema: multiple schemas 2000-01-01"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2000-01-01</version></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module clixon-example {yang-version 1.1;namespace urn:example:clixon;prefix ex;revision 2000-01-01;}</data></rpc-reply>"
new "get-schema: multiple schemas 2022-01-01"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-schema xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\"><identifier>clixon-example</identifier><version>2022-01-01</version></get-schema></rpc>" "<rpc-reply $DEFAULTNS><data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">module clixon-example {yang-version 1.1;namespace urn:example:clixon;prefix ex;revision 2022-01-01;}</data></rpc-reply>"
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
fi
rm -rf $dir
new "endtest"
endtest

View file

@ -146,6 +146,7 @@ function testrun()
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
<CLICON_YANG_UNKNOWN_ANYDATA>$unknown</CLICON_YANG_UNKNOWN_ANYDATA>
<CLICON_STREAM_DISCOVERY_RFC8040>false</CLICON_STREAM_DISCOVERY_RFC8040>
<CLICON_NETCONF_MONITORING>false</CLICON_NETCONF_MONITORING>
$F
$RESTCONFIG
</clixon-config>

View file

@ -42,7 +42,7 @@ datarootdir = @datarootdir@
YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in
YANGSPECS = clixon-config@2022-03-21.yang # 5.7
YANGSPECS = clixon-config@2022-11-01.yang # 6.0
YANGSPECS += clixon-lib@2021-12-05.yang # 5.5
YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang

File diff suppressed because it is too large Load diff