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";
+
+ }
+ }
+}