From ca7b4d3d9ebe2f9e8fe48be4c24e9b371b0b2957 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 1 Oct 2017 12:33:12 +0200 Subject: [PATCH] Migrated to XML configure file. --- CHANGELOG.md | 9 ++- apps/cli/cli_main.c | 94 +++++++++++++++++++++++++++--- apps/cli/cli_plugin.c | 5 +- clixon.conf.cpp.cpp | 2 +- example/routing.conf.local | 15 ----- example/routing.xml | 2 +- lib/src/clixon_options.c | 29 ++++----- lib/src/clixon_yang.c | 4 +- test/README.md | 2 +- test/lib.sh | 2 +- test/test_restconf.sh | 5 +- yang/clixon-config@2017-07-02.yang | 2 +- 12 files changed, 117 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03e91d01..dbf1c3fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,15 @@ Sep 27 18:11:58: Commit failed. Edit and try again or discard changes: protocol invalid-value Missing mandatory variable: type ``` -* If clixon config file has .xml ending, yang/clixon-config.yang is used as +* Migrated to XML configure file. +** If clixon config file has .xml ending, yang/clixon-config.yang is used as model for an xml-based configuration file. Otherwise legacy format is used. +** As migration utility from legacy to XML configure file, clixon_cli -x can be used to print new format, eg: + +``` +clixon_cli -f /usr/local/etc/routing.conf -1x +``` + * The clixon config file format has changed. It now uses XML and YANG. Old configuration files work, but you can use the new by setting an .xml suffix. The yang model is yang/clixon-config.yang. diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index dc31fa71..b2c763a4 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -70,7 +70,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:y:" +#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:y:x" /*! terminate cli application */ static int @@ -109,9 +109,10 @@ cli_signal_init (clicon_handle h) * @param[in] h CLICON handle * @see cligen_loop */ -static void +static int cli_interactive(clicon_handle h) { + int retval = -1; int res; char *cmd; char *new_mode; @@ -123,11 +124,72 @@ cli_interactive(clicon_handle h) new_mode = cli_syntax_mode(h); if ((cmd = clicon_cliread(h)) == NULL) { cli_set_exiting(h, 1); /* EOF */ - break; + retval = -1; + goto done; } if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0) - break; + goto done; } + retval = 0; + done: + return retval; +} + +/*! Read file as configuration file and print xml file for migrating to new fmt + * @see clicon_option_readfile_xml + */ +static int +dump_configfile_xml_fn(FILE *fout, + const char *filename) +{ + struct stat st; + char opt[1024]; + char val[1024]; + char line[1024]; + char *cp; + FILE *f = NULL; + int retval = -1; + char *suffix; + + if (filename == NULL || !strlen(filename)){ + clicon_err(OE_UNIX, 0, "Not specified"); + goto done; + } + if ((suffix = rindex(filename, '.')) != NULL && + strcmp((suffix+1), "xml") == 0){ + clicon_err(OE_CFG, 0, "Configfile %s should not be XML", filename); + 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); + return -1; + } + clicon_debug(2, "Reading config file %s", __FUNCTION__, filename); + fprintf(fout, "\n"); + while (fgets(line, sizeof(line), f)) { + if ((cp = strchr(line, '\n')) != NULL) /* strip last \n */ + *cp = '\0'; + /* Trim out comments, strip whitespace, and remove CR */ + if ((cp = strchr(line, '#')) != NULL) + memcpy(cp, "\n", 2); + if (sscanf(line, "%s %s", opt, val) < 2) + continue; + fprintf(fout, "\t<%s>%s\n", opt, val, opt); + } + fprintf(fout, "\n"); + retval = 0; + done: + if (f) + fclose(f); + return retval; } static void @@ -142,6 +204,7 @@ usage(char *argv0, clicon_handle h) "\t-h \t\tHelp\n" "\t-D \tDebug\n" "\t-f \tConfig-file (mandatory)\n" + "\t-x\t\tDump configuration file as XML on stdout (migration utility)\n" "\t-F \tRead commands from file (default stdin)\n" "\t-1\t\tDo not enter interactive mode\n" "\t-u \tconfig UNIX domain path (default: %s)\n" @@ -178,6 +241,7 @@ main(int argc, char **argv) int len; int logdst = CLICON_LOG_STDERR; char *restarg = NULL; /* what remains after options */ + int dump_configfile_xml = 0; /* Defaults */ @@ -216,6 +280,9 @@ main(int argc, char **argv) usage(argv[0], h); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); break; + case 'x': /* dump config file as xml */ + dump_configfile_xml++; + break; case 'l': /* Log destination: s|e|o */ switch (optarg[0]){ case 's': @@ -239,6 +306,14 @@ main(int argc, char **argv) clicon_debug_init(debug, NULL); + /* Use cli as util tool to dump config file as xml for migration */ + if (dump_configfile_xml) { + clicon_hash_t *copt = clicon_options(h); + char *configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL); + if (dump_configfile_xml_fn(stdout, configfile) < 0) + goto done; + } + /* Find and read configfile */ if (clicon_options_main(h) < 0){ if (help) @@ -254,6 +329,7 @@ main(int argc, char **argv) case 'D' : /* debug */ case 'f': /* config file */ case 'l': /* Log destination */ + case 'x': /* dump config file as xml */ break; /* see above */ case 'F': /* read commands from file */ if (freopen(optarg, "r", stdin) == NULL){ @@ -326,7 +402,9 @@ main(int argc, char **argv) goto done; } - /* Create tree generated from dataspec */ + /* Create tree generated from dataspec. If no other trees exists, this is + * the only one. + */ if (clicon_cli_genmodel(h)){ yang_spec *yspec; /* yang spec */ parse_tree pt = {0,}; /* cli parse tree */ @@ -367,10 +445,8 @@ main(int argc, char **argv) fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n"); goto done; } - if (cli_tree(h, cli_syntax_mode(h)) == NULL){ - fprintf (stderr, "FATAL: No such cli mode: %s\n", cli_syntax_mode(h)); - goto done; - } + if (cli_tree(h, cli_syntax_mode(h)) == NULL) + clicon_log(LOG_WARNING, "No such cli mode: %s (Specify cli mode with CLICON_CLI_MODE in config file or -m on command line", cli_syntax_mode(h)); if (logclisyntax) cli_logsyntax_set(h, logclisyntax); diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 66b3a3e9..2aedcbb1 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -713,8 +713,9 @@ done: return res; } -/* - * Read command from CLIgen's cliread() using current syntax mode. +/*! Read command from CLIgen's cliread() using current syntax mode. + * @retval string char* buffer containing CLIgen command + * @retval NULL Fatal error */ char * clicon_cliread(clicon_handle h) diff --git a/clixon.conf.cpp.cpp b/clixon.conf.cpp.cpp index 3c384ad1..f8d9d8f7 100644 --- a/clixon.conf.cpp.cpp +++ b/clixon.conf.cpp.cpp @@ -105,7 +105,7 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile # CLICON_CLI_GENMODEL 1 # Generate code for CLI completion of existing db symbols -# CLICON_CLI_GENMODEL_COMPLETION 0 +# CLICON_CLI_GENMODEL_COMPLETION 1 # How to generate and show CLI syntax: VARS|ALL # CLICON_CLI_GENMODEL_TYPE VARS diff --git a/example/routing.conf.local b/example/routing.conf.local index 660be03e..a6caa01a 100644 --- a/example/routing.conf.local +++ b/example/routing.conf.local @@ -12,21 +12,6 @@ CLICON_YANG_MODULE_MAIN example # [@] #CLICON_YANG_MODULE_REVISION 2014-06-16 -# Generate code for CLI completion of existing db symbols -# CLICON_CLI_GENMODEL_COMPLETION 0 -CLICON_CLI_GENMODEL_COMPLETION 1 - -# How to generate and show CLI syntax: VARS|ALL -# CLICON_CLI_GENMODEL_TYPE VARS -CLICON_CLI_GENMODEL_TYPE VARS - -# Enabled uses "startup" configuration on boot -CLICON_USE_STARTUP_CONFIG 0 - -# XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch]) -CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/text.so -#CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/keyvalue.so - # 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 CLICON_CLI_LINESCROLLING 0 diff --git a/example/routing.xml b/example/routing.xml index 71625ec8..d80707f5 100644 --- a/example/routing.xml +++ b/example/routing.xml @@ -3,7 +3,7 @@ /usr/local/share/routing/yang example routing - /usr/local/lib/routing/backend> + /usr/local/lib/routing/backend /usr/local/lib/routing/netconf /usr/local/lib/routing/restconf /usr/local/lib/routing/cli diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index c258e392..fd0d51de 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -69,12 +69,11 @@ #include "clixon_xsl.h" #include "clixon_xml_map.h" -/* - * clicon_option_dump - * Print registry on file. For debugging. +/*! Print registry on file. For debugging. */ void -clicon_option_dump(clicon_handle h, int dbglevel) +clicon_option_dump(clicon_handle h, + int dbglevel) { clicon_hash_t *hash = clicon_options(h); int i; @@ -272,7 +271,7 @@ clicon_option_default(clicon_hash_t *copt) goto done; } if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_COMPLETION")){ - if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0) + if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "1", strlen("1")+1) < 0) goto done; } /* Default is to use line-scrolling */ @@ -280,18 +279,12 @@ clicon_option_default(clicon_hash_t *copt) if (hash_add(copt, "CLICON_CLI_LINESCROLLING", "1", strlen("1")+1) < 0) goto done; } - /* Default is to use line-scrolling */ - if (!hash_lookup(copt, "CLICON_CLI_LINESCROLLING")){ - if (hash_add(copt, "CLICON_CLI_LINESCROLLING", "1", strlen("1")+1) < 0) - goto done; - } retval = 0; done: return retval; } /*! Check that options are set - * XXX dont detect extra XML */ static int clicon_option_sanity(clicon_hash_t *copt) @@ -440,7 +433,7 @@ clicon_option_str_set(clicon_handle h, } /*! Get options as integer but stored as string - + * * @param h clicon handle * @param name name of option * @retval int An integer as aresult of atoi @@ -465,7 +458,7 @@ clicon_option_int(clicon_handle h, const char *name) return atoi(s); } -/*! set option given as int. +/*! Set option given as int. */ int clicon_option_int_set(clicon_handle h, const char *name, int val) @@ -477,7 +470,7 @@ clicon_option_int_set(clicon_handle h, const char *name, int val) return clicon_option_str_set(h, name, s); } -/*! delete option +/*! Delete option */ int clicon_option_del(clicon_handle h, const char *name) @@ -557,7 +550,7 @@ clicon_xmldb_plugin(clicon_handle h) return clicon_option_str(h, "CLICON_XMLDB_PLUGIN"); } -/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */ +/*! Get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */ int clicon_sock_family(clicon_handle h) { @@ -609,7 +602,7 @@ clicon_master_plugin(clicon_handle h) return clicon_option_str(h, "CLICON_MASTER_PLUGIN"); } -/* return initial clicon cli mode */ +/*! Return initial clicon cli mode */ char * clicon_cli_mode(clicon_handle h) { @@ -630,7 +623,7 @@ clicon_cli_genmodel(clicon_handle h) return 0; } -/* How to generate and show CLI syntax: VARS|ALL */ +/*! How to generate and show CLI syntax: VARS|ALL */ enum genmodel_type clicon_cli_genmodel_type(clicon_handle h) { @@ -709,7 +702,7 @@ clicon_cli_genmodel_completion(clicon_handle h) return 0; } -/* Where are "running" and "candidate" databases? */ +/*! Where are "running" and "candidate" databases? */ char * clicon_xmldb_dir(clicon_handle h) { diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index fa9e6867..5bc67c62 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1504,7 +1504,7 @@ yang_parse_recurse(clicon_handle h, yang_spec *ysp) { yang_stmt *yi = NULL; /* import */ - yang_stmt *ymod; + yang_stmt *ymod = NULL; yang_stmt *yrev; char *modname; char *subrevision; @@ -1527,7 +1527,7 @@ yang_parse_recurse(clicon_handle h, if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0) goto done; if (nr == 0){ - clicon_err(OE_YANG, errno, "No matching %s yang files found", module); + clicon_err(OE_YANG, errno, "No matching %s yang files found (expected modulenameor absolute filename)", module); goto done; } } diff --git a/test/README.md b/test/README.md index 7d078fde..7598af48 100644 --- a/test/README.md +++ b/test/README.md @@ -1,7 +1,7 @@ # Clixon tests This directory contains testing code for clixon and the example -routing application: +routing application. Assumes setup of http daemon as describe under apps/restonf - clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script - all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically. - test_cli.sh CLI tests diff --git a/test/lib.sh b/test/lib.sh index cf9e4471..46ee84c2 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -2,7 +2,7 @@ testnr=0 testnname= -clixon_cf=/usr/local/etc/routing.conf +clixon_cf=/usr/local/etc/routing.xml # error and exit, arg is optional extra errmsg err(){ echo "Error in Test$testnr [$testname]:" diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 9bad8f9f..83c4d875 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -1,5 +1,6 @@ #!/bin/bash -# Test3: backend and restconf basic functionality +# Restconf basic functionality +# Assume http server setup, such as nginx described in apps/restconf/README.md # include err() and new() functions . ./lib.sh @@ -20,7 +21,7 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.conf # -D +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.xml # -D sleep 1 diff --git a/yang/clixon-config@2017-07-02.yang b/yang/clixon-config@2017-07-02.yang index 668e37c7..0c3a2050 100644 --- a/yang/clixon-config@2017-07-02.yang +++ b/yang/clixon-config@2017-07-02.yang @@ -146,7 +146,7 @@ } leaf CLICON_CLI_GENMODEL_COMPLETION { type int32; - default 0; + default 1; description "Generate code for CLI completion of existing db symbols"; } leaf CLICON_CLI_GENMODEL_TYPE {