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

@ -16,6 +16,8 @@ Expected: October 2024
### Features ### Features
* Added yang domains for mount-point isolation
* New option: `CLICON_YANG_DOMAIN_DIR`
* Restconf: Support for list of media in Accept header * Restconf: Support for list of media in Accept header
* Refactoring of schema mount-points * Refactoring of schema mount-points
* Add new top-level `Y_MOUNTS` and add top-level yangs and mountpoints in yspecs * Add new top-level `Y_MOUNTS` and add top-level yangs and mountpoints in yspecs
@ -28,6 +30,8 @@ Expected: October 2024
* New: [CLI simple alias](https://github.com/clicon/cligen/issues/112) * New: [CLI simple alias](https://github.com/clicon/cligen/issues/112)
* See: https://clixon-docs.readthedocs.io/en/latest/cli.html#cli-aliases * See: https://clixon-docs.readthedocs.io/en/latest/cli.html#cli-aliases
* List pagination: Added where, sort-by and direction parameter for configured data * List pagination: Added where, sort-by and direction parameter for configured data
* New `clixon-config@2024-08-01.yang` revision
- Added: CLICON_YANG_DOMAIN_DIR
* New `clixon-lib@2024-08-01.yang` revision * New `clixon-lib@2024-08-01.yang` revision
- Added: list-pagination-partial-state extension - Added: list-pagination-partial-state extension
@ -47,6 +51,9 @@ Users may have to change how they access the system
Developers may need to change their code Developers may need to change their code
* Added `domain` argument to yang parse functions. Upgrade as follows:
* `yang_file_find_match(h, m, r, f)` -> `yang_file_find_match(h, m, r, NULL, f)`
* `yang_parse_module(h, m, r, y, o)` -> `yang_parse_module(h, m, r, y, NULL, o)`
* Replaced `clixon_get_logflags()` with `clixon_logflags_get()` * Replaced `clixon_get_logflags()` with `clixon_logflags_get()`
* New `yn_iter()` yang iterator replaces `yn_each()` * New `yn_iter()` yang iterator replaces `yn_each()`
* Use an integer iterator instead of yang object * Use an integer iterator instead of yang object

View file

@ -466,6 +466,7 @@ You can set-up the example for a simple RFC 8528 Yang schema mount. A single top
1. Enable CLICON_YANG_SCHEMA_MOUNT 1. Enable CLICON_YANG_SCHEMA_MOUNT
2. Define the mount-point using the ietf-yang-schema-mount mount-point extension 2. Define the mount-point using the ietf-yang-schema-mount mount-point extension
3. Start the backend, cli and restconf with `-- -m <name> -M <urn>`, where `name` and `urn` is the name and namespace of the mounted YANG, respectively. 3. Start the backend, cli and restconf with `-- -m <name> -M <urn>`, where `name` and `urn` is the name and namespace of the mounted YANG, respectively.
4. Note that the module-set name is hard-coded to "mylabel". If you support isolated domains this must be changed.
A simple example on how to define a mount-point A simple example on how to define a mount-point
``` ```

View file

@ -90,6 +90,7 @@ static char *_action_instanceid = NULL;
* *
* Start backend with -- -m <yang> -M <namespace> * Start backend with -- -m <yang> -M <namespace>
* Mount this yang on mountpoint * Mount this yang on mountpoint
* Note module-set hard-coded to "mylabel"
*/ */
static char *_mount_yang = NULL; static char *_mount_yang = NULL;
static char *_mount_namespace = NULL; static char *_mount_namespace = NULL;

View file

@ -37,6 +37,7 @@
* argc/argv after -- in clixon_cli: * argc/argv after -- in clixon_cli:
* -m <yang> Mount this yang on mountpoint * -m <yang> Mount this yang on mountpoint
* -M <namespace> Namespace of mountpoint, note both -m and -M must exist * -M <namespace> Namespace of mountpoint, note both -m and -M must exist
* Note module-set hard-coded to "mylabel"
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -221,7 +222,7 @@ example_cli_yang_mount(clixon_handle h,
} }
cprintf(cb, "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"); cprintf(cb, "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">");
cprintf(cb, "<module-set>"); cprintf(cb, "<module-set>");
cprintf(cb, "<name>mount</name>"); cprintf(cb, "<name>mylabel</name>");
cprintf(cb, "<module>"); cprintf(cb, "<module>");
/* In yang name+namespace is mandatory, but not revision */ /* In yang name+namespace is mandatory, but not revision */
cprintf(cb, "<name>%s</name>", _mount_yang); // mandatory cprintf(cb, "<name>%s</name>", _mount_yang); // mandatory

View file

@ -61,6 +61,7 @@ static const char Pad64 = '=';
* *
* Start restconf with -- -m <yang> -M <namespace> * Start restconf with -- -m <yang> -M <namespace>
* Mount this yang on mountpoint * Mount this yang on mountpoint
* Note module-set hard-coded to "mylabel"
*/ */
static char *_mount_yang = NULL; static char *_mount_yang = NULL;
static char *_mount_namespace = NULL; static char *_mount_namespace = NULL;

View file

@ -33,10 +33,10 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* This file contains access functions for two types of clixon vars: * This file contains access functions for options, one type of clixon variables
* - options, ie string based variables from Clixon configuration files. * ie string based variables from Clixon configuration files.
* Accessed with clicon_options(h). * 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_ #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){ static inline char *clicon_yang_main_dir(clixon_handle h){
return clicon_option_str(h, "CLICON_YANG_MAIN_DIR"); 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){ static inline char *clicon_yang_module_main(clixon_handle h){
return clicon_option_str(h, "CLICON_YANG_MODULE_MAIN"); 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 */ /* Other functions */
yang_stmt *yspec_new(clixon_handle h, char *name); 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_new(enum rfc_6020 keyw);
yang_stmt *ys_prune(yang_stmt *yp, int i); yang_stmt *ys_prune(yang_stmt *yp, int i);
int ys_prune_self(yang_stmt *ys); 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_deviation(yang_stmt *ys, void *arg);
int yang_spec_print(FILE *f, yang_stmt *yspec); int yang_spec_print(FILE *f, yang_stmt *yspec);
int yang_spec_dump(yang_stmt *yspec, int debuglevel); 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 if_feature(yang_stmt *yspec, char *module, char *feature);
int ys_populate(yang_stmt *ys, void *arg); int ys_populate(yang_stmt *ys, void *arg);
int ys_populate2(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); 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_annotation_check(cxobj *x, yang_stmt *ymod, int *ismeta);
int yang_metadata_init(clixon_handle h); 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_ */ #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); 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); 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_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_post(clixon_handle h, yang_stmt *yspec, int modmin);
int yang_parse_optimize_uses(clixon_handle h, yang_stmt *yspec); int yang_parse_optimize_uses(clixon_handle h, yang_stmt *yspec);
int yang_spec_parse_module(clixon_handle h, const char *module, int yang_spec_parse_module(clixon_handle h, const char *module,

View file

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

View file

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

View file

@ -823,14 +823,44 @@ yspec_new(clixon_handle h,
goto done; goto done;
if (yn_insert(ymounts, yspec) < 0) if (yn_insert(ymounts, yspec) < 0)
goto done; goto done;
/* Special trick for shared yspecs */
if (yang_cvec_add(yspec, CGV_STRING, name) < 0)
goto done;
return yspec; return yspec;
done: done:
return NULL; 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 /*! Create new yang node/statement given size
* *
* Size parameter for variable size, eg extended YANG struct * Size parameter for variable size, eg extended YANG struct
@ -2199,7 +2229,7 @@ yang_print(FILE *f,
/*! Print yang top-level modules only /*! Print yang top-level modules only
* *
* @param[in] f File to print to. * @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 * @see yang_print_cbuf
*/ */
int int
@ -2211,7 +2241,7 @@ yang_spec_print(FILE *f,
int inext; int inext;
if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){ 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; return -1;
} }
inext = 0; inext = 0;
@ -2227,6 +2257,39 @@ yang_spec_print(FILE *f,
return 0; 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 /* Log/debug info about top-level (sub)modules no recursion
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] dbglevel Debug level * @param[in] dbglevel Debug level

View file

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

View file

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

View file

@ -1002,31 +1002,44 @@ filename2revision(const char *filename,
return retval; 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] h CLICON handle
* @param[in] module Name of main YANG module. * @param[in] module Name of main YANG module.
* @param[in] revision Revision or NULL * @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) * @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 0 No matching entry found
* @retval -1 Error * @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 int
yang_file_find_match(clixon_handle h, yang_file_find_match(clixon_handle h,
const char *module, const char *module,
const char *revision, const char *revision,
const char *domain,
cbuf *fbuf) cbuf *fbuf)
{ {
int retval = -1; int retval = -1;
cbuf *regex = NULL; cbuf *regex = NULL;
cxobj *x; cxobj *x;
cxobj *xc; cxobj *xc;
char *dir; char *dir;
cvec *cvv = NULL; cvec *cvv = NULL;
cg_var *cv = NULL; cg_var *cv = NULL;
cg_var *bestcv = NULL; cg_var *bestcv = NULL;
cbuf *cb = NULL;
struct dirent *dp = NULL;
int ndp;
/* get clicon config file in xml form */ /* get clicon config file in xml form */
if ((x = clicon_conf_xml(h)) == NULL) if ((x = clicon_conf_xml(h)) == NULL)
@ -1044,35 +1057,38 @@ yang_file_find_match(clixon_handle h,
else else
cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$", cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$",
module); module);
xc = NULL; /* First look in Main YANG dir, either MAIN or DOMAIN */
if (domain != NULL &&
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) { (dir = clicon_yang_domain_dir(h)) != NULL){
if (strcmp(xml_name(xc), "CLICON_YANG_MAIN_DIR") == 0){ if ((cb = cbuf_new()) == NULL){
struct dirent *dp = NULL; clixon_err(OE_UNIX, errno, "cbuf_new");
int ndp; goto done;
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);
} }
else if (strcmp(xml_name(xc), "CLICON_YANG_DIR") == 0 && cprintf(cb, "%s/%s", dir, domain);
(dir = xml_body(xc)) != NULL){ 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 */ /* get all matching files in this directory recursively */
if ((cvv = cvec_new(0)) == NULL){ if ((cvv = cvec_new(0)) == NULL){
clixon_err(OE_UNIX, errno, "cvec_new"); clixon_err(OE_UNIX, errno, "cvec_new");
@ -1094,8 +1110,7 @@ yang_file_find_match(clixon_handle h,
if (bestcv){ if (bestcv){
if (fbuf) if (fbuf)
cprintf(fbuf, "%s", cv_string_get(bestcv)); /* file path */ cprintf(fbuf, "%s", cv_string_get(bestcv)); /* file path */
retval = 1; /* found */ goto found;
goto done;
} }
if (cvv){ if (cvv){
cvec_free(cvv); cvec_free(cvv);
@ -1106,11 +1121,18 @@ yang_file_find_match(clixon_handle h,
ok: ok:
retval = 0; retval = 0;
done: done:
if (dp)
free(dp);
if (cb)
cbuf_free(cb);
if (cvv) if (cvv)
cvec_free(cvv); cvec_free(cvv);
if (regex) if (regex)
cbuf_free(regex); cbuf_free(regex);
return retval; return retval;
found:
retval = 1;
goto done;
} }
/*! Open a file, read into a string and invoke yang parsing /*! 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] module Module name
* @param[in] revision Revision (or NULL) * @param[in] revision Revision (or NULL)
* @param[in] yspec Yang statement * @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 * @param[in] origname Name of yang module triggering this parsing, for logging
* @retval ymod YANG (sub)module * @retval ymod YANG (sub)module
* @retval NULL Failed * @retval NULL Failed
@ -1173,6 +1196,7 @@ yang_parse_module(clixon_handle h,
const char *module, const char *module,
const char *revision, const char *revision,
yang_stmt *yspec, yang_stmt *yspec,
char *domain,
char *origname) char *origname)
{ {
cbuf *fbuf = NULL; cbuf *fbuf = NULL;
@ -1189,7 +1213,7 @@ yang_parse_module(clixon_handle h,
goto done; goto done;
} }
/* Match a yang file with or without revision in yang-dir list */ /* 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; goto done;
if (nr == 0){ if (nr == 0){
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
@ -1289,7 +1313,7 @@ yang_parse_recurse(clixon_handle h,
keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE, keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
submodule) == NULL){ submodule) == NULL){
/* recursive call */ /* 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; goto done;
/* Sanity check: if submodule, its belongs-to statement shall point to the module */ /* Sanity check: if submodule, its belongs-to statement shall point to the module */
if (keyw == Y_INCLUDE){ 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) if (yang_find_module_by_name_revision(yspec, name, revision) != NULL)
goto ok; goto ok;
/* Find a yang module and parse it and all its submodules */ /* 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; goto done;
if (yang_parse_post(h, yspec, modmin) < 0) if (yang_parse_post(h, yspec, modmin) < 0)
goto done; goto done;

View file

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

View file

@ -74,7 +74,7 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in) # clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_REV="2024-08-01" CLIXON_AUTOCLI_REV="2024-08-01"
CLIXON_LIB_REV="2024-08-01" CLIXON_LIB_REV="2024-08-01"
CLIXON_CONFIG_REV="2024-04-01" CLIXON_CONFIG_REV="2024-08-01"
CLIXON_RESTCONF_REV="2022-08-01" CLIXON_RESTCONF_REV="2022-08-01"
CLIXON_EXAMPLE_REV="2022-11-01" CLIXON_EXAMPLE_REV="2022-11-01"

238
test/test_yang_mount_isolate.sh Executable file
View file

@ -0,0 +1,238 @@
#!/usr/bin/env bash
# Test for isolating modules between mount-points.
# That is, so that two modules with same name, revision and namespaces can have different content
# in two separate mount-points.
# Note the mylabel domain is hardcoded in example_backend.c ca_yang_mount callback
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
cfg=$dir/conf_mount.xml
clispec=$dir/automode.cli
test -d $dir/main || mkdir $dir/main
fyang=$dir/main/clixon-example.yang
test -d $dir/domains/mylabel || mkdir -p $dir/domains/mylabel
#fyang0=$dir/domains/mylabel/clixon-mount0.yang
fyang0=$dir/domains/mylabel/clixon-example.yang
fyang1=$dir/domains/mylabel/clixon-mount1.yang
fyang2=$dir/domains/mylabel/clixon-mount2.yang
CFD=$dir/conf.d
test -d $CFD || mkdir -p $CFD
AUTOCLI=$(autocli_config clixon-\* kw-nokey false)
if [ $? -ne 0 ]; then
err1 "Error when generating certs"
fi
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_CONFIGDIR>$CFD</CLICON_CONFIGDIR>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${dir}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_YANG_DOMAIN_DIR>$dir/domains</CLICON_YANG_DOMAIN_DIR>
<CLICON_YANG_LIBRARY>true</CLICON_YANG_LIBRARY>
<CLICON_CLISPEC_DIR>$dir</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/run/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/run/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_NETCONF_MONITORING>true</CLICON_NETCONF_MONITORING>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277>
<CLICON_YANG_SCHEMA_MOUNT>true</CLICON_YANG_SCHEMA_MOUNT>
</clixon-config>
EOF
cat <<EOF > $CFD/autocli.xml
<clixon-config xmlns="http://clicon.org/config">
<autocli>
<module-default>false</module-default>
<list-keyword-default>kw-nokey</list-keyword-default>
<grouping-treeref>true</grouping-treeref>
<rule>
<name>include clixon</name>
<operation>enable</operation>
<module-name>clixon-*</module-name>
</rule>
</autocli>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import ietf-yang-schema-mount {
prefix yangmnt;
}
container top{
list mylist{
key name;
leaf name{
type string;
}
container root{
presence "Otherwise root is not visible";
yangmnt:mount-point "mylabel"{
description "Root for other yang models";
}
}
}
}
}
EOF
cat <<EOF > $fyang0
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix m0;
import clixon-mount1 {
prefix m1;
}
import clixon-mount2 {
prefix m2;
}
}
EOF
cat <<EOF > $fyang1
module clixon-mount1{
yang-version 1.1;
namespace "urn:example:mount1";
prefix m1;
container mount1{
list mylist1{
key name1;
leaf name1{
type string;
}
}
}
}
EOF
cat <<EOF > $fyang2
module clixon-mount2{
yang-version 1.1;
namespace "urn:example:mount2";
prefix m2;
import clixon-mount1 {
prefix m1;
}
grouping ag2 {
leaf option2{
type string;
}
}
grouping ag1 {
container options {
leaf option1{
type string;
}
uses ag2;
}
}
augment /m1:mount1/m1:mylist1 {
uses ag1;
}
}
EOF
cat <<EOF > $clispec
CLICON_MODE="example";
CLICON_PROMPT="%U@%H %W> ";
CLICON_PLUGIN="example_cli";
# Autocli syntax tree operations
set @datamodel, cli_auto_set();
merge @datamodel, cli_auto_merge();
create @datamodel, cli_auto_create();
delete("Delete a configuration item") @datamodel, cli_auto_del();
validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit();
quit("Quit"), cli_quit();
show("Show a particular state of the system"){
configuration("Show configuration"), cli_show_auto_mode("candidate", "xml", true, false);{
xml("Show configuration as XML"), cli_show_auto_mode("candidate", "xml", false, false);
cli("Show configuration as CLI commands"), cli_show_auto_mode("candidate", "cli", false, false, "report-all", "set ");
netconf("Show configuration as netconf edit-config operation"), cli_show_auto_mode("candidate", "netconf", false, false);
text("Show configuration as text"), cli_show_auto_mode("candidate", "text", false, false);
json("Show configuration as JSON"), cli_show_auto_mode("candidate", "json", false, false);
}
state("Show configuration and state"), cli_show_auto_mode("running", "xml", false, true);
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg -- -m clixon-example -M urn:example:clixon"
start_backend -s init -f $cfg -- -m clixon-example -M urn:example:clixon
fi
new "wait backend"
wait_backend
new "Add two mountpoints: x and y"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root/></mylist><mylist><name>y</name><root/></mylist></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "netconf commit"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Retrieve schema-mounts with <get> Operation"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"></schema-mounts></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\"><mount-point><module>clixon-example</module><label>mylabel</label><config>true</config><inline/></mount-point></schema-mounts></data></rpc-reply>"
new "get yang-lib at mountpoint"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get><filter type=\"subtree\"><top xmlns=\"urn:example:clixon\"><mylist/></top>></filter></get></rpc>" "<rpc-reply $DEFAULTNS><data><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root><yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>mylabel</name><module><name>clixon-example</name><namespace>urn:example:clixon</namespace></module></module-set></yang-library></root></mylist><mylist><name>y</name><root><yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module-set><name>mylabel</name><module><name>clixon-example</name><namespace>urn:example:clixon</namespace></module></module-set></yang-library></root></mylist></top></data></rpc-reply>"
new "check there is statistics from mountpoint"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><stats xmlns=\"http://clicon.org/lib\"></stats></rpc>" '<module-set><name>mountpoint: /top/mylist\[name="x"\]/root</name><nr>'
new "Add data to mounts"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root><mount1 xmlns=\"urn:example:mount1\"><mylist1><name1>x1</name1></mylist1></mount1></root></mylist></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "netconf commit"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Add mounted augment data 2"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root><mount1 xmlns=\"urn:example:mount1\"><mylist1><name1>x1</name1><options xmlns=\"urn:example:mount2\"><option2>bar</option2></options></mylist1></mount1></root></mylist></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "netconf commit"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><commit/></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "get mounted augment data"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>" "<rpc-reply $DEFAULTNS><data><top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root><mount1 xmlns=\"urn:example:mount1\"><mylist1><name1>x1</name1><options xmlns=\"urn:example:mount2\"><option2>bar</option2></options></mylist1></mount1></root></mylist><mylist><name>y</name><root/></mylist></top></data></rpc-reply>"
new "cli show config"
expectpart "$($clixon_cli -1 -f $cfg show config xml -- -m clixon-example -M urn:example:clixon)" 0 "<top xmlns=\"urn:example:clixon\"><mylist><name>x</name><root><mount1 xmlns=\"urn:example:mount1\"><mylist1><name1>x1</name1><options xmlns=\"urn:example:mount2\"><option2>bar</option2></options></mylist1></mount1></root></mylist><mylist><name>y</name><root/></mylist></top>"
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
fi
sudo rm -rf $dir
new "endtest"
endtest

View file

@ -42,7 +42,7 @@ datarootdir = @datarootdir@
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
# Note: mirror these to test/config.sh.in # Note: mirror these to test/config.sh.in
YANGSPECS = clixon-config@2024-04-01.yang # 7.1 YANGSPECS = clixon-config@2024-08-01.yang # 7.2
YANGSPECS += clixon-lib@2024-08-01.yang # 7.2 YANGSPECS += clixon-lib@2024-08-01.yang # 7.2
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang

View file

@ -49,6 +49,26 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
revision 2024-08-01 {
description
"Added options:
CLICON_YANG_DOMAIN_DIR
Released in Clixon 7.2";
}
revision 2024-04-01 {
description
"Added options:
CLICON_NETCONF_DUPLICATE_ALLOW: Disable duplicate check in NETCONF messages.
CLICON_LOG_DESTINATION: Default log destination
CLICON_LOG_FILE: Which file to log to if file logging
CLICON_DEBUG: Debug flags.
CLICON_YANG_SCHEMA_MOUNT_SHARE: Share same YANGs of equal moint-points.
CLICON_SOCK_PRIO: Enable socket event priority
CLICON_XMLDB_MULTI: Split datastore into multiple sub files
CLICON_CLI_OUTPUT_FORMAT: Default CLI output format
CLICON_AUTOLOCK: Implicit locks
Released in Clixon 7.1";
}
revision 2024-01-01 { revision 2024-01-01 {
description description
"Changed semantics: "Changed semantics:
@ -415,6 +435,33 @@ module clixon-config {
} }
} }
} }
typedef log_destination_t {
description
"Log destination flags
Can also be given directly as -l <flag> to clixon commands
Note there are also constants in the code (logdstmap) that need to be
in sync with these values.
The duplication is because of bootstrapping, logging is needed before YANG
loaded";
type bits {
bit syslog {
position 0;
description "Syslog";
}
bit stderr {
position 1;
description "Standard I/O Error";
}
bit stdout {
position 2;
description "Standard I/O Output";
}
bit file {
position 3;
description "Log to file. By default clixon.log int current directory";
}
}
}
container clixon-config { container clixon-config {
container restconf { container restconf {
uses clrc:clixon-restconf; uses clrc:clixon-restconf;
@ -445,6 +492,7 @@ module clixon-config {
Ensure that YANG_INSTALLDIR (default Ensure that YANG_INSTALLDIR (default
/usr/local/share/clixon) is present in the path"; /usr/local/share/clixon) is present in the path";
} }
/* Configuration */
leaf CLICON_CONFIGFILE{ leaf CLICON_CONFIGFILE{
type string; type string;
description description
@ -462,7 +510,9 @@ module clixon-config {
AFTER the main config file (CLICON_CONFIGFILE) in the following way: AFTER the main config file (CLICON_CONFIGFILE) in the following way:
- leaf values are overwritten - leaf values are overwritten
- leaf-list values are appended - leaf-list values are appended
The files in this directory will be loaded alphabetically. The files in this directory are loaded alphabetically.
Only files ending with .xml are read
Sub-structures, eg <autocli> are replaced with the latest (alphabetically)
If the dir is given but does not exist will result in an error. If the dir is given but does not exist will result in an error.
You can override file setting with -E <dir> command-line option. You can override file setting with -E <dir> command-line option.
Note that due to bootstraping this value is only meaningful in the main config file"; Note that due to bootstraping this value is only meaningful in the main config file";
@ -476,6 +526,7 @@ module clixon-config {
This field is a 'bootstrap' field. This field is a 'bootstrap' field.
"; ";
} }
/* YANG */
leaf CLICON_YANG_MAIN_FILE { leaf CLICON_YANG_MAIN_FILE {
type string; type string;
description description
@ -489,6 +540,18 @@ module clixon-config {
"If given, load all modules in this directory (all .yang files) "If given, load all modules in this directory (all .yang files)
See also CLICON_YANG_DIR which specifies a path of dirs"; See also CLICON_YANG_DIR which specifies a path of dirs";
} }
leaf CLICON_YANG_DOMAIN_DIR {
type string;
description
"Virtual domain directory for RFC 8528 mount-points.
If set and domain is given, instead of loading from CLICON_YANG_MAIN_DIR,
look for .yang files first in CLICON_YANG_DOMAIN_DIR/domain,
where domain is given as yangmnt:mount-point <domain>;
Useful in eg mountpoints where another YANG domain may be required,
even isolated from the main YANG context, as well as from other moint-points.
Note that CLICON_YANG_DIR that may be given as library YANGs are not isolated.
If not set, use CLICON_YANG_MAIN_DIR as default.";
}
leaf CLICON_YANG_MODULE_MAIN { leaf CLICON_YANG_MODULE_MAIN {
type string; type string;
description description
@ -526,12 +589,6 @@ module clixon-config {
Note this is similar to what happens to YANG nodes that are disabled by a false Note this is similar to what happens to YANG nodes that are disabled by a false
if-feature statement."; if-feature statement.";
} }
leaf CLICON_BACKEND_DIR {
type string;
description
"Location of backend .so plugins. Load all .so
plugins in this dir as backend plugins";
}
leaf CLICON_YANG_SCHEMA_MOUNT{ leaf CLICON_YANG_SCHEMA_MOUNT{
type boolean; type boolean;
description description
@ -544,12 +601,102 @@ module clixon-config {
Further, autocli syntax is added by definining a tree resolve wrapper"; Further, autocli syntax is added by definining a tree resolve wrapper";
default false; default false;
} }
leaf CLICON_YANG_SCHEMA_MOUNT_SHARE {
type boolean;
description
"For optimization purposes, share same YANGs of equal moint-points.
The mount-points need to be 'equal' in the sense that it has the same YANG
(yangmnt:mount-point is on same node).
A comparison is made between yang modules and revision and must match exactly.
If so, a new yang-spec is not created, instead the other is used.
Only if CLICON_YANG_SCHEMA_MOUNT is enabled";
default false;
}
leaf CLICON_YANG_AUGMENT_ACCEPT_BROKEN {
type boolean;
default false;
description
"Debug option. If enabled, accept broken augments on the form:
augment <target> { ... }
where <target> is an XPath which MUST be an existing node but for many
yangmodels do not.
There are several cases why this may be the case:
- syntax errors,
- features that need to be enabled
- wrong XPaths, etc
This option should be enabled only for passing some testcases it should
normally never be enabled in system YANGs that are used in a system.";
}
leaf CLICON_YANG_LIBRARY {
type boolean;
default true;
description
"Enable YANG library support as state data according to RFC8525.
If enabled, module info will appear when doing netconf get or
restconf GET.
The module state data is on the form:
<yang-library><module-set>...
instead where the module state is on the form:
<modules-state>...
See also CLICON_XMLDB_MODSTATE where the module state info is used to tag datastores
with module information.";
}
/* Backend */
leaf CLICON_BACKEND_DIR {
type string;
description
"Location of backend .so plugins. Load all .so
plugins in this dir as backend plugins";
}
leaf CLICON_BACKEND_REGEXP { leaf CLICON_BACKEND_REGEXP {
type string; type string;
description description
"Regexp of matching backend plugins in CLICON_BACKEND_DIR"; "Regexp of matching backend plugins in CLICON_BACKEND_DIR";
default "(.so)$"; default "(.so)$";
} }
leaf CLICON_BACKEND_USER {
type string;
description
"User name for backend (both foreground and daemonized).
If you set this value the backend if started as root will lower
the privileges after initialization.
The ownership of files created by the backend will also be set to this
user (eg datastores).
It also sets the backend unix socket owner to this user, but its group
is set by CLICON_SOCK_GROUP.
See also CLICON_BACKEND_PRIVILEGES setting";
}
leaf CLICON_BACKEND_PRIVILEGES {
type priv_mode;
default none;
description
"Backend privileges mode.
If CLICON_BACKEND_USER user is set, mode can be set to drop_perm or
drop_temp.
Drop privs may not be used together with CLICON_XMLDB_MULTI";
}
leaf CLICON_BACKEND_PIDFILE {
type string;
mandatory true;
description "Process-id file of backend daemon";
}
leaf CLICON_BACKEND_RESTCONF_PROCESS {
type boolean;
default false;
description
"If set, enable process-control of restconf daemon, ie start/stop restconf
daemon internally from backend daemon.
Also, if set, restconf daemon queries backend for its config
if not set, restconf daemon reads its config from main config file
It uses clixon-restconf.yang for config and clixon-lib.yang for RPC
Process control of restconf daemon is as follows:
- on RPC start, if enable is true, start the service, if false, error or ignore it
- on RPC stop, stop the service
- on backend start make the state as configured
- on enable change, make the state as configured
Disable if you start the restconf daemon by other means.";
}
/* Netconf */
leaf CLICON_NETCONF_DIR{ leaf CLICON_NETCONF_DIR{
type string; type string;
description "Location of netconf (frontend) .so plugins"; description "Location of netconf (frontend) .so plugins";
@ -607,6 +754,32 @@ module clixon-config {
config"; config";
status obsolete; status obsolete;
} }
leaf CLICON_NETCONF_MONITORING {
type boolean;
default true;
description
"Enable Netconf monitoring support as state data according to RFC6022.
If enabled, netconf monitoring info will appear when doing netconf get or
restconf GET.";
}
leaf CLICON_NETCONF_MONITORING_LOCATION {
type string;
description
"Extra Netconf monitoring location directory where schemas can be retrieved
apart from NETCONF.
Only if CLICON_NETCONF_MONITORING";
}
leaf CLICON_NETCONF_DUPLICATE_ALLOW {
type boolean;
default false;
description
"Disable duplicate check in NETCONF messages.
In Clixon 7.0, a stricter check of duplicate entries in incoming NETCONF messages was made.
More specifically: lists and leaf-lists with non-unique entries.
Enable to disable this check, and to allow duplicates in incoming NETCONF messages.
Note that this is an error by such a client, but there is some legacy code that uses this";
}
/* HTTP and Restconf */
leaf CLICON_RESTCONF_API_ROOT { leaf CLICON_RESTCONF_API_ROOT {
type string; type string;
default "/restconf"; default "/restconf";
@ -722,6 +895,7 @@ module clixon-config {
Both feature clixon-restconf:http-data and restconf/enable-http-data Both feature clixon-restconf:http-data and restconf/enable-http-data
must be enabled for this match to occur."; must be enabled for this match to occur.";
} }
/* Clixon CLI */
leaf CLICON_CLI_DIR { leaf CLICON_CLI_DIR {
type string; type string;
description description
@ -760,8 +934,8 @@ module clixon-config {
type int32; type int32;
default 1; default 1;
description description
"Set to 0 if you want CLI to wrap to next line. "Set to 0 if you want CLI INPUT to wrap to next line.
Set to 1 if you want CLI to scroll sideways when approaching Set to 1 if you want CLI INPUT to scroll sideways when approaching
right margin"; right margin";
} }
leaf CLICON_CLI_LINES_DEFAULT { leaf CLICON_CLI_LINES_DEFAULT {
@ -863,6 +1037,13 @@ module clixon-config {
While setting this value makes sense for adding new values, it makes less sense for While setting this value makes sense for adding new values, it makes less sense for
deleting."; deleting.";
} }
leaf CLICON_CLI_OUTPUT_FORMAT {
type cl:datastore_format;
default xml;
description
"Default CLI output format.";
}
/* Internal socket */
leaf CLICON_SOCK_FAMILY { leaf CLICON_SOCK_FAMILY {
type socket_address_family; type socket_address_family;
default UNIX; default UNIX;
@ -900,46 +1081,17 @@ module clixon-config {
"Group membership to access clixon_backend unix socket and gid for "Group membership to access clixon_backend unix socket and gid for
deamon"; deamon";
} }
leaf CLICON_BACKEND_USER { leaf CLICON_SOCK_PRIO {
type string;
description
"User name for backend (both foreground and daemonized).
If you set this value the backend if started as root will lower
the privileges after initialization.
The ownership of files created by the backend will also be set to this
user (eg datastores).
It also sets the backend unix socket owner to this user, but its group
is set by CLICON_SOCK_GROUP.
See also CLICON_BACKEND_PRIVILEGES setting";
}
leaf CLICON_BACKEND_PRIVILEGES {
type priv_mode;
default none;
description
"Backend privileges mode.
If CLICON_BACKEND_USER user is set, mode can be set to drop_perm or
drop_temp.";
}
leaf CLICON_BACKEND_PIDFILE {
type string;
mandatory true;
description "Process-id file of backend daemon";
}
leaf CLICON_BACKEND_RESTCONF_PROCESS {
type boolean; type boolean;
default false; default false;
description description
"If set, enable process-control of restconf daemon, ie start/stop restconf "Enable socket event priority.
daemon internally from backend daemon. If enabled, a file-descriptor can be registered as high prio.
Also, if set, restconf daemon queries backend for its config Presently, the backend socket has higher prio than others.
if not set, restconf daemon reads its config from main config file (should be made more generic)
It uses clixon-restconf.yang for config and clixon-lib.yang for RPC Note that a side-effect of enabling this option is that fairness of
Process control of restconf daemon is as follows: non-prio events is disabled
- on RPC start, if enable is true, start the service, if false, error or ignore it This is useful if the backend opens other sockets, such as the controller";
- on RPC stop, stop the service
- on backend start make the state as configured
- on enable change, make the state as configured
Disable if you start the restconf daemon by other means.";
} }
leaf CLICON_AUTOCOMMIT { leaf CLICON_AUTOCOMMIT {
type int32; type int32;
@ -951,12 +1103,17 @@ module clixon-config {
persistent confirming commit. persistent confirming commit.
(consider boolean)"; (consider boolean)";
} }
leaf CLICON_XMLDB_DIR { leaf CLICON_AUTOLOCK {
type string; type boolean;
mandatory true; default false;
description description
"Directory where \"running\", \"candidate\" and \"startup\" are placed."; "Set if all edit-config implicitly locks without the need of an explicit lock-db
In short, the lock is obtained by edit-config and copy-config and released by
discard and commit.
Also, any edits in candidate are discarded if the client closes the connection.
This effectively disables shared candidate";
} }
/* Datastore XMLDB */
leaf CLICON_DATASTORE_CACHE { leaf CLICON_DATASTORE_CACHE {
type datastore_cache; type datastore_cache;
default cache; default cache;
@ -968,6 +1125,16 @@ module clixon-config {
Note that from 7.0 this is OBSOLETED, only datastore_cache is supported"; Note that from 7.0 this is OBSOLETED, only datastore_cache is supported";
status obsolete; status obsolete;
} }
leaf CLICON_XMLDB_DIR {
type string;
mandatory true;
description
"Directory where datastores such as \"running\", \"candidate\" and \"startup\"
are placed.
If CLICON_XMLDB_MULTI is enabled, this is the directory where a datastore
subdir is stored, such as \"running.d/\"
";
}
leaf CLICON_XMLDB_FORMAT { leaf CLICON_XMLDB_FORMAT {
type cl:datastore_format; type cl:datastore_format;
default xml; default xml;
@ -986,7 +1153,9 @@ module clixon-config {
default false; default false;
description description
"If set, tag datastores with RFC 8525 YANG Module Library "If set, tag datastores with RFC 8525 YANG Module Library
info. When loaded at startup, a check is made if the system info.
By default, modstate is added last in datastore.
When loaded at startup, a check is made if the system
yang modules match."; yang modules match.";
} }
leaf CLICON_XMLDB_UPGRADE_CHECKOLD { leaf CLICON_XMLDB_UPGRADE_CHECKOLD {
@ -999,6 +1168,20 @@ module clixon-config {
Will fail startup if old yang not found or if old config does not match. Will fail startup if old yang not found or if old config does not match.
If not set, no yang check of old config is made until it is upgraded to new yang."; If not set, no yang check of old config is made until it is upgraded to new yang.";
} }
leaf CLICON_XMLDB_MULTI {
type boolean;
default false;
description
"Split configure datastore into multiple sub files
Uses .d/ directory structure with <digest>.xml and 0.xml as root
JSON not supported.
Splits are marked in YANG using extension xl:xmldb-split, (typical usage is
mount-points).
Note that algorithm for not updating unchanged files only applies to edits,
commit copies all files regardless.
May not work together with CLICON_BACKEND_PRIVILEGES=drop and root, since
new files need to be created in XMLDB_DIR";
}
leaf CLICON_XML_CHANGELOG { leaf CLICON_XML_CHANGELOG {
type boolean; type boolean;
default false; default false;
@ -1056,21 +1239,6 @@ module clixon-config {
If true: The symbols defined by this shared object will be made available for symbol res If true: The symbols defined by this shared object will be made available for symbol res
olution of subsequently loaded shared objects."; olution of subsequently loaded shared objects.";
} }
leaf CLICON_YANG_AUGMENT_ACCEPT_BROKEN {
type boolean;
default false;
description
"Debug option. If enabled, accept broken augments on the form:
augment <target> { ... }
where <target> is an XPath which MUST be an existing node but for many
yangmodels do not.
There are several cases why this may be the case:
- syntax errors,
- features that need to be enabled
- wrong XPaths, etc
This option should be enabled only for passing some testcases it should
normally never be enabled in system YANGs that are used in a system.";
}
leaf CLICON_NAMESPACE_NETCONF_DEFAULT { leaf CLICON_NAMESPACE_NETCONF_DEFAULT {
type boolean; type boolean;
default false; default false;
@ -1080,7 +1248,6 @@ module clixon-config {
If defined, top-level rpc calls need not have namespaces (eg using xmlns=<ns>) If defined, top-level rpc calls need not have namespaces (eg using xmlns=<ns>)
since the default NETCONF namespace will be assumed. (This is not standard). since the default NETCONF namespace will be assumed. (This is not standard).
See rfc6241 3.1: urn:ietf:params:xml:ns:netconf:base:1.0."; See rfc6241 3.1: urn:ietf:params:xml:ns:netconf:base:1.0.";
} }
leaf CLICON_STARTUP_MODE { leaf CLICON_STARTUP_MODE {
type startup_mode; type startup_mode;
@ -1094,6 +1261,7 @@ module clixon-config {
The current only case where such a user is used is in RESTCONF authentication when The current only case where such a user is used is in RESTCONF authentication when
auth-type=none and no known user is known."; auth-type=none and no known user is known.";
} }
/* Network Configuration Access Control Model (NACM) */
leaf CLICON_NACM_MODE { leaf CLICON_NACM_MODE {
type nacm_mode; type nacm_mode;
default disabled; default disabled;
@ -1136,20 +1304,6 @@ module clixon-config {
If this option is set, Clixon disables NACM if a datastore does NOT contain a If this option is set, Clixon disables NACM if a datastore does NOT contain a
NACM config on load."; NACM config on load.";
} }
leaf CLICON_YANG_LIBRARY {
type boolean;
default true;
description
"Enable YANG library support as state data according to RFC8525.
If enabled, module info will appear when doing netconf get or
restconf GET.
The module state data is on the form:
<yang-library><module-set>...
instead where the module state is on the form:
<modules-state>...
See also CLICON_XMLDB_MODSTATE where the module state info is used to tag datastores
with module information.";
}
leaf CLICON_MODULE_SET_ID { leaf CLICON_MODULE_SET_ID {
type string; type string;
default "0"; default "0";
@ -1162,21 +1316,7 @@ module clixon-config {
If CLICON_MODULE_LIBRARY_RFC7895 is enabled, it sets the modules-state/module-set-id If CLICON_MODULE_LIBRARY_RFC7895 is enabled, it sets the modules-state/module-set-id
instead"; instead";
} }
leaf CLICON_NETCONF_MONITORING { /* Notification streams */
type boolean;
default true;
description
"Enable Netconf monitoring support as state data according to RFC6022.
If enabled, netconf monitoring info will appear when doing netconf get or
restconf GET.";
}
leaf CLICON_NETCONF_MONITORING_LOCATION {
type string;
description
"Extra Netconf monitoring location directory where schemas can be retrieved
apart from NETCONF.
Only if CLICON_NETCONF_MONITORING";
}
leaf CLICON_STREAM_DISCOVERY_RFC5277 { leaf CLICON_STREAM_DISCOVERY_RFC5277 {
type boolean; type boolean;
default false; default false;
@ -1232,7 +1372,27 @@ module clixon-config {
units s; units s;
description "Retention for stream replay buffers in seconds, ie how much description "Retention for stream replay buffers in seconds, ie how much
data to store before dropping. 0 means no retention"; data to store before dropping. 0 means no retention";
}
/* Log and debug */
leaf CLICON_DEBUG{
type cl:clixon_debug_t;
description
"Debug flags as bitfields.
Can also be given directly as -D <flag> to clixon commands (which overrides this).";
}
leaf CLICON_LOG_DESTINATION {
type log_destination_t;
description
"Log destination.
If not given, default log destination is syslog for all applications,
except clixon_cli where default is stderr.
See also command-line option -l <s|e|o|n|f>";
}
leaf CLICON_LOG_FILE {
type string;
description
"Which file to log to if log destination is file
That is CLIXON_LOG_DESTINATION is FILE or command started with -l f";
} }
leaf CLICON_LOG_STRING_LIMIT { leaf CLICON_LOG_STRING_LIMIT {
type uint32; type uint32;
@ -1241,8 +1401,8 @@ module clixon-config {
"Length limitation of debug and log strings. "Length limitation of debug and log strings.
Especially useful for dynamic debug strings, such as packet dumps. Especially useful for dynamic debug strings, such as packet dumps.
0 means no limit"; 0 means no limit";
} }
/* SNMP */
leaf-list CLICON_SNMP_MIB { leaf-list CLICON_SNMP_MIB {
description description
"Names of MIBs that are used by clixon_snmp. "Names of MIBs that are used by clixon_snmp.