diff --git a/CHANGELOG.md b/CHANGELOG.md index 97d41926..ce0fc239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/example/main/README.md b/example/main/README.md index 89434f67..ce6e8da1 100644 --- a/example/main/README.md +++ b/example/main/README.md @@ -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 -M `, 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 ``` diff --git a/example/main/example_backend.c b/example/main/example_backend.c index 45e2acfc..4dc595bd 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -90,6 +90,7 @@ static char *_action_instanceid = NULL; * * Start backend with -- -m -M * Mount this yang on mountpoint + * Note module-set hard-coded to "mylabel" */ static char *_mount_yang = NULL; static char *_mount_namespace = NULL; diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 941c0c33..ff39cb5b 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -37,6 +37,7 @@ * argc/argv after -- in clixon_cli: * -m Mount this yang on mountpoint * -M Namespace of mountpoint, note both -m and -M must exist + * Note module-set hard-coded to "mylabel" */ #include #include @@ -221,7 +222,7 @@ example_cli_yang_mount(clixon_handle h, } cprintf(cb, ""); cprintf(cb, ""); - cprintf(cb, "mount"); + cprintf(cb, "mylabel"); cprintf(cb, ""); /* In yang name+namespace is mandatory, but not revision */ cprintf(cb, "%s", _mount_yang); // mandatory diff --git a/example/main/example_restconf.c b/example/main/example_restconf.c index 8b80d8c0..65f4b292 100644 --- a/example/main/example_restconf.c +++ b/example/main/example_restconf.c @@ -61,6 +61,7 @@ static const char Pad64 = '='; * * Start restconf with -- -m -M * Mount this yang on mountpoint + * Note module-set hard-coded to "mylabel" */ static char *_mount_yang = NULL; static char *_mount_namespace = NULL; diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index e4bc0103..49839f66 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -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"); } diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index f496c83a..3f539788 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -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); diff --git a/lib/clixon/clixon_yang_module.h b/lib/clixon/clixon_yang_module.h index c2edf278..eda1e79a 100644 --- a/lib/clixon/clixon_yang_module.h +++ b/lib/clixon/clixon_yang_module.h @@ -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_ */ diff --git a/lib/clixon/clixon_yang_parse_lib.h b/lib/clixon/clixon_yang_parse_lib.h index 25a31ff2..cf564dbc 100644 --- a/lib/clixon/clixon_yang_parse_lib.h +++ b/lib/clixon/clixon_yang_parse_lib.h @@ -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, diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index 201e8ce4..29ec8391 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -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, diff --git a/lib/src/clixon_err.c b/lib/src/clixon_err.c index 22e358cc..6fc0cb63 100644 --- a/lib/src/clixon_err.c +++ b/lib/src/clixon_err.c @@ -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, diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index bf5b8239..db6368f1 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -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 diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h index 312126d4..58f780b6 100644 --- a/lib/src/clixon_yang_internal.h +++ b/lib/src/clixon_yang_internal.h @@ -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_ */ diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index c9c35cd1..fa47e0ca 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -853,6 +853,7 @@ yang_metadata_init(clixon_handle h) * @param[in] h Clixon handle * @param[in] xyanglib XML tree on the form ... * @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) diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c index 2d9f95bc..11e23fa5 100644 --- a/lib/src/clixon_yang_parse_lib.c +++ b/lib/src/clixon_yang_parse_lib.c @@ -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; diff --git a/lib/src/clixon_yang_schema_mount.c b/lib/src/clixon_yang_schema_mount.c index f2e29f7a..93647ca6 100644 --- a/lib/src/clixon_yang_schema_mount.c +++ b/lib/src/clixon_yang_schema_mount.c @@ -44,6 +44,7 @@ * container root{ (ymnt) * yangmnt:mount-point "mylabel"; (yext) * } + * (note the argument "mylabel" defines an optional isolated YANG domain * * # 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; diff --git a/test/config.sh.in b/test/config.sh.in index 3a69f051..b9fc13e7 100755 --- a/test/config.sh.in +++ b/test/config.sh.in @@ -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" diff --git a/test/test_yang_mount_isolate.sh b/test/test_yang_mount_isolate.sh new file mode 100755 index 00000000..41e9285f --- /dev/null +++ b/test/test_yang_mount_isolate.sh @@ -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 < $cfg + + $cfg + $CFD + clixon-restconf:allow-auth-none + ${YANG_INSTALLDIR} + ${dir} + $fyang + $dir/domains + true + $dir + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/run/$APPNAME.sock + /usr/local/lib/$APPNAME/backend + /usr/local/var/run/$APPNAME.pidfile + $dir + true + true + true + true + +EOF + +cat < $CFD/autocli.xml + + + false + kw-nokey + true + + include clixon + enable + clixon-* + + + +EOF + +cat < $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 < $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 < $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 < $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 < $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" "xy" "" "" + +new "netconf commit" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Retrieve schema-mounts with Operation" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "clixon-exampletrue" + +new "get yang-lib at mountpoint" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" ">" "xmylabelclixon-exampleurn:example:clixonymylabelclixon-exampleurn:example:clixon" + +new "check there is statistics from mountpoint" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" 'mountpoint: /top/mylist\[name="x"\]/root' + +new "Add data to mounts" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "xx1" "" "" + +new "netconf commit" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Add mounted augment data 2" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "xx1bar" "" "" + +new "netconf commit" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "get mounted augment data" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "xx1bary" + +new "cli show config" +expectpart "$($clixon_cli -1 -f $cfg show config xml -- -m clixon-example -M urn:example:clixon)" 0 "xx1bary" + +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 diff --git a/test/test_yang_schema_mount.sh b/test/test_yang_mount_schema.sh similarity index 100% rename from test/test_yang_schema_mount.sh rename to test/test_yang_mount_schema.sh diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index a7836c26..1e2d9e79 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -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 diff --git a/yang/clixon/clixon-config@2024-01-01.yang b/yang/clixon/clixon-config@2024-08-01.yang similarity index 87% rename from yang/clixon/clixon-config@2024-01-01.yang rename to yang/clixon/clixon-config@2024-08-01.yang index 93ec0443..303448da 100644 --- a/yang/clixon/clixon-config@2024-01-01.yang +++ b/yang/clixon/clixon-config@2024-08-01.yang @@ -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 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 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 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 ; + 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 { ... } + where 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: + ... + instead where the module state is on the form: + ... + 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 .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 { ... } - where 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=) 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: - ... - instead where the module state is on the form: - ... - 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 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 "; + } + 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.