* 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
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