Auto-cli updates and sync with clixon-docs

This commit is contained in:
Olof hagsand 2020-06-15 16:07:04 +02:00
parent cea5746597
commit 5120409a56
13 changed files with 178 additions and 99 deletions

View file

@ -30,7 +30,7 @@ Expected: July 2020
* Auto-CLI enhancements
* A generated clispec including state (default @datanodestate) also generated along with the config clispec tree (default @datanode)
* New mode `GT_HIDE` set by option `CLICON_CLI_GENMODEL_TYPE` to collapse non-presence containers that only contain a single list
* Added a prfix for cli_show_config/cli_show_auto so that it can produce parseable output
* Added a prefix for cli_show_config/cli_show_auto so that it can produce parseable output
* Thanks dcornejo@netgate.com for trying it out and suggestions
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. Experimental.
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API.
@ -40,7 +40,6 @@ Expected: July 2020
* `--with-restconf=evhtp Integrate restconf with libevhtp server`
* `--without-restconf Disable restconf altogether`
### C/CLI-API changes on existing features (For developers)
* Added prefix for cli_show_config/cli_show_auto so that it can produce parseable output

View file

@ -63,9 +63,9 @@
#include "cli_plugin.h"
#include "cli_generate.h"
/* This is the default callback function. But this is typically overwritten */
#define GENERATE_CALLBACK "overwrite_me"
/*
* Constants
*/
/* variable expand function */
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
@ -725,7 +725,6 @@ yang2cli_container(clicon_handle h,
cprintf(cb, ";{\n");
}
yc = NULL;
while ((yc = yn_each(ys, yc)) != NULL)
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
@ -972,10 +971,10 @@ yang2cli(clicon_handle h,
goto done;
cvec_free(globals);
/* Resolve the expand callback functions in the generated syntax.
This "should" only be GENERATE_EXPAND_XMLDB
handle=NULL for global namespace, this means expand callbacks must be in
CLICON namespace, not in a cli frontend plugin.
*/
* This "should" only be GENERATE_EXPAND_XMLDB
* handle=NULL for global namespace, this means expand callbacks must be in
* CLICON namespace, not in a cli frontend plugin.
*/
if (cligen_expandv_str2fn(ptnew, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
goto done;

View file

@ -37,6 +37,18 @@
#ifndef _CLI_GENERATE_H_
#define _CLI_GENERATE_H_
/*
* Constants
*/
/* This is the default "virtual" callback function of the auto-cli. It should be overwritten by
* a callback specified in a clispec, such as:
* @code
* set @datamodel, cli_set();
* @endcode
* where the virtual callback (overwrite_me) is overwritten by cli_set.
*/
#define GENERATE_CALLBACK "overwrite_me"
/*
* Prototypes
*/

View file

@ -236,6 +236,103 @@ cli_interactive(clicon_handle h)
return retval;
}
/*! Generate one autocli clispec tree
*
* @param[in] h Clixon handle
* @param[in] name Name of tree
* @param[in] gt genmodel-type, ie HOW to generate the CLI
* @param[in] printgen Print CLI syntax to stderr
*
* Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
* (as a separate mode)
* This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
* syntax, ie @datamodel
* @param[in] h Clixon handle
* @param[in] printgen Print CLI syntax generated from dbspec
* @retval 0 OK
* @retval -1 Error
*
* @note that yang2cli generates syntax for ALL modules under the loaded yangspec.
*/
static int
autocli_tree(clicon_handle h,
char *name,
enum genmodel_type gt,
int state,
int printgen)
{
int retval = -1;
parse_tree *pt = NULL; /* cli parse tree */
yang_stmt *yspec;
if ((pt = pt_new()) == NULL){
clicon_err(OE_UNIX, errno, "pt_new");
goto done;
}
yspec = clicon_dbspec_yang(h);
/* Generate tree (this is where the action is) */
if (yang2cli(h, yspec, gt, printgen, state, pt) < 0)
goto done;
/* Append cligen tree and name it */
if (cligen_tree_add(cli_cligen(h), name, pt) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Generate autocli, ie if enabled, generate clispec from YANG and add to cligen parse-trees
*
* Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
* (as a separate mode)
* This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
* syntax, ie @datamodel
* Also (if enabled) generate a second "state" tree called @datamodelstate
*
* @param[in] h Clixon handle
* @param[in] printgen Print CLI syntax generated from dbspec
* @retval 0 OK
* @retval -1 Error
*/
static int
autocli_start(clicon_handle h,
int printgen)
{
int retval = -1;
int autocli_model = 0;
cbuf *treename = NULL;
enum genmodel_type gt;
/* If autocli disabled quit */
if ((autocli_model = clicon_cli_genmodel(h)) == 0)
goto ok;
/* Get the autocli type, ie HOW the cli is generated (could be much more here) */
gt = clicon_cli_genmodel_type(h);
/* Create treename cbuf */
if ((treename = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* The tree name is by default @datamodel but can be changed by option (why would one do that?) */
cprintf(treename, "%s", clicon_cli_model_treename(h));
if (autocli_tree(h, cbuf_get(treename), gt, 0, printgen) < 0)
goto done;
/* Create a tree for config+state. This tree's name has appended "state" to @datamodel (XXX)
*/
if (autocli_model > 1){
cprintf(treename, "state");
if (autocli_tree(h, cbuf_get(treename), gt, 1, printgen) < 0)
goto done;
}
ok:
retval = 0;
done:
if (treename)
cbuf_free(treename);
return retval;
}
static void
usage(clicon_handle h,
char *argv0)
@ -536,49 +633,9 @@ main(int argc,
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Create tree generated from dataspec. If no other trees exists, this is
* the only one.
* The following code creates the tree @datamodel
* This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR)
* using the "tree reference"
* syntax, ie @datamodel
* But note that yang2cli generates syntax for ALL modules, not just for
* <module>.
*/
if (clicon_cli_genmodel(h)){
parse_tree *pt = NULL; /* cli parse tree */
cbuf *cbtreename;
parse_tree *pts = NULL; /* cli parse tree */
if ((pt = pt_new()) == NULL){
clicon_err(OE_UNIX, errno, "pt_new");
goto done;
}
if ((cbtreename = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cbtreename, "%s", clicon_cli_model_treename(h));
/* Create cli command tree from dbspec
* label this tree @datamodel per default
*/
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), printgen, 0, pt) < 0)
goto done;
cligen_tree_add(cli_cligen(h), cbuf_get(cbtreename), pt);
/* same for config+state
* label this tree @datamodelstate per default
*/
if ((pts = pt_new()) == NULL){
clicon_err(OE_UNIX, errno, "pt_new");
goto done;
}
cprintf(cbtreename, "state");
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), 0, 1, pts) < 0)
goto done;
cligen_tree_add(cli_cligen(h), cbuf_get(cbtreename), pts);
if (cbtreename)
cbuf_free(cbtreename);
}
/* Create autocli from YANG */
if (autocli_start(h, printgen) < 0)
goto done;
/* Initialize cli syntax */
if (cli_syntax_load(h) < 0)

View file

@ -66,6 +66,7 @@
#include "clixon_cli_api.h"
#include "cli_plugin.h"
#include "cli_handle.h"
#include "cli_generate.h"
/*
*
@ -177,7 +178,7 @@ cli_syntax_unload(clicon_handle h)
* @param[in] handle Handle to plugin .so module as returned by dlopen
* @param[out] error Static error string, if set indicates error
* @retval fn Function pointer
* @retval NULL FUnction not found or symbol NULL (check error for proper handling)
* @retval NULL Function not found or symbol NULL (check error for proper handling)
* @see see cli_plugin_load where (optional) handle opened
* @note the returned function is not type-checked which may result in segv at runtime
*/
@ -188,8 +189,15 @@ clixon_str2fn(char *name,
{
void *fn = NULL;
/* Reset error */
*error = NULL;
/* Special check for auto-cli. If the virtual callback is used, it should be overwritten later
* by a callback given in the clispec, eg: set @datamodel, cli_set();
*/
if (strcmp(name, GENERATE_CALLBACK) == 0)
return NULL;
/* First check given plugin if any */
if (handle) {

View file

@ -450,7 +450,7 @@ cli_show_config1(clicon_handle h,
cvec *nsc = NULL;
char *prefix = NULL;
if (cvec_len(argv) < 3 && cvec_len(argv) > 5){
if (cvec_len(argv) < 3 || cvec_len(argv) > 5){
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
goto done;
@ -708,7 +708,7 @@ cli_show_auto1(clicon_handle h,
char *api_path = NULL;
char *prefix = NULL;
if (cvec_len(argv) < 3 && cvec_len(argv) > 4){
if (cvec_len(argv) < 3 || cvec_len(argv) > 4){
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format> <prefix>. (*) generated.");
goto done;
}
@ -801,6 +801,7 @@ cli_show_auto1(clicon_handle h,
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* <prefix> to print before cli syntax outptu
* @see cli_show_auto_state For config and state
* @note SHOULD be used: ... @datamodel, cli_show_auto(<dbname>,...) to get correct #args
*/
int
cli_show_auto(clicon_handle h,

View file

@ -68,11 +68,11 @@ Clixon is written in C. The plugins are written in C. The CLI
specification uses [CLIgen](http://github.com/olofhagsand/cligen)
## How to best understand Clixon?
Run the Clixon example, in the [example](../example) directory.
Run the Clixon main example, in the [example](../example) directory or [examples repo](https://github.com/clicon/clixon-examples), or [main documentation](https://clixon-docs.readthedocs.io)
## Hello world?
One of the examples is [a hello world example](../example/hello). Please start with that.
One of the examples is [a hello world example](https://github.com/clicon/clixon-examples/hello). Please start with that.
## How do you build and install Clixon?
Clixon:
@ -128,8 +128,7 @@ clicon:x:1001:<user>,www-data
## How do I use the CLI?
The easiest way to use Clixon is via the CLI. Once the backend is started
Example:
The easiest way to use Clixon is via the CLI. In the main example, once the backend is started you can start the auto-cli. Example:
```
clixon_cli -f /usr/local/etc/example.xml
cli> set interfaces interface eth9 ?

View file

@ -78,8 +78,8 @@ show("Show a particular state of the system"){
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{
@datamodel, cli_show_auto("candidate", "xml");
}
cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/");{
@datamodel, cli_show_auto("candidate", "cli");
cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/", 0, "set ");{
@datamodel, cli_show_auto("candidate", "cli", "set ");
}
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{
@datamodel, cli_show_auto("candidate", "netconf");

View file

@ -613,7 +613,7 @@ clicon_option_del(clicon_handle h,
* But sometimes there are type conversions, etc which makes it more
* convenient to make wrapper functions. Or not?
*-----------------------------------------------------------------*/
/*! Wether to generate CLIgen syntax from datamodel or not (0 or 1)
/*! Whether to generate CLIgen syntax from datamodel or not (0, 1 or 2)
* Must be used with a previous clicon_option_exists().
* @param[in] h Clicon handle
* @retval flag If set, generate CLI code from yang model, otherwise not

View file

@ -189,13 +189,13 @@ new "cli set protocol udp"
expectfn "$clixon_cli -1 -f $cfg -l o set system protocol udp" 0 "^$"
new "cli get protocol udp"
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^system protocol udp$"
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^set system protocol udp$"
new "cli change protocol to tcp"
expectfn "$clixon_cli -1 -f $cfg -l o set system protocol tcp" 0 "^$"
new "cli get protocol tcp"
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^system protocol tcp$"
expectfn "$clixon_cli -1 -f $cfg -l o show configuration cli " 0 "^set system protocol tcp$"
new "cli delete all"
expectfn "$clixon_cli -1 -f $cfg -l o delete all" 0 "^$"

View file

@ -49,24 +49,25 @@ if [ $BE -ne 0 ]; then
fi
new "cli configure top"
expectfn "$clixon_cli -1 -f $cfg set interfaces" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg set interfaces)" 0 "^$"
new "cli show configuration top (no presence)"
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg show conf cli)" 0 "^$"
new "cli configure delete top"
expectfn "$clixon_cli -1 -f $cfg delete interfaces" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg delete interfaces)" 0 "^$"
new "cli show configuration delete top"
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg show conf cli)" 0 "^$"
new "cli configure set interfaces"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg set interfaces interface eth/0/0)" 0 "^$"
new "cli show configuration"
expectfn "$clixon_cli -1 -f $cfg show conf cli" 0 '^interfaces interface eth/0/0 interfaces interface eth/0/0 enabled true'
expectpart "$($clixon_cli -1 -f $cfg show conf cli)" 0 "^set interfaces interface eth/0/0" "^set interfaces interface eth/0/0 enabled true"
new "cli configure using encoded chars data <&"
# problems in changing to expectpart with escapes
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description \"foo<&bar\"" 0 ""
new "cli configure using encoded chars name <&"
@ -76,53 +77,53 @@ new "cli failed validate"
expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 255 "Validate failed. Edit and try again or discard changes: application missing-element Mandatory variable <bad-element>type</bad-element>"
new "cli configure ip addr"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24)" 0 "^$"
new "cli configure ip descr"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description mydesc" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg set interfaces interface eth/0/0 description mydesc)" 0 "^$"
new "cli configure ip type"
expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type ex:eth" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg set interfaces interface eth/0/0 type ex:eth)" 0 "^$"
new "cli show xpath description"
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces" 0 "<description>mydesc</description>"
expectpart "$($clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces)" 0 "<description>mydesc</description>"
new "cli delete description"
expectfn "$clixon_cli -1 -f $cfg -l o delete interfaces interface eth/0/0 description mydesc" 0 ""
expectpart "$($clixon_cli -1 -f $cfg -l o delete interfaces interface eth/0/0 description mydesc)" 0 ""
new "cli show xpath no description"
expectfn "$clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o show xpath /interfaces/interface/description urn:ietf:params:xml:ns:yang:ietf-interfaces)" 0 "^$"
new "cli copy interface"
expectfn "$clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg copy interface eth/0/0 to eth99)" 0 "^$"
new "cli success validate"
expectfn "$clixon_cli -1 -f $cfg -l o validate" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 0 "^$"
new "cli commit"
expectfn "$clixon_cli -1 -f $cfg -l o commit" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o commit)" 0 "^$"
new "cli save"
expectfn "$clixon_cli -1 -f $cfg -l o save /tmp/foo" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o save /tmp/foo)" 0 "^$"
new "cli delete all"
expectfn "$clixon_cli -1 -f $cfg -l o delete all" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o delete all)" 0 "^$"
new "cli load"
expectfn "$clixon_cli -1 -f $cfg -l o load /tmp/foo" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o load /tmp/foo)" 0 "^$"
new "cli check load"
expectfn "$clixon_cli -1 -f $cfg -l o show conf cli" 0 "interfaces interface eth/0/0 ipv4 enabled true"
expectpart "$($clixon_cli -1 -f $cfg -l o show conf cli)" 0 "interfaces interface eth/0/0 ipv4 enabled true"
new "cli debug set"
expectfn "$clixon_cli -1 -f $cfg -l o debug level 1" 0 "^$"
expectpart "$($clixon_cli -1 -f $cfg -l o debug level 1)" 0 "^$"
# How to test this?
new "cli debug reset"
expectfn "$clixon_cli -1 -f $cfg -l o debug level 0" 0 "^$"
new "cli rpc"
expectfn "$clixon_cli -1 -f $cfg -l o rpc ipv4" 0 '<rpc-reply><x xmlns="urn:example:clixon">ipv4</x><y xmlns="urn:example:clixon">42</y></rpc-reply>'
expectpart "$($clixon_cli -1 -f $cfg -l o rpc ipv4)" 0 '<rpc-reply><x xmlns="urn:example:clixon">ipv4</x><y xmlns="urn:example:clixon">42</y></rpc-reply>'
if [ $BE -eq 0 ]; then
exit # BE

View file

@ -33,6 +33,7 @@ cat <<EOF > $cfg
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_CLI_GENMODEL>2</CLICON_CLI_GENMODEL>
<CLICON_CLI_GENMODEL_TYPE>VARS</CLICON_CLI_GENMODEL_TYPE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
@ -57,14 +58,14 @@ module $APPNAME {
}
}
container exstate{
config false;
list sender{
key ref;
leaf ref{
type string;
}
}
}
config false;
list sender{
key ref;
leaf ref{
type string;
}
}
}
}
EOF
@ -90,6 +91,7 @@ show config @datamodel, cli_show_auto("candidate", "cli", "set ");
show state, cli_show_config_state("running", "cli", "/", "set ");
show state @datamodelstate, cli_show_auto_state("running", "cli", "set ");
show xml, cli_show_config("candidate", "xml", "/");
show xml @datamodel, cli_show_auto("candidate", "xml");
commit, cli_commit();
discard, discard_changes();

View file

@ -402,11 +402,12 @@ module clixon-config {
type int32;
default 1;
description
"If set, generate CLI specification for CLI completion of
loaded Yang modules. This CLI tree can be accessed in CLI
spec files using the tree reference syntax (eg @datamodel).
See also CLICON_CLI_MODEL_TREENAME.
(consider boolean)";
"0: Do not generate CLISPEC syntax for the auto-cli.
1: Generate a CLI specification for CLI completion of all loaded Yang modules.
This CLI tree can be accessed in CLI-spec files using the tree reference syntax (eg
@datamodel).
2: Same including state syntax in a tree called @datamodelstate.
See also CLICON_CLI_MODEL_TREENAME.";
}
leaf CLICON_CLI_MODEL_TREENAME {
type string;