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:
parent
f5372fb124
commit
cc194ac7c5
21 changed files with 696 additions and 198 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue