* YANG schema mount RFC 8528, Initial commit (work in progress)
* Keep track of YANG unknowns with ys_cvec of EXTENSION * C-API: Init ys_cvec to NULL, added yang_cvec_add() and adjusted code to use it
This commit is contained in:
parent
8451a20db7
commit
b3dcee9639
21 changed files with 552 additions and 79 deletions
|
|
@ -43,7 +43,12 @@ Expected: beginning of 2023
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
|
|
||||||
* Netconf monitoring, part 2
|
* YANG schema mount RFC 8528 (work in progress)
|
||||||
|
* Restrictions:
|
||||||
|
* only schema-ref=inline, not shared-schema
|
||||||
|
* Standards: RFC 8528
|
||||||
|
* Enable `YANG_SCHEMA_MOUNT`
|
||||||
|
* Netconf monitoring RFC 6022 , part 2
|
||||||
* Datastores and sessions
|
* Datastores and sessions
|
||||||
* Added clixon-specific transport identities: cli, snmp, netconf, restconf
|
* Added clixon-specific transport identities: cli, snmp, netconf, restconf
|
||||||
* Added source-host fro native restonf, but no other transports
|
* Added source-host fro native restonf, but no other transports
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,19 @@ get_client_statedata(clicon_handle h,
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
#ifdef YANG_SCHEMA_MOUNT
|
||||||
|
if ((ret = schema_mounts_state_get(h, yspec, xpath, nsc, xret, &xerr)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0){
|
||||||
|
if (clixon_netconf_internal_error(xerr, " . Internal error, schema_mounts_state_get returned invalid XML", NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
if (*xret)
|
||||||
|
xml_free(*xret);
|
||||||
|
*xret = xerr;
|
||||||
|
xerr = NULL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
/* Use plugin state callbacks */
|
/* Use plugin state callbacks */
|
||||||
if ((ret = clixon_plugin_statedata_all(h, yspec, nsc, xpath, wdef, xret)) < 0)
|
if ((ret = clixon_plugin_statedata_all(h, yspec, nsc, xpath, wdef, xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -266,12 +266,6 @@ expand_dbvar(void *h,
|
||||||
/* Transform api-path to xpath for netconf */
|
/* Transform api-path to xpath for netconf */
|
||||||
if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
|
if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (nsc != NULL){
|
|
||||||
cvec_free(nsc);
|
|
||||||
nsc = NULL;
|
|
||||||
}
|
|
||||||
if (xml_nsctx_yang(y, &nsc) < 0)
|
|
||||||
goto done;
|
|
||||||
if ((cbxpath = cbuf_new()) == NULL){
|
if ((cbxpath = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@ match_list_keys(yang_stmt *y,
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
switch (yang_keyword_get(y)){
|
switch (yang_keyword_get(y)){
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
cvk = yang_cvec_get(y); /* Use Y_LIST cache, see ys_populate_list() */
|
if ((cvk = yang_cvec_get(y)) == NULL) /* Use Y_LIST cache, see ys_populate_list() */
|
||||||
|
break;
|
||||||
cvi = NULL;
|
cvi = NULL;
|
||||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||||
keyname = cv_string_get(cvi);
|
keyname = cv_string_get(cvi);
|
||||||
|
|
|
||||||
|
|
@ -900,7 +900,8 @@ snmp_yang2xpath_cb(yang_stmt *ys,
|
||||||
}
|
}
|
||||||
switch (yang_keyword_get(ys)){
|
switch (yang_keyword_get(ys)){
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */
|
if ((cvk = yang_cvec_get(ys)) == NULL) /* Use Y_LIST cache, see ys_populate_list() */
|
||||||
|
break;
|
||||||
/* Iterate over individual keys */
|
/* Iterate over individual keys */
|
||||||
assert(keyvec && cvec_len(cvk) == cvec_len(keyvec));
|
assert(keyvec && cvec_len(cvk) == cvec_len(keyvec));
|
||||||
for (i=0; i<cvec_len(cvk); i++){
|
for (i=0; i<cvec_len(cvk); i++){
|
||||||
|
|
|
||||||
|
|
@ -191,3 +191,9 @@
|
||||||
* To keep the previous behavior (as in 6.0) set this option with #define
|
* To keep the previous behavior (as in 6.0) set this option with #define
|
||||||
*/
|
*/
|
||||||
#undef NETCONF_DEFAULT_RETRIEVAL_REPORT_ALL
|
#undef NETCONF_DEFAULT_RETRIEVAL_REPORT_ALL
|
||||||
|
|
||||||
|
/*! Development option for RFC 8528 YANG schema mount
|
||||||
|
* Work-in-progress
|
||||||
|
* See also test/test_yang_schema_mount.sh
|
||||||
|
*/
|
||||||
|
#undef YANG_SCHEMA_MOUNT
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ extern "C" {
|
||||||
#include <clixon/clixon_xml_sort.h>
|
#include <clixon/clixon_xml_sort.h>
|
||||||
#include <clixon/clixon_yang_parse_lib.h>
|
#include <clixon/clixon_yang_parse_lib.h>
|
||||||
#include <clixon/clixon_yang_module.h>
|
#include <clixon/clixon_yang_module.h>
|
||||||
|
#include <clixon/clixon_yang_schema_mount.h>
|
||||||
#include <clixon/clixon_netconf_monitoring.h>
|
#include <clixon/clixon_netconf_monitoring.h>
|
||||||
#include <clixon/clixon_stream.h>
|
#include <clixon/clixon_stream.h>
|
||||||
#include <clixon/clixon_proto.h>
|
#include <clixon/clixon_proto.h>
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,8 @@ typedef int (plgdaemon_t)(clicon_handle); /* Plugin pre/post daemon
|
||||||
*/
|
*/
|
||||||
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
||||||
|
|
||||||
/* For yang extension handling.
|
/* For yang extension/unknown handling.
|
||||||
* Called at parsing of yang module containing a statement of an extension.
|
* Called at parsing of yang module containing an unknown statement of an extension.
|
||||||
* A plugin may identify the extension by its name, and perform actions
|
* A plugin may identify the extension by its name, and perform actions
|
||||||
* on the yang statement, such as transforming the yang.
|
* on the yang statement, such as transforming the yang.
|
||||||
* A callback is made for every statement, which means that several calls per
|
* A callback is made for every statement, which means that several calls per
|
||||||
|
|
@ -294,7 +294,7 @@ struct clixon_plugin_api{
|
||||||
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
||||||
plgstart_t *ca_start; /* Plugin start */
|
plgstart_t *ca_start; /* Plugin start */
|
||||||
plgexit_t *ca_exit; /* Plugin exit */
|
plgexit_t *ca_exit; /* Plugin exit */
|
||||||
plgextension_t *ca_extension; /* Yang extension handler */
|
plgextension_t *ca_extension; /* Yang extension/unknown handler */
|
||||||
union {
|
union {
|
||||||
struct { /* cli-specific */
|
struct { /* cli-specific */
|
||||||
cli_prompthook_t *ci_prompt; /* Prompt hook */
|
cli_prompthook_t *ci_prompt; /* Prompt hook */
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,9 @@ typedef enum yang_bind yang_bind;
|
||||||
typedef struct xml cxobj; /* struct defined in clicon_xml.c */
|
typedef struct xml cxobj; /* struct defined in clicon_xml.c */
|
||||||
|
|
||||||
/*! Callback function type for xml_apply
|
/*! Callback function type for xml_apply
|
||||||
|
*
|
||||||
|
* @param[in] x XML node
|
||||||
|
* @param[in] arg General-purpose argument
|
||||||
* @retval -1 Error, aborted at first error encounter, return -1 to end user
|
* @retval -1 Error, aborted at first error encounter, return -1 to end user
|
||||||
* @retval 0 OK, continue
|
* @retval 0 OK, continue
|
||||||
* @retval 1 Abort, dont continue with others, return 1 to end user
|
* @retval 1 Abort, dont continue with others, return 1 to end user
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ int modstate_diff_free(modstate_diff_t *);
|
||||||
|
|
||||||
int yang_modules_init(clicon_handle h);
|
int yang_modules_init(clicon_handle h);
|
||||||
char *yang_modules_revision(clicon_handle h);
|
char *yang_modules_revision(clicon_handle h);
|
||||||
|
int yang_modules_state_build(clicon_handle h, yang_stmt *yspec, char *msid, int brief, cbuf *cb);
|
||||||
int yang_modules_state_get(clicon_handle h, yang_stmt *yspec, char *xpath,
|
int yang_modules_state_get(clicon_handle h, yang_stmt *yspec, char *xpath,
|
||||||
cvec *nsc, int brief, cxobj **xret);
|
cvec *nsc, int brief, cxobj **xret);
|
||||||
int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb);
|
int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb);
|
||||||
|
|
|
||||||
55
lib/clixon/clixon_yang_schema_mount.h
Normal file
55
lib/clixon/clixon_yang_schema_mount.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2023 Olof Hagsand
|
||||||
|
|
||||||
|
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 *****
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CLIXON_YANG_SCHEMA_MOUNT_H_
|
||||||
|
#define _CLIXON_YANG_SCHEMA_MOUNT_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* RFC 8528 YANG Schema Mount
|
||||||
|
*/
|
||||||
|
#define YANG_SCHEMA_MOUNT_NAMESPACE "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prototypes
|
||||||
|
*/
|
||||||
|
int yang_schema_mount_point(yang_stmt *y);
|
||||||
|
|
||||||
|
int schema_mounts_state_get(clicon_handle h, yang_stmt *yspec, char *xpath, cvec *nsc, cxobj **xret, cxobj **xerr);
|
||||||
|
int yang_schema_unknown(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
|
||||||
|
|
||||||
|
#endif /* _CLIXON_YANG_SCHEMA_MOUNT_H_ */
|
||||||
|
|
@ -84,7 +84,8 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
|
||||||
clixon_xml_default.c clixon_xml_bind.c clixon_json.c clixon_proc.c \
|
clixon_xml_default.c clixon_xml_bind.c clixon_json.c clixon_proc.c \
|
||||||
clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_netconf_monitoring.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_parse_lib.c clixon_yang_sub_parse.c \
|
||||||
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
|
clixon_yang_cardinality.c clixon_yang_schema_mount.c \
|
||||||
|
clixon_xml_changelog.c clixon_xml_nsctx.c \
|
||||||
clixon_path.c clixon_validate.c clixon_validate_minmax.c \
|
clixon_path.c clixon_validate.c clixon_validate_minmax.c \
|
||||||
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
|
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
|
||||||
clixon_proto.c clixon_proto_client.c \
|
clixon_proto.c clixon_proto_client.c \
|
||||||
|
|
|
||||||
|
|
@ -527,6 +527,7 @@ text_modify(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x1name = xml_name(x1);
|
x1name = xml_name(x1);
|
||||||
|
|
||||||
if (yang_keyword_get(y0) == Y_LEAF_LIST ||
|
if (yang_keyword_get(y0) == Y_LEAF_LIST ||
|
||||||
yang_keyword_get(y0) == Y_LEAF){
|
yang_keyword_get(y0) == Y_LEAF){
|
||||||
/* This is a check that a leaf does not have sub-elements
|
/* This is a check that a leaf does not have sub-elements
|
||||||
|
|
@ -897,6 +898,7 @@ text_modify(clicon_handle h,
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x0c = x0vec[i++];
|
x0c = x0vec[i++];
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
|
|
||||||
yc = yang_find_datanode(y0, x1cname);
|
yc = yang_find_datanode(y0, x1cname);
|
||||||
if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t,
|
if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t,
|
||||||
yc, op,
|
yc, op,
|
||||||
|
|
@ -963,10 +965,8 @@ text_modify(clicon_handle h,
|
||||||
|
|
||||||
/*! Modify a top-level base tree x0 with modification tree x1
|
/*! Modify a top-level base tree x0 with modification tree x1
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
* @param[in] x0t Base xml tree (can be NULL in add scenarios)
|
||||||
* @param[in] x0t Top level of existing tree, eg needed for NACM rules
|
* @param[in] x1t XML tree which modifies base
|
||||||
* @param[in] x1 XML tree which modifies base
|
|
||||||
* @param[in] x1t Request root node (nacm needs this)
|
|
||||||
* @param[in] yspec Top-level yang spec (if y is NULL)
|
* @param[in] yspec Top-level yang spec (if y is NULL)
|
||||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
||||||
* @param[in] username User name of requestor for nacm
|
* @param[in] username User name of requestor for nacm
|
||||||
|
|
@ -980,9 +980,7 @@ text_modify(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
text_modify_top(clicon_handle h,
|
text_modify_top(clicon_handle h,
|
||||||
cxobj *x0,
|
|
||||||
cxobj *x0t,
|
cxobj *x0t,
|
||||||
cxobj *x1,
|
|
||||||
cxobj *x1t,
|
cxobj *x1t,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
enum operation_type op,
|
enum operation_type op,
|
||||||
|
|
@ -1002,7 +1000,7 @@ text_modify_top(clicon_handle h,
|
||||||
char *createstr = NULL;
|
char *createstr = NULL;
|
||||||
|
|
||||||
/* Check for operations embedded in tree according to netconf */
|
/* Check for operations embedded in tree according to netconf */
|
||||||
if ((ret = attr_ns_value(x1,
|
if ((ret = attr_ns_value(x1t,
|
||||||
"operation", NETCONF_BASE_NAMESPACE,
|
"operation", NETCONF_BASE_NAMESPACE,
|
||||||
cbret, &opstr)) < 0)
|
cbret, &opstr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1011,25 +1009,25 @@ text_modify_top(clicon_handle h,
|
||||||
if (opstr != NULL)
|
if (opstr != NULL)
|
||||||
if (xml_operation(opstr, &op) < 0)
|
if (xml_operation(opstr, &op) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((ret = attr_ns_value(x1, "objectcreate", NULL, cbret, &createstr)) < 0)
|
if ((ret = attr_ns_value(x1t, "objectcreate", NULL, cbret, &createstr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
/* Special case if incoming x1 is empty, top-level only <config/> */
|
/* Special case if incoming x1t is empty, top-level only <config/> */
|
||||||
if (xml_child_nr_type(x1, CX_ELMNT) == 0){
|
if (xml_child_nr_type(x1t, CX_ELMNT) == 0){
|
||||||
if (xml_child_nr_type(x0, CX_ELMNT)){ /* base tree not empty */
|
if (xml_child_nr_type(x0t, CX_ELMNT)){ /* base tree not empty */
|
||||||
switch(op){
|
switch(op){
|
||||||
case OP_DELETE:
|
case OP_DELETE:
|
||||||
case OP_REMOVE:
|
case OP_REMOVE:
|
||||||
case OP_REPLACE:
|
case OP_REPLACE:
|
||||||
if (!permit && xnacm){
|
if (!permit && xnacm){
|
||||||
if ((ret = nacm_datanode_write(h, x0, x0t, NACM_DELETE, username, xnacm, cbret)) < 0)
|
if ((ret = nacm_datanode_write(h, x0t, x0t, NACM_DELETE, username, xnacm, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
permit = 1;
|
permit = 1;
|
||||||
}
|
}
|
||||||
while ((x0c = xml_child_i(x0, 0)) != 0)
|
while ((x0c = xml_child_i(x0t, 0)) != 0)
|
||||||
if (xml_purge(x0c) < 0)
|
if (xml_purge(x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1057,25 +1055,25 @@ text_modify_top(clicon_handle h,
|
||||||
/* Special case top-level replace */
|
/* Special case top-level replace */
|
||||||
else if (op == OP_REPLACE || op == OP_DELETE){
|
else if (op == OP_REPLACE || op == OP_DELETE){
|
||||||
if (createstr != NULL){
|
if (createstr != NULL){
|
||||||
if (xml_child_nr_type(x0, CX_ELMNT)) /* base tree not empty */
|
if (xml_child_nr_type(x0t, CX_ELMNT)) /* base tree not empty */
|
||||||
clicon_data_set(h, "objectexisted", "true");
|
clicon_data_set(h, "objectexisted", "true");
|
||||||
else
|
else
|
||||||
clicon_data_set(h, "objectexisted", "false");
|
clicon_data_set(h, "objectexisted", "false");
|
||||||
}
|
}
|
||||||
if (!permit && xnacm){
|
if (!permit && xnacm){
|
||||||
if ((ret = nacm_datanode_write(h, x1, x1t, NACM_UPDATE, username, xnacm, cbret)) < 0)
|
if ((ret = nacm_datanode_write(h, x1t, x1t, NACM_UPDATE, username, xnacm, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
permit = 1;
|
permit = 1;
|
||||||
}
|
}
|
||||||
while ((x0c = xml_child_i(x0, 0)) != 0)
|
while ((x0c = xml_child_i(x0t, 0)) != 0)
|
||||||
if (xml_purge(x0c) < 0)
|
if (xml_purge(x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Loop through children of the modification tree */
|
/* Loop through children of the modification tree */
|
||||||
x1c = NULL;
|
x1c = NULL;
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1t, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
|
|
@ -1101,7 +1099,7 @@ text_modify_top(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
if (match_base_child(x0t, x1c, yc, &x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (x0c && (yc != xml_spec(x0c))){
|
if (x0c && (yc != xml_spec(x0c))){
|
||||||
/* There is a match but is should be replaced (choice)*/
|
/* There is a match but is should be replaced (choice)*/
|
||||||
|
|
@ -1109,7 +1107,7 @@ text_modify_top(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
}
|
}
|
||||||
if ((ret = text_modify(h, x0c, x0, x0t, x1c, x1t,
|
if ((ret = text_modify(h, x0c, x0t, x0t, x1c, x1t,
|
||||||
yc, op,
|
yc, op,
|
||||||
username, xnacm, permit, cbret)) < 0)
|
username, xnacm, permit, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1228,7 +1226,7 @@ xmldb_put(clicon_handle h,
|
||||||
* Modify base tree x with modification x1. This is where the
|
* Modify base tree x with modification x1. This is where the
|
||||||
* new tree is made.
|
* new tree is made.
|
||||||
*/
|
*/
|
||||||
if ((ret = text_modify_top(h, x0, x0, x1, x1, yspec, op, username, xnacm, permit, cbret)) < 0)
|
if ((ret = text_modify_top(h, x0, x1, yspec, op, username, xnacm, permit, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
|
|
|
||||||
|
|
@ -1344,6 +1344,7 @@ api_path_resolve(clixon_path *cplist,
|
||||||
int i;
|
int i;
|
||||||
cg_var *cva;
|
cg_var *cva;
|
||||||
cg_var *cvy;
|
cg_var *cvy;
|
||||||
|
cvec *cvk;
|
||||||
|
|
||||||
if ((cp = cplist) != NULL){
|
if ((cp = cplist) != NULL){
|
||||||
do {
|
do {
|
||||||
|
|
@ -1372,7 +1373,8 @@ api_path_resolve(clixon_path *cplist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (yang_keyword_get(yc) == Y_LIST){
|
else if (yang_keyword_get(yc) == Y_LIST){
|
||||||
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
cvk = yang_cvec_get(yc);
|
||||||
|
if (cvec_len(cp->cp_cvk) > cvec_len(cvk)){
|
||||||
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list");
|
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -1384,7 +1386,7 @@ api_path_resolve(clixon_path *cplist,
|
||||||
cv_name_get(cva));
|
cv_name_get(cva));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
cvy = cvec_i(yang_cvec_get(yc), i++);
|
cvy = cvec_i(cvk, i++);
|
||||||
cv_name_set(cva, cv_string_get(cvy));
|
cv_name_set(cva, cv_string_get(cvy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,6 @@ check_unique_list_direct(cxobj *x,
|
||||||
char *str;
|
char *str;
|
||||||
cvec *cvk;
|
cvec *cvk;
|
||||||
|
|
||||||
cvk = yang_cvec_get(yu);
|
|
||||||
/* If list and is sorted by system, then it is assumed elements are in key-order which is optimized
|
/* If list and is sorted by system, then it is assumed elements are in key-order which is optimized
|
||||||
* Other cases are "unique" constraint or list sorted by user which is quadratic in complexity
|
* Other cases are "unique" constraint or list sorted by user which is quadratic in complexity
|
||||||
* This second case COULD be optimized if binary insert is made on the vec vector.
|
* This second case COULD be optimized if binary insert is made on the vec vector.
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,8 @@ strip_body_objects(cxobj *xt)
|
||||||
*
|
*
|
||||||
* @param[in] xt XML tree node
|
* @param[in] xt XML tree node
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
* @param[out] xerr Reason for failure, or NULL
|
||||||
* @retval 1 OK Yang assignment made
|
|
||||||
* @retval 2 OK Yang assignment not made because yang parent is anyxml or anydata
|
* @retval 2 OK Yang assignment not made because yang parent is anyxml or anydata
|
||||||
|
* @retval 1 OK Yang assignment made
|
||||||
* @retval 0 Yang assigment not made and xerr set
|
* @retval 0 Yang assigment not made and xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note retval = 2 is special
|
* @note retval = 2 is special
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ xml_default_choice(yang_stmt *yc,
|
||||||
yang_stmt *yca = NULL;
|
yang_stmt *yca = NULL;
|
||||||
yang_stmt *ydef;
|
yang_stmt *ydef;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(2, "%s", __FUNCTION__);
|
||||||
/* 1. Is there a default case and no child under this choice?
|
/* 1. Is there a default case and no child under this choice?
|
||||||
*/
|
*/
|
||||||
x = NULL;
|
x = NULL;
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@
|
||||||
#include "clixon_yang_parse_lib.h"
|
#include "clixon_yang_parse_lib.h"
|
||||||
#include "clixon_yang_cardinality.h"
|
#include "clixon_yang_cardinality.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
|
#include "clixon_yang_schema_mount.h"
|
||||||
#include "clixon_yang_internal.h" /* internal included by this file only, not API*/
|
#include "clixon_yang_internal.h" /* internal included by this file only, not API*/
|
||||||
|
|
||||||
#ifdef XML_EXPLICIT_INDEX
|
#ifdef XML_EXPLICIT_INDEX
|
||||||
|
|
@ -274,6 +275,7 @@ yang_cv_set(yang_stmt *ys,
|
||||||
|
|
||||||
/*! Get yang statement CLIgen variable vector
|
/*! Get yang statement CLIgen variable vector
|
||||||
* @param[in] ys Yang statement node
|
* @param[in] ys Yang statement node
|
||||||
|
* @note To add entries, use yang_cvec_add(), since this function may return NULL
|
||||||
*/
|
*/
|
||||||
cvec*
|
cvec*
|
||||||
yang_cvec_get(yang_stmt *ys)
|
yang_cvec_get(yang_stmt *ys)
|
||||||
|
|
@ -297,6 +299,41 @@ yang_cvec_set(yang_stmt *ys,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Add new value to yang cvec, create if not exist
|
||||||
|
*
|
||||||
|
* @param[in] ys Yang statement
|
||||||
|
* @param[in] type Append a new cv to the vector with this type
|
||||||
|
* @param[in] name Name of variable
|
||||||
|
* @retval cv The new cligen variable
|
||||||
|
* @retval NULL Error
|
||||||
|
*/
|
||||||
|
static cg_var *
|
||||||
|
yang_cvec_add(yang_stmt *ys,
|
||||||
|
enum cv_type type,
|
||||||
|
char *name)
|
||||||
|
|
||||||
|
{
|
||||||
|
cg_var *cv;
|
||||||
|
cvec *cvv;
|
||||||
|
|
||||||
|
if ((cvv = yang_cvec_get(ys)) == NULL){
|
||||||
|
if ((cvv = cvec_new(0)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "cvec_new");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
yang_cvec_set(ys, cvv);
|
||||||
|
}
|
||||||
|
if ((cv = cvec_add(cvv, type)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "cvec_add");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cv_name_set(cv, name) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "cv_name_set(%s)", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Get yang stmt flags, used for internal algorithms
|
/*! Get yang stmt flags, used for internal algorithms
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] flag Flags value(s) to get, see YANG_FLAG_*
|
* @param[in] flag Flags value(s) to get, see YANG_FLAG_*
|
||||||
|
|
@ -594,7 +631,6 @@ yang_stmt *
|
||||||
ys_new(enum rfc_6020 keyw)
|
ys_new(enum rfc_6020 keyw)
|
||||||
{
|
{
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
cvec *cvv;
|
|
||||||
|
|
||||||
if ((ys = malloc(sizeof(*ys))) == NULL){
|
if ((ys = malloc(sizeof(*ys))) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "malloc");
|
clicon_err(OE_YANG, errno, "malloc");
|
||||||
|
|
@ -602,13 +638,6 @@ ys_new(enum rfc_6020 keyw)
|
||||||
}
|
}
|
||||||
memset(ys, 0, sizeof(*ys));
|
memset(ys, 0, sizeof(*ys));
|
||||||
ys->ys_keyword = keyw;
|
ys->ys_keyword = keyw;
|
||||||
/* The cvec contains stmt-specific variables. Only few stmts need variables so the
|
|
||||||
cvec could be lazily created to save some heap and cycles. */
|
|
||||||
if ((cvv = cvec_new(0)) == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "cvec_new");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
yang_cvec_set(ys, cvv);
|
|
||||||
_stats_yang_nr++;
|
_stats_yang_nr++;
|
||||||
return ys;
|
return ys;
|
||||||
}
|
}
|
||||||
|
|
@ -1082,13 +1111,17 @@ yang_match(yang_stmt *yn,
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Find child data node with matching argument (container, leaf, list, leaf-list)
|
/*! Find child data node with matching argument (container, leaf, list, leaf-list)
|
||||||
*
|
*
|
||||||
* @param[in] yn Yang node, current context node.
|
* @param[in] yn Yang node, current context node.
|
||||||
* @param[in] argument if NULL, match any(first) argument. XXX is that really a case?
|
* @param[in] argument Argument that child should match with
|
||||||
|
* @retval ymatch Matching child
|
||||||
|
* @retval NULL No match or error
|
||||||
*
|
*
|
||||||
* @see yang_find Looks for any node
|
* @see yang_find Looks for any node
|
||||||
* @note May deviate from RFC since it explores choice/case not just return it.
|
* @note May deviate from RFC since it explores choice/case not just return it.
|
||||||
|
* XXX: differentiate between not found and error
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
yang_find_datanode(yang_stmt *yn,
|
yang_find_datanode(yang_stmt *yn,
|
||||||
|
|
@ -1099,7 +1132,16 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
yang_stmt *ysmatch = NULL;
|
yang_stmt *ysmatch = NULL;
|
||||||
char *name;
|
char *name;
|
||||||
|
#ifdef YANG_SCHEMA_MOUNT
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Sanity-check mount-point extension */
|
||||||
|
if ((ret = yang_schema_mount_point(yn)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 1){
|
||||||
|
; // NYI
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ys = NULL;
|
ys = NULL;
|
||||||
while ((ys = yn_each(yn, ys)) != NULL){
|
while ((ys = yn_each(yn, ys)) != NULL){
|
||||||
if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */
|
if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */
|
||||||
|
|
@ -1109,14 +1151,11 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
ysmatch = yang_find_datanode(yc, argument);
|
ysmatch = yang_find_datanode(yc, argument);
|
||||||
else
|
else
|
||||||
if (yang_datanode(yc)){
|
if (yang_datanode(yc)){
|
||||||
if (argument == NULL)
|
|
||||||
ysmatch = yc;
|
|
||||||
else
|
|
||||||
if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
|
if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
|
||||||
ysmatch = yc;
|
ysmatch = yc;
|
||||||
}
|
}
|
||||||
if (ysmatch)
|
if (ysmatch)
|
||||||
goto match; // maybe break?
|
goto done; // maybe break?
|
||||||
}
|
}
|
||||||
} /* Y_CHOICE */
|
} /* Y_CHOICE */
|
||||||
else if (yang_keyword_get(ys) == Y_INPUT ||
|
else if (yang_keyword_get(ys) == Y_INPUT ||
|
||||||
|
|
@ -1131,7 +1170,7 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||||
ysmatch = ys;
|
ysmatch = ys;
|
||||||
if (ysmatch)
|
if (ysmatch)
|
||||||
goto match; // maybe break?
|
goto done; // maybe break?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Special case: if not match and yang node is module or submodule, extend
|
/* Special case: if not match and yang node is module or submodule, extend
|
||||||
|
|
@ -1150,7 +1189,7 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match:
|
done:
|
||||||
return ysmatch;
|
return ysmatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2265,14 +2304,12 @@ bound_add(yang_stmt *ys,
|
||||||
char *reason = NULL;
|
char *reason = NULL;
|
||||||
int ret = 1;
|
int ret = 1;
|
||||||
|
|
||||||
if ((cv = cvec_add(ys->ys_cvec, cvtype)) == NULL){
|
if (ys == NULL){
|
||||||
clicon_err(OE_YANG, errno, "cvec_add");
|
clicon_err(OE_YANG, EINVAL, "ys is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cv_name_set(cv, name) == NULL){
|
if ((cv = yang_cvec_add(ys, cvtype, name)) == NULL)
|
||||||
clicon_err(OE_YANG, errno, "cv_name_set(%s)", name);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
if (cvtype == CGV_DEC64)
|
if (cvtype == CGV_DEC64)
|
||||||
cv_dec64_n_set(cv, fraction_digits);
|
cv_dec64_n_set(cv, fraction_digits);
|
||||||
if (strcmp(val, "min") == 0)
|
if (strcmp(val, "min") == 0)
|
||||||
|
|
@ -2480,7 +2517,6 @@ ys_populate_identity(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *yc = NULL;
|
yang_stmt *yc = NULL;
|
||||||
yang_stmt *ybaseid;
|
yang_stmt *ybaseid;
|
||||||
cg_var *cv;
|
|
||||||
char *baseid;
|
char *baseid;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
char *id = NULL;
|
char *id = NULL;
|
||||||
|
|
@ -2526,17 +2562,10 @@ ys_populate_identity(clicon_handle h,
|
||||||
if (cvec_find(idrefvec, idref) != NULL)
|
if (cvec_find(idrefvec, idref) != NULL)
|
||||||
continue;
|
continue;
|
||||||
/* Add derived id to ybaseid */
|
/* Add derived id to ybaseid */
|
||||||
if ((cv = cv_new(CGV_STRING)) == NULL){
|
if (yang_cvec_add(ybaseid, CGV_STRING, idref) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cv_new");
|
clicon_err(OE_UNIX, errno, "cv_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* add prefix */
|
|
||||||
cv_name_set(cv, idref);
|
|
||||||
cvec_append_var(idrefvec, cv); /* cv copied */
|
|
||||||
if (cv){
|
|
||||||
cv_free(cv);
|
|
||||||
cv = NULL;
|
|
||||||
}
|
|
||||||
/* Transitive to the root */
|
/* Transitive to the root */
|
||||||
if (ys_populate_identity(h, ybaseid, idref) < 0)
|
if (ys_populate_identity(h, ybaseid, idref) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2662,11 +2691,20 @@ ys_populate_unique(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Populate unknown node with extension
|
/*! Populate unknown node with extension
|
||||||
|
*
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] ys The yang statement (unknown) to populate.
|
* @param[in] ys The yang statement (unknown) to populate.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
* RFC 7950 Sec 7.19:
|
* RFC 7950 Sec 7.19:
|
||||||
* If no "argument" statement is present, the keyword expects no argument when
|
* If no "argument" statement is present, the keyword expects no argument when
|
||||||
* it is used.
|
* it is used.
|
||||||
|
* A pointer to the unknwon node is stored in the cvec of the extension:
|
||||||
|
* 1 n
|
||||||
|
* yang-extension ext1 (cvec) ---> unknown of ext1
|
||||||
|
* (yext) (ys)
|
||||||
|
*
|
||||||
|
* @note this breaks if yangs are freed
|
||||||
* Note there is some complexity in different yang modules with unknown-statements.
|
* Note there is some complexity in different yang modules with unknown-statements.
|
||||||
* y0) The location of the extension definition. E.g. extension autocli-op
|
* y0) The location of the extension definition. E.g. extension autocli-op
|
||||||
* y1) The location of the unknown-statement (ys). This is for example: cl:autocli-op hide.
|
* y1) The location of the unknown-statement (ys). This is for example: cl:autocli-op hide.
|
||||||
|
|
@ -2717,6 +2755,10 @@ ys_populate_unknown(clicon_handle h,
|
||||||
clicon_debug(1, "plugin_extension() failed");
|
clicon_debug(1, "plugin_extension() failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef YANG_SCHEMA_MOUNT
|
||||||
|
if (yang_schema_unknown(h, yext, ys) < 0)
|
||||||
|
goto done;
|
||||||
#endif
|
#endif
|
||||||
/* Make extension callbacks that may alter yang structure
|
/* Make extension callbacks that may alter yang structure
|
||||||
* Note: this may be a "layering" violation: assuming plugins are loaded
|
* Note: this may be a "layering" violation: assuming plugins are loaded
|
||||||
|
|
@ -2724,6 +2766,12 @@ ys_populate_unknown(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
if (clixon_plugin_extension_all(h, yext, ys) < 0)
|
if (clixon_plugin_extension_all(h, yext, ys) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
#if 1
|
||||||
|
/* Add unknown to vector in extension */
|
||||||
|
if ((cv = yang_cvec_add(yext, CGV_VOID, yang_argument_get(ys))) == NULL)
|
||||||
|
goto done;
|
||||||
|
cv_void_set(cv, ys);
|
||||||
|
#endif
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (prefix)
|
if (prefix)
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ struct yang_stmt{
|
||||||
Y_TYPE & identity: store all derived
|
Y_TYPE & identity: store all derived
|
||||||
types as <module>:<id> list
|
types as <module>:<id> list
|
||||||
Y_UNIQUE: vector of descendant schema node ids
|
Y_UNIQUE: vector of descendant schema node ids
|
||||||
|
Y_EXTENSION: vector of instantiated UNKNOWNSo
|
||||||
*/
|
*/
|
||||||
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
|
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
|
||||||
char *ys_when_xpath; /* Special conditional for a "when"-associated augment/uses xpath */
|
char *ys_when_xpath; /* Special conditional for a "when"-associated augment/uses xpath */
|
||||||
|
|
|
||||||
|
|
@ -192,8 +192,8 @@ yang_modules_revision(clicon_handle h)
|
||||||
* If also CLICON_MODULE_LIBRARY_RFC7895 is set, module-state is built according to RFC7895 instead
|
* If also CLICON_MODULE_LIBRARY_RFC7895 is set, module-state is built according to RFC7895 instead
|
||||||
* @see RFC8525
|
* @see RFC8525
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
yms_build(clicon_handle h,
|
yang_modules_state_build(clicon_handle h,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
char *msid,
|
char *msid,
|
||||||
int brief,
|
int brief,
|
||||||
|
|
@ -361,7 +361,7 @@ yang_modules_state_get(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Build a cb string: <modules-state>... */
|
/* Build a cb string: <modules-state>... */
|
||||||
if (yms_build(h, yspec, msid, brief, cb) < 0)
|
if (yang_modules_state_build(h, yspec, msid, brief, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Parse cb, x is on the form: <top><modules-state>...
|
/* Parse cb, x is on the form: <top><modules-state>...
|
||||||
* Note, list is not sorted since it is state (should not be)
|
* Note, list is not sorted since it is state (should not be)
|
||||||
|
|
@ -391,7 +391,6 @@ yang_modules_state_get(clicon_handle h,
|
||||||
/* Remove everything that is not marked */
|
/* Remove everything that is not marked */
|
||||||
if (xml_tree_prune_flagged_sub(x, XML_FLAG_MARK, 1, NULL) < 0)
|
if (xml_tree_prune_flagged_sub(x, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
|
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
|
||||||
346
lib/src/clixon_yang_schema_mount.c
Normal file
346
lib/src/clixon_yang_schema_mount.c
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2023 Olof Hagsand
|
||||||
|
|
||||||
|
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 8525 Yang schema mount support
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <string.h>
|
||||||
|
|
||||||
|
/* cligen */
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* clixon */
|
||||||
|
#include "clixon_queue.h"
|
||||||
|
#include "clixon_hash.h"
|
||||||
|
#include "clixon_handle.h"
|
||||||
|
#include "clixon_err.h"
|
||||||
|
#include "clixon_log.h"
|
||||||
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_xml_io.h"
|
||||||
|
#include "clixon_yang_module.h"
|
||||||
|
#include "clixon_netconf_lib.h"
|
||||||
|
|
||||||
|
#include "clixon_yang_schema_mount.h"
|
||||||
|
|
||||||
|
/*! Check if y is a RFC 8525 YANG schema mount
|
||||||
|
*
|
||||||
|
* Check if:
|
||||||
|
* - y is CONTAINER or LIST, AND
|
||||||
|
* - y has YANG schema mount "mount-point" as child element, AND
|
||||||
|
* - the extension label matches y (see note below)
|
||||||
|
* If so, then return 1
|
||||||
|
* @param[in] y Yang statement
|
||||||
|
* @retval 1 Yes, y is a RFC 8525 YANG mount-point
|
||||||
|
* @retval 0 No, y is not
|
||||||
|
* @retval -1 Error
|
||||||
|
* @note That this may be a restriction on the usage of "label". The RFC is somewhat unclear.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
yang_schema_mount_point(yang_stmt *y)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
enum rfc_6020 keyw;
|
||||||
|
int exist = 0;
|
||||||
|
char *value = NULL;
|
||||||
|
|
||||||
|
if (y == NULL){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "y is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
keyw = yang_keyword_get(y);
|
||||||
|
if (keyw != Y_CONTAINER && keyw != Y_LIST)
|
||||||
|
goto fail;
|
||||||
|
if (yang_extension_value(y, "mount-point", YANG_SCHEMA_MOUNT_NAMESPACE, &exist, &value) < 0)
|
||||||
|
goto done;
|
||||||
|
if (exist == 0)
|
||||||
|
goto fail;
|
||||||
|
if (value == NULL)
|
||||||
|
goto fail;
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Find schema mounts - callback function for xml_apply
|
||||||
|
*
|
||||||
|
* @param[in] x XML node
|
||||||
|
* @param[in] arg cvec, if match add node
|
||||||
|
* @retval -1 Error, aborted at first error encounter, return -1 to end user
|
||||||
|
* @retval 0 OK, continue
|
||||||
|
* @retval 1 Abort, dont continue with others, return 1 to end user
|
||||||
|
* @retval 2 Locally abort this subtree, continue with others
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
find_schema_mounts(cxobj *x,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
yang_stmt *y;
|
||||||
|
cvec *cvv = (cvec *)arg;
|
||||||
|
cg_var *cv;
|
||||||
|
|
||||||
|
if ((y = xml_spec(x)) == NULL)
|
||||||
|
return 2;
|
||||||
|
if (yang_config(y) == 0)
|
||||||
|
return 2;
|
||||||
|
if ((ret = yang_schema_mount_point(y)) < 0)
|
||||||
|
return -1;
|
||||||
|
if (ret == 0)
|
||||||
|
return 0;
|
||||||
|
if ((cv = cvec_add(cvv, CGV_VOID)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cv_void_set(cv, x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Find mount-points and return yang-library state
|
||||||
|
*
|
||||||
|
* Brute force: traverse whole XML, match all x that have ymount as yspec
|
||||||
|
* Add yang-library state for all x
|
||||||
|
* @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,out] xret Existing XML tree, merge x into this
|
||||||
|
* @param[out] xerr XML error tree, if retval = 0
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Validation failed, error in xret
|
||||||
|
* @retval -1 Error (fatal)
|
||||||
|
*
|
||||||
|
* RFC 8528 Section 3.4:
|
||||||
|
* A schema for a mount point contained in a mounted module can be
|
||||||
|
* specified by implementing the "ietf-yang-library" and
|
||||||
|
* "ietf-yang-schema-mount" modules in the mounted schema and specifying
|
||||||
|
* the schemas in exactly the same way as the top-level schema.
|
||||||
|
* Alt: see snmp_yang2xml to get instances instead of brute force traverse of whole tree
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
schema_mounts_yang_library(clicon_handle h,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
char *xpath,
|
||||||
|
cvec *nsc,
|
||||||
|
cxobj **xret,
|
||||||
|
cxobj **xerr)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cvec *cvv = NULL;
|
||||||
|
cg_var *cv;
|
||||||
|
cxobj *xmp; /* xml mount-point */
|
||||||
|
cxobj *xylib = NULL; /* xml yang-lib */
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
char *msid = "mount-point"; /* modules-set-id dummy */
|
||||||
|
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, 0, "clicon buffer");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cvv = cvec_new(0)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xml_apply(*xret, CX_ELMNT, find_schema_mounts, cvv) < 0)
|
||||||
|
goto done;
|
||||||
|
cv = NULL;
|
||||||
|
while ((cv = cvec_each(cvv, cv)) != NULL) {
|
||||||
|
xmp = cv_void_get(cv);
|
||||||
|
/* addsub here */
|
||||||
|
cbuf_reset(cb);
|
||||||
|
// XXX change yspec to mount-point
|
||||||
|
/* Build a cb string: <modules-state>... */
|
||||||
|
if (yang_modules_state_build(h, yspec, msid, 0, cb) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Parse cb, x is on the form: <top><modules-state>...
|
||||||
|
* Note, list is not sorted since it is state (should not be)
|
||||||
|
*/
|
||||||
|
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &xylib, NULL) < 0){
|
||||||
|
if (xret && netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (xml_rootchild(xylib, 0, &xylib) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_addsub(xmp, xylib) < 0)
|
||||||
|
goto done;
|
||||||
|
xylib = NULL;
|
||||||
|
}
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (cvv)
|
||||||
|
cvec_free(cvv);
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get modules state according to RFC 8528
|
||||||
|
*
|
||||||
|
* @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,out] xret Existing XML tree, merge x into this
|
||||||
|
* @param[out] xerr XML error tree, if retval = 0
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Validation failed, error in xret
|
||||||
|
* @retval -1 Error (fatal)
|
||||||
|
* @note Only "inline" specification of mounted schema supported, not "shared schema"
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
schema_mounts_state_get(clicon_handle h,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
char *xpath,
|
||||||
|
cvec *nsc,
|
||||||
|
cxobj **xret,
|
||||||
|
cxobj **xerr)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
int ret;
|
||||||
|
yang_stmt *yext;
|
||||||
|
yang_stmt *ymount;
|
||||||
|
yang_stmt *ymodext;
|
||||||
|
yang_stmt *ymod;
|
||||||
|
cg_var *cv;
|
||||||
|
cg_var *cv1;
|
||||||
|
char *label;
|
||||||
|
cvec *cvv;
|
||||||
|
cxobj *x1 = NULL;
|
||||||
|
|
||||||
|
if ((ymodext = yang_find(yspec, Y_MODULE, "ietf-yang-schema-mount")) == NULL ||
|
||||||
|
(yext = yang_find(ymodext, Y_EXTENSION, "mount-point")) == NULL){
|
||||||
|
goto ok;
|
||||||
|
// clicon_err(OE_YANG, 0, "yang schema mount-point extension not found");
|
||||||
|
// goto done;
|
||||||
|
}
|
||||||
|
if ((cvv = yang_cvec_get(yext)) != NULL){
|
||||||
|
if ((cb = cbuf_new()) ==NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "<schema-mounts xmlns=\"%s\">", YANG_SCHEMA_MOUNT_NAMESPACE); // XXX only if hit
|
||||||
|
cv = NULL;
|
||||||
|
while ((cv = cvec_each(cvv, cv)) != NULL){
|
||||||
|
ymount = (yang_stmt*)cv_void_get(cv);
|
||||||
|
ymod = ys_module(ymount);
|
||||||
|
if ((cv1 = yang_cv_get(ymount)) == NULL){
|
||||||
|
clicon_err(OE_YANG, 0, "mount-point extension must have label");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
label = cv_string_get(cv1);
|
||||||
|
cprintf(cb, "<mount-point>");
|
||||||
|
cprintf(cb, "<module>%s</module>", yang_argument_get(ymod));
|
||||||
|
cprintf(cb, "<label>%s</label>", label);
|
||||||
|
cprintf(cb, "<inline/>");
|
||||||
|
cprintf(cb, "</mount-point>");
|
||||||
|
}
|
||||||
|
cprintf(cb, "</schema-mounts>");
|
||||||
|
if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x1, xerr)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
if ((ret = netconf_trymerge(x1, yspec, xret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
/* Find mount-points and return yang-library state */
|
||||||
|
if (0 && schema_mounts_yang_library(h, yspec, xpath, nsc, xret, xerr) < 0)
|
||||||
|
goto done;
|
||||||
|
ok:
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (x1)
|
||||||
|
xml_free(x1);
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
return retval;
|
||||||
|
fail:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Callback for yang schema mount-point extension
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] yext Yang node of extension
|
||||||
|
* @param[in] ys Yang node of (unknown) statement belonging to extension
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
// XXX his may not even be necessary
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
yang_schema_unknown(clicon_handle h,
|
||||||
|
yang_stmt *yext,
|
||||||
|
yang_stmt *ys)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *extname;
|
||||||
|
char *modname;
|
||||||
|
yang_stmt *ymod;
|
||||||
|
cg_var *cv;
|
||||||
|
char *label;
|
||||||
|
|
||||||
|
ymod = ys_module(yext);
|
||||||
|
modname = yang_argument_get(ymod);
|
||||||
|
extname = yang_argument_get(yext);
|
||||||
|
if (strcmp(modname, "ietf-yang-schema-mount") != 0 || strcmp(extname, "mount-point") != 0)
|
||||||
|
goto ok;
|
||||||
|
if ((cv = yang_cv_get(ys)) == NULL){
|
||||||
|
clicon_err(OE_YANG, 0, "mount-point extension must have label");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
label = cv_string_get(cv);
|
||||||
|
clicon_debug(1, "%s Enabled extension:%s:%s label:%s", __FUNCTION__, modname, extname, label);
|
||||||
|
// XXX his may not even be necessary
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue