Added yang domains for mount-point isolation

New option: `CLICON_YANG_DOMAIN_DIR`
New `clixon-config@2024-08-01.yang` revision
This commit is contained in:
Olof hagsand 2024-09-04 12:03:11 +02:00
parent f5372fb124
commit cc194ac7c5
21 changed files with 696 additions and 198 deletions

View file

@ -33,10 +33,10 @@
***** END LICENSE BLOCK *****
* This file contains access functions for two types of clixon vars:
* - options, ie string based variables from Clixon configuration files.
* This file contains access functions for options, one type of clixon variables
* ie string based variables from Clixon configuration files.
* Accessed with clicon_options(h).
* @see clixon_data.[ch] for free-type runtime get/set *
* @see clixon_data.[ch] for the other type: free-type runtime get/set *
*/
#ifndef _CLIXON_OPTIONS_H_
@ -143,6 +143,9 @@ static inline char *clicon_yang_main_file(clixon_handle h){
static inline char *clicon_yang_main_dir(clixon_handle h){
return clicon_option_str(h, "CLICON_YANG_MAIN_DIR");
}
static inline char *clicon_yang_domain_dir(clixon_handle h){
return clicon_option_str(h, "CLICON_YANG_DOMAIN_DIR");
}
static inline char *clicon_yang_module_main(clixon_handle h){
return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN");
}

View file

@ -285,6 +285,7 @@ int yang_stats(yang_stmt *y, enum rfc_6020 keyw, uint64_t *nrp, size_t *s
/* Other functions */
yang_stmt *yspec_new(clixon_handle h, char *name);
yang_stmt *yspec_new_shared(clixon_handle h, char *name, yang_stmt *yspec0);
yang_stmt *ys_new(enum rfc_6020 keyw);
yang_stmt *ys_prune(yang_stmt *yp, int i);
int ys_prune_self(yang_stmt *ys);
@ -320,6 +321,7 @@ int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal, int pretty);
int yang_deviation(yang_stmt *ys, void *arg);
int yang_spec_print(FILE *f, yang_stmt *yspec);
int yang_spec_dump(yang_stmt *yspec, int debuglevel);
int yang_mounts_print(FILE *f, yang_stmt *ymounts);
int if_feature(yang_stmt *yspec, char *module, char *feature);
int ys_populate(yang_stmt *ys, void *arg);
int ys_populate2(yang_stmt *ys, void *arg);

View file

@ -79,6 +79,6 @@ yang_stmt *yang_find_module_by_name_revision(yang_stmt *yspec, const char *name,
yang_stmt *yang_find_module_by_name(yang_stmt *yspec, char *name);
int yang_metadata_annotation_check(cxobj *x, yang_stmt *ymod, int *ismeta);
int yang_metadata_init(clixon_handle h);
int yang_lib2yspec(clixon_handle h, cxobj *yanglib, char *mntpnt, yang_stmt *yspec);
int yang_lib2yspec(clixon_handle h, cxobj *yanglib, char *mntpnt, char *domain, yang_stmt *yspec);
#endif /* _CLIXON_YANG_MODULE_H_ */

View file

@ -53,9 +53,9 @@
*/
int ys_grouping_resolve(yang_stmt *yuses, char *prefix, char *name, yang_stmt **ygrouping0);
yang_stmt *yang_parse_file(FILE *fp, const char *name, yang_stmt *ysp);
int yang_file_find_match(clixon_handle h, const char *module, const char *revision, cbuf *fbuf);
int yang_file_find_match(clixon_handle h, const char *module, const char *revision, const char *domain, cbuf *fbuf);
yang_stmt *yang_parse_filename(clixon_handle h, const char *filename, yang_stmt *ysp);
yang_stmt *yang_parse_module(clixon_handle h, const char *module, const char *revision, yang_stmt *yspec, char *origname);
yang_stmt *yang_parse_module(clixon_handle h, const char *module, const char *revision, yang_stmt *yspec, char *domain, char *origname);
int yang_parse_post(clixon_handle h, yang_stmt *yspec, int modmin);
int yang_parse_optimize_uses(clixon_handle h, yang_stmt *yspec);
int yang_spec_parse_module(clixon_handle h, const char *module,

View file

@ -85,7 +85,7 @@
* @param[out] val Data value as string
* @retval 0 OK
* @retval -1 Not found (or error)
* @see clicon_option_str
* @see clicon_option_str For file options
*/
int
clicon_data_get(clixon_handle h,

View file

@ -354,7 +354,6 @@ clixon_err_args(clixon_handle h,
* printf("%s", cbuf_get(cb));
* cbuf_free(cb);
* @endcode
* @see clixon_error_netconf
*/
int
netconf_err2cb(clixon_handle h,

View file

@ -823,14 +823,44 @@ yspec_new(clixon_handle h,
goto done;
if (yn_insert(ymounts, yspec) < 0)
goto done;
/* Special trick for shared yspecs */
if (yang_cvec_add(yspec, CGV_STRING, name) < 0)
goto done;
return yspec;
done:
return NULL;
}
/*! Create or add a shared yspec
*
* @param[in] h Clixon handle
* @param[in] name Typically an xpath
* @param[in] yspec0 Input NULL if no previous shared exist, otherwise a shared yspec but new name
* @retval yspec1 New or (previously shared)
* @retval NULL Error
*/
yang_stmt *
yspec_new_shared(clixon_handle h,
char *name,
yang_stmt *yspec0)
{
yang_stmt *yspec1 = NULL;
if (yspec0 != NULL){ /* shared */
assert(clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT_SHARE") != 0);
yspec1 = yspec0;
}
else {
if ((yspec1 = yspec_new(h, name)) == NULL)
goto done;
yang_flag_set(yspec1, YANG_FLAG_SPEC_MOUNT);
clixon_debug(CLIXON_DBG_YANG, "new yang-spec: %p", yspec1);
}
if (yang_cvec_add(yspec1, CGV_STRING, name) < 0){
yspec1 = NULL;
goto done;
}
done:
return yspec1;
}
/*! Create new yang node/statement given size
*
* Size parameter for variable size, eg extended YANG struct
@ -2199,7 +2229,7 @@ yang_print(FILE *f,
/*! Print yang top-level modules only
*
* @param[in] f File to print to.
* @param[in] yn Yang node to print
* @param[in] yspec Yang spec to print
* @see yang_print_cbuf
*/
int
@ -2211,7 +2241,7 @@ yang_spec_print(FILE *f,
int inext;
if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
clixon_err(OE_YANG, EINVAL, "yspec is not of type YSPEC");
clixon_err(OE_YANG, EINVAL, "yspec is not of type Y_SPEC");
return -1;
}
inext = 0;
@ -2227,6 +2257,39 @@ yang_spec_print(FILE *f,
return 0;
}
/*! Print yang top-level specs
*
* @param[in] f File to print to.
* @param[in] ymounts Yang mounts to print
*/
int
yang_mounts_print(FILE *f,
yang_stmt *ymounts)
{
yang_stmt *yspec;
int inext;
cg_var *cv;
cvec *cvv;
if (ymounts == NULL || yang_keyword_get(ymounts) != Y_MOUNTS){
clixon_err(OE_YANG, EINVAL, "yspec is not of type Y_MOUNTS");
return -1;
}
inext = 0;
while ((yspec = yn_iter(ymounts, &inext)) != NULL) {
fprintf(f, " %s\n", yang_argument_get(yspec));
if ((cv = yang_cv_get(yspec)) != NULL){
cv_print(f, yang_cv_get(yspec));
fprintf(f, "\n");
}
if ((cvv = yang_cvec_get(yspec)) != NULL){
cvec_print(f, cvv);
fprintf(f, "\n");
}
}
return 0;
}
/* Log/debug info about top-level (sub)modules no recursion
* @param[in] yspec Yang spec
* @param[in] dbglevel Debug level

View file

@ -108,14 +108,11 @@ struct yang_stmt {
Y_UNIQUE: vector of descendant schema node ids
Y_UNKNOWN: app-dep: yang-mount-points
*/
yang_stmt *ys_orig; /* Pointer to original (for uses/augment copies) */
union { /* Depends on ys_keyword */
rpc_callback_t *ysu_action_cb; /* Y_ACTION: Action callback list*/
char *ysu_filename; /* Y_MODULE/Y_SUBMODULE: For debug/errors: filename */
yang_type_cache *ysu_typecache; /* Y_TYPE: cache all typedef data except unions */
int ysu_ref; /* Y_SPEC: Reference count for free: 0 means
* no sharing, 1: two references */
} u;
};
@ -123,6 +120,5 @@ struct yang_stmt {
#define ys_action_cb u.ysu_action_cb
#define ys_filename u.ysu_filename
#define ys_typecache u.ysu_typecache
#define ys_ref u.ysu_ref
#endif /* _CLIXON_YANG_INTERNAL_H_ */

View file

@ -853,6 +853,7 @@ yang_metadata_init(clixon_handle h)
* @param[in] h Clixon handle
* @param[in] xyanglib XML tree on the form <yang-lib>...
* @param[in] mntpnt Name of mount-point for logs
* @param[in] domain YANG domain (NULL is default)
* @param[in] yspec Will be populated with YANGs, is consumed
* @retval 1 OK
* @retval 0 Parse error
@ -864,6 +865,7 @@ int
yang_lib2yspec(clixon_handle h,
cxobj *xyanglib,
char *mntpnt,
char *domain,
yang_stmt *yspec)
{
int retval = -1;
@ -902,7 +904,7 @@ yang_lib2yspec(clixon_handle h,
continue;
}
}
if (yang_parse_module(h, name, revision, yspec, NULL) == NULL)
if (yang_parse_module(h, name, revision, yspec, domain, NULL) == NULL)
goto fail;
/* Sanity check: if given namespace differs from namespace in file */
if (ns != NULL &&
@ -920,7 +922,7 @@ yang_lib2yspec(clixon_handle h,
strcmp(yang_argument_get(yrev), "2019-01-04") == 0){
modmin++;
}
else if (yang_parse_module(h, "ietf-yang-library", "2019-01-04", yspec, NULL) < 0)
else if (yang_parse_module(h, "ietf-yang-library", "2019-01-04", yspec, domain, NULL) < 0)
goto fail;
#endif
if ((modmin = yang_len_get(yspec) - (1+veclen - modmin)) < 0)

View file

@ -1002,31 +1002,44 @@ filename2revision(const char *filename,
return retval;
}
/*! No specific revision give. Match a yang file given module
/*! Find matching YANG file given module name. No specific revision given
*
* Look first in CLICON_YANG_MAIN_DIR for top-level, or CLICON_YANG_DOMAIN_DIR for specific domains.
* Then look in recursive CLICON_YANG_DIRs
* @param[in] h CLICON handle
* @param[in] module Name of main YANG module.
* @param[in] revision Revision or NULL
* @param[in] domain YANG isolation device-domain or NULL for yang main
* @param[out] fbuf Buffer containing filename or NULL (if retval=1)
* @retval 1 Match found, Most recent entry returned in fbuf
* @retval 1 Match found, Entry returned in fbuf if given
* @retval 0 No matching entry found
* @retval -1 Error
* @note for bootstrapping, dir may have to be set.
*/
* Returned entry in fbuf according to the following algorithm:
* 1) Exact or most recent revision match in CLICON_YANG_MAIN_DIR or CLICON_YANG_DOMAIN_DIR
* 2) Exact or most recent revision match in first CLICON_YANG_DIR recursively
* 3) Exact or most recent revision match in second CLICON_YANG_DIR
* 4) ...
* @note This means that the most recent revision entry globally may not be returned,
* only most recent in first first match
*/
int
yang_file_find_match(clixon_handle h,
const char *module,
const char *revision,
const char *domain,
cbuf *fbuf)
{
int retval = -1;
cbuf *regex = NULL;
cxobj *x;
cxobj *xc;
char *dir;
cvec *cvv = NULL;
cg_var *cv = NULL;
cg_var *bestcv = NULL;
int retval = -1;
cbuf *regex = NULL;
cxobj *x;
cxobj *xc;
char *dir;
cvec *cvv = NULL;
cg_var *cv = NULL;
cg_var *bestcv = NULL;
cbuf *cb = NULL;
struct dirent *dp = NULL;
int ndp;
/* get clicon config file in xml form */
if ((x = clicon_conf_xml(h)) == NULL)
@ -1044,35 +1057,38 @@ yang_file_find_match(clixon_handle h,
else
cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$",
module);
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xc), "CLICON_YANG_MAIN_DIR") == 0){
struct dirent *dp = NULL;
int ndp;
dir = xml_body(xc);
/* get all matching files in this directory */
if ((ndp = clicon_file_dirent(dir,
&dp,
cbuf_get(regex),
S_IFREG)) < 0)
goto done;
/* Entries are sorted, last entry should be most recent date
*/
if (ndp != 0){
if (fbuf)
cprintf(fbuf, "%s/%s", dir, dp[ndp-1].d_name);
if (dp)
free(dp);
retval = 1;
goto done;
}
if (dp)
free(dp);
/* First look in Main YANG dir, either MAIN or DOMAIN */
if (domain != NULL &&
(dir = clicon_yang_domain_dir(h)) != NULL){
if ((cb = cbuf_new()) == NULL){
clixon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
else if (strcmp(xml_name(xc), "CLICON_YANG_DIR") == 0 &&
(dir = xml_body(xc)) != NULL){
cprintf(cb, "%s/%s", dir, domain);
dir = cbuf_get(cb);
}
else
dir = clicon_yang_main_dir(h);
if (dir != NULL) {
/* get all matching files in this directory */
if ((ndp = clicon_file_dirent(dir,
&dp,
cbuf_get(regex),
S_IFREG)) < 0)
goto done;
/* Entries are sorted, last entry should be most recent date
*/
if (ndp != 0){
if (fbuf)
cprintf(fbuf, "%s/%s", dir, dp[ndp-1].d_name);
goto found;
}
}
/* Second look in recursive YANG lib dirs */
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xc), "CLICON_YANG_DIR") == 0 &&
(dir = xml_body(xc)) != NULL){
/* get all matching files in this directory recursively */
if ((cvv = cvec_new(0)) == NULL){
clixon_err(OE_UNIX, errno, "cvec_new");
@ -1094,8 +1110,7 @@ yang_file_find_match(clixon_handle h,
if (bestcv){
if (fbuf)
cprintf(fbuf, "%s", cv_string_get(bestcv)); /* file path */
retval = 1; /* found */
goto done;
goto found;
}
if (cvv){
cvec_free(cvv);
@ -1106,11 +1121,18 @@ yang_file_find_match(clixon_handle h,
ok:
retval = 0;
done:
if (dp)
free(dp);
if (cb)
cbuf_free(cb);
if (cvv)
cvec_free(cvv);
if (regex)
cbuf_free(regex);
return retval;
found:
retval = 1;
goto done;
}
/*! Open a file, read into a string and invoke yang parsing
@ -1161,6 +1183,7 @@ yang_parse_filename(clixon_handle h,
* @param[in] module Module name
* @param[in] revision Revision (or NULL)
* @param[in] yspec Yang statement
* @param[in] domain YANG isolation device-domain or NULL for yang-main
* @param[in] origname Name of yang module triggering this parsing, for logging
* @retval ymod YANG (sub)module
* @retval NULL Failed
@ -1173,6 +1196,7 @@ yang_parse_module(clixon_handle h,
const char *module,
const char *revision,
yang_stmt *yspec,
char *domain,
char *origname)
{
cbuf *fbuf = NULL;
@ -1189,7 +1213,7 @@ yang_parse_module(clixon_handle h,
goto done;
}
/* Match a yang file with or without revision in yang-dir list */
if ((nr = yang_file_find_match(h, module, revision, fbuf)) < 0)
if ((nr = yang_file_find_match(h, module, revision, domain, fbuf)) < 0)
goto done;
if (nr == 0){
if ((cb = cbuf_new()) == NULL){
@ -1289,7 +1313,7 @@ yang_parse_recurse(clixon_handle h,
keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
submodule) == NULL){
/* recursive call */
if ((subymod = yang_parse_module(h, submodule, subrevision, ysp, yang_argument_get(ymod))) == NULL)
if ((subymod = yang_parse_module(h, submodule, subrevision, ysp, NULL, yang_argument_get(ymod))) == NULL)
goto done;
/* Sanity check: if submodule, its belongs-to statement shall point to the module */
if (keyw == Y_INCLUDE){
@ -1683,7 +1707,7 @@ yang_spec_parse_module(clixon_handle h,
if (yang_find_module_by_name_revision(yspec, name, revision) != NULL)
goto ok;
/* Find a yang module and parse it and all its submodules */
if (yang_parse_module(h, name, revision, yspec, NULL) == NULL)
if (yang_parse_module(h, name, revision, yspec, NULL, NULL) == NULL)
goto done;
if (yang_parse_post(h, yspec, modmin) < 0)
goto done;

View file

@ -44,6 +44,7 @@
* container root{ (ymnt)
* yangmnt:mount-point "mylabel"; (yext)
* }
* (note the argument "mylabel" defines an optional isolated YANG domain
*
* <config> # Your XML config
* ...
@ -813,14 +814,14 @@ yang_schema_find_share(clixon_handle h,
cxobj *xyanglib,
yang_stmt **yspecp)
{
int retval = -1;
cvec *cvv = NULL;
cg_var *cv;
cxobj *xroot;
cxobj *xmnt;
cxobj *xylib;
int config = 1;
int ret;
int retval = -1;
cvec *cvv = NULL;
cg_var *cv;
cxobj *xroot;
cxobj *xmnt;
cxobj *xylib;
int config = 1;
int ret;
xroot = xml_root(xt);
/* Get all XML mtpoints */
@ -870,10 +871,12 @@ yang_schema_yanglib_parse_mount(clixon_handle h,
{
int retval = -1;
cxobj *xyanglib = NULL;
yang_stmt *yspec = NULL;
int ret;
int shared = 0;
cxobj *xb;
yang_stmt *yspec0 = NULL;
yang_stmt *yspec1 = NULL;
char *xpath = NULL;
char *domain = NULL;
int ret;
/* 1. Get modstate (xyanglib) of node: xyanglib, by querying backend state (via callback)
* XXX this xyanglib is not proper RFC8525, submodules appear as modules WHY?
@ -882,45 +885,42 @@ yang_schema_yanglib_parse_mount(clixon_handle h,
goto done;
if (xyanglib == NULL)
goto anydata;
/* Optimization: find equal yspec from other mount-point */
if (clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT_SHARE")) {
if (yang_schema_find_share(h, xt, xyanglib, &yspec) < 0)
goto done;
if (yspec)
shared++;
if ((xb = xpath_first(xyanglib, NULL, "module-set/name")) != NULL)
domain = xml_body(xb);
if (domain == NULL){
clixon_err(OE_YANG, 0, "domain not found");
goto done;
}
/* XXX done later too */
/* Get xpath */
if ((ret = yang_mount_xmnt2ymnt_xpath(h, xt, NULL, &xpath)) < 0)
goto done;
if (ret == 0){
clixon_err(OE_YANG, 0, "Mapping xmnt to ymnt and xpath");
goto done;
}
if (yspec == NULL){
/* Parse it and set mount-point */
if ((yspec = yspec_new(h, xpath)) == NULL)
/* Optimization: find equal yspec from other mount-point */
if (clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT_SHARE")) {
if (yang_schema_find_share(h, xt, xyanglib, &yspec0) < 0)
goto done;
yang_flag_set(yspec, YANG_FLAG_SPEC_MOUNT);
clixon_debug(CLIXON_DBG_YANG, "new yang-spec: %p", yspec);
if ((ret = yang_lib2yspec(h, xyanglib, xpath, yspec)) < 0)
}
if ((yspec1 = yspec_new_shared(h, xpath, yspec0)) < 0)
goto done;
/* Either yspec0 = NULL and yspec1 is new, or yspec0 == yspec1 != NULL (shared) */
if (yspec0 == NULL && yspec1 != NULL){
if ((ret = yang_lib2yspec(h, xyanglib, xpath, domain, yspec1)) < 0)
goto done;
if (ret == 0)
goto anydata;
}
else
clixon_debug(CLIXON_DBG_YANG, "shared yang-spec: %p", yspec);
if (xml_yang_mount_set(h, xt, yspec) < 0)
if (xml_yang_mount_set(h, xt, yspec1) < 0)
goto done;
if (shared)
if (yang_cvec_add(yspec, CGV_STRING, xpath) < 0)
goto done;
yspec = NULL;
yspec1 = NULL;
retval = 1;
done:
if (xpath)
free(xpath);
if (yspec)
ys_free(yspec);
if (yspec1)
ys_free(yspec1);
if (xyanglib)
xml_free(xyanglib);
return retval;