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
* Added yang domains for mount-point isolation
* New option: `CLICON_YANG_DOMAIN_DIR`
* Restconf: Support for list of media in Accept header
* Refactoring of schema mount-points
* 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)
* See: https://clixon-docs.readthedocs.io/en/latest/cli.html#cli-aliases
* 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
- 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
* 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()`
* New `yn_iter()` yang iterator replaces `yn_each()`
* 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
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.
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
```

View file

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

View file

@ -37,6 +37,7 @@
* argc/argv after -- in clixon_cli:
* -m <yang> Mount this yang on mountpoint
* -M <namespace> Namespace of mountpoint, note both -m and -M must exist
* Note module-set hard-coded to "mylabel"
*/
#include <stdio.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, "<module-set>");
cprintf(cb, "<name>mount</name>");
cprintf(cb, "<name>mylabel</name>");
cprintf(cb, "<module>");
/* In yang name+namespace is mandatory, but not revision */
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>
* Mount this yang on mountpoint
* Note module-set hard-coded to "mylabel"
*/
static char *_mount_yang = NULL;
static char *_mount_namespace = NULL;

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;

View file

@ -74,7 +74,7 @@ DATASTORE_TOP="config"
# clixon yang revisions occuring in tests (see eg yang/clixon/Makefile.in)
CLIXON_AUTOCLI_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_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@
# 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-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang

View file

@ -49,6 +49,26 @@ module clixon-config {
***** 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 {
description
"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 restconf {
uses clrc:clixon-restconf;
@ -445,6 +492,7 @@ module clixon-config {
Ensure that YANG_INSTALLDIR (default
/usr/local/share/clixon) is present in the path";
}
/* Configuration */
leaf CLICON_CONFIGFILE{
type string;
description
@ -462,7 +510,9 @@ module clixon-config {
AFTER the main config file (CLICON_CONFIGFILE) in the following way:
- leaf values are overwritten
- 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.
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";
@ -476,6 +526,7 @@ module clixon-config {
This field is a 'bootstrap' field.
";
}
/* YANG */
leaf CLICON_YANG_MAIN_FILE {
type string;
description
@ -489,6 +540,18 @@ module clixon-config {
"If given, load all modules in this directory (all .yang files)
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 {
type string;
description
@ -526,12 +589,6 @@ module clixon-config {
Note this is similar to what happens to YANG nodes that are disabled by a false
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{
type boolean;
description
@ -544,12 +601,102 @@ module clixon-config {
Further, autocli syntax is added by definining a tree resolve wrapper";
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 {
type string;
description
"Regexp of matching backend plugins in CLICON_BACKEND_DIR";
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{
type string;
description "Location of netconf (frontend) .so plugins";
@ -607,6 +754,32 @@ module clixon-config {
config";
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 {
type string;
default "/restconf";
@ -722,6 +895,7 @@ module clixon-config {
Both feature clixon-restconf:http-data and restconf/enable-http-data
must be enabled for this match to occur.";
}
/* Clixon CLI */
leaf CLICON_CLI_DIR {
type string;
description
@ -760,8 +934,8 @@ module clixon-config {
type int32;
default 1;
description
"Set to 0 if you want CLI to wrap to next line.
Set to 1 if you want CLI to scroll sideways when approaching
"Set to 0 if you want CLI INPUT to wrap to next line.
Set to 1 if you want CLI INPUT to scroll sideways when approaching
right margin";
}
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
deleting.";
}
leaf CLICON_CLI_OUTPUT_FORMAT {
type cl:datastore_format;
default xml;
description
"Default CLI output format.";
}
/* Internal socket */
leaf CLICON_SOCK_FAMILY {
type socket_address_family;
default UNIX;
@ -900,46 +1081,17 @@ module clixon-config {
"Group membership to access clixon_backend unix socket and gid for
deamon";
}
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.";
}
leaf CLICON_BACKEND_PIDFILE {
type string;
mandatory true;
description "Process-id file of backend daemon";
}
leaf CLICON_BACKEND_RESTCONF_PROCESS {
leaf CLICON_SOCK_PRIO {
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.";
"Enable socket event priority.
If enabled, a file-descriptor can be registered as high prio.
Presently, the backend socket has higher prio than others.
(should be made more generic)
Note that a side-effect of enabling this option is that fairness of
non-prio events is disabled
This is useful if the backend opens other sockets, such as the controller";
}
leaf CLICON_AUTOCOMMIT {
type int32;
@ -951,12 +1103,17 @@ module clixon-config {
persistent confirming commit.
(consider boolean)";
}
leaf CLICON_XMLDB_DIR {
type string;
mandatory true;
leaf CLICON_AUTOLOCK {
type boolean;
default false;
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 {
type datastore_cache;
default cache;
@ -968,6 +1125,16 @@ module clixon-config {
Note that from 7.0 this is OBSOLETED, only datastore_cache is supported";
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 {
type cl:datastore_format;
default xml;
@ -986,7 +1153,9 @@ module clixon-config {
default false;
description
"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.";
}
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.
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 {
type boolean;
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
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 {
type boolean;
default false;
@ -1080,7 +1248,6 @@ module clixon-config {
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).
See rfc6241 3.1: urn:ietf:params:xml:ns:netconf:base:1.0.";
}
leaf CLICON_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
auth-type=none and no known user is known.";
}
/* Network Configuration Access Control Model (NACM) */
leaf CLICON_NACM_MODE {
type nacm_mode;
default disabled;
@ -1136,20 +1304,6 @@ module clixon-config {
If this option is set, Clixon disables NACM if a datastore does NOT contain a
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 {
type string;
default "0";
@ -1162,21 +1316,7 @@ module clixon-config {
If CLICON_MODULE_LIBRARY_RFC7895 is enabled, it sets the modules-state/module-set-id
instead";
}
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";
}
/* Notification streams */
leaf CLICON_STREAM_DISCOVERY_RFC5277 {
type boolean;
default false;
@ -1232,7 +1372,27 @@ module clixon-config {
units s;
description "Retention for stream replay buffers in seconds, ie how much
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 {
type uint32;
@ -1241,8 +1401,8 @@ module clixon-config {
"Length limitation of debug and log strings.
Especially useful for dynamic debug strings, such as packet dumps.
0 means no limit";
}
/* SNMP */
leaf-list CLICON_SNMP_MIB {
description
"Names of MIBs that are used by clixon_snmp.