From 6f2c4a076d5023e739437f5eea57b635c36b80c5 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 1 Oct 2020 11:04:25 +0200 Subject: [PATCH] * Configuration directory * A new configuration option `CLICON_CONFIGDIR` has been added for loading of extra config files * If not given, only the main configfile is loaded. * If given, and if the directory exists, the files in this directory will be loaded alphabetically AFTER the main config file in the following way: * leaf values are overwritten * leaf-list values are appended * You can override file setting with `-E ` command-line option. * New clixon-config@2020-10-01.yang revision * Added option for configuration directory: `CLICON_CONFIGDIR` --- CHANGELOG.md | 11 +- apps/backend/backend_main.c | 11 +- apps/cli/cli_main.c | 12 +- apps/netconf/netconf_main.c | 11 +- apps/restconf/restconf_main_evhtp.c | 9 +- apps/restconf/restconf_main_fcgi.c | 9 +- lib/src/clixon_options.c | 249 ++++-- test/test_configdir.sh | 128 ++++ yang/clixon/Makefile.in | 2 +- yang/clixon/clixon-config.yang | 1 + yang/clixon/clixon-config@2020-10-01.yang | 882 ++++++++++++++++++++++ 11 files changed, 1245 insertions(+), 80 deletions(-) create mode 100755 test/test_configdir.sh create mode 120000 yang/clixon/clixon-config.yang create mode 100644 yang/clixon/clixon-config@2020-10-01.yang diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d5f7b6..a5615a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,11 +37,20 @@ Expected: 15 October 2020 * Verification of XPath functions is done at startup when yang modules are loaded, not when XPaths are evaluated. * Separation of "not found" and "not implemented" XPath functions * Both give a fatal error (backend does not start). +* Configuration directory + * A new configuration option `CLICON_CONFIGDIR` has been added for loading of extra config files + * If not given, only the main configfile is loaded. + * If given, and if the directory exists, the files in this directory will be loaded alphabetically AFTER the main config file in the following way: + * leaf values are overwritten + * leaf-list values are appended + * You can override file setting with `-E ` command-line option. ### API changes on existing protocol/config features Users may have to change how they access the system +* New clixon-config@2020-10-01.yang revision + * Added option for configuration directory: `CLICON_CONFIGDIR` * Not implemented XPath functions will cause a backend exit on startup, instead of being ignored. ### Minor changes @@ -59,7 +68,7 @@ Users may have to change how they access the system 14 September 2020 This release is primarily a bugfix and usability improvement release, no major new features. -ppp + ### API changes on existing protocol/config features Users may have to change how they access the system diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index cd7d2939..43be8f8d 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -76,7 +76,7 @@ #include "backend_startup.h" /* Command line options to be passed to getopt(3) */ -#define BACKEND_OPTS "hD:f:l:d:p:b:Fza:u:P:1qs:c:U:g:y:o:" +#define BACKEND_OPTS "hD:f:E:l:d:p:b:Fza:u:P:1qs:c:U:g:y:o:" #define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log" @@ -402,6 +402,7 @@ usage(clicon_handle h, "\t-h\t\tHelp\n" "\t-D \tDebug level\n" "\t-f \tCLICON config file\n" + "\t-E \tExtra configuration file directory\n" "\t-l (s|e|o|f) Log on (s)yslog, std(e)rr or std(o)ut (stderr is default) Only valid if -F, if background syslog is on syslog.\n" "\t-d \tSpecify backend plugin directory (default: %s)\n" "\t-p \tYang directory path (see CLICON_YANG_DIR)\n" @@ -500,6 +501,11 @@ main(int argc, usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'E': /* extra config directory */ + if (!strlen(optarg)) + usage(h, argv[0]); + clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg); + break; case 'l': /* Log destination: s|e|o */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv[0]); @@ -523,7 +529,7 @@ main(int argc, if (clicon_options_main(h) < 0){ if (help) usage(h, argv[0]); - return -1; + goto done; } /* External NACM file? */ nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE"); @@ -539,6 +545,7 @@ main(int argc, case 'h' : /* help */ case 'D' : /* debug */ case 'f': /* config file */ + case 'E': /* extra config dir */ case 'l' : break; /* see above */ case 'd': /* Plugin directory */ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 0be09a1b..3897e953 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -72,7 +72,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "hD:f:l:F:1a:u:d:m:qp:GLy:c:U:o:" +#define CLI_OPTS "hD:f:E:l:F:1a:u:d:m:qp:GLy:c:U:o:" /*! Check if there is a CLI history file and if so dump the CLI histiry to it * Just log if file does not exist or is not readable @@ -361,6 +361,7 @@ usage(clicon_handle h, "\t-h \t\tHelp\n" "\t-D \tDebug level\n" "\t-f \tConfig-file (mandatory)\n" + "\t-E \tExtra configuration file directory\n" "\t-F \tRead commands from file (default stdin)\n" "\t-1\t\tDo not enter interactive mode\n" "\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n" @@ -456,6 +457,11 @@ main(int argc, usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'E': /* extra config directory */ + if (!strlen(optarg)) + usage(h, argv[0]); + clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg); + break; case 'l': /* Log destination: s|e|o|f */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv[0]); @@ -476,9 +482,8 @@ main(int argc, if (clicon_options_main(h) < 0){ if (help) usage(h, argv[0]); - return -1; + goto done; } - /* Now rest of options */ opterr = 0; optind = 1; @@ -486,6 +491,7 @@ main(int argc, switch (c) { case 'D' : /* debug */ case 'f': /* config file */ + case 'E': /* extra config dir */ case 'l': /* Log destination */ break; /* see above */ case 'F': /* read commands from file */ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index e056a44a..56dd32c9 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -71,7 +71,7 @@ #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ -#define NETCONF_OPTS "hD:f:l:qa:u:d:p:y:U:t:eo:" +#define NETCONF_OPTS "hD:f:E:l:qa:u:d:p:y:U:t:eo:" #define NETCONF_LOGFILE "/tmp/clixon_netconf.log" @@ -369,6 +369,7 @@ usage(clicon_handle h, "\t-h\t\tHelp\n" "\t-D \tDebug level\n" "\t-f \tConfiguration file (mandatory)\n" + "\t-E \tExtra configuration file directory\n" "\t-l (e|o|s|f) Log on std(e)rr, std(o)ut, (s)yslog(default), (f)ile\n" "\t-q\t\tQuiet: dont send hello prompt\n" "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" @@ -434,6 +435,11 @@ main(int argc, usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'E': /* extra config directory */ + if (!strlen(optarg)) + usage(h, argv[0]); + clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg); + break; case 'l': /* Log destination: s|e|o */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv[0]); @@ -452,7 +458,7 @@ main(int argc, /* Find, read and parse configfile */ if (clicon_options_main(h) < 0) - return -1; + goto done; /* Now rest of options */ optind = 1; @@ -462,6 +468,7 @@ main(int argc, case 'h' : /* help */ case 'D' : /* debug */ case 'f': /* config file */ + case 'E': /* extra config dir */ case 'l': /* log */ break; /* see above */ case 'q': /* quiet: dont write hello */ diff --git a/apps/restconf/restconf_main_evhtp.c b/apps/restconf/restconf_main_evhtp.c index 87cd020c..73494c85 100644 --- a/apps/restconf/restconf_main_evhtp.c +++ b/apps/restconf/restconf_main_evhtp.c @@ -80,7 +80,7 @@ #include "restconf_root.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:ro:scP:" +#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:scP:" /* See see listen(5) */ #define SOCKET_LISTEN_BACKLOG 16 @@ -594,6 +594,7 @@ usage(clicon_handle h, "\t-h \t\t Help\n" "\t-D \t Debug level\n" "\t-f \t Configuration file (mandatory)\n" + "\t-E \t Extra configuration file directory\n" "\t-l > \t Log on (s)yslog, (f)ile (syslog is default)\n" "\t-p \t Yang directory path (see CLICON_YANG_DIR)\n" "\t-d \t Specify restconf plugin directory dir (default: %s)\n" @@ -666,6 +667,11 @@ main(int argc, usage(h, argv0); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'E': /* extra config directory */ + if (!strlen(optarg)) + usage(h, argv[0]); + clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg); + break; case 'l': /* Log destination: s|e|o */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv0); @@ -715,6 +721,7 @@ main(int argc, case 'h' : /* help */ case 'D' : /* debug */ case 'f': /* config file */ + case 'E': /* extra config dir */ case 'l': /* log */ break; /* see above */ case 'p' : /* yang dir path */ diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index 6778bc8d..d4e50865 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -88,7 +88,7 @@ #include "restconf_stream.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:ro:" +#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:" /*! Convert FCGI parameters to clixon runtime data * @param[in] h Clixon handle @@ -172,6 +172,7 @@ usage(clicon_handle h, "\t-h \t\t Help\n" "\t-D \t Debug level\n" "\t-f \t Configuration file (mandatory)\n" + "\t-E \t Extra configuration file directory\n" "\t-l > \t Log on (s)yslog, (f)ile (syslog is default)\n" "\t-p \t Yang directory path (see CLICON_YANG_DIR)\n" "\t-d \t Specify restconf plugin directory dir (default: %s)\n" @@ -239,6 +240,11 @@ main(int argc, usage(h, argv[0]); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'E': /* extra config directory */ + if (!strlen(optarg)) + usage(h, argv[0]); + clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg); + break; case 'l': /* Log destination: s|e|o */ if ((logdst = clicon_log_opt(optarg[0])) < 0) usage(h, argv[0]); @@ -281,6 +287,7 @@ main(int argc, case 'h' : /* help */ case 'D' : /* debug */ case 'f': /* config file */ + case 'E': /* extra config dir */ case 'l': /* log */ break; /* see above */ case 'p' : /* yang dir path */ diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 5d30288d..a9106d50 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -50,6 +50,8 @@ #include #include #include +#include +#include /* dirname */ #include #include #include @@ -65,6 +67,7 @@ #include "clixon_queue.h" #include "clixon_hash.h" #include "clixon_handle.h" +#include "clixon_file.h" #include "clixon_log.h" #include "clixon_yang.h" #include "clixon_xml.h" @@ -190,51 +193,29 @@ clicon_option_dump(clicon_handle h, return retval; } -/*! Read filename and set values to global options registry. XML variant. - * - * @param[out] xconfig Pointer to xml config tree. Should be freed by caller - * @retval 0 OK - * @retval -1 Error +/*! Open and parse single config file + * @param[in] filename + * @param[in] yspec + * @param[out] xconfig Pointer to xml config tree. Should be freed by caller */ static int -parse_configfile(clicon_handle h, - const char *filename, - yang_stmt *yspec, - cxobj **xconfig) +parse_configfile_one(const char *filename, + yang_stmt *yspec, + cxobj **xconfig) { - struct stat st; - FILE *f = NULL; - int retval = -1; - int fd; - cxobj *xt = NULL; - cxobj *xc = NULL; - cxobj *x = NULL; - char *name; - char *body; - clicon_hash_t *copt = clicon_options(h); - cbuf *cbret = NULL; - cxobj *xerr = NULL; - int ret; - cvec *nsc = NULL; + int retval = -1; + int fd = -1; + cxobj *xt = NULL; + cxobj *xerr = NULL; + cxobj *xa; + cbuf *cbret = NULL; + int ret; - if (filename == NULL || !strlen(filename)){ - clicon_err(OE_UNIX, 0, "Not specified"); - goto done; - } - if (stat(filename, &st) < 0){ - clicon_err(OE_UNIX, errno, "%s", filename); - goto done; - } - if (!S_ISREG(st.st_mode)){ - clicon_err(OE_UNIX, 0, "%s is not a regular file", filename); - goto done; - } - if ((f = fopen(filename, "r")) == NULL) { - clicon_err(OE_UNIX, errno, "configure file: %s", filename); + if ((fd = open(filename, O_RDONLY)) < 0){ + clicon_err(OE_UNIX, errno, "open configure file: %s", filename); return -1; } clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename); - fd = fileno(f); if ((ret = clixon_xml_parse_file(fd, yspec?YB_MODULE:YB_NONE, yspec, NULL, &xt, &xerr)) < 0) goto done; if (ret == 0){ @@ -248,21 +229,137 @@ parse_configfile(clicon_handle h, clixon_netconf_error(xerr, NULL, NULL); goto done; } - if (xml_child_nr(xt)==1 && xml_child_nr_type(xt, CX_BODY)==1){ - clicon_err(OE_CFG, 0, "Config file %s: Expected XML but is probably old sh style", filename); + /* Ensure a single root */ + if (xt == NULL || xml_child_nr(xt) != 1){ + clicon_err(OE_CFG, 0, "Config file %s: Lacks single top element", filename); goto done; } - /* Hard-coded config for < 3.10 and clixon-config for >= 3.10 */ - if ((nsc = xml_nsctx_init(NULL, CLIXON_CONF_NS)) == NULL) + if (xml_rootchild(xt, 0, &xt) < 0) goto done; - if ((xc = xpath_first(xt, nsc, "clixon-config")) == NULL){ + /* Check well-formedness */ + if (strcmp(xml_name(xt), "clixon-config") != 0 || + (xa = xml_find_type(xt, NULL, "xmlns", CX_ATTR)) == NULL || + strcmp(xml_value(xa), CLIXON_CONF_NS) != 0){ clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: ", filename, CLIXON_CONF_NS); - goto done; } - if (xml_default_recurse(xc, 0) < 0) + *xconfig = xt; + xt = NULL; + retval = 0; + done: + if (xt) + xml_free(xt); + if (fd != -1) + close(fd); + if (cbret) + cbuf_free(cbret); + if (xerr) + xml_free(xerr); + return retval; +} + +/*! Read filename and set values to global options registry. XML variant. + * + * @param[in] h Clixon handle + * @param[in] filename Main configuration file + * @param[in] extraconfig0 Override (if set use that, othewrwise get from main file) + * @param[in] yspec Yang spec + * @param[out] xconfig Pointer to xml config tree. Should be freed by caller + * @retval 0 OK + * @retval -1 Error + */ +static int +parse_configfile(clicon_handle h, + const char *filename, + char *extraconfdir0, + yang_stmt *yspec, + cxobj **xconfig) +{ + int retval = -1; + struct stat st; + cxobj *xt = NULL; + cxobj *xc = NULL; + cxobj *x = NULL; + char *name; + char *body; + clicon_hash_t *copt = clicon_options(h); + cbuf *cbret = NULL; + cxobj *xerr = NULL; + int ret; + cvec *nsc = NULL; + int i; + int ndp; + struct dirent *dp = NULL; + char filename1[MAXPATHLEN]; + char *extraconfdir = NULL; + cxobj *xe = NULL; + cxobj *xec; + DIR *dirp; + + if (filename == NULL || !strlen(filename)){ + clicon_err(OE_UNIX, 0, "Not specified"); + goto done; + } + if (stat(filename, &st) < 0){ + clicon_err(OE_UNIX, errno, "%s", filename); + goto done; + } + if (!S_ISREG(st.st_mode)){ + clicon_err(OE_UNIX, 0, "%s is not a regular file", filename); + goto done; + } + /* Parse main config file */ + if (parse_configfile_one(filename, yspec, &xt) < 0) + goto done; + /* xt is a single-rooted: ... + * If no override (eg from command-line) + * Bootstrap: Shortcut to read extra confdir inline */ + if ((extraconfdir = extraconfdir0) == NULL) + if ((xc = xpath_first(xt, 0, "CLICON_CONFIGDIR")) != NULL) + extraconfdir = xml_body(xc); + if (extraconfdir){ /* If extra dir, parse extra config files */ + /* A check it exists (also done in clicon_file_dirent) */ + if ((dirp = opendir(extraconfdir)) == NULL) { + clicon_err(OE_UNIX, errno, "CLICON_CONFIGDIR: %s opendir", extraconfdir); + goto done; + } + closedir(dirp); + if((ndp = clicon_file_dirent(extraconfdir, &dp, NULL, S_IFREG)) < 0) /* Read dir */ + goto done; + /* Loop through files */ + for (i = 0; i < ndp; i++){ + snprintf(filename1, sizeof(filename1), "%s/%s", extraconfdir, dp[i].d_name); + if (parse_configfile_one(filename1, yspec, &xe) < 0) + goto done; + /* Drain objects from extrafile and replace/append to main */ + while ((xec = xml_child_i_type(xe, 0, CX_ELMNT)) != NULL) { + name = xml_name(xec); + body = xml_body(xec); + /* Ignored from file due to bootstrapping */ + if (strcmp(name,"CLICON_CONFIGFILE")==0) + continue; + /* List options for configure options that are leaf-lists: append to main */ + if (strcmp(name,"CLICON_FEATURE")==0 || + strcmp(name,"CLICON_YANG_DIR")==0){ + if (xml_addsub(xt, xec) < 0) + goto done; + continue; + } + /* Remove existing in master if any */ + if ((x = xml_find_type(xt, NULL, name, CX_ELMNT)) != NULL) + xml_purge(x); + /* Append to master (removed from xe) */ + if (xml_addsub(xt, xec) < 0) + goto done; + } + if (xe) + xml_free(xe); + xe = NULL; + } + } + if (xml_default_recurse(xt, 0) < 0) goto done; - if ((ret = xml_yang_validate_add(h, xc, &xerr)) < 0) + if ((ret = xml_yang_validate_add(h, xt, &xerr)) < 0) goto done; if (ret == 0){ if ((cbret = cbuf_new()) ==NULL){ @@ -274,34 +371,38 @@ parse_configfile(clicon_handle h, clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret)); goto done; } - while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) { + x = NULL; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { name = xml_name(x); body = xml_body(x); - if (name==NULL || body == NULL){ + if (name == NULL || body == NULL){ clicon_log(LOG_WARNING, "%s option NULL: name:%s body:%s", __FUNCTION__, name, body); continue; } - /* hard-coded exceptions for configure options that are leaf-lists (not leaf) + /* Ignored from file due to bootstrapping */ + if (strcmp(name,"CLICON_CONFIGFILE")==0) + continue; + /* List options for configure options that are leaf-lists (not leaf) * They must be accessed directly by looping over clicon_conf_xml(h) */ if (strcmp(name,"CLICON_FEATURE")==0) continue; if (strcmp(name,"CLICON_YANG_DIR")==0) continue; - /* Used as an arg to this fn */ - if (strcmp(name,"CLICON_CONFIGFILE")==0) - continue; + if (clicon_hash_add(copt, - name, - body, - strlen(body)+1) == NULL) + name, + body, + strlen(body)+1) == NULL) goto done; } retval = 0; *xconfig = xt; xt = NULL; - done: + done: + if (dp) + free(dp); if (nsc) xml_nsctx_free(nsc); if (cbret) @@ -310,8 +411,6 @@ parse_configfile(clicon_handle h, xml_free(xerr); if (xt) xml_free(xt); - if (f) - fclose(f); return retval; } @@ -375,6 +474,7 @@ clicon_options_main(clicon_handle h) char xml = 0; /* Configfile is xml, otherwise legacy */ cxobj *xconfig = NULL; yang_stmt *yspec = NULL; + char *extraconfdir = NULL; /* Create configure yang-spec */ if ((yspec = yspec_new()) == NULL) @@ -396,20 +496,27 @@ clicon_options_main(clicon_handle h) clicon_err(OE_CFG, 0, "%s: suffix %s not recognized", configfile, suffix); goto done; } - /* Read configfile first without yangspec, for bootstrapping, see second - * time below with proper yangspec. - * (You need to read the config-file to get the YANG_DIR to find the - * the clixon yang-spec) + + /* Override extraconfdir */ + if (clicon_option_str(h, "CLICON_CONFIGDIR") && + (extraconfdir = strdup(clicon_option_str(h, "CLICON_CONFIGDIR"))) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } + + /* Read configfile first without yangspec, and without extra config dir for bootstrapping, + * see second time below with proper yangspec and extra config dir + * (You need to read the config-file to get the YANG_DIR to find the clixon yang-spec) * Difference from parsing with yangspec is: * - no default values * - no sanity checks + * - no extra config dir */ - if (parse_configfile(h, configfile, NULL, &xconfig) < 0) + if (parse_configfile(h, configfile, extraconfdir, NULL, &xconfig) < 0) goto done; - if (xml_rootchild(xconfig, 0, &xconfig) < 0) - goto done; - /* Set clixon_conf pointer to handle */ + clicon_conf_xml_set(h, xconfig); + /* Parse clixon yang spec */ if (yang_spec_parse_module(h, "clixon-config", NULL, yspec) < 0) goto done; @@ -418,10 +525,9 @@ clicon_options_main(clicon_handle h) xml_free(xconfig); xconfig = NULL; } + /* Read configfile second time now with check yang spec */ - if (parse_configfile(h, configfile, yspec, &xconfig) < 0) - goto done; - if (xml_rootchild(xconfig, 0, &xconfig) < 0) + if (parse_configfile(h, configfile, extraconfdir, yspec, &xconfig) < 0) goto done; if (xml_spec(xconfig) == NULL){ clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: or clixon-config.yang not found?", configfile, CLIXON_CONF_NS); @@ -430,12 +536,17 @@ clicon_options_main(clicon_handle h) /* Set yang config spec (must store to free at exit, since conf_xml below uses it) */ if (clicon_config_yang_set(h, yspec) < 0) goto done; + yspec = NULL; /* Set clixon_conf pointer to handle */ if (clicon_conf_xml_set(h, xconfig) < 0) goto done; retval = 0; done: + if (yspec) + ys_free(yspec); + if (extraconfdir) + free(extraconfdir); return retval; } diff --git a/test/test_configdir.sh b/test/test_configdir.sh new file mode 100755 index 00000000..d17429a2 --- /dev/null +++ b/test/test_configdir.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# Test config file and extra config dir. +# Use clixon_cli and assume clixon_backend/restconf/netconf behaves the same +# Start without configdir as baseline +# Start with wrong configdir +# Start with empty configfile +# Start with 1 extra configfile +# Start with 2 extra configfiles +# Start with 2 extra configfiles + command-line +# Two options are used for testing: +# CLICON_MODULE_SET_ID is a single var (replaced) +# CLICON_FEATURE is a list var (append) +# +# 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_yang.xml +cdir=$dir/conf.d +cfile1=$cdir/00a.xml +cfile2=$cdir/01a.xml + +test -d $cdir || mkdir $cdir + +cat < $cfg + + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + /usr/local/share/clixon + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + 1 + test1 + +EOF + +cat < $cfile1 + + 2 + test2 + +EOF + +new "Start without configdir as baseline" +expectpart "$($clixon_cli -1 -f $cfg show options)" 0 'CLICON_MODULE_SET_ID: "1"' 'CLICON_FEATURE: "test1"' --not-- 'CLICON_FEATURE: "test2"' + +cat < $cfg + + $dir/dontexist + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + /usr/local/share/clixon + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + 1 + test1 + +EOF + +new "Start with wrong configdir" +expectpart "$($clixon_cli -1 -f $cfg -l o show options)" 255 "UNIX error: CLICON_CONFIGDIR:" "opendir: No such file or directory" + +rm -f $cfile1 +cat < $cfg + + $dir/notexist + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + /usr/local/share/clixon + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + 1 + test1 + +EOF + +new "Start with wrong configdir -E override" +expectpart "$($clixon_cli -1 -f $cfg -E $cdir show options)" 0 'CLICON_MODULE_SET_ID: "1"' 'CLICON_FEATURE: "test1"' --not-- 'CLICON_FEATURE: "test2"' + +cat < $cfg + + $cdir + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + /usr/local/share/clixon + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + 1 + test1 + +EOF + +new "Start with empty configdir" +expectpart "$($clixon_cli -1 -f $cfg -l o show options)" 0 'CLICON_MODULE_SET_ID: "1"' 'CLICON_FEATURE: "test1"' --not-- 'CLICON_FEATURE: "test2"' + +cat < $cfile1 + + 2 + test2 + +EOF + +new "Start with 1 extra configfile" +expectpart "$($clixon_cli -1 -f $cfg -l o show options)" 0 'CLICON_MODULE_SET_ID: "2"' 'CLICON_FEATURE: "test1"' 'CLICON_FEATURE: "test2"' + +cat < $cfile2 + + 3 + test3 + +EOF + +new "Start with 2 extra configfiles" +expectpart "$($clixon_cli -1 -f $cfg -l o show options)" 0 'CLICON_MODULE_SET_ID: "3"' 'CLICON_FEATURE: "test1"' 'CLICON_FEATURE: "test2"' 'CLICON_FEATURE: "test3"' + +new "Start with 2 extra configfiles + command-line" +expectpart "$($clixon_cli -1 -f $cfg -o CLICON_MODULE_SET_ID=4 -o CLICON_FEATURE=test4 -l o show options)" 0 'CLICON_MODULE_SET_ID: "4"' 'CLICON_FEATURE: "test1"' 'CLICON_FEATURE: "test2"' 'CLICON_FEATURE: "test3"' 'CLICON_FEATURE: "test4"' + +rm -rf $dir diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index db586b62..362b5ab4 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -42,7 +42,7 @@ datarootdir = @datarootdir@ # See also OPT_YANG_INSTALLDIR for the standard yang files YANG_INSTALLDIR = @YANG_INSTALLDIR@ -YANGSPECS = clixon-config@2020-08-17.yang +YANGSPECS = clixon-config@2020-10-01.yang YANGSPECS += clixon-lib@2020-04-23.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang diff --git a/yang/clixon/clixon-config.yang b/yang/clixon/clixon-config.yang new file mode 120000 index 00000000..5756b75f --- /dev/null +++ b/yang/clixon/clixon-config.yang @@ -0,0 +1 @@ +clixon-config@2020-10-01.yang \ No newline at end of file diff --git a/yang/clixon/clixon-config@2020-10-01.yang b/yang/clixon/clixon-config@2020-10-01.yang new file mode 100644 index 00000000..13f9e12f --- /dev/null +++ b/yang/clixon/clixon-config@2020-10-01.yang @@ -0,0 +1,882 @@ +module clixon-config { + yang-version 1.1; + namespace "http://clicon.org/config"; + prefix cc; + + organization + "Clicon / Clixon"; + + contact + "Olof Hagsand "; + + description + "Clixon configuration file + ***** BEGIN LICENSE BLOCK ***** + Copyright (C) 2009-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate) + + This file is part of CLIXON + + Licensed under the Apache License, Version 2.0 (the \"License\"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an \"AS IS\" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the \"GPL\"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK *****"; + + revision 2020-10-01 { + description + "Added: CLICON_CONFIGDIR"; + } + revision 2020-08-17 { + description + "Added: CLICON_RESTCONF_IPV4_ADDR, CLICON_RESTCONF_IPV6_ADDR, + CLICON_RESTCONF_HTTP_PORT, CLICON_RESTCONF_HTTPS_PORT + CLICON_NAMESPACE_NETCONF_DEFAULT, + CLICON_CLI_HELPSTRING_TRUNCATE, CLICON_CLI_HELPSTRING_LINES"; + } + revision 2020-06-17 { + description + "Added: CLICON_CLI_LINES_DEFAULT + Added enum HIDE to CLICON_CLI_GENMODEL + Added CLICON_SSL_SERVER_CERT, CLICON_SSL_SERVER_KEY, CLICON_SSL_CA_CERT + Added CLICON_NACM_DISABLED_ON_EMPTY + Removed default valude of CLICON_NACM_RECOVERY_USER"; + } + revision 2020-04-23 { + description + "Added: CLICON_YANG_UNKNOWN_ANYDATA to treat unknown XML (wrt YANG) as anydata. + Deleted: xml-stats non-config data (replaced by rpc stats in clixon-lib.yang)"; + } + revision 2020-02-22 { + description + "Added: search index extension, + Added: clixon-stats state for clixon XML and memory statistics. + Added: CLICON_CLI_BUF_START and CLICON_CLI_BUF_THRESHOLD for quadratic and linear + growth of CLIgen buffers (cbuf:s) + Added: CLICON_VALIDATE_STATE_XML for controling validation of user state XML + Added: CLICON_CLICON_YANG_LIST_CHECK to skip list key checks"; + } + revision 2019-09-11 { + description + "Added: CLICON_BACKEND_USER: drop of privileges to user, + CLICON_BACKEND_PRIVILEGES: how to drop privileges + CLICON_NACM_CREDENTIALS: If and how to check backend sock priveleges with NACM + CLICON_NACM_RECOVERY_USER: Name of NACM recovery user."; + } + revision 2019-06-05 { + description + "Added: CLICON_YANG_REGEXP, CLICON_CLI_TAB_MODE, + CLICON_CLI_HIST_FILE, CLICON_CLI_HIST_SIZE, + CLICON_XML_CHANGELOG, CLICON_XML_CHANGELOG_FILE; + Renamed CLICON_XMLDB_CACHE to CLICON_DATASTORE_CACHE (changed type) + Deleted: CLICON_XMLDB_PLUGIN, CLICON_USE_STARTUP_CONFIG"; + } + revision 2019-03-05{ + description + "Changed URN. Changed top-level symbol to clixon-config. + Released in Clixon 3.10"; + } + revision 2019-02-06 { + description + "Released in Clixon 3.9"; + } + revision 2018-10-21 { + description + "Released in Clixon 3.8"; + } + extension search_index { + description "This list argument acts as a search index using optimized binary search. + "; + } + typedef startup_mode{ + description + "Which method to boot/start clicon backend. + The methods differ in how they reach a running state + Which source database to commit from, if any."; + type enumeration{ + enum none{ + description + "Do not touch running state + Typically after crash when running state and db are synched"; + } + enum init{ + description + "Initialize running state. + Start with a completely clean running state"; + } + enum running{ + description + "Commit running db configuration into running state + After reboot if a persistent running db exists"; + } + enum startup{ + description + "Commit startup configuration into running state + After reboot when no persistent running db exists"; + } + } + } + typedef datastore_format{ + description + "Datastore format."; + type enumeration{ + enum xml{ + description "Save and load xmldb as XML"; + } + enum json{ + description "Save and load xmldb as JSON"; + } + } + } + typedef datastore_cache{ + description + "XML configuration, ie running/candididate/ datastore cache behaviour."; + type enumeration{ + enum nocache{ + description "No cache always work directly with file"; + } + enum cache{ + description "Use in-memory cache. + Make copies when accessing internally."; + } + enum cache-zerocopy{ + description "Use in-memory cache and dont copy. + Fastest but opens up for callbacks changing cache."; + } + } + } + typedef cli_genmodel_type{ + description + "How to generate CLI from YANG model, + eg {container c {list a{ key x; leaf x; leaf y;}}"; + type enumeration{ + enum NONE{ + description "No extra keywords: c a "; + } + enum VARS{ + description "Keywords on non-key variables: c a y "; + } + enum ALL{ + description "Keywords on all variables: c a x y "; + } + enum HIDE{ + description "Keywords on non-key variables and hide container around lists: a y "; + } + } + } + typedef nacm_mode{ + description + "Mode of RFC8341 Network Configuration Access Control Model. + It is unclear from the RFC whether NACM rules are internal + in a configuration (ie embedded in regular config) or external/OOB + in s separate, specific NACM-config"; + type enumeration{ + enum disabled{ + description "NACM is disabled"; + } + enum internal{ + description "NACM is enabled and available in the regular config"; + } + enum external{ + description "NACM is enabled and available in a separate config"; + } + } + } + typedef regexp_mode{ + description + "The regular expression engine Clixon uses in its validation of + Yang patterns, and in the CLI. + Yang RFC 7950 stipulates XSD XML Schema regexps + according to W3 CXML Schema Part 2: Datatypes Second Edition, + see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028#regexs"; + type enumeration{ + enum posix { + description + "Translate XSD XML Schema regexp:s to Posix regexp. This is + not a complete translation, but can be considered good-enough + for Yang use-cases as defined by openconfig and yang-models + for example."; + } + enum libxml2 { + description + "Use libxml2 XSD XML Schema regexp engine. This is a complete + XSD regexp engine.. + Requires libxml2 to be available at configure time + (HAVE_LIBXML2 should be set)"; + } + } + } + typedef priv_mode{ + description + "Privilege mode, used for dropping (or not) priveleges to a non-provileged + user after initialization"; + type enumeration{ + enum none { + description + "Make no drop/change in privileges."; + } + enum drop_perm { + description + "After initialization, drop privileges permanently to a uid"; + } + enum drop_temp { + description + "After initialization, drop privileges temporarily to a euid"; + } + } + } + typedef nacm_cred_mode{ + description + "How NACM user should be matched with unix socket peer credentials. + This means nacm user must match socket peer user accessing the + backend socket. For IP sockets only mode none makes sense."; + type enumeration{ + enum none { + description + "Dont match NACM user to any user credentials. Any user can pose + as any other user. Set this for IP sockets, or dont use NACM."; + } + enum exact { + description + "Exact match between NACM user and unix socket peer user."; + } + enum except { + description + "Exact match between NACM user and unix socket peer user, except + for root and www user (restconf)."; + } + } + } + + container clixon-config { + leaf-list CLICON_FEATURE { + description + "Supported features as used by YANG feature/if-feature + value is: :, where and + are either names, or the special character '*'. + *:* means enable all features + :* means enable all features in the specified module + *: means enable the specific feature in all modules"; + type string; + } + leaf-list CLICON_YANG_DIR { + ordered-by user; + type string; + description + "Yang directory path for finding module and submodule files. + A list of these options should be in the configuration. + When loading a Yang module, Clixon searches this list in the order + they appear. Ensure that YANG_INSTALLDIR(default + /usr/local/share/clixon) is present in the path"; + } + leaf CLICON_CONFIGFILE{ + type string; + description + "Location of the main configuration-file. + Default is CLIXON_DEFAULT_CONFIG=/usr/local/etc/clicon.xml set in configure. + Note that due to bootstrapping, this value is not actually read from file + and therefore a default value would be meaningless."; + } + leaf CLICON_CONFIGDIR{ + type string; + description + "Location of directory of extra configuration files. + If not given, only main configfile is read. + If given, and if the directory exists, all files in this directory will be loaded + 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. + 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"; + } + leaf CLICON_YANG_MAIN_FILE { + type string; + description + "If specified load a yang module in a specific absolute filename. + This corresponds to the -y command-line option in most CLixon + programs."; + } + leaf CLICON_YANG_MAIN_DIR { + type string; + description + "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_MODULE_MAIN { + type string; + description + "Option used to construct initial yang file: + [@]"; + } + leaf CLICON_YANG_MODULE_REVISION { + type string; + description + "Option used to construct initial yang file: + [@]. + Used together with CLICON_YANG_MODULE_MAIN"; + } + leaf CLICON_YANG_REGEXP { + type regexp_mode; + default posix; + description + "The regular expression engine Clixon uses in its validation of + Yang patterns, and in the CLI. + There is a 'good-enough' posix translation mode and a complete + libxml2 mode"; + } + leaf CLICON_YANG_LIST_CHECK { + type boolean; + default true; + description + "If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2: + The 'key' statement, which MUST be present if the list represents configuration. + Some yang specs seem not to fulfil this. However, if you reset this, there may + be follow-up errors due to code that assumes a configuration list has keys"; + } + leaf CLICON_YANG_UNKNOWN_ANYDATA{ + type boolean; + default false; + description + "Treat unknown XML/JSON nodes as anydata when loading from startup db. + This does not apply to namespaces, which means a top-level node: xxx:yyy + is accepted only if yyy is unknown, not xxx. + Note that this option has several caveats which needs to be fixed. Please + use with care. + The primary issue is that the unknown->anydata handling is not restricted to + only loading from startup but may occur in other circumstances as well. This + means that sanity checks of erroneous XML/JSON may not be properly signalled."; + } + 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_NETCONF_DIR { + type string; + description "Location of netconf (frontend) .so plugins"; + } + leaf CLICON_RESTCONF_DIR { + type string; + description + "Location of restconf (frontend) .so plugins. Load all .so + plugins in this dir as restconf code plugins"; + } + leaf CLICON_RESTCONF_PATH { + type string; + default "/www-data/fastcgi_restconf.sock"; + description + "FastCGI unix socket. Should be specified in webserver + Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock"; + } + leaf CLICON_RESTCONF_PRETTY { + type boolean; + default true; + description + "Restconf return value pretty print. + Restconf clients may add HTTP header: + Accept: application/yang-data+json, or + Accept: application/yang-data+xml + to get return value in XML or JSON. + RFC 8040 examples print XML and JSON in pretty-printed form. + Setting this value to false makes restconf return not pretty-printed + which may be desirable for performance or tests"; + } + leaf CLICON_RESTCONF_IPV4_ADDR { + type string; + default "0.0.0.0"; + description + "RESTCONF IPv4 socket binding address. + Applies to native http by config option --with-restconf=evhtp."; + } + leaf CLICON_RESTCONF_IPV6_ADDR { + type string; + default "::"; + description + "RESTCONF IPv6 socket binding address. + Applies to native http by config option --with-restconf=evhtp."; + } + leaf CLICON_RESTCONF_HTTP_PORT { + type uint16; + default 80; + description + "RESTCONF socket binding port, non-ssl + In the restconf daemon, it can be overriden by -P + Applies to native http only by config option --with-restconf=evhtp."; + } + leaf CLICON_RESTCONF_HTTPS_PORT { + type uint16; + default 443; + description + "RESTCONF socket binding port, ssl + In the restconf daemon, this is the port chosen if -s is given. + Note it can be overriden by -P + Applies to native http by config option --with-restconf=evhtp."; + } + leaf CLICON_SSL_SERVER_CERT { + type string; + default "/etc/ssl/certs/clixon-server-crt.pem"; + description + "SSL server cert for restconf https. + Applies to native http only by config option --with-restconf=evhtp."; + } + leaf CLICON_SSL_SERVER_KEY { + type string; + default "/etc/ssl/private/clixon-server-key.pem"; + description + "SSL server private key for restconf https. + Applies to native http only by config option --with-restconf=evhtp."; + } + leaf CLICON_SSL_CA_CERT { + type string; + default "/etc/ssl/certs/clixon-ca_crt.pem"; + description + "SSL CA cert for client authentication. + Applies to native http only by config option --with-restconf=evhtp."; + } + leaf CLICON_CLI_DIR { + type string; + description + "Directory containing frontend cli loadable plugins. Load all .so + plugins in this directory as CLI object plugins"; + } + leaf CLICON_CLISPEC_DIR { + type string; + description + "Directory containing frontend cligen spec files. Load all .cli + files in this directory as CLI specification files. + See also CLICON_CLISPEC_FILE."; + } + leaf CLICON_CLISPEC_FILE { + type string; + description + "Specific frontend cligen spec file as aletrnative or complement + to CLICON_CLISPEC_DIR. Also available as -c in clixon_cli."; + } + leaf CLICON_CLI_MODE { + type string; + default "base"; + description + "Startup CLI mode. This should match a CLICON_MODE variable set in + one of the clispec files"; + } + leaf CLICON_CLI_GENMODEL { + type int32; + default 1; + description + "0: Do not generate CLISPEC syntax for the auto-cli. + 1: Generate a CLI specification for CLI completion of all loaded Yang modules. + This CLI tree can be accessed in CLI-spec files using the tree reference syntax (eg + @datamodel). + 2: Same including state syntax in a tree called @datamodelstate. + See also CLICON_CLI_MODEL_TREENAME."; + } + leaf CLICON_CLI_MODEL_TREENAME { + type string; + default "datamodel"; + description + "If set, CLI specs can reference the + model syntax using this reference. + Example: set @datamodel, cli_set(); + A second tree called eg @datamodelstate is created that + also contains state together with config."; + } + leaf CLICON_CLI_GENMODEL_COMPLETION { + type int32; + default 1; + description "Generate code for CLI completion of existing db symbols. + (consider boolean)"; + } + leaf CLICON_CLI_GENMODEL_TYPE { + type cli_genmodel_type; + default "VARS"; + description "How to generate and show CLI syntax: VARS|ALL"; + } + leaf CLICON_CLI_VARONLY { + type int32; + default 1; + description + "Dont include keys in cvec in cli vars callbacks, + ie a & k in 'a k ' ignored + (consider boolean)"; + } + leaf CLICON_CLI_LINESCROLLING { + 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 + right margin"; + } + leaf CLICON_CLI_LINES_DEFAULT { + type int32; + default 24; + description + "Set to number of CLI terminal rows for pageing/scrolling. 0 means unlimited. + The number is set statically UNLESS: + - there is no terminal, such as file input, in which case nr lines is 0 + - there is a terminal sufficiently powerful to read the number of lines from + ioctl calls. + In other words, this setting is used ONLY on raw terminals such as serial + consoles."; + } + leaf CLICON_CLI_TAB_MODE { + type int8; + default 0; + description + "Set CLI tab mode. This is actually a bitfield of three + combinations: + bit 1: 0: shows short info of available commands + 1: has same output as , ie line per command + bit 2: 0: On , select a command over a if both exist + 1: Commands and vars have same preference. + bit 3: 0: On , never complete more than one level per + 1: Complete all levels at once if possible. + "; + } + leaf CLICON_CLI_UTF8 { + type int8; + default 0; + description + "Set to 1 to enable CLIgen UTF-8 experimental mode. + Note that this feature is EXPERIMENTAL and may not properly handle + scrolling, control characters, etc + (consider boolean)"; + } + leaf CLICON_CLI_HIST_FILE { + type string; + default "~/.clixon_cli_history"; + description + "Name of CLI history file. If not given, history is not saved. + The number of lines is saved is given by CLICON_CLI_HIST_SIZE."; + } + leaf CLICON_CLI_HIST_SIZE { + type int32; + default 300; + description + "Number of lines to save in CLI history. + Also, if CLICON_CLI_HIST_FILE is set, also the size in lines + of the saved history."; + } + leaf CLICON_CLI_BUF_START { + type uint32; + default 256; + description + "CLIgen buffer (cbuf) initial size. + When the buffer needs to grow, the allocation grows quadratic up to a threshold + after which linear growth continues. + See CLICON_CLI_BUF_THRESHOLD"; + } + leaf CLICON_CLI_BUF_THRESHOLD { + type uint32; + default 65536; + description + "CLIgen buffer (cbuf) threshold size. + When the buffer exceeds the threshold, the allocation grows by adding the threshold + value to the buffer length. + If 0, the growth continues with quadratic growth. + See CLICON_CLI_BUF_THRESHOLD"; + } + leaf CLICON_CLI_HELPSTRING_TRUNCATE { + type boolean; + default false; + description + "CLIgen help string on query (?): Truncate help string on right margin mode + This only applies if you have long help strings, such as when generating them from a + spec such as the autocli"; + } + leaf CLICON_CLI_HELPSTRING_LINES { + type int32; + default 0; + description + "CLIgen help string on query (?) limit of number of lines to show, 0 means unlimited. + This only applies if you have multi-line help strings, such as when generating + from a spec, such as in the autocli."; + } + leaf CLICON_SOCK_FAMILY { + type string; + default "UNIX"; + description + "Address family for communicating with clixon_backend + (UNIX|IPv4). IPv6 not yet implemented. + Note that UNIX socket makes credential check as follows: + (1) client needs rw access to the socket + (2) NACM credentials can be checked according to CLICON_NACM_CREDENTIALS + Warning: IPv4 and IPv6 sockets have no credential mechanism. + "; + } + leaf CLICON_SOCK { + type string; + mandatory true; + description + "If family above is AF_UNIX: Unix socket for communicating + with clixon_backend. If family is AF_INET: IPv4 address"; + } + leaf CLICON_SOCK_PORT { + type int32; + default 4535; + description + "Inet socket port for communicating with clixon_backend + (only IPv4|IPv6)"; + } + leaf CLICON_SOCK_GROUP { + type string; + default "clicon"; + description + "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_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_AUTOCOMMIT { + type int32; + default 0; + description + "Set if all configuration changes are committed automatically + on every edit change. Explicit commit commands unnecessary + (consider boolean)"; + } + leaf CLICON_XMLDB_DIR { + type string; + mandatory true; + description + "Directory where \"running\", \"candidate\" and \"startup\" are placed."; + } + leaf CLICON_DATASTORE_CACHE { + type datastore_cache; + default cache; + description + "Clixon datastore cache behaviour. There are three values: no cache, + cache with copy, or cache without copy."; + } + leaf CLICON_XMLDB_FORMAT { + type datastore_format; + default xml; + description "XMLDB datastore format."; + } + leaf CLICON_XMLDB_PRETTY { + type boolean; + default true; + description + "XMLDB datastore pretty print. + If set, insert spaces and line-feeds making the XML/JSON human + readable. If not set, make the XML/JSON more compact."; + } + leaf CLICON_XMLDB_MODSTATE { + type boolean; + default false; + description + "If set, tag datastores with RFC 7895 YANG Module Library + info. When loaded at startup, a check is made if the system + yang modules match. + See also CLICON_MODULE_LIBRARY_RFC7895"; + } + leaf CLICON_XML_CHANGELOG { + type boolean; + default false; + description "If true enable automatic upgrade using yang clixon + changelog."; + } + leaf CLICON_XML_CHANGELOG_FILE { + type string; + description "Name of file with module revision changelog. + If CLICON_XML_CHANGELOG is true, Clixon + reads the module changelog from this file."; + } + leaf CLICON_VALIDATE_STATE_XML { + type boolean; + default false; + description + "Validate user state callback content. + Users may register state callbacks using ca_statedata callback + When set, the XML returned from the callback is validated after merging with + the running db. If it fails, an internal error is returned to the originating + user. + If the option is not set, the XML returned by the user is not validated. + Note that enabling currently causes a large performance overhead for large + lists, therefore it is recommended to enable it during development and debugging + but disable it in production, until this has been resolved."; + } + leaf CLICON_NAMESPACE_NETCONF_DEFAULT { + type boolean; + default false; + description + "Undefine if you want to ensure strict namespace assignment on all netconf + and XML statements according to the standard RFC 6241. + 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; + description "Which method to boot/start clicon backend"; + } + leaf CLICON_TRANSACTION_MOD { + type boolean; + default false; + description "If set, modifications in validation and commit + callbacks are written back into the datastore. + This is a bad idea and therefore obsoleted."; + status obsolete; + } + leaf CLICON_NACM_MODE { + type nacm_mode; + default disabled; + description + "RFC8341 network access configuration control model (NACM) mode: disabled, + in regular (internal) config or separate external file given by CLICON_NACM_FILE"; + } + leaf CLICON_NACM_FILE { + type string; + description + "RFC8341 NACM external configuration file (if CLIXON_NACM_MODE is external)"; + } + leaf CLICON_NACM_CREDENTIALS { + type nacm_cred_mode; + default except; + description + "Verify nacm user credentials with unix socket peer cred. + This means nacm user must match unix user accessing the backend + socket."; + } + leaf CLICON_NACM_RECOVERY_USER { + type string; + description + "RFC8341 defines a 'recovery session' as outside its scope. Clixon + defines this user as having special admin rights to exempt from + all access control enforcements. + Note setting of CLICON_NACM_CREDENTIALS is important, if set to + exact for example, this user must exist and be used, otherwise + another user (such as root or www) can pose as the recovery user."; + } + leaf CLICON_NACM_DISABLED_ON_EMPTY { + type boolean; + default false; + description + "RFC 8341 and ietf-netconf-acm@2018-02-14.yang defines enable-nacm as true by + default. Since also write-default is deny by default it leads to that empty + configs can not be edited. + This means that a startup config must always have a NACM configuration or + that the NACM recovery session is used to edit an empty config. + If this option is set, Clixon disables NACM if a datastore does NOT contain a + NACM config on load."; + } + leaf CLICON_MODULE_LIBRARY_RFC7895 { + type boolean; + default true; + description + "Enable RFC 7895 YANG Module library support as state data. If + enabled, module info will appear when doing netconf get or + restconf GET. + See also CLICON_XMLDB_MODSTATE"; + } + leaf CLICON_MODULE_SET_ID { + type string; + default "0"; + description "If RFC 7895 YANG Module library enabled: + Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + leaf CLICON_STREAM_DISCOVERY_RFC5277 { + type boolean; + default false; + description "Enable event stream discovery as described in RFC 5277 + sections 3.2. If enabled, available streams will appear + when doing netconf get or restconf GET"; + } + leaf CLICON_STREAM_DISCOVERY_RFC8040 { + type boolean; + default false; + description + "Enable monitoring information for the RESTCONF protocol from RFC 8040"; + } + leaf CLICON_STREAM_PATH { + type string; + default "streams"; + description "Stream path appended to CLICON_STREAM_URL to form + stream subscription URL."; + } + leaf CLICON_STREAM_URL { + type string; + default "https://localhost"; + description "Prepend this to CLICON_STREAM_PATH to form URL. + See RFC 8040 Sec 9.3 location leaf: + 'Contains a URL that represents the entry point for + establishing notification delivery via server-sent events.' + Prepend this constant to name of stream. + Example: https://localhost/streams/NETCONF. Note this is the + external URL, not local behind a reverse-proxy. + Note that -s command-line option to clixon_restconf + should correspond to last path of url (eg 'streams')"; + } + leaf CLICON_STREAM_PUB { + type string; + description "For stream publish using eg nchan, the base address + to publish to. Example value: http://localhost/pub + Example: stream NETCONF would then be pushed to + http://localhost/pub/NETCONF. + Note this may be a local/provate URL behind reverse-proxy. + If not given, do NOT enable stream publishing using NCHAN."; + } + leaf CLICON_STREAM_RETENTION { + type uint32; + default 3600; + units s; + description "Retention for stream replay buffers in seconds, ie how much + data to store before dropping. 0 means no retention"; + + } + } +}