From 496d676ad3850e01807e61a709108b64666329e0 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 19 Nov 2017 11:52:30 +0100 Subject: [PATCH] Backend daemon startup modes --- CHANGELOG.md | 30 +- apps/backend/backend_main.c | 602 +++++++++++++++++++-------- apps/backend/backend_plugin.c | 11 +- apps/backend/backend_plugin.h | 4 +- apps/backend/clixon_backend.h | 2 +- datastore/keyvalue/clixon_keyvalue.c | 26 +- datastore/keyvalue/clixon_keyvalue.h | 18 +- datastore/text/clixon_xmldb_text.c | 23 +- datastore/text/clixon_xmldb_text.h | 18 +- doc/FAQ.md | 4 +- example/routing_backend.c | 60 ++- include/clixon_custom.h | 7 + lib/clixon/clixon_options.h | 8 + lib/clixon/clixon_xml_db.h | 38 +- lib/src/clixon_file.c | 2 +- lib/src/clixon_options.c | 36 +- lib/src/clixon_xml_db.c | 22 +- test/test_cli.sh | 2 +- test/test_leafref.sh | 2 +- test/test_netconf.sh | 2 +- test/test_perf.sh | 2 +- test/test_restconf.sh | 2 +- test/test_startup.sh | 90 ++++ test/test_type.sh | 2 +- test/test_yang.sh | 2 +- yang/clixon-config@2017-07-02.yang | 34 +- 26 files changed, 769 insertions(+), 280 deletions(-) create mode 100755 test/test_startup.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eeaf5c5..44e5febc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ -# Clixon CHANGELOG +# 3.3.3 (Upcoming) -## 3.3.3 Upcoming +### Known issues +### Major changes: +* Introducing backend daemon startup modes. The flags -IRCr and option CLICON_USE_STARTUP_CONFIG are replaced with command-line option -s and option CLICON_STARTUP_MODE. You need to replace the starting of clixon_backend as follows: + -I replace with "init" (as -s command line option or CLICON_STARTUP_MODE option) + -CIr replace with "running" + null replace with "none" + CLICON_USE_STARTUP_CONFIG=1 replace with "startup" +Backward compatibility is enabled by defining BACKEND_STARTUP_BACKWARD_COMPAT in include/clixon_custom.h +### Minor changes: * When user callbacks such as statedata() call returns -1, clixon_backend no longer silently exits. Instead a log is printed and an RPC error is returned. * Added Floating point and negative number support to JSON @@ -45,7 +53,7 @@ clixon_cli -f /usr/local/etc/routing.conf -1x * Support for non-line scrolling in CLI, eg wrap lines. Set with: CLICON_CLI_LINESCROLLING 0 -## 3.3.2 Aug 27 2017 +# 3.3.2 (Aug 27 2017) ### Known issues * Please use text datastore, key-value datastore no up-to-date @@ -138,7 +146,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed: * Restconf GET will return state data also, if defined. * You need to define state data in a backend callback. See the example and documentation for more details. -### Minor changes: +### Minor Changes * Added xpath support for predicate: current(), eg /interface[name=current()/../name] * Added prefix parsing of xpath, allowing eg /p:x/p:y, but prefix ignored. * Corrected Yang union CLI generation and type validation. Recursive unions did not work. @@ -154,7 +162,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed: * Removed vector return values from xmldb_get() * Generalized yang type resolution to all included (sub)modules not just the topmost -## 3.3.1 June 7 2017 +# 3.3.1 (June 7 2017) * Fixed yang leafref cli completion for absolute paths. @@ -162,9 +170,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed: * Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000 -## 3.3.0 - -May 2017 +# 3.3.0 (May 2017) * Datastore text module is now default. @@ -205,7 +211,7 @@ May 2017 Instead use the rpc calls in clixon_proto_client.[ch] In clients (eg cli/netconf) replace xmldb_get() in client code with clicon_rpc_get_config(). - If you use the vector arguments of xmldb_get(), replace as follows: + pIf you use the vector arguments of xmldb_get(), replace as follows: xmldb_get(h, db, api_path, &xt, &xvec, &xlen); with clicon_rpc_get_config(h, dbstr, api_path, &xt); @@ -273,7 +279,9 @@ May 2017 * Added union type check for non-cli (eg xml) input * Empty yang type. Relaxed yang types for unions, eg two strings with different length. -Dec 2016: Dual license: both GPLv3 and APLv2 +# (Dec 2016) +* Dual license: both GPLv3 and APLv2 -Feb 2016: Forked new clixon repository from clicon +# (Feb 2016) +* Forked new clixon repository from clicon diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 92cccc08..971adcf0 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -73,7 +73,11 @@ #include "backend_handle.h" /* Command line options to be passed to getopt(3) */ -#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1IRCc:rg:py:x:" +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT +#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1s:c:IRCrg:py:x:" /* substitute s: for IRCc:r */ +#else +#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1s:c:g:py:x:" /* substitute s: for IRCc:r */ +#endif /*! Terminate. Cannot use h after this */ static int @@ -114,8 +118,7 @@ backend_sig_term(int arg) clicon_exit_set(); /* checked in event_loop() */ } -/* - * usage +/*! usage */ static void usage(char *argv0, clicon_handle h) @@ -137,11 +140,14 @@ usage(char *argv0, clicon_handle h) " -1\t\tRun once and then quit (dont wait for events)\n" " -u \tConfig UNIX domain path / ip address (default: %s)\n" " -P \tPid filename (default: %s)\n" + " -s \tSpecify backend startup mode: none|startup|running|init (replaces -IRCc:r:\n" + " -c \tLoad extra xml configuration, but don't commit.\n" +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT " -I\t\tInitialize running state database\n" " -R\t\tCall plugin_reset() in plugins to reset system state in running db (use with -I)\n" " -C\t\tCall plugin_reset() in plugins to reset system state in candidate db (use with -I)\n" - " -c \tLoad specified application config.\n" " -r\t\tReload running database\n" +#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */ " -p \t\tPrint database yang specification\n" " -g \tClient membership required to this group (default: %s)\n" " -y \tOverride yang spec file (dont include .yang suffix)\n" @@ -166,69 +172,26 @@ db_reset(clicon_handle h, return 0; } -/*! Initialize running-config from file application configuration - * - * @param[in] h clicon handle - * @param[in] app_config_file clicon application configuration file - * @param[in] running_db Name of running db - * @retval 0 OK - * @retval -1 Error. clicon_err set +/*! Merge db1 into db2 without commit */ static int -rundb_main(clicon_handle h, - char *app_config_file) +db_merge(clicon_handle h, + const char *db1, + const char *db2) { - int retval = -1; - int fd = -1; - cxobj *xt = NULL; - cxobj *xn; + int retval = -1; + cxobj *xt = NULL; - if (xmldb_create(h, "tmp") < 0) + /* Get data as xml from db1 */ + if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0) goto done; - if (xmldb_copy(h, "running", "tmp") < 0){ - clicon_err(OE_UNIX, errno, "file copy"); - goto done; - } - if ((fd = open(app_config_file, O_RDONLY)) < 0){ - clicon_err(OE_UNIX, errno, "open(%s)", app_config_file); - goto done; - } - if (clicon_xml_parse_file(fd, &xt, "") < 0) - goto done; - if ((xn = xml_child_i(xt, 0)) != NULL) - if (xmldb_put(h, "tmp", OP_MERGE, xn) < 0) - goto done; - if (candidate_commit(h, "tmp") < 0) - goto done; - if (xmldb_delete(h, "tmp") < 0) + /* Merge xml into db2. WIthout commit */ + if (xmldb_put(h, (char*)db2, OP_MERGE, xt) < 0) goto done; retval = 0; -done: + done: if (xt) xml_free(xt); - if (fd != -1) - close(fd); - return retval; -} - -static int -candb_reset(clicon_handle h) -{ - int retval = -1; - - if (xmldb_copy(h, "running", "tmp") < 0){ - clicon_err(OE_UNIX, errno, "file copy"); - goto done; - } - /* Request plugins to reset system state, eg initiate running from system - * -R - */ - if (plugin_reset_state(h, "tmp") < 0) - goto done; - if (candidate_commit(h, "tmp") < 0) - goto done; - retval = 0; - done: return retval; } @@ -291,6 +254,337 @@ backend_log_cb(int level, return retval; } +/*! Call plugin_start with -- user options */ +static int +plugin_start_useroptions(clicon_handle h, + char *argv0, + int argc, + char **argv) +{ + char *tmp; + + tmp = *(argv-1); + *(argv-1) = argv0; + if (plugin_start_argv(h, argc+1, argv-1) < 0) + return -1; + *(argv-1) = tmp; + return 0; +} + +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT +/*! Initialize running-config from file application configuration + * + * @param[in] h clicon handle + * @param[in] extraxml_file clicon application configuration file + * @param[in] running_db Name of running db + * @retval 0 OK + * @retval -1 Error. clicon_err set + */ +static int +rundb_main(clicon_handle h, + char *extraxml_file) +{ + int retval = -1; + int fd = -1; + cxobj *xt = NULL; + cxobj *xn; + + if (xmldb_create(h, "tmp") < 0) + goto done; + if (xmldb_copy(h, "running", "tmp") < 0) + goto done; + if ((fd = open(extraxml_file, O_RDONLY)) < 0){ + clicon_err(OE_UNIX, errno, "open(%s)", extraxml_file); + goto done; + } + if (clicon_xml_parse_file(fd, &xt, "") < 0) + goto done; + if ((xn = xml_child_i(xt, 0)) != NULL) + if (xmldb_put(h, "tmp", OP_MERGE, xn) < 0) + goto done; + if (candidate_commit(h, "tmp") < 0) + goto done; + if (xmldb_delete(h, "tmp") < 0) + goto done; + retval = 0; +done: + if (xt) + xml_free(xt); + if (fd != -1) + close(fd); + return retval; +} + +static int +candb_reset(clicon_handle h) +{ + int retval = -1; + + if (xmldb_copy(h, "running", "tmp") < 0) + goto done; + /* Request plugins to reset system state, eg initiate running from system + * -R + */ + if (plugin_reset_state(h, "tmp") < 0) + goto done; + if (candidate_commit(h, "tmp") < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Legacy (old-style) startup mode where flags -IRCcr was used + */ +static int +fragmented_startup_mode(clicon_handle h, + char *argv0, + int argc, + char *argv[], + int reload_running, + int init_rundb, + int reset_state_candidate, + int reset_state_running, + char *extraxml_file) +{ + int retval = -1; + + /* First check for startup config */ + if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){ + if (xmldb_exists(h, "startup") == 1){ + /* copy startup config -> running */ + if (xmldb_copy(h, "startup", "running") < 0) + goto done; + } + else + if (db_reset(h, "running") < 0) + goto done; + if (xmldb_create(h, "candidate") < 0) + goto done; + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + } + /* If running exists and reload_running set, make a copy to candidate */ + if (reload_running){ + if (xmldb_exists(h, "running") != 1){ + clicon_log(LOG_NOTICE, "%s: -r (reload running) option given but no running_db found, proceeding without", __PROGRAM__); + reload_running = 0; /* void it, so we dont commit candidate below */ + } + else + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + } + /* Init running db + * -I or if it isnt there + */ + if (init_rundb || xmldb_exists(h, "running") != 1){ + if (db_reset(h, "running") < 0) + goto done; + } + /* If candidate does not exist, create it from running */ + if (xmldb_exists(h, "candidate") != 1){ + if (xmldb_create(h, "candidate") < 0) + goto done; + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + } + + /* Load plugins and call plugin_init() */ + if (plugin_initiate(h) != 0) + goto done; + + if (reset_state_candidate){ + if (candb_reset(h) < 0) + goto done; + } + else + if (reset_state_running){ + if (plugin_reset_state(h, "running") < 0) + goto done; + } + + if (plugin_start_useroptions(h, argv0, argc, argv) <0) + goto done; + + if (reload_running){ + /* This could be a failed validation, and we should not fail for that */ + (void)candidate_commit(h, "candidate"); + } + + /* Have we specified a config file to load? eg + * -c [] + */ + if (extraxml_file) + if (rundb_main(h, extraxml_file) < 0) + goto done; + /* Initiate the shared candidate. */ + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + retval = 0; + done: + return retval; +} +#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */ + +/*! Merge xml in filename into database + */ +static int +load_extraxml(clicon_handle h, + char *filename, + const char *db) +{ + int retval = -1; + cxobj *xt = NULL; + int fd = -1; + + if (filename == NULL) + return 0; + if ((fd = open(filename, O_RDONLY)) < 0){ + clicon_err(OE_UNIX, errno, "open(%s)", filename); + goto done; + } + if (clicon_xml_parse_file(fd, &xt, "") < 0) + goto done; + /* Replace parent w first child */ + if (xml_rootchild(xt, 0, &xt) < 0) + goto done; + /* Merge user reset state */ + if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0) + goto done; + retval = 0; + done: + if (fd != -1) + close(fd); + if (xt) + xml_free(xt); + return retval; +} + +/*! Clixon none startup modes Do not touch running state + */ +static int +startup_mode_none(clicon_handle h) +{ + int retval = -1; + + /* If it is not there, create candidate from running */ + if (xmldb_exists(h, "candidate") != 1) + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + /* Load plugins and call plugin_init() */ + if (plugin_initiate(h) != 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Clixon init startup modes Start with a completely clean running state + */ +static int +startup_mode_init(clicon_handle h) +{ + int retval = -1; + + /* Reset running, regardless */ + if (db_reset(h, "running") < 0) + goto done; + /* If it is not there, create candidate from running */ + if (xmldb_exists(h, "candidate") != 1) + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + /* Load plugins and call plugin_init() */ + if (plugin_initiate(h) != 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Clixon running startup mode: Commit running db configuration into running + * + copy reset commit merge +running----+ |--------------------+-----+------> + \ / / +candidate +--------------------+ / + / +tmp |-------+-----+---------+ + reset extra file + */ +static int +startup_mode_running(clicon_handle h, + char *extraxml_file) +{ + int retval = -1; + + /* Stash original running to candidate for later commit */ + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + /* Load plugins and call plugin_init() */ + if (plugin_initiate(h) != 0) + goto done; + /* Clear tmp db */ + if (db_reset(h, "tmp") < 0) + goto done; + /* Application may define extra xml in its reset function*/ + if (plugin_reset_state(h, "tmp") < 0) + goto done; + /* Get application extra xml from file */ + if (load_extraxml(h, extraxml_file, "tmp") < 0) + goto done; + /* Commit original running */ + if (candidate_commit(h, "candidate") < 0) + goto done; + /* Merge user reset state and extra xml file (no commit) */ + if (db_merge(h, "tmp", "running") < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Clixon startup startup mode: Commit startup configuration into running state + reset commit merge +running |--------------------+-----+------> + / / +startup --------------------+ / + / +tmp |-------+-----+---------+ + reset extra file + */ +static int +startup_mode_startup(clicon_handle h, + char *extraxml_file) +{ + int retval = -1; + + /* If startup does not exist, clear it */ + if (xmldb_exists(h, "startup") != 1) /* diff */ + if (xmldb_create(h, "startup") < 0) /* diff */ + return -1; + /* Load plugins and call plugin_init() */ + if (plugin_initiate(h) != 0) + goto done; + /* Clear tmp db */ + if (db_reset(h, "tmp") < 0) + goto done; + /* Application may define extra xml in its reset function*/ + if (plugin_reset_state(h, "tmp") < 0) + goto done; + /* Get application extra xml from file */ + if (load_extraxml(h, extraxml_file, "tmp") < 0) + goto done; + /* Commit startup */ + if (candidate_commit(h, "startup") < 0) /* diff */ + goto done; + /* Merge user reset state and extra xml file (no commit) */ + if (db_merge(h, "tmp", "running") < 0) + goto done; + retval = 0; + done: + return retval; +} + int main(int argc, char **argv) { @@ -298,14 +592,16 @@ main(int argc, char **argv) int zap; int foreground; int once; - int init_rundb; - int reload_running; - int reset_state_running; - int reset_state_candidate; - char *app_config_file = NULL; + enum startup_mode_t startup_mode; +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT + int init_rundb = 0; + int reset_state_running = 0; + int reset_state_candidate = 0; + int reload_running = 0; +#endif + char *extraxml_file; char *config_group; char *argv0 = argv[0]; - char *tmp; struct stat st; clicon_handle h; int help = 0; @@ -317,7 +613,6 @@ main(int argc, char **argv) char *xmldb_plugin; /* In the startup, logs to stderr & syslog and debug flag set later */ - clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG); /* Initiate CLICON handle */ if ((h = backend_handle_init()) == NULL) @@ -327,10 +622,7 @@ main(int argc, char **argv) foreground = 0; once = 0; zap = 0; - init_rundb = 0; - reload_running = 0; - reset_state_running = 0; - reset_state_candidate = 0; + extraxml_file = NULL; /* * Command-line options for help, debug, and config-file @@ -402,32 +694,41 @@ main(int argc, char **argv) case 'z': /* Zap other process */ zap++; break; - case 'u': /* config unix domain path / ip address */ + case 'u': /* config unix domain path / ip address */ if (!strlen(optarg)) usage(argv[0], h); clicon_option_str_set(h, "CLICON_SOCK", optarg); - break; - case 'P': /* pidfile */ - clicon_option_str_set(h, "CLICON_BACKEND_PIDFILE", optarg); - break; - case 'I': /* Initiate running db */ - init_rundb++; - break; - case 'R': /* Reset state directly into running */ - reset_state_running++; - break; - case 'C': /* Reset state into candidate and then commit it */ - reset_state_candidate++; - break; - case 'c': /* Load application config */ - app_config_file = optarg; - break; - case 'r': /* Reload running */ - reload_running++; - break; - case 'g': /* config socket group */ - clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg); - break; + break; + case 'P': /* pidfile */ + clicon_option_str_set(h, "CLICON_BACKEND_PIDFILE", optarg); + break; + case 's' : /* startup mode */ + clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg); + if (clicon_startup_mode(h) < 0){ + fprintf(stderr, "Invalid startup mode: %s\n", optarg); + usage(argv[0], h); + } + break; + case 'c': /* Load application config */ + extraxml_file = optarg; + break; +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT + case 'I': /* Initiate running db */ + init_rundb++; + break; + case 'R': /* Reset state directly into running */ + reset_state_running++; + break; + case 'C': /* Reset state into candidate and then commit it */ + reset_state_candidate++; + break; + case 'r': /* Reload running */ + reload_running++; + break; +#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */ + case 'g': /* config socket group */ + clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg); + break; case 'p' : /* Print spec */ printspec++; break; @@ -522,87 +823,52 @@ main(int argc, char **argv) goto done; if (xmldb_setopt(h, "yangspec", clicon_dbspec_yang(h)) < 0) goto done; - - /* First check for startup config - XXX the options below have become out-of-hand. - Too complex, need to simplify*/ - if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){ - if (xmldb_exists(h, "startup") == 1){ - /* copy startup config -> running */ - if (xmldb_copy(h, "startup", "running") < 0) - goto done; - } - else - if (db_reset(h, "running") < 0) - goto done; - if (xmldb_create(h, "candidate") < 0) + /* If startup mode is not defined, eg via OPTION or -s, assume old method */ + startup_mode = clicon_startup_mode(h); + if (startup_mode == -1){ /* Old style, fragmented mode, phase out */ +#ifdef BACKEND_STARTUP_BACKWARD_COMPAT + if (fragmented_startup_mode(h, + argv0, argc, argv, + reload_running, init_rundb, + reset_state_candidate, reset_state_running, + extraxml_file + ) < 0) goto done; +#else + clicon_log(LOG_ERR, "Startup mode indefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend.\n"); +#endif + } + else { + /* Init running db if it is not there + */ + if (xmldb_exists(h, "running") != 1) + if (xmldb_create(h, "running") < 0) + return -1; + switch (startup_mode){ + case SM_NONE: + if (startup_mode_none(h) < 0) + goto done; + break; + case SM_INIT: /* -I */ + if (startup_mode_init(h) < 0) + goto done; + break; + case SM_RUNNING: /* -CIr */ + if (startup_mode_running(h, extraxml_file) < 0) + goto done; + break; + case SM_STARTUP: /* startup configuration */ + if (startup_mode_startup(h, extraxml_file) < 0) + goto done; + break; + } + /* Initiate the shared candidate. */ if (xmldb_copy(h, "running", "candidate") < 0) goto done; - } - /* If running exists and reload_running set, make a copy to candidate */ - if (reload_running){ - if (xmldb_exists(h, "running") != 1){ - clicon_log(LOG_NOTICE, "%s: -r (reload running) option given but no running_db found, proceeding without", __PROGRAM__); - reload_running = 0; /* void it, so we dont commit candidate below */ - } - else - if (xmldb_copy(h, "running", "candidate") < 0) - goto done; - } - /* Init running db - * -I or if it isnt there - */ - if (init_rundb || xmldb_exists(h, "running") != 1){ - if (db_reset(h, "running") < 0) + /* Call plugin_start with user -- options */ + if (plugin_start_useroptions(h, argv0, argc, argv) <0) goto done; } - /* If candidate does not exist, create it from running */ - if (xmldb_exists(h, "candidate") != 1){ - if (xmldb_create(h, "candidate") < 0) - goto done; - if (xmldb_copy(h, "running", "candidate") < 0) - goto done; - } - - /* Initialize plugins - (also calls plugin_init() and plugin_start(argc,argv) in each plugin */ - if (plugin_initiate(h) != 0) - goto done; - - if (reset_state_candidate){ - if (candb_reset(h) < 0) - goto done; - } - else - if (reset_state_running){ - if (plugin_reset_state(h, "running") < 0) - goto done; - } - /* Call plugin_start */ - tmp = *(argv-1); - *(argv-1) = argv0; - if (plugin_start_hooks(h, argc+1, argv-1) < 0) - goto done; - *(argv-1) = tmp; - - if (reload_running){ - /* This could be a failed validation, and we should not fail for that */ - (void)candidate_commit(h, "candidate"); - } - - /* Have we specified a config file to load? eg - * -c [] - */ - if (app_config_file) - if (rundb_main(h, app_config_file) < 0) - goto done; - - /* Initiate the shared candidate. Maybe we should not do this? - * Too strict access - */ - if (xmldb_copy(h, "running", "candidate") < 0) - goto done; if (once) goto done; diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 6c837513..8003013a 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -71,7 +71,7 @@ * @note the following should match the prototypes in clixon_backend.h */ #define PLUGIN_RESET "plugin_reset" -typedef int (plgreset_t)(clicon_handle h, char *dbname); /* Reset system status */ +typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system status */ /*! Plugin callback, if defined called to get state data from plugin * @param[in] h Clicon handle @@ -253,19 +253,18 @@ backend_plugin_load (clicon_handle h, * @retval -1 Error */ int -plugin_reset_state(clicon_handle h, - char *dbname) +plugin_reset_state(clicon_handle h, + const char *db) { int i; struct plugin *p; - for (i = 0; i < _nplugins; i++) { p = &_plugins[i]; if (p->p_reset) { clicon_debug(1, "Calling plugin_reset() for %s\n", p->p_name); - if (((p->p_reset)(h, dbname)) < 0) { + if (((p->p_reset)(h, db)) < 0) { clicon_err(OE_FATAL, 0, "plugin_reset() failed for %s\n", p->p_name); return -1; @@ -283,7 +282,7 @@ plugin_reset_state(clicon_handle h, * @retval -1 Error */ int -plugin_start_hooks(clicon_handle h, +plugin_start_argv(clicon_handle h, int argc, char **argv) { diff --git a/apps/backend/backend_plugin.h b/apps/backend/backend_plugin.h index 320f8b47..c2d2b8a0 100644 --- a/apps/backend/backend_plugin.h +++ b/apps/backend/backend_plugin.h @@ -66,8 +66,8 @@ int backend_plugin_init(clicon_handle h); int plugin_initiate(clicon_handle h); int plugin_finish(clicon_handle h); -int plugin_reset_state(clicon_handle h, char *dbname); -int plugin_start_hooks(clicon_handle h, int argc, char **argv); +int plugin_reset_state(clicon_handle h, const char *db); +int plugin_start_argv(clicon_handle h, int argc, char **argv); int backend_statedata_call(clicon_handle h, char *xpath, cxobj *xml); diff --git a/apps/backend/clixon_backend.h b/apps/backend/clixon_backend.h index 49eb5654..f9b0d28f 100644 --- a/apps/backend/clixon_backend.h +++ b/apps/backend/clixon_backend.h @@ -70,7 +70,7 @@ int plugin_exit(clicon_handle h); /*! Reset system state to original state. Eg at reboot before running thru config. * @see plgreset_t */ -int plugin_reset(clicon_handle h, char *dbname); +int plugin_reset(clicon_handle h, const char *db); /*! Retreive statedata, add statedata to XML tree * @see plgstatedata_ t diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index 0c1ff566..35a65c40 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -150,8 +150,8 @@ static int _startup_locked = 0; */ static int kv_db2file(struct kv_handle *kh, - char *db, - char **filename) + const char *db, + char **filename) { int retval = -1; cbuf *cb; @@ -565,7 +565,7 @@ kv_setopt(xmldb_handle xh, */ int kv_get(xmldb_handle xh, - char *db, + const char *db, char *xpath, int config, cxobj **xtop) @@ -783,7 +783,7 @@ put(char *dbfile, */ int kv_put(xmldb_handle xh, - char *db, + const char *db, enum operation_type op, cxobj *xt) { @@ -836,8 +836,8 @@ kv_put(xmldb_handle xh, */ int kv_copy(xmldb_handle xh, - char *from, - char *to) + const char *from, + const char *to) { int retval = -1; struct kv_handle *kh = handle(xh); @@ -869,8 +869,8 @@ kv_copy(xmldb_handle xh, */ int kv_lock(xmldb_handle xh, - char *db, - int pid) + const char *db, + int pid) { int retval = -1; // struct kv_handle *kh = handle(xh); @@ -900,7 +900,7 @@ kv_lock(xmldb_handle xh, */ int kv_unlock(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; // struct kv_handle *kh = handle(xh); @@ -949,7 +949,7 @@ kv_unlock_all(xmldb_handle xh, */ int kv_islocked(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; // struct kv_handle *kh = handle(xh); @@ -974,7 +974,7 @@ kv_islocked(xmldb_handle xh, */ int kv_exists(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; struct kv_handle *kh = handle(xh); @@ -1001,7 +1001,7 @@ kv_exists(xmldb_handle xh, */ int kv_delete(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; struct kv_handle *kh = handle(xh); @@ -1026,7 +1026,7 @@ kv_delete(xmldb_handle xh, */ int kv_create(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; struct kv_handle *kh = handle(xh); diff --git a/datastore/keyvalue/clixon_keyvalue.h b/datastore/keyvalue/clixon_keyvalue.h index 83654df0..33da4962 100644 --- a/datastore/keyvalue/clixon_keyvalue.h +++ b/datastore/keyvalue/clixon_keyvalue.h @@ -39,16 +39,16 @@ /* * Prototypes */ -int kv_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop); -int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt); +int kv_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop); +int kv_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt); int kv_dump(FILE *f, char *dbfilename, char *rxkey); -int kv_copy(xmldb_handle h, char *from, char *to); -int kv_lock(xmldb_handle h, char *db, int pid); -int kv_unlock(xmldb_handle h, char *db); +int kv_copy(xmldb_handle h, const char *from, const char *to); +int kv_lock(xmldb_handle h, const char *db, int pid); +int kv_unlock(xmldb_handle h, const char *db); int kv_unlock_all(xmldb_handle h, int pid); -int kv_islocked(xmldb_handle h, char *db); -int kv_exists(xmldb_handle h, char *db); -int kv_delete(xmldb_handle h, char *db); -int kv_init(xmldb_handle h, char *db); +int kv_islocked(xmldb_handle h, const char *db); +int kv_exists(xmldb_handle h, const char *db); +int kv_delete(xmldb_handle h, const char *db); +int kv_init(xmldb_handle h, const char *db); #endif /* _CLIXON_KEYVALUE_H */ diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index e95b6d8d..c0077221 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -99,7 +99,7 @@ text_handle_check(xmldb_handle xh) */ static int text_db2file(struct text_handle *th, - char *db, + const char *db, char **filename) { int retval = -1; @@ -281,7 +281,7 @@ singleconfigroot(cxobj *xt, */ int text_get(xmldb_handle xh, - char *db, + const char *db, char *xpath, int config, cxobj **xtop) @@ -681,7 +681,7 @@ xml_container_presence(cxobj *x, */ int text_put(xmldb_handle xh, - char *db, + const char *db, enum operation_type op, cxobj *x1) { @@ -807,8 +807,8 @@ text_put(xmldb_handle xh, */ int text_copy(xmldb_handle xh, - char *from, - char *to) + const char *from, + const char *to) { int retval = -1; struct text_handle *th = handle(xh); @@ -840,7 +840,7 @@ text_copy(xmldb_handle xh, */ int text_lock(xmldb_handle xh, - char *db, + const char *db, int pid) { struct text_handle *th = handle(xh); @@ -860,7 +860,7 @@ text_lock(xmldb_handle xh, */ int text_unlock(xmldb_handle xh, - char *db) + const char *db) { struct text_handle *th = handle(xh); int zero = 0; @@ -905,7 +905,7 @@ text_unlock_all(xmldb_handle xh, */ int text_islocked(xmldb_handle xh, - char *db) + const char *db) { struct text_handle *th = handle(xh); size_t vlen; @@ -926,7 +926,7 @@ text_islocked(xmldb_handle xh, */ int text_exists(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; @@ -954,7 +954,7 @@ text_exists(xmldb_handle xh, */ int text_delete(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; char *filename = NULL; @@ -974,6 +974,7 @@ text_delete(xmldb_handle xh, } /*! Create / init database + * If it exists dont change. * @param[in] xh XMLDB handle * @param[in] db Database * @retval 0 OK @@ -981,7 +982,7 @@ text_delete(xmldb_handle xh, */ int text_create(xmldb_handle xh, - char *db) + const char *db) { int retval = -1; struct text_handle *th = handle(xh); diff --git a/datastore/text/clixon_xmldb_text.h b/datastore/text/clixon_xmldb_text.h index c3c24c22..36d33633 100644 --- a/datastore/text/clixon_xmldb_text.h +++ b/datastore/text/clixon_xmldb_text.h @@ -39,16 +39,16 @@ /* * Prototypes */ -int text_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop); -int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt); +int text_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop); +int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt); int text_dump(FILE *f, char *dbfilename, char *rxkey); -int text_copy(xmldb_handle h, char *from, char *to); -int text_lock(xmldb_handle h, char *db, int pid); -int text_unlock(xmldb_handle h, char *db); +int text_copy(xmldb_handle h, const char *from, const char *to); +int text_lock(xmldb_handle h, const char *db, int pid); +int text_unlock(xmldb_handle h, const char *db); int text_unlock_all(xmldb_handle h, int pid); -int text_islocked(xmldb_handle h, char *db); -int text_exists(xmldb_handle h, char *db); -int text_delete(xmldb_handle h, char *db); -int text_init(xmldb_handle h, char *db); +int text_islocked(xmldb_handle h, const char *db); +int text_exists(xmldb_handle h, const char *db); +int text_delete(xmldb_handle h, const char *db); +int text_init(xmldb_handle h, const char *db); #endif /* _CLIXON_XMLDB_TEXT_H */ diff --git a/doc/FAQ.md b/doc/FAQ.md index 982fb236..33300881 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -23,8 +23,8 @@ APIs. There are currently plugins for: CLI, Netconf, Restconf, the datastore an Clixon is written in C. The plugins are written in C. The CLI specification uses cligen (http://cligen.se) -There is a project for writing plugins in Python. It is reasonable -simple to spawn an external script from a backend. +It is possible ro write plugins in Python. It is reasonable +simple to spawn an external script from a backend (but needs to be done). ## How to best understand Clixon? Run the ietf yang routing example, in the example directory. diff --git a/example/routing_backend.c b/example/routing_backend.c index d5bbc1f3..cb6dcb74 100644 --- a/example/routing_backend.c +++ b/example/routing_backend.c @@ -179,8 +179,10 @@ plugin_statedata(clicon_handle h, return retval; } -/* - * Plugin initialization +/*! Plugin initialization. Create rpc callbacks + * plugin_init is called as soon as the plugin has been loaded and is + * assumed initialize the plugin's internal state if any as well as register + * any callbacks, configuration dependencies. */ int plugin_init(clicon_handle h) @@ -205,3 +207,57 @@ plugin_init(clicon_handle h) return retval; } +/*! Plugin state reset. Add xml or set state in backend machine. + * Called in each backend plugin. plugin_reset is called after all plugins + * have been initialized. This give the application a chance to reset + * system state back to a base state. + * This is generally done when a system boots up to + * make sure the initial system state is well defined. This can be creating + * default configuration files for various daemons, set interface flags etc. + * @param[in] h Clicon handle + * @param[in] db Name of database. Not may be other than "running" + * In this example, a loopback interface is added + */ +int +plugin_reset(clicon_handle h, + const char *db) +{ + int retval = -1; + cxobj *xt = NULL; + + if (clicon_xml_parse_str("" + "lolocal" + "", &xt) < 0) + goto done; + /* Replace parent w fiorst child */ + if (xml_rootchild(xt, 0, &xt) < 0) + goto done; + /* Merge user reset state */ + if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0) + goto done; + retval = 0; + done: + if (xt != NULL) + free(xt); + return retval; +} + +/*! Plugin start. + * @param[in] h Clicon handle + * @param[in] argc Argument vector length (args after -- to backend_main) + * @param[in] argv Argument vector + * + * plugin_start is called once everything has been initialized, right before + * the main event loop is entered. Command line options can be passed to the + * plugins by using "-- " where is any choice of + * options specific to the application. These options are passed to the + * plugin_start function via the argc and argv arguments which + * can be processed with the standard getopt(3). + */ +int +plugin_start(clicon_handle h, + int argc, + char **argv) +{ + return 0; +} diff --git a/include/clixon_custom.h b/include/clixon_custom.h index f514230f..473ae77c 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -47,3 +47,10 @@ int strverscmp (__const char *__s1, __const char *__s2); * Experimental */ #define XML_CHILD_HASH 1 + +/* Backward compatible clixon backend daemon startup sequences + * This has been replaced with -s and CLICON_STARTUP_MODE + * define if enable backward compatible behaviour + * Remove in 3.3.4 + */ +#undef BACKEND_STARTUP_BACKWARD_COMPAT diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 7f3bfbc4..cbd2647f 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -63,6 +63,13 @@ enum genmodel_type{ GT_ALL, /* Keywords on all variables */ }; +/*! See clixon-config.yang type startup_mode */ +enum startup_mode_t{ + SM_NONE=0, + SM_STARTUP, + SM_RUNNING, + SM_INIT +}; /* * Prototypes @@ -93,6 +100,7 @@ char *clicon_clispec_dir(clicon_handle h); char *clicon_netconf_dir(clicon_handle h); char *clicon_restconf_dir(clicon_handle h); char *clicon_xmldb_plugin(clicon_handle h); +int clicon_startup_mode(clicon_handle h); int clicon_sock_family(clicon_handle h); char *clicon_sock(clicon_handle h); int clicon_sock_port(clicon_handle h); diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h index 23a04ca0..647dfe04 100644 --- a/lib/clixon/clixon_xml_db.h +++ b/lib/clixon/clixon_xml_db.h @@ -75,34 +75,34 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value); typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value); /* Type of xmldb get function */ -typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, int config, cxobj **xtop); +typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop); /* Type of xmldb put function */ -typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op, cxobj *xt); +typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt); /* Type of xmldb copy function */ -typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to); +typedef int (xmldb_copy_t)(xmldb_handle xh, const char *from, const char *to); /* Type of xmldb lock function */ -typedef int (xmldb_lock_t)(xmldb_handle xh, char *db, int pid); +typedef int (xmldb_lock_t)(xmldb_handle xh, const char *db, int pid); /* Type of xmldb unlock function */ -typedef int (xmldb_unlock_t)(xmldb_handle xh, char *db); +typedef int (xmldb_unlock_t)(xmldb_handle xh, const char *db); /* Type of xmldb unlock_all function */ typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid); /* Type of xmldb islocked function */ -typedef int (xmldb_islocked_t)(xmldb_handle xh, char *db); +typedef int (xmldb_islocked_t)(xmldb_handle xh, const char *db); /* Type of xmldb exists function */ -typedef int (xmldb_exists_t)(xmldb_handle xh, char *db); +typedef int (xmldb_exists_t)(xmldb_handle xh, const char *db); /* Type of xmldb delete function */ -typedef int (xmldb_delete_t)(xmldb_handle xh, char *db); +typedef int (xmldb_delete_t)(xmldb_handle xh, const char *db); /* Type of xmldb init function */ -typedef int (xmldb_create_t)(xmldb_handle xh, char *db); +typedef int (xmldb_create_t)(xmldb_handle xh, const char *db); /* plugin init struct for the api */ struct xmldb_api{ @@ -133,20 +133,20 @@ struct xmldb_api{ int xmldb_plugin_load(clicon_handle h, char *filename); int xmldb_plugin_unload(clicon_handle h); -int xmldb_validate_db(char *db); +int xmldb_validate_db(const char *db); int xmldb_connect(clicon_handle h); int xmldb_disconnect(clicon_handle h); int xmldb_getopt(clicon_handle h, char *optname, void **value); int xmldb_setopt(clicon_handle h, char *optname, void *value); -int xmldb_get(clicon_handle h, char *db, char *xpath, int config, cxobj **xtop); -int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt); -int xmldb_copy(clicon_handle h, char *from, char *to); -int xmldb_lock(clicon_handle h, char *db, int pid); -int xmldb_unlock(clicon_handle h, char *db); +int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop); +int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt); +int xmldb_copy(clicon_handle h, const char *from, const char *to); +int xmldb_lock(clicon_handle h, const char *db, int pid); +int xmldb_unlock(clicon_handle h, const char *db); int xmldb_unlock_all(clicon_handle h, int pid); -int xmldb_islocked(clicon_handle h, char *db); -int xmldb_exists(clicon_handle h, char *db); -int xmldb_delete(clicon_handle h, char *db); -int xmldb_create(clicon_handle h, char *db); +int xmldb_islocked(clicon_handle h, const char *db); +int xmldb_exists(clicon_handle h, const char *db); +int xmldb_delete(clicon_handle h, const char *db); +int xmldb_create(clicon_handle h, const char *db); #endif /* _CLIXON_XML_DB_H */ diff --git a/lib/src/clixon_file.c b/lib/src/clixon_file.c index bb1868dd..7282aece 100644 --- a/lib/src/clixon_file.c +++ b/lib/src/clixon_file.c @@ -185,7 +185,7 @@ quit: return retval; } -/*! Make a copy of file src +/*! Make a copy of file src. Overwrite existing * @retval 0 OK * @retval -1 Error */ diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index fd0d51de..3d6ce6fb 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -58,6 +58,7 @@ /* clicon */ #include "clixon_err.h" +#include "clixon_string.h" #include "clixon_queue.h" #include "clixon_hash.h" #include "clixon_handle.h" @@ -69,6 +70,16 @@ #include "clixon_xsl.h" #include "clixon_xml_map.h" +/* Mapping between Clicon startup modes string <--> constants, + see clixon-config.yang type startup_mode */ +static const map_str2int startup_mode_map[] = { + {"none", SM_NONE}, + {"running", SM_RUNNING}, + {"startup", SM_STARTUP}, + {"init", SM_INIT}, + {NULL, -1} +}; + /*! Print registry on file. For debugging. */ void @@ -332,7 +343,6 @@ clicon_option_sanity(clicon_hash_t *copt) return retval; } - /*! Initialize option values * * Set default options, Read config-file, Check that all values are set. @@ -387,9 +397,12 @@ clicon_options_main(clicon_handle h) } /*! Check if a clicon option has a value + * @param[in] h clicon_handle + * @param[in] name option name */ int -clicon_option_exists(clicon_handle h, const char *name) +clicon_option_exists(clicon_handle h, + const char *name) { clicon_hash_t *copt = clicon_options(h); @@ -449,7 +462,8 @@ clicon_option_str_set(clicon_handle h, * supply a defualt value as shown in the example. */ int -clicon_option_int(clicon_handle h, const char *name) +clicon_option_int(clicon_handle h, + const char *name) { char *s; @@ -461,7 +475,9 @@ clicon_option_int(clicon_handle h, const char *name) /*! Set option given as int. */ int -clicon_option_int_set(clicon_handle h, const char *name, int val) +clicon_option_int_set(clicon_handle h, + const char *name, + int val) { char s[64]; @@ -473,7 +489,8 @@ clicon_option_int_set(clicon_handle h, const char *name, int val) /*! Delete option */ int -clicon_option_del(clicon_handle h, const char *name) +clicon_option_del(clicon_handle h, + const char *name) { clicon_hash_t *copt = clicon_options(h); @@ -550,6 +567,15 @@ clicon_xmldb_plugin(clicon_handle h) return clicon_option_str(h, "CLICON_XMLDB_PLUGIN"); } +int +clicon_startup_mode(clicon_handle h) +{ + char *mode; + if ((mode = clicon_option_str(h, "CLICON_STARTUP_MODE")) == NULL) + return -1; + return clicon_str2int(startup_mode_map, mode); +} + /*! Get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */ int clicon_sock_family(clicon_handle h) diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index d0f096df..383eb4fa 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -175,7 +175,7 @@ xmldb_plugin_unload(clicon_handle h) * @retval -1 Failed validate, xret set to error */ int -xmldb_validate_db(char *db) +xmldb_validate_db(const char *db) { if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0 && @@ -336,7 +336,7 @@ xmldb_setopt(clicon_handle h, */ int xmldb_get(clicon_handle h, - char *db, + const char *db, char *xpath, int config, cxobj **xtop) @@ -393,7 +393,7 @@ xmldb_get(clicon_handle h, */ int xmldb_put(clicon_handle h, - char *db, + const char *db, enum operation_type op, cxobj *xt) { @@ -439,8 +439,8 @@ xmldb_put(clicon_handle h, */ int xmldb_copy(clicon_handle h, - char *from, - char *to) + const char *from, + const char *to) { int retval = -1; xmldb_handle xh; @@ -472,7 +472,7 @@ xmldb_copy(clicon_handle h, */ int xmldb_lock(clicon_handle h, - char *db, + const char *db, int pid) { int retval = -1; @@ -506,7 +506,7 @@ xmldb_lock(clicon_handle h, */ int xmldb_unlock(clicon_handle h, - char *db) + const char *db) { int retval = -1; xmldb_handle xh; @@ -569,7 +569,7 @@ xmldb_unlock_all(clicon_handle h, */ int xmldb_islocked(clicon_handle h, - char *db) + const char *db) { int retval = -1; xmldb_handle xh; @@ -601,7 +601,7 @@ xmldb_islocked(clicon_handle h, */ int xmldb_exists(clicon_handle h, - char *db) + const char *db) { int retval = -1; xmldb_handle xh; @@ -632,7 +632,7 @@ xmldb_exists(clicon_handle h, */ int xmldb_delete(clicon_handle h, - char *db) + const char *db) { int retval = -1; xmldb_handle xh; @@ -663,7 +663,7 @@ xmldb_delete(clicon_handle h, */ int xmldb_create(clicon_handle h, - char *db) + const char *db) { int retval = -1; xmldb_handle xh; diff --git a/test/test_cli.sh b/test/test_cli.sh index 40853c37..c5d224f5 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -22,7 +22,7 @@ if [ $? -ne 0 ]; then fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf +sudo clixon_backend -s init -f $clixon_cf if [ $? -ne 0 ]; then err fi diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 7317e560..546400cb 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -55,7 +55,7 @@ fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -y /tmp/leafref.yang +sudo clixon_backend -s init -f $clixon_cf -y /tmp/leafref.yang if [ $? -ne 0 ]; then err fi diff --git a/test/test_netconf.sh b/test/test_netconf.sh index 70cd15b3..3f1da8a1 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -16,7 +16,7 @@ if [ $? -ne 0 ]; then fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf +sudo clixon_backend -s init -f $clixon_cf if [ $? -ne 0 ]; then err fi diff --git a/test/test_perf.sh b/test/test_perf.sh index b804b355..24f15e60 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -50,7 +50,7 @@ fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -y $fyang +sudo clixon_backend -s init -f $clixon_cf -y $fyang if [ $? -ne 0 ]; then err fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index ec733af8..f03e1ebf 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -12,7 +12,7 @@ if [ $? -ne 0 ]; then err fi new "start backend" -sudo clixon_backend -If $clixon_cf +sudo clixon_backend -s init -f $clixon_cf if [ $? -ne 0 ]; then err fi diff --git a/test/test_startup.sh b/test/test_startup.sh new file mode 100755 index 00000000..d568ab6b --- /dev/null +++ b/test/test_startup.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# Startup test: Start clicon daemon in the (four) different startup modes +# and the dbs and files are setup as follows: +# - The example reset_state callback adds "lo" interface +# - An extra xml configuration file starts with an "extra" interface +# - running db starts with a "run" interface +# - startup db starts with a "start" interface + +# include err() and new() functions +. ./lib.sh + +# For memcheck +# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" +clixon_netconf=clixon_netconf +clixon_cli=clixon_cli + +run(){ + mode=$1 + expect=$2 + + cat < /tmp/db + + + + run + eth + + + +EOF + sudo mv /tmp/db /usr/local/var/routing/running_db + + cat < /tmp/db + + + + startup + eth + + + +EOF + sudo mv /tmp/db /usr/local/var/routing/startup_db + + cat < /tmp/config + + + + extra + eth + + + +EOF + + # kill old backend (if any) + new "kill old backend" + sudo clixon_backend -zf $clixon_cf $yang + if [ $? -ne 0 ]; then + err + fi + + new "start backend" + # start new backend + sudo clixon_backend -f $clixon_cf $yang -s $mode -c /tmp/config + if [ $? -ne 0 ]; then + err + fi + + new "Check $mode" + expecteof "$clixon_netconf -qf $clixon_cf $yang" ']]>]]>' "^$expect]]>]]>$" + + new "Kill backend" + # Check if still alive + pid=`pgrep clixon_backend` + if [ -z "$pid" ]; then + err "backend already dead" + fi + # kill backend + sudo clixon_backend -zf $clixon_cf + if [ $? -ne 0 ]; then + err "kill backend" + fi +} + +run init '' +run none 'runethtrue' +run running 'runethtruelolocaltrueextraethtrue' +run startup 'startupethtruelolocaltrueextraethtrue' + diff --git a/test/test_type.sh b/test/test_type.sh index 8cfb3305..988dedc6 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -69,7 +69,7 @@ if [ $? -ne 0 ]; then fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -y /tmp/type.yang +sudo clixon_backend -s init -f $clixon_cf -y /tmp/type.yang if [ $? -ne 0 ]; then err fi diff --git a/test/test_yang.sh b/test/test_yang.sh index 018bf446..8c0b6dc3 100755 --- a/test/test_yang.sh +++ b/test/test_yang.sh @@ -70,7 +70,7 @@ fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -y /tmp/test.yang +sudo clixon_backend -s init -f $clixon_cf -y /tmp/test.yang if [ $? -ne 0 ]; then err fi diff --git a/yang/clixon-config@2017-07-02.yang b/yang/clixon-config@2017-07-02.yang index 851075d1..2cb9a8a0 100644 --- a/yang/clixon-config@2017-07-02.yang +++ b/yang/clixon-config@2017-07-02.yang @@ -38,9 +38,32 @@ ***** END LICENSE BLOCK *****"; - revision 2017-07-02 { + revision 2017-11-12 { description - "Initial revision"; + "Added startup config"; + } + 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 + Typical 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 + Usecase: after reboot if a persistent running db exists"; + } + enum startup{ + description "Commit startup configuration into running state + Usecase: after reboot when no persistent running db exists"; + } + } } container config { leaf CLICON_CONFIGFILE{ @@ -185,12 +208,17 @@ type int32; default 0; description "Enabled uses \"startup\" configuration on boot. It is called - startup_db and exists in XMLDB_DIR"; + startup_db and exists in XMLDB_DIR. NOTE: Obsolete with CLICON_STARTUP_MODE"; } leaf CLICON_XMLDB_PLUGIN { type string; mandatory true; description "XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])"; } + leaf CLICON_STARTUP_MODE { + type startup_mode; + description "Which method to boot/start clicon backend"; + } + } }