diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ce6aadd4..84df96ff 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -29,6 +29,8 @@ # # ***** END LICENSE BLOCK ***** +- Datastore text module is now the default. + - Refined netconf "none" semantics in tests and text datastore - Moved apps/dbctrl to datastore/ diff --git a/README.md b/README.md index 47347b22..48780720 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,254 @@ -# CLIXON +# Clixon -CLIXON is an automatic configuration manager where you from a YANG +Clixon is an automatic configuration manager where you from a YANG specification generate interactive CLI, NETCONF, RESTCONF and embedded databases with transaction support. -CLIXON is a fork of CLICON where legacy key specification has been -replaced completely by YANG. This means that legacy CLICON -applications such as CLICON/ROST does not run on CLIXON. +Presentations and tutorial is found on the [CLICON project page](http://www.clicon.org) -Presentations and tutorial is found on the [CLICON project -page](http://www.clicon.org) - -## Installation +## 1. Installation A typical installation is as follows: +``` + configure # Configure clixon to platform + make # Compile + sudo make install # Install libs, binaries, and config-files + sudo make install-include # Install include files (for compiling) +``` +One example applications is provided, a IETF IP YANG datamodel with generated CLI and configuration interface. - > configure # Configure clixon to platform - > make # Compile - > sudo make install # Install libs, binaries, and config-files - > sudo make install-include # Install include files (for compiling) +## 2. Documentation -One example applications is provided, the IETF IP YANG datamodel with generated CLI and configuration interface. It all origins from work at -[KTH](http://www.csc.kth.se/~olofh/10G_OSR) +- [Frequently asked questions](http://www.clicon.org/FAQ.html) +- [Reference manual(http://www.clicon.org/doxygen/index.html) (may not be 100%% synched) -## Dependencies +## 3. Dependencies -[CLIgen](http://www.cligen.se) is required for building CLIXON. If you need +Clixon is dependend on the following packages +- [CLIgen](http://www.cligen.se) is required for building CLIXON. If you need to build and install CLIgen: - +``` git clone https://github.com/olofhagsand/cligen.git cd cligen; configure; make; make install +``` +- Yacc/bison +- Lex/Flex +- Fcgi (if restconf is enabled) +- Qdbm key-value store (if keyvalue datastore is enabled) -## Licenses +## 4. Licenses CLIXON is dual license. Either Apache License, Version 2.0 or GNU General Public License Version 2. You choose. See LICENSE.md for license, CHANGELOG for recent changes. -## Client code +## 5. History -[CLI](apps/restconf). -[Restconf](apps/restconf). -[Netconf](apps/netconf). -[Netconf](apps/netconf). +CLIXON is a fork of CLICON where legacy key specification has been +replaced completely by YANG. This means that legacy CLICON +applications such as CLICON/ROST does not run on CLIXON. +Clixon origins from work at [KTH](http://www.csc.kth.se/~olofh/10G_OSR) +## 6. Clixon Datastore +The Clixon datastore is a stand-alone XML based datastore used by +Clixon. The idea is to be able to use different datastores. There is +currently a key-value plugin based on qdbm and a plain text-file +datastore. + +The datastore is primarily designed to be used by Clixon but can be used +separately. + +A datastore is a dynamic plugin that is loaded at runtime with a +well-defined API. This means it is possible to create your own +datastore and plug it in a Clixon backend at runtime. + +### The functional API +``` +int xmldb_plugin_load(clicon_handle h, char *filename); +int xmldb_plugin_unload(clicon_handle h); +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, + cxobj **xtop, cxobj ***xvec, size_t *xlen); +int xmldb_put(clicon_handle h, char *db, enum operation_type op, + char *api_path, 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_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); +``` + +### Using the API + +To use the API, a client needs the following: +- A clicon handle. +- A datastore plugin, such as a text.so or keyvalue.so. These are normally built and installed at Clixon make. +- A directory where to store databases +- A yang specification. This needs to be parsed using the Clixon yang_parse() method. + +A client calling the API needs to (1)load a plugin and (2)connect to a +datastore. You can connect to several datastores, even concurrently, +but in practice in Clixon, you connect to a single store. + +After connecting to a datastore, you can create and modify databases +within the datastore, and set and get options of the datastore itself. + +When done, you disconnect from the datastore and unload the plugin. + +Within a datastore, the following four databases may exist: +- running +- candidate +- startup +- tmp + +Initially, a database does not exist but is created by +xmldb_create(). It is deleted by xmldb_delete(). You may check for +existence with xmldb_exists(). You need to create a database before +you can perform any data access on it. + +You may lock a database for exclusive modification according to +Netconf semantics. You may also unlock a single dabase, unlock all frm +a specific session. + +You can read a database with xmldb_get() and modify a database with +xmldb_put(), and xmldb_copy(). + +A typical datastore session can be as follows, see the source code of +datastore_client.c for a more elaborate example. + +``` + h = clicon_handle_init(); + xmldb_plugin_load(h, plugin); + xmldb_connect(h); + xmldb_setopt(h, "dbdir", dbdir); + xmldb_setopt(h, "yangspec", yspec); + /* From here databases in the datastore may be accessed */ + xmldb_create(h, "candidate"); + xmldb_copy(h, "running", "candidate"); + xmldb_lock(h, "candidate", 7878); + xmldb_put(h, "candidate", OP_CREATE, "/interfaces/interface=eth0", xml); + xmldb_unlock(h, "candidate"); + xmldb_get(h, "candidate", "/", &xml, &xvec, &xlen); + xmldb_disconnect(h) + xmdlb_plugin_unload(h); +``` + +## 7. YANG + +Clixon implements YANG RFC 6020. Clixon generates an interactive CLI +for YANG specifications. It also provides Restconf and Netconf clients. + +Clixon YANG currently does not provide the following support: +- type object-references +- if-feature +- unique +- rpc + +## 8. Netconf + +Clixon Netconf implements the following NETCONF standards: +- RFC 4741 (NETCONF Configuration Protocol) +- RFC 4742 (Using the NETCONF Configuration Protocol over Secure SHell (SSH)) +- RFC 5277 (NETCONF Event Notifications) + +It needs to be updated to RFC6241 and RFC 6242. + +Clixon NETCONF currently does not support the following Netconf features: + +- :url capability +- copy-config source config +- edit-config testopts +- edit-config erropts +- edit-config config-text + +## 9. Restconf + +### Features + +Clixon restconf is a daemon based on FASTCGI. Instructions are available to +run with NGINX. +The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE. +and is based on draft-ietf-netconf-restconf-13. +There is currently (2017) a RFC 8040, many of those features are _not_ implemented, +including: +- query parameters (section 4.9) +- notifications (sec 6) +- only rudimentary error reporting exists (sec 7) + +### Installation using Nginx + +Define nginx config file/etc/nginx/sites-available/default +``` +server { + ... + location /restconf { + root /usr/share/nginx/html/restconf; + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } +} +``` +Start nginx daemon +``` +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 +``` + +Make restconf calls with curl +``` +olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces +[ + { + "interfaces": { + "interface":[ + { + "name": "eth0", + "type": "eth", + "enabled": "true", + "name": "eth9", + "type": "eth", + "enabled": "true" + } + ] + } + } +] +olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=eth9/type +[ + { + "type": "eth" + } +] + +curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data +``` + +### Debugging + +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 +``` +Look at syslog: +``` +tail -f /var/log/syslog | grep clixon_restconf +``` + +Send command: +``` +curl -G http://127.0.0.1/restconf/data/* +``` diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index ce424606..6dd5511e 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -632,7 +632,7 @@ from_client_delete_config(clicon_handle h, "", clicon_err_reason); goto ok; } - if (xmldb_init(h, target) < 0){ + if (xmldb_create(h, target) < 0){ cprintf(cbret, "" "operation-failed" "protocol" diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index d3973ac6..4600eedd 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -159,7 +159,7 @@ db_reset(clicon_handle h, { if (xmldb_delete(h, db) != 0 && errno != ENOENT) return -1; - if (xmldb_init(h, db) < 0) + if (xmldb_create(h, db) < 0) return -1; return 0; } @@ -181,7 +181,7 @@ rundb_main(clicon_handle h, cxobj *xt = NULL; cxobj *xn; - if (xmldb_init(h, "tmp") < 0) + if (xmldb_create(h, "tmp") < 0) goto done; if (xmldb_copy(h, "running", "tmp") < 0){ clicon_err(OE_UNIX, errno, "file copy"); @@ -538,7 +538,7 @@ main(int argc, char **argv) else if (db_reset(h, "running") < 0) goto done; - if (xmldb_init(h, "candidate") < 0) + if (xmldb_create(h, "candidate") < 0) goto done; if (xmldb_copy(h, "running", "candidate") < 0) goto done; @@ -562,7 +562,7 @@ main(int argc, char **argv) } /* If candidate does not exist, create it from running */ if (xmldb_exists(h, "candidate") != 1){ - if (xmldb_init(h, "candidate") < 0) + if (xmldb_create(h, "candidate") < 0) goto done; if (xmldb_copy(h, "running", "candidate") < 0) goto done; diff --git a/apps/netconf/README.md b/apps/netconf/README.md new file mode 100644 index 00000000..3878aa56 --- /dev/null +++ b/apps/netconf/README.md @@ -0,0 +1,17 @@ +# Clixon Netconf + +Clixon netconf implements the following standards: +- RFC 4741 (NETCONF Configuration Protocol), +- RFC 4742 (Using the NETCONF Configuration Protocol over Secure SHell (SSH)) and +- RFC 5277 (NETCONF Event Notifications). + +It needs to be updated to RFC6241 and RFC 6242. It also does not implement the following features: + +- :url capability +- copy-config source config +- edit-config testopts +- edit-config erropts +- edit-config config-text + + + diff --git a/apps/restconf/README.md b/apps/restconf/README.md index d058225b..36916db1 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -1,13 +1,12 @@ -Clixon Restconf -=============== +# Clixon Restconf Contents: 1. Features 2. Installation using NGINX 3. Debugging -1. FEATURES -+++++++++++ +## 1. FEATURES + Clixon restconf is a daemon based on FASTCGI. Instructions are available to run with NGINX. The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE. @@ -18,10 +17,10 @@ including: - notifications (sec 6) - only rudimentary error reporting exists (sec 7) -2. INSTALLATION using NGINX -+++++++++++++++++++++++++++ +## 2. INSTALLATION using NGINX -# Define nginx config file/etc/nginx/sites-available/default +Define nginx config file/etc/nginx/sites-available/default +``` server { ... location /restconf { @@ -30,13 +29,19 @@ server { include fastcgi_params; } } -# Start nginx daemon +``` +Start nginx daemon +``` sudo /etc/init.d nginx start +``` -# Start clixon restconf daemon +Start clixon restconf daemon +``` olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.conf " -s /bin/sh www-data +``` -# Make restconf calls with curl +Make restconf calls with curl +``` olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces [ { @@ -62,16 +67,21 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et ] curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data +``` +## DEBUGGING -3. DEBUGGING -++++++++++++ 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.conf" -s /bin/sh www- +data +``` Look at syslog: +``` tail -f /var/log/syslog | grep clixon_restconf +``` Send command: +``` curl -G http://127.0.0.1/restconf/data/* - +``` diff --git a/clixon.conf.cpp.cpp b/clixon.conf.cpp.cpp index e6481452..1a6f441d 100644 --- a/clixon.conf.cpp.cpp +++ b/clixon.conf.cpp.cpp @@ -118,7 +118,7 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile CLICON_XMLDB_DIR localstatedir/APPNAME # XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch]) -CLICON_XMLDB_PLUGIN libdir/xmldb/keyvalue.so +CLICON_XMLDB_PLUGIN libdir/xmldb/text.so # Dont include keys in cvec in cli vars callbacks, ie a & k in 'a k ' ignored # CLICON_CLI_VARONLY 1 diff --git a/datastore/README.md b/datastore/README.md new file mode 100644 index 00000000..f9baa58b --- /dev/null +++ b/datastore/README.md @@ -0,0 +1,67 @@ +# Clixon datastore + +The Clixon datastore is a stand-alone XML based datastore used by +Clixon. The idea is to be able to use different datastores. There is +currently a Key-value plugin based on qdbm and a plain text-file +datastore. + +The datastore is primarily designed to be used by Clixon but can be used +separately. See datastore_client.c for an example of how to use a +datastore plugin for other applications + +## The functional API +``` +int xmldb_plugin_load(clicon_handle h, char *filename); +int xmldb_plugin_unload(clicon_handle h); +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, + cxobj **xtop, cxobj ***xvec, size_t *xlen); +int xmldb_put(clicon_handle h, char *db, enum operation_type op, + char *api_path, 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_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_init(clicon_handle h, char *db); +``` + +## Using the API + +This section described how the API is used. Please refer to datastore/datastore_client.c for an example of an actual implementation. + +### Prerequisities +To use the API, a client needs the following: +- A clicon handle. +- A datastore plugin, such as a text.so or keyvalue.so. These are normally built and installed at Clixon make. +- A directory where to store the datastores +- A yang specification. This needs to be parsed using the Clixon yang_parse() method. + +### Dynamics + +A client calling the API needs to load a plugin and connect to a +datastore. In principle, you cannot connect to several datastores, +even concurrently, but in practice in Clixon, you connect to a single +store. + +Within a datastore, there may be + +``` + h = clicon_handle_init(); + xmldb_plugin_load(h, plugin); +``` +The plugin is the complete path-name of a file. + +Thereafter, a connection is made to a specific plugin, such as a text plugin. It is possible to connect to several datastore at once, although this is not supported by CLixon: +``` +xmldb_connect(h); +xmldb_setopt(h, "dbdir", dbdir); +xmldb_setopt(h, "yangspec", yspec); +``` + + diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c index 01eb48b1..d9ceaf61 100644 --- a/datastore/datastore_client.c +++ b/datastore/datastore_client.c @@ -286,7 +286,7 @@ main(int argc, char **argv) else if (strcmp(cmd, "init")==0){ if (argc != 1) usage(argv0); - if (xmldb_init(h, db) < 0) + if (xmldb_create(h, db) < 0) goto done; } else{ diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index 46e9f0b0..68b2527d 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -1552,14 +1552,14 @@ kv_delete(xmldb_handle xh, return retval; } -/*! Initialize database +/*! Create / Initialize database * @param[in] xh XMLDB handle * @param[in] db Database * @retval 0 OK * @retval -1 Error */ int -kv_init(xmldb_handle xh, +kv_create(xmldb_handle xh, char *db) { int retval = -1; @@ -1618,7 +1618,7 @@ static const struct xmldb_api api = { kv_islocked, kv_exists, kv_delete, - kv_init, + kv_create, }; diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index 3de5bd2e..d9c86ac5 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -1216,14 +1216,14 @@ text_delete(xmldb_handle xh, return retval; } -/*! Initialize database +/*! Create / init database * @param[in] xh XMLDB handle * @param[in] db Database * @retval 0 OK * @retval -1 Error */ int -text_init(xmldb_handle xh, +text_create(xmldb_handle xh, char *db) { int retval = -1; @@ -1246,7 +1246,7 @@ text_init(xmldb_handle xh, return retval; } -/*! plugin init function */ +/*! plugin exit function */ int text_plugin_exit(void) { @@ -1287,7 +1287,7 @@ static const struct xmldb_api api = { text_islocked, text_exists, text_delete, - text_init, + text_create, }; diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h index 6b814f46..87f65411 100644 --- a/lib/clixon/clixon_xml_db.h +++ b/lib/clixon/clixon_xml_db.h @@ -104,7 +104,7 @@ typedef int (xmldb_exists_t)(xmldb_handle xh, char *db); typedef int (xmldb_delete_t)(xmldb_handle xh, char *db); /* Type of xmldb init function */ -typedef int (xmldb_init_t)(xmldb_handle xh, char *db); +typedef int (xmldb_create_t)(xmldb_handle xh, char *db); /* plugin init struct for the api */ struct xmldb_api{ @@ -125,7 +125,7 @@ struct xmldb_api{ xmldb_islocked_t *xa_islocked_fn; xmldb_exists_t *xa_exists_fn; xmldb_delete_t *xa_delete_fn; - xmldb_init_t *xa_init_fn; + xmldb_create_t *xa_create_fn; }; /* @@ -150,6 +150,6 @@ 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_init(clicon_handle h, char *db); +int xmldb_create(clicon_handle h, char *db); #endif /* _CLIXON_XML_DB_H */ diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index bbfc215f..9bc2c2c0 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -652,15 +652,15 @@ xmldb_delete(clicon_handle h, return retval; } -/*! Initialize database. Open database for writing. +/*! Create a database. Open database for writing. * @param[in] h Clicon handle * @param[in] db Database * @retval 0 OK * @retval -1 Error */ int -xmldb_init(clicon_handle h, - char *db) +xmldb_create(clicon_handle h, + char *db) { int retval = -1; xmldb_handle xh; @@ -670,7 +670,7 @@ xmldb_init(clicon_handle h, clicon_err(OE_DB, 0, "No xmldb plugin"); goto done; } - if (xa->xa_init_fn == NULL){ + if (xa->xa_create_fn == NULL){ clicon_err(OE_DB, 0, "No xmldb function"); goto done; } @@ -678,7 +678,7 @@ xmldb_init(clicon_handle h, clicon_err(OE_DB, 0, "Not connected to datastore plugin"); goto done; } - retval = xa->xa_init_fn(xh, db); + retval = xa->xa_create_fn(xh, db); done: return retval; } diff --git a/test/test3.sh b/test/test3.sh index ed1335e7..45efc73d 100755 --- a/test/test3.sh +++ b/test/test3.sh @@ -56,13 +56,14 @@ $' new "Re-Delete eth0 using none should generate error" expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "Not Found" -return +if false; then # XXX restconf dont support patch and put fully -new "restconf PATCH config" -expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" + new "restconf PATCH config" + expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" -new "restconf PUT" -expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' "" + new "restconf PUT" + expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' "" +fi new "Kill restconf daemon" #sudo pkill -u www-data clixon_restconf