diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index b2c763a4..e5e0f643 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -250,6 +250,7 @@ main(int argc, char **argv) /* Initiate CLICON handle */ if ((h = cli_handle_init()) == NULL) goto done; + if (cli_plugin_init(h) != 0) goto done; once = 0; @@ -486,7 +487,8 @@ main(int argc, char **argv) // Gets in your face if we log on stderr clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */ clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid()); - cli_terminate(h); + if (h) + cli_terminate(h); return 0; } diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 252c7610..c1128f6b 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -99,7 +99,8 @@ expand_dbvar(void *h, cvec *helptexts) { int retval = -1; - char *api_path; + char *api_path_fmt; + char *api_path = NULL; char *dbstr; cxobj *xt = NULL; char *xpath = NULL; @@ -145,13 +146,16 @@ expand_dbvar(void *h, clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument "); goto done; } - api_path = cv_string_get(cv); - /* api_path = /interface/%s/address/%s + api_path_fmt = cv_string_get(cv); + /* api_path_fmt = /interface/%s/address/%s --> ^/interface/eth0/address/.*$ --> /interface/[name=eth0]/address */ - if (api_path_fmt2xpath(api_path, cvv, &xpath) < 0) - goto done; + if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0) + goto done; + if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) + goto done; + /* XXX read whole configuration, why not send xpath? */ if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0) goto done; @@ -165,7 +169,7 @@ expand_dbvar(void *h, if ((xtop = xml_new("config", NULL)) == NULL) goto done; xbot = xtop; - /* This is primarily to get "y", XXX xbot can be broken (contains =%s) + /* This is primarily to get "y", * xpath2xml would have worked!! */ if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0) @@ -236,6 +240,8 @@ expand_dbvar(void *h, } retval = 0; done: + if (api_path) + free(api_path); if (xvec) free(xvec); if (xtop) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index ab941091..807483d3 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -31,7 +31,7 @@ sudo /etc/init.d nginx start Start clixon restconf daemon ``` -olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.conf " -s /bin/sh www-data +olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.xml " -s /bin/sh www-data ``` Make restconf calls with curl @@ -67,7 +67,7 @@ curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enable Start the restconf fastcgi program with debug flag: ``` -sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-data +sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml" -s /bin/sh www-data ``` Look at syslog: ``` diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 97cf3f36..464d8fed 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -39,7 +39,7 @@ * sudo apt-get install libfcgi-dev * gcc -o fastcgi fastcgi.c -lfcgi - * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data + * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml " -s /bin/sh www-data * This is the interface: * api/data/profile=/metric= PUT data:enable= diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index e2d7412f..27989928 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -39,7 +39,7 @@ * sudo apt-get install libfcgi-dev * gcc -o fastcgi fastcgi.c -lfcgi - * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data + * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml " -s /bin/sh www-data * This is the interface: * api/data/profile=/metric= PUT data:enable= diff --git a/doc/FAQ.md b/doc/FAQ.md index 5e431fd7..982fb236 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -59,9 +59,9 @@ Build using 'make doc' and aim your browser at doc/html/index.html or use the web resource: http://clicon.org/ref/index.html ## How do you run the example? -- Start a backend server: 'clixon_backend -Ff /usr/local/etc/routing.conf' -- Start a cli session: clixon_cli -f /usr/local/etc/routing.conf -- Start a netconf session: clixon_netconf -f /usr/local/etc/routing.conf +- Start a backend server: 'clixon_backend -Ff /usr/local/etc/routing.xml' +- Start a cli session: clixon_cli -f /usr/local/etc/routing.xml +- Start a netconf session: clixon_netconf -f /usr/local/etc/routing.xml ## How is configuration data stored? Configuration data is stored in an XML datastore. The default is a @@ -82,10 +82,7 @@ is the core functionality of a clixon system. ## What is a Clixon configuration file? Clixon options are stored in a configuration file you must specify when you start a backend or client using -f. The example configuration -file is /usr/local/etc/routing.conf. -This file is generated from the base source clixon.conf.cpp.cpp and -is merged with local configuration files, such as routing.conf.local. -This is slightly confusing and could be improved. +file is installed at /usr/local/etc/routing.xml. ## Can I run Clixon as docker containers? Yes, the example works as docker containers as well. backend and cli needs a @@ -107,12 +104,12 @@ You may also push the containers with 'make push' but you may then consider chan As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application. Example: - echo "]]>]]>" | clixon_netconf -f /usr/local/etc/routing.conf + echo "]]>]]>" | clixon_netconf -f /usr/local/etc/routing.xml However, more useful is to run clixon_netconf as an SSH subsystem. Register the subsystem in /etc/sshd_config: ``` - Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/routing.conf + Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/routing.xml ``` and then invoke it from a client using ``` @@ -147,7 +144,7 @@ cli> ``` or via netconf: ``` -clixon_netconf -qf /usr/local/etc/routing.conf +clixon_netconf -qf /usr/local/etc/routing.xml ROUTING]]>]]> ]]>]]> Routing notification]]>]]> @@ -156,7 +153,7 @@ clixon_netconf -qf /usr/local/etc/routing.conf ``` ## I want to program. How do I extend the example? -- routing.conf.local - Override default settings +- routing.xml - Change the configuration file - The yang specifications - This is the central part. It changes the XML, database and the config cli. - routing_cli.cli - Change the fixed part of the CLI commands - routing_cli.c - Cli C-commands are placed here. diff --git a/example/README.md b/example/README.md index 31fa83a7..08beb87f 100644 --- a/example/README.md +++ b/example/README.md @@ -7,15 +7,15 @@ ``` Start backend: ``` - clixon_backend -f /usr/local/etc/routing.conf -I + clixon_backend -f /usr/local/etc/routing.xml -I ``` Edit cli: ``` - clixon_cli -f /usr/local/etc/routing.conf + clixon_cli -f /usr/local/etc/routing.xml ``` Send netconf command: ``` - clixon_netconf -f /usr/local/etc/routing.conf + clixon_netconf -f /usr/local/etc/routing.xml ``` ## Setting data example using netconf diff --git a/example/routing.xml b/example/routing.xml index d80707f5..1eef83e2 100644 --- a/example/routing.xml +++ b/example/routing.xml @@ -1,5 +1,5 @@ - /usr/local/etc/routing.conf + /usr/local/etc/routing.xml /usr/local/share/routing/yang example routing diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 959b6ea8..5d45ad20 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -993,16 +993,22 @@ yang2api_path_fmt(yang_stmt *ys, /*! Transform an xml key format and a vector of values to an XML key * Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey() * Example: - * xmlkeyfmt: /aaa/%s/name - * cvv: key=17 - * xmlkey: /aaa/17/name + * xmlkeyfmt: /interfaces/interface=%s/ipv4/address=%s + * cvv: 0 : set interfaces interface e ipv4 address 1.2.3.4 + * 1 : name = "e" + * 2 : ip = "1.2.3.4" + * api_path: /interfaces/interface=e/ipv4/address=1.2.3.4 * @param[in] api_path_fmt XML key format, eg /aaa/%s/name - * @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt + * @param[in] cvv cligen variable vector, one for every wildchar in + * api_path_fmt * @param[out] api_path api_path, eg /aaa/17. Free after use * @param[out] yang_arg yang-stmt argument name. Free after use * @note first and last elements of cvv are not used,.. - * @see cli_dbxml where this function is called - */ + * @see api_path_fmt2xpath + * + * /interfaces/interface=%s/name --> /interfaces/interface/name + * /interfaces/interface=%s/ipv4/address=%s --> /interfaces/interface=e/ipv4/address + */ int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, @@ -1016,19 +1022,19 @@ api_path_fmt2api_path(char *api_path_fmt, int j; char *str; char *strenc=NULL; - + cg_var *cv; + +#if 1 /* Sanity check */ -#if 0 j = 0; /* Count % */ for (i=0; i cvec_len(cvv)) { //cvec_len can be longer + clicon_log(LOG_WARNING, "%s api_path_fmt number of %% is %d, does not match number of cvv entries %d", api_path_fmt, j, - cvec_len(cvv), - cv_string_get(cvec_i(cvv, 0))); + cvec_len(cvv)); goto done; } #endif @@ -1043,24 +1049,30 @@ api_path_fmt2api_path(char *api_path_fmt, esc = 0; if (c!='s') continue; - if ((str = cv2str_dup(cvec_i(cvv, j++))) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); - goto done; + if (j == cvec_len(cvv)) /* last element */ + ; + else{ + cv = cvec_i(cvv, j++); + if ((str = cv2str_dup(cv)) == NULL){ + clicon_err(OE_UNIX, errno, "cv2str_dup"); + goto done; + } + if (percent_encode(str, &strenc) < 0) + goto done; + cprintf(cb, "%s", strenc); + free(strenc); strenc = NULL; + free(str); str = NULL; } - if (percent_encode(str, &strenc) < 0) - goto done; - cprintf(cb, "%s", strenc); - free(strenc); strenc = NULL; - free(str); str = NULL; } else if (c == '%') esc++; - else if (c == '/'){ - cprintf(cb, "%c", c); + else{ + if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%' && j == cvec_len(cvv)) + ; /* skip */ + else + cprintf(cb, "%c", c); } - else - cprintf(cb, "%c", c); } if ((*api_path = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); @@ -1073,12 +1085,12 @@ api_path_fmt2api_path(char *api_path_fmt, return retval; } + /*! Transform an xml key format and a vector of values to an XML path * Used to input xmldb_get() or xmldb_get_vec * Add .* in last %s position. * Example: - * xmlkeyfmt: /interface/%s/address/%s OLDXXX - * xmlkeyfmt: /interface=%s/address=%s + * api_path_fmt: /interface/%s/address/%s * cvv: name=eth0 * xmlkey: /interface/[name=eth0]/address * Example2: @@ -1102,21 +1114,20 @@ api_path_fmt2xpath(char *api_path_fmt, int j; char *str; cg_var *cv; - int skip = 0; /* Sanity check: count '%' */ -#if 0 +#if 1 j = 0; /* Count % */ for (i=0; i cvec_len(cvv)) { clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s", api_path_fmt, j, cvec_len(cvv), cv_string_get(cvec_i(cvv, 0))); - // goto done; + goto done; } #endif if ((cb = cbuf_new()) == NULL){ @@ -1130,9 +1141,7 @@ api_path_fmt2xpath(char *api_path_fmt, esc = 0; if (c!='s') continue; - if (j == cvec_len(cvv)) /* last element */ - //skip++; ; else{ cv = cvec_i(cvv, j++); @@ -1148,13 +1157,10 @@ api_path_fmt2xpath(char *api_path_fmt, if (c == '%') esc++; else{ - if (skip) - skip=0; + if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%') + ; /* skip */ else - if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%') - ; /* skip */ - else - cprintf(cb, "%c", c); + cprintf(cb, "%c", c); } } if ((*xpath = strdup4(cbuf_get(cb))) == NULL){ @@ -1687,7 +1693,7 @@ api_path2xml_vec(char **vec, } switch (y->ys_keyword){ case Y_LEAF_LIST: - if (restval==NULL){ + if (0 && restval==NULL){ clicon_err(OE_XML, 0, "malformed key, expected '='"); goto done; } @@ -1697,7 +1703,7 @@ api_path2xml_vec(char **vec, if ((xb = xml_new("body", x)) == NULL) goto done; xml_type_set(xb, CX_BODY); - if (xml_value_set(xb, restval) < 0) + if (restval && xml_value_set(xb, restval) < 0) goto done; break; case Y_LIST: @@ -1773,11 +1779,11 @@ api_path2xml_vec(char **vec, } /*! Create xml tree from api-path - * @param[in] api_path API-path as defined in RFC 8040 - * @param[in] yspec Yang spec + * @param[in] api_path API-path as defined in RFC 8040 + * @param[in] yspec Yang spec * @param[in] schemanode If set use schema nodes otherwise data nodes. - * @param[out] xpathp Resulting xml tree - * @param[out] ypathp Yang spec matching xpathp + * @param[out] xpathp Resulting xml tree + * @param[out] ypathp Yang spec matching xpathp * @see api_path2xml_vec */ int @@ -1983,3 +1989,70 @@ done: return retval; } +/* + * Turn this on for uni-test programs + * Usage: clixon_string join + * Example compile: + gcc -g -o clixon_xml_map -I. -I../clixon ./clixon_xml_map.c -lclixon -lcligen + * Example run: +/interfaces/interface=%s/name --> interfaces/interface/name +/interfaces/interface=%s/ipv4/address=%s e --> /interfaces/interface=e/ipv4/address +/interfaces/interface=%s,%s/ipv4/address=%s e f --> /interfaces/interface=e,f/ipv4/address +/interfaces/interface=%s/ipv4/address=%s,%s e f --> /interfaces/interface=e/ipv4/address=f + +/interfaces/interface=%s/ipv4/address=%s/prefix-length eth 1.2.3.4 --> +/interfaces/interface=eth/ipv4/address=1.2.3.4/prefix-length + +*/ +#if 0 /* Test program */ + +static int +usage(char *argv0) +{ + fprintf(stderr, "usage:%s , ,...\n", argv0); + exit(0); +} + +int +main(int argc, char **argv) +{ + int nvec; + char **vec; + char *str0; + char *str1; + int i; + char *api_path_fmt; + cg_var *cv; + cvec *cvv; + char *api_path=NULL; + + clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR); + if (argc < 2){ + usage(argv[0]); + return 0; + } + api_path_fmt = argv[1]; + if ((cvv = cvec_new(0)) == NULL){ + perror("cvec_new"); + return -1; + } + cv = cv_new(CGV_STRING); + cv_string_set(cv, "CLI base command"); + cvec_append_var(cvv, cv); + for (i=2; i