xml config default; api_path_fmt2api_path cleanup

This commit is contained in:
Olof hagsand 2017-10-03 22:41:12 +02:00
parent e1b7ea8449
commit 5cea5fa768
10 changed files with 165 additions and 70 deletions

View file

@ -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;
}

View file

@ -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 <api_path>");
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)
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)

View file

@ -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:
```

View file

@ -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=<name>/metric=<name> PUT data:enable=<flag>

View file

@ -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=<name>/metric=<name> PUT data:enable=<flag>

View file

@ -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 "<rpc><get-config><source><candidate/></source><configuration/></get-config></rpc>]]>]]>" | clixon_netconf -f /usr/local/etc/routing.conf
echo "<rpc><get-config><source><candidate/></source><configuration/></get-config></rpc>]]>]]>" | 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
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]>
<notification><event>Routing notification</event></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.

View file

@ -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

View file

@ -1,5 +1,5 @@
<config>
<CLICON_CONFIGFILE>/usr/local/etc/routing.conf</CLICON_CONFIGFILE>
<CLICON_CONFIGFILE>/usr/local/etc/routing.xml</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLI_MODE>routing</CLICON_CLI_MODE>

View file

@ -993,15 +993,21 @@ 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,
@ -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<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%')
j++;
if (j+2 < cvec_len(cvv)) {
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
if (j > 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<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%')
j++;
if (j < cvec_len(cvv)-1) {
if (j > 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 '=<restval>'");
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 <api_path_fmt> <cv0>, <cv1>,...\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<argc; i++){
cv = cv_new(CGV_STRING);
if (cv_parse(argv[i], cv) < 0){
perror("cv_parse");
return -1;
}
cvec_append_var(cvv, cv);
}
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
return -1;
printf("%s\n", api_path);
return 0;
}
#endif /* Test program */

View file

@ -32,6 +32,17 @@ module example{
}
}
}
list sender{
key name;
leaf name{
type string;
}
leaf template{
type leafref{
path "/sender/name";
}
}
}
}
EOF
@ -96,6 +107,12 @@ expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set default-addre
new "cli leafref validate"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o validate" "^$"
new "cli sender"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender a" "^$"
new "cli sender template"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender b template a" "^$"
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`