The Clixon CLI provides an interactive command-line interface to a user. Each usage instantiates a new process which communicates via NETCONF with the backend daemon over an IPC socket.
The Clixon CLI uses CLIgen, an interactive interpreter of commands. Syntax is given as cli-specifications which specify callbacks defined in plugins.
For details on CLIgen syntax and behavior, please consult the CLIgen tutorial.
Clixon comes with a generated CLI, the autocli, where all configuration-related syntax is generated from YANG.
You can also create a completely manually-made CLI.
The CLI depends on the following:
- Clixon-config: The Clixon config-file contains initial CLI configuration, such as where to find cli-specs, plugins and autocli configuration.
- Cli-specs: CLI specification files written in CLIgen syntax.
- Plugins: Dynamic loadable plugin files loaded at startup. Callbacks from cli-spec files are resolved and need to exist as symbols either in the Clixon libs or in the plugin file.
The following example from the main example. First, a cli-spec file containing two commands:
set("Set configuration symbol") @datamodel, cli_auto_set();
show("Show a particular state of the system") configuration("Show configuration"), cli_show_config("candidate", "default", "/");
example("Callback example") <var:int32>("any number"), mycallback("myarg");
In the CLI, these generate CLI commands such as:
set interfaces interface eth9 show config example 23
The effect of typing the commands above is calling callbacks, either
library functions in Clixon libs(cli_show_config()), or
application-defined in a plugin(mycallback())
In this way, a designer writes cli command specifications which invokes C-callbacks. If there are no appropriate callbacks the designer must write a new callback function.
The following example shows an auto-cli session from the main example how to add an interface in candidate, validate and commit it to running, then look at it as xml and cli and finally delete it:
clixon_cli -f /usr/local/etc/clixon/example.xml
user@host> set interfaces interface eth9 ?
description enabled ipv4
ipv6 link-up-down-trap-enable type
user@host> set interfaces interface eth9 type ex:eth
user@host> validate
user@host> commit
user@host> show configuration xml
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>eth9</name>
<type>ex:eth</type>
<enabled>true</enabled>
</interface>
</interfaces>
user@host> show configuration cli
set interfaces interface eth9
set interfaces interface eth9 type ex:eth
set interfaces interface eth9 enabled true
user@host> delete interfaces interface eth9
- The clixon_cli client has the following command-line options:
-h Help -V Show version and exit -D <level> Debug level -f <file> Clixon config file -E <dir> Extra configuration directory -l <option> Log on (s)yslog, std(e)rr, std(o)ut, (n)one or (f)ile. Stderr is default. -C <format> Dump configuration options on stdout after loading. Format is one of xml|json|text|cli|default -F <file> Read commands from file (default stdin) -1 Run once, do not enter interactive mode -s Disable output scrolling -a <family> Internal IPC backend socket family: UNIX|IPv4|IPv6 -u <path|addr> Internal IPC socket domain path or IP addr (see -a) -d <dir> Specify cli plugin directory -m <mode> Specify plugin syntax mode -q Quiet mode, do not print greetings or prompt, terminate on ctrl-C -p <dir> Add Yang directory path (see CLICON_YANG_DIR) -G Print auo-cli CLI syntax generated from YANG -L Debug print dynamic CLI syntax including completions and expansions -y <file> Load yang spec file (override yang main modul)e -c <file> Specify cli spec file -U <user> Override unix user with a pseudo user for NACM. -g <group> Override unix groups with a masquerading group for NACM mode NONE -o <option=value> Give configuration option overriding config file (see clixon-config.yang)
CLI commands can be given directly after the options. These are executed directly:
clixon_cli -f example.xml show config clixon_cli -f example.xml set table parameter b \; show config
Multiple commands are separated with ;. One can also add extra application-dependent plugin options after -- which can be read with clicon_argv_get():
clixon_cli -f example.xml show config -- -x extra-option
The following config options are related to clispec and plugin files (clixon config options), ie they are set in the XML Clixon config file:
- CLICON_CLI_DIR
- Directory containing frontend cli loadable plugins. Load all .so plugins in this directory as CLI object plugins.
- CLICON_CLISPEC_DIR
- Directory containing frontend cligen spec files. Load all .cli files in this directory as CLI specification files.
- CLICON_CLISPEC_FILE
- Specific frontend cligen spec file as alternative or complement to CLICON_CLISPEC_DIR. Also available as -c in clixon_cli.
- CLICON_AUTOCLI_CACHE_DIR
- Directory containing backend generated cligen spec files from YANG, see also clispec-cache
- CLICON_NACM_AUTOCLI
- Enable NACM rules for autocli. If set, the autocli will not expand commands the user does not have read-access to.
- CLICON_CLI_PIPE_DIR
- Directory where CLI pipe scripts or binaries are searched for in cli callback
- CLICON_CLI_OUTPUT_FORMAT
- Default CLI output format.
Clixon CLI have the following configuration options related to terminal I/O:
- CLICON_CLI_LINESCROLLING
- 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 (default).
CLICON_CLI_LINES_DEFAULT Set to number of CLI terminal rows for pagination/scrolling. 0 means unlimited. The number is set statically UNLESS:
- there is no terminal, such as file input, in which case nr lines is 0
- there is a terminal sufficiently powerful to read the number of lines from ioctl calls.
In other words, this setting is used ONLY on raw terminals such as serial consoles.
- CLICON_CLI_TAB_MODE
Set CLI tab mode flags:
- Set bit 1 (0x01) if you want to has same output as
?, ie line per command, otherwise short info of available commands - Set bit 2 (0x02) if you want commands and variables to have same preference, otherwise select command
- Set bit 3 (0x04) if you want to complete multiple command levels at once, otherwise complete only one level per TAB
- Set bit 4 (0x08) if you want to complete and show available commands in one step, otherwise a second TAB shows alternatives. By default this bit is set.
- Set bit 1 (0x01) if you want to has same output as
Clixon CLI supports persistent command history. There are two CLI history related configuration options:
- CLICON_CLI_HIST_FILE
- The file containing the history, default value is: ~/.clixon_cli_history
- CLICON_CLI_HIST_SIZE
- Max number of history line, default value is 300.
- The design is similar to bash history but is simpler in some respects:
- The CLI loads/saves its complete history to a file on entry and exit, respectively
- The size (number of lines) of the file is the same as the history in memory
- Only the latest session dumping its history will survive (bash merges multiple session history).
Further, tilde-expansion is supported and if history files are not found or lack appropriate access will not cause an exit but are logged at debug level
The history API also allows for adding CLI command logging. Register the callback in the plugin init, and then call logging (or debug) for each CLI command. Example:
static int
cli_history_cb(cligen_handle ch,
char *cmd,
void *arg)
{
int retval = -1;
return clixon_log(arg, LOG_INFO, "command: %s", cmd);
}
clixon_plugin_api *
clixon_plugin_init(clixon_handle h)
{
...
cligen_hist_fn_set(cli_cligen(h), cli_history_cb, h);
Help strings are specified using the following example syntax: ("help string"). help strings are shown at queries, eg "?":
user@host> show <?> all Show all routing Show routing files Show files
For long or multi-line help strings the following configure options exists:
- CLICON_CLI_HELPSTRING_TRUNCATE
- Set to 0 to wrap long help strings to the next line. (default) Set to 1 to truncate long help strings at the right margin
- CLICON_CLI_HELPSTRING_LINES
- Set to 0 to have no limit on the number of help string lines per command Set to <n> to limit the the number of help string lines
Long and multi-line help strings may especially be needed in the auto-cli, see autocli.
The CLI can have different modes which is controlled by a config option and some internal clispec variables. The config options are:
- CLICON_CLI_MODE
- Startup CLI mode. This should match a
CLICON_MODEvariable setting in one of the clispec files. Default is "base". - CLICON_CLI_VARONLY
- Do not include keys in cvec in cli vars callbacks
Inside the clispec files CLICON_MODE is used to specify to which modes the syntax in a specific file defines. For example, if you have major modes configure and operation you can have a file with commands for only that mode, or files with commands in both, (or in all).
First, lets add a single command in the configure mode:
CLICON_MODE="configure"; show configure;
Then add syntax to both modes:
CLICON_MODE="operation:configure";
show("Show") files("Show files");
Finally, add a command to all modes:
CLICON_MODE="*";
show("Show") all("Show all");
Note that CLI command trees are merged so that show commands in other files are shown together. Thus, for example, using the clispecs above the two modes are the three commands in total for the configure mode:
> clixon_cli -m configure user@host> show <TAB> all routing files
but only two commands in the operation mode:
> clixon_cli -m operation user@host> show <TAB> all files
A CLI specification file (note not clixon config file) typically starts with the following variables:
- CLICON_MODE
- A colon-separated list of CLIgen modes. The CLI spec in the file are added to all modes specified in the list. You can also use wildcards
*and '?`. - CLICON_PROMPT
- A string describing the CLI prompt using a very simple format with:
%H(host) ,%U(user) ,%T(tty),%W(last element of working path),%w(full working path). - CLICON_PLUGIN
- The name of the object file containing callbacks in this file.
- CLICON_PIPETREE
- Name of a pipe output tree as described in
The Clixon CLI contains parts that are generated from a YANG specification. This autocli is generated from YANG into CLI specifications, parsed and merged into the top-level Clixon CLI.
The autocli is configured using three basic mechanisms:
- Config file : Modify behavior of the generated tree
- Tree expansion: How the generated cli is merged into the overall CLI
- YANG Extensions: Modify CLI behavior via YANG
Each mechanism is described in sub-sections below, but first an overview of autocli usage.
Consider a (simplified) YANG specification, such as:
module example {
container table {
list parameter{
key name;
leaf name{
type string;
}
}
}
}
An example of a generated syntax is as follows (again simplified):
table; {
parameter <name:string>;
}
The auto-cli syntax is loaded using a sub-tree operator such as @datamodel into the Clixon CLI as follows:
CLICON_PROMPT="%U@%H %W> ";
set @datamodel, cli_auto_set();
merge @datamodel, cli_auto_merge();
delete @datamodel, @add:leafref-no-refer, @add:ac-strict-expand, cli_auto_del();
show config, cli_auto_show("datamodel", "candidate", "text", true, false);{
@datamodel, cli_show_auto("candidate", "text");
}
For example, the set part is expanded using the CLIgen tree-operator to something like:
set table, cli_auto_set(); {
parameter <name:string>, cli_auto_set();
}
An example run of the above example is as follows:
> clixon_cli
user@host /> set table ?
<cr>
parameter
user@host /> set table parameter 23
user@host /> show config
table {
parameter {
name 23;
}
}
user@host />
where the generated autocli extends the Clixon CLI with YANG-derived configuration statements.
The autocli set/merge/delete commands are modelled after NETCONF operations as defined in the NETCONF RFC, with the following (shortened) definition, and mapping to the autocli operations above):
- merge: (Autocli merge) The configuration data is merged with the configuration in the configuration datastore
- replace: (Autocli set) The configuration data replaces any related configuration in the configuration datastore.
- create: The configuration data is added to the configuration if and only if the configuration data does not already exist in the configuration datastore.
- delete: The configuration data is deleted from the configuration if and only if the configuration data currently exists in the configuration datastore.
- remove: (Autocli delete) The configuration data is deleted from the configuration if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, the "remove" operation is silently ignored
In particular, the autocli set operation may cause some confusion. For terminals, i.e., CLI commands derived from YANG leaf or leaf-list, the behavior of "replace/set" and "merge" are identical. However for non-terminals (i.e., CLI commands derived from YANG container or list) "replace/set" and "merge" differ: "set" replaces the existing configuration. "Merge" merges the existing configuration.
Example, where x is (derived from) a container and y is (derived from) a leaf:
set x y 22 set x y 24 # Replace y: y changes value to 24 set x # Replace x: y is removed merge x y 26 merge x y 28 # Merge y: y changes value to 28 merge x # Merge x: y still has value 28
Therefore, most users may want to use merge as default autocli operation, instead of set.
Note
Use autocli merge as default operation
The clixon config file has a <autocli> sub-clause for global
autocli configurations. A typical CLI configuration
with default autocli settings is as follows:
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>/usr/local/etc/clixon/example.xml</CLICON_CONFIGFILE>
...
<autocli>
<module-default>true</module-default>
<list-keyword-default>kw-nokey</list-keyword-default>
<treeref-state-default>false</treeref-state-default>
<edit-mode-default>list container</edit-mode-default>
<completion-default>true</completion-default>
</autocli>
</clixon-config>
The autocli configuration consists of a set of default options, followed by a set of rules. For more info see the clixon-autocli.yang specification.
The following options set default values to the auto-cli, some of these may be further refined by successive rules.
- module-default
How to generate the autocli from modules:
- If true, all modules with a top-level datanode are generated, ie they get a top-level entry in the
@basemodeltree. You can explicitly disable modules. This is default - If false, you need to explicitly enable modules for autocli generation using module enable rules.
- If true, all modules with a top-level datanode are generated, ie they get a top-level entry in the
- list-keyword-default
How to generate the autocli from YANG lists. There are several variants defined. To understand the different variants, consider a simple YANG LIST defintion as follows:
list a { key x; leaf x; leaf y; }The different variants with the resulting autocli are as follows:
- kw-none : No extra keywords, only variables:
a <x> <y> - kw-nokey : Keywords on non-key variables:
a <x> y <y>. This is default. - kw-all : Keywords on all variables:
a x <x> y <y>
- kw-none : No extra keywords, only variables:
- treeref-state-default
If generate autocli from YANG state data. The motivation for this option is that many specs have very large state parts. In particular, some openconfig YANG specifications have ca 10 times larger state than config parts.
- If true, generate CLI from YANG state/non-config statements, not only from config data.
- If false do not generate autocli commands from YANG state data. This is default.
- edit-mode-default
Open automatic edit-modes for some YANG keywords and do not allow others. A CLI edit mode opens a carriage-return option and changes the context to be in that local context. For example:
user@host> interfaces interface e0<cr> eth0>
Default is to generate edit-modes for all YANG containers and lists. For more info see edit modes
- completion-default
- Generate code for CLI completion of existing db symbols. That is, check existing configure database for completion options. This is normally always enabled.
- grouping-treeref
- Controls the behaviour when generating CLISPEC of YANG uses statements into the
corresponding grouping definition. If true, use indirect tree reference
@treerefto reference the grouping definition. This may reduces memory footprint of the CLI. - clispec-cache
- Autocli cache mode for saving generated autocli clispecs between runs. Either disabled or read. Set to read to activate the cache, if so, the CLICON_AUTOCLI_CACHE_DIR must also be set in the backend.
To complement options, a set of rules to further define the autocli can be defined. Common rule fields are:
- name
- Arbitrary name assigned for the rule, must be unique.
- operation
- Rule operation, There are currently two operations defined: module enable and command compress.
- module-name
- Name of the module associated with this rule.
Wildchars '*' and '?' can be used (glob pattern).
Revision and yang suffix are omitted.
Example:
openconfig-*
Module enable rules are used in combination with
module-default=false to enable CLI generation for a limited set of
YANG modules.
For example, assume you want to enable modules example1, example2 and no others:
<autocli>
<module-default>false</module-default>
<rule>
<name>include example</name>
<operation>enable</operation>
<module-name>example*</module-name>
</rule>
</autocli>
If the option module-default is true, module enable rules have no effect since all modules are already enabled.
You can also disable all modules by default, and enable them individually, like the following example shows:
<autocli>
<module-default>true</module-default>
<rule>
<name>exclude example 2</name>
<operation>disable</operation>
<module-name>example2</module-name>
</rule>
</autocli>
Likewise, disable rules have no effect if module-default is false.
Compress rules are used to skip CLI commands, making the complete command name shorter.
For example, assume YANG definition:
container interfaces {
list interface {
...
}
}
Instead of typing interfaces interface e0 you would want to type only interface e0.
The following rule matches all YANG containers with lists as its only child, and removes the keyword interfaces:
<rule> <name>compress</name> <operation>compress</operation> <yang-keyword>container</yang-keyword> <yang-keyword-child>list</yang-keyword-child> </rule>
Note that this matches the openconfig compress rule: The surrounding 'container' entities are removed from 'list' nodes
A second openconfig compress rule is The 'config' and 'state' containers are "compressed" out of the schema. as examplified here (for 'config' only):
<rule> <name>openconfig compress</name> <operation>compress</operation> <yang-keyword>container</yang-keyword> <schema-nodeid>config</schema-nodeid> <module-name>openconfig*</module-name> </rule>
Specific fields for compress are:
- yang-keyword
- If present identifes a YANG keyword which the rule applies to.
Example:
container - schema-nodeid
- A single <id> identifying a YANG schema-node identifier as defined in RFC 7950 Sec 6.5.
Example:
config - yang-keyword-child
- The YANG statement has a single child, and the yang type of the child is the value of this option.
Example: :
container - extension
- The extension is set either in the node itself, or in the module the node belongs to.
Extension prefix must be set.
Example:
oc-ext:openconfig-version
In the example above, the tree-reference @datamodel is used to
merge the YANG-generated cli-spec into the overall cli-spec. There are
several variants of how the generated tree is expanded with slight differences in which
symbols are shown, how completion works, etc.
They are all derivates of the basic @basemodel tree. The following tree variants are
defined:
@basemodel- The most basic tree including everything@datamodel- The most common tree for configuration with state@datamodelshow- A tree made for showing configuration syntax@datamodelmode- A tree for editing modes@datamodelstate- A tree for showing state as well as configuration
Note to use @datamodelstate config option treeref-state-default must be set.
A third method to define the autocli is using :ref:`YANG extensions<clixon_yang>`, where a YANG specification is annotated with extension.
Clixon provides a dedicated YANG extension for the autocli for this purpose: clixon-lib:autocli.
The following example shows the main example usage of the "hide" extension of the "hidden" leaf:
import clixon-autocli{
prefix autocli;
}
container table{
list parameter{
...
leaf hidden{
type string;
autocli:hide;
}
}
}
The CLI hidden command is not shown but the command still exists:
cli /> set table parameter a ?
value
<cr>
cli /> set table parameter a hidden 99
cli /> show configuration
table {
parameter {
name a;
hidden 99;
}
}
The following autocli extensions are defined:
hide- Do not show the command in eg auto-completion. This was primarily intended for operational commands such as
start shellbut is this context used for hiding commands generated from the associated YANG node. hide-show- Do not show the config in show configuration commands. However, retreiving a config via NETCONF or examining the datastore directly shows the hidden configure commands.
skip- Skip the command altogether.
strict-expand- Only show exactly the expanded options of a variable. It should not be possible to add a new value that is not in the expanded list. See also
ac-strict-expandin Section Autocli tree labels. alias- Replace the command with another value, only implemented for YANG leaves.
The autocli supports automatic edit modes where by entering a <cr>, you enter an edit mode. An edit mode is created for every YANG container or list.
For example, the example YANG previously given and the following cli-spec:
edit @datamodelmode, cli_auto_edit("basemodel");
up, cli_auto_up("basemodel");
top, cli_auto_top("basemodel");
set @datamodel, cli_auto_set();
Then an example session for illustration is as follows, where first a small config is created, then a list instance mode is entered(parameter a), a value changed, and a container mode (table):
user@host /> set table parameter a value 42
user@host /> set table parameter b value 77
user@host /> edit table parameter a
user@host parameter=a/>
user@host parameter=a/> show configuration
name a;
value 42;
user@host parameter=a/> set value 99
user@host parameter=a/> up
user@host table> show configuration
parameter {
name a;
value 99;
}
parameter {
name b;
value 77;
}
user@host table> top
user@host />
When NACM is enabled, the autocli can be configured to hide CLI commands that
the current user does not have read access to. This is controlled by the
CLICON_NACM_AUTOCLI option and requires clispec-cache=read autocli
mode (see Config file).
For a general introduction to NACM, see the :ref:`NACM <clixon_netconf>` documentation.
When CLICON_NACM_AUTOCLI is true, the CLI fetches the NACM rules for
the current user from the backend at startup. During tab-completion and
?-expansion, any YANG data node whose schema path is covered by a NACM
deny rule (or is not covered by a permit rule when read-default is
deny) is silently hidden from the completion list.
Two NACM read-default policies are supported:
permit(default)- All nodes are visible unless an explicit
denyrule matches. Only the denied nodes are hidden. deny- All nodes are hidden unless an explicit
permitrule matches. Ancestors of permitted nodes are shown so the user can navigate down to them; descendants of permitted nodes are also shown.
Note
NACM autocli filtering is a cosmetic aid: the backend still enforces full NACM access control on every datastore operation. A user who crafts a command bypassing the autocli will still be denied by the backend.
Enable NACM autocli by adding the following to the Clixon config file:
<CLICON_NACM_AUTOCLI>true</CLICON_NACM_AUTOCLI>
The autocli cache must be set to read mode (the CLI fetches clispecs from
the backend, which allows per-user NACM filtering):
<autocli> ... <clispec-cache>read</clispec-cache> </autocli>
Disable NACM autocli filtering on a per-invocation basis with the -o
command-line option:
clixon_cli -o CLICON_NACM_AUTOCLI=false
Consider a YANG model with top-level containers public and secret, and
the following NACM configuration that permits all reads but explicitly denies
/secret for the limited group:
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<enable-nacm>true</enable-nacm>
<read-default>permit</read-default>
<rule-list>
<name>limited-acl</name>
<group>limited</group>
<rule>
<name>deny-secret</name>
<module-name>*</module-name>
<path xmlns:ex="urn:example">/ex:secret</path>
<access-operations>read</access-operations>
<action>deny</action>
</rule>
</rule-list>
</nacm>
A user in the limited group sees only public at tab-completion:
limited@host /> set ? public Public configuration
An admin user sees both:
admin@host /> set ? public Public configuration secret Secret configuration
The same filtering applies in edit mode. If the user navigates into a
container and then types set ?, only the sub-nodes that are permitted by
NACM are shown.
CLI callback functions are "library" functions that an application may call from a clispec. A user is expected to create new application-specific callbacks.
There are two major types of CLI callbacks:
- Command callbacks, to implement the semantics of a CLI command
- Expand callbacks, for providing a set of values to a variable
A CLI command callback is invoked as a result of a command and implements its semantics, and is given as the last statement in a command.
Clixon provides several command callbacks, but a developer typically adds new commands applicable for the application.
Consider the following clispec example of the command callback mycallback():
example("Callback example") <var:int32>("any number"), mycallback("myarg");
The example contains a keyword (example) and a variable (var), while mycallback is the CLI callback with argument: (myarg).
In C, the callback has the following signature:
int mycallback(clixon_handle h, cvec *cvv, cvec *argv);
Suppose a user enters the following command in the CLI:
user@host> example 23
The callback is called with the following parameters:
cvv: 0: example 23 1: 23 argv: 0: "myarg"
which means that cvv contains dynamic values set by the user, and argv contains static values set by the clispec designer.
Some variables may need a dynamic set of values to choose from, for example, currently existing interface names.
An expand callback is always given inside a variable declaration.
Clixon currently provides the following set of expand callbacks:
expand_dbvar(): finding datastore symbolsexpand_dir(): for files in a directoryexpand_yang_list(): finding YANG symbols
Just like command callbacks, a developer can add new expand functions.
An example of finding datastore symbols in the candidate datastore:
table parameter <var:int32 expand_dbvar("candidate", "/clixon-example:table/parameter=%s/value")>
where the second argument is an api-path (see Section Api-path-fmt) defining a datastore object.
Example, assume there are two table parameters in the candidate datastore:
cli> table parameter <?>
a Table paramter
b Table parameter
When completing a leafref variable, the CLI can either expand using the referred node (the leafref target path), or expand using the existing values in the datastore (the node itself).
The behavior depends on how expand_dbvar() is used:
- Autocli-generated clispec (
@datamodel/@tree): The autocli generator automatically adds a
leafref-referlabel to variables whose YANG type isleafreforunion(possibly containing leafrefs). This causesexpand_dbvar()to follow the leafref and offer values from the referred node.To suppress this and expand from the existing values instead, add the
leafref-no-referlabel, typically via@add:leafref-no-refer:set @tree, cli_add(); delete @tree, @add:leafref-no-refer, cli_delete();
This results in the following behavior for adding and removing leafrefs, respectively:
set: expands to values in the referred node (e.g.parameter/name)delete: expands to the existing values in the datastore (what is actually stored)
- Manually crafted clispec:
expand_dbvar()defaults to not following leafrefs (leafref-refer=false). This is correct for most manual CLI commands that want to expand from existing datastore values, not from a leafref target.To opt into leafref following in a manual clispec, add the
leafref-referlabel explicitly:set leafref <var:string leafref-refer expand_dbvar("candidate", "/ex:list/leafref")>; delete leafref <var:string expand_dbvar("candidate", "/ex:list/leafref")>;
In summary:
| Scenario | leafref-refer |
leafref-no-refer |
Result |
|---|---|---|---|
| Autocli, leafref/union type | set by generator | absent | follows leafref ✓ |
Autocli + @add:leafref-no-refer |
set by generator | set by @add | uses existing values ✓ |
| Manual clispec (default) | absent | absent | uses existing values ✓ |
Manual clispec + leafref-refer |
set explicitly | absent | follows leafref ✓ |
Assume the following YANG:
list parameter{
key name;
leaf name{
description "referred node";
type string;
}
}
leaf-list leafref{
description "referring node";
type leafref{
path "../parameter/name";
}
}
Example using autocli tree references:
set @tree, cli_add(); delete @tree, @add:leafref-no-refer, cli_delete();
This results in the following behavior for adding and removing leafrefs, respectively:
cli> set parameter a
cli> set parameter b
cli> set leafref ?
a
b
cli> set leafref a
cli> set leafref c
cli> delete leafref ?
a
c
cli>
In other words, when adding new nodes, the CLI expands to referred, original values ("a" and "b"), but when deleting nodes, the actual values, non-referred ("a" and "c") are offered as potential choices.
Clixon includes show commands for showing datastore and state content. An application may use these functions as basis for more specialized show functions. Some show functions are:
cli_show_config()- Multi-purpose show function for manual CLI show commandscli_show_auto()- Used in conjunction with the autocli with expansion treescli_show_auto_mode()- Used in conjunction with the autocli with edit-modescli_pagination()- Show paginated data of a large list
The CLI show functions are utility functions in the sense that they are not part of the core functionality and a user or product may want to specialize them.
Note
CLI library functions are subject to change in new releases
The cli_show_config is a basic function to display datastore and state data. A typical use in a cli spec is as follows:
show("Show configuration"), cli_show_config("candidate", "text");
Using this command in the CLI could provicde the following output:
cli> show
<table xmlns="urn:example:clixon">
<parameter>
<name>a</name>
<value>x</value>
</parameter>
</table>
The callback has the following parameters, only the first is mandatory:
- dbname : Name of datastore to show, such as "running", "candidate" or "startup"
- format : Show format, one of text, xml, json, cli, netconf, or default (see :ref:`datastore formats <clixon_datastore>`)
- xpath : Static xpath (only present in this API function)
- namespace : Default namespace for xpath (only present in this API function)
- pretty : If true, make output pretty-printed
- state : If true, include non-config data in output
- default : Optional default retrieval mode: one of report-all, trim, explicit, report-all-tagged. See also extended values below
- prepend : Optional prefix to prepend before cli syntax output, only valid for CLI format.
- fromroot : If false show from xpath node, if true show from root
The cli_show_auto() callback is used together with the autocli to show sub-parts of a configured tree using expansion. A typical definition is as follows:
show("Show expand") @datamodelshow, cli_show_auto("candidate", "xml");
That is, it must always be used together with a tree-reference as described in Section autocli.
An example CLI usage is:
cli> show table parameter a <parameter> <name>a</name> <value>x</value> </parameter>
The arguments are similar to cli_show_config with the difference that the xpath is implicitly defined by the current position of the tree reference:
* `dbname` : Name of datastore to show, such as "running", "candidate" or "startup" * `format` : Show format, one of `text`, `xml`, `json`, `cli`, `netconf`, or `default` (see :ref:`datastore formats <clixon_datastore>`) * `pretty` : If `true`, make output pretty-printed * `state` : If `true`, include non-config data in output * `default` : Optional default retrieval mode: one of `report-all`, `trim`, `explicit`, `report-all-tagged`. See also extended values below * `prepend` : Opional prefix to print before cli syntax output, only valid for CLI format.
The cli_show_auto_mode() callback also used together with the autocli but instead of expansion uses the edit-modes (see Section edit modes).
A typical definition is:
show, cli_show_auto_mode("candidate");
An example usage using edit-modes is:
cli> edit table cli> show <parameter> <name>a</name> <value>x</value> </parameter>
Same parameters as cli_show_auto
You need to add a callback and its name in the .cli file.
Here is an example of how you can do it:
run_program_err("Run program"), cli_start_program();
run_program_python3("Run program"), cli_start_program("python3");
run_program_python3_source_arg("Run program"), cli_start_program("python3", "/tmp/test.py");
run_program_python3_source_arg_vector("Run program") <source:rest>("Path program"), cli_start_program("python3");
run_program_python3_source_arg_vector_err("Run program") <source:rest>("Path program"), cli_start_program("python3", "/tmp/test2.py");
run_program_bash("Run program"), cli_start_program("bash");
What the above-described callbacks do:
Starts a new process and the specified program in it. The function creates a new child process and starts the specified program in it. Before starting the program in the child process, environment variables are set from the cvv. The function checks the parameters passed to it. Situations where at least one function argument is absent or when two arguments are present both in the function and in the cvv vector are considered invalid.
run_program_python3_source_arg_vector_err will fail because you can either enter interactively or write in a call:
roma@f-15 /> run_program_python3_source_arg_vector_err /tmp/test.py Aug 14 08:52:47.375126: cli_start_program: 830: Plugins: A lot of arguments: Invalid argument CLI command error
run_program_err will fail because there are no parameters:
user@pc_name /> run_program_err Aug 14 08:37:41.386765: cli_start_program: 826: Plugins: Can not found argument in a function: Invalid argument CLI command error
run_program_bash executes bash:
user@pc_name /> run_program_bash user_pc@pc_name:~#
run_program_python3 runs python3:
user@pc_name /> run_program_python3 Python 3.11.2 (main, May 2 2024, 11:59:08) [GCC 12.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
run_program_python3_source_arg runs /tmp/test.py using python3.
/tmp/test.py file
#!/usr/bin/python3
print("Hello world! :)")
Run:
user@pc_name /> run_program_python3_source_arg Hello world! :) user@pc_name />
run_program_python3_source_arg_vector entails interactive input of the filename
user@pc_name run_program_python3_source_arg_vector /tmp/test.py Hello world! :) user@pc_name />
All show commands have an optional with-default retrieval mode: one of report-all, trim, explicit, report-all-tagged. There are also extra propriatary modes of default serving as examples:
- NULL, default with-default value, usually report-all
- report-all-tagged-default, which gets the config as report-all-tagged but strips the tags/attributes (same as report-all).
- report-all-tagged-strip, which also gets the config as report-all-tagged but strips the nodes associated with the default tags (same as trim).
All show commands have a pretty-print parameter. If true the putput is pretty-printed.
Indentation level is controlled by the PRETTYPRINT_INDENT compile-time option
Clixon provides utility callbacks to load configuration from a file into the candidate datastore and to save a datastore to a file. These callbacks are typically used in CLI spec files together with a filename argument.
load_config_file reads a configuration file and sends its content to the backend as an edit-config operation on the candidate datastore. In a typical clispec the operation and format are exposed as nested sub-commands:
load("Load configuration") <filename:string>("Filename"), load_config_file("filename", "replace");{
replace("Replace candidate with file contents"), load_config_file("filename", "replace");{
xml("Replace candidate with XML file"), load_config_file("filename", "replace", "xml");
json("Replace candidate with JSON file"), load_config_file("filename", "replace", "json");
text("Replace candidate with text file"), load_config_file("filename", "replace", "text");
cli("Replace candidate with CLI file"), load_config_file("filename", "replace", "cli");
}
merge("Merge file with existing candidate"), load_config_file("filename", "merge");{
xml("Merge XML file into candidate"), load_config_file("filename", "merge", "xml");
json("Merge JSON file into candidate"), load_config_file("filename", "merge", "json");
text("Merge text file into candidate"), load_config_file("filename", "merge", "text");
cli("Merge CLI file into candidate"), load_config_file("filename", "merge", "cli");
}
}
This produces a command hierarchy where the operation and format are optional sub-commands:
> load /path/to/file # replace, xml (defaults) > load /path/to/file merge # merge, xml (default format) > load /path/to/file merge json # merge, json
The callback takes the following arguments:
varname: Name of the CLIgen variable that holds the filename, as given on the command line.operation: Eithermerge(merge file content with the existing candidate) orreplace(replace the entire candidate with the file content).format: Optional. One ofxml(default),json,text,cli, ornetconf.
save_config_file retrieves the content of a datastore and writes it to a local file. A typical CLI spec entry is:
save("Save configuration") <filename:string>("Filename"), save_config_file("running", "filename", "json");
The callback takes the following arguments:
dbname: The datastore to read from:running,candidate, orstartup.varname: Name of the CLIgen variable holding the output filename.format: Optional. One ofxml(default),json,text,cli, ornetconf.
The saved file can be reloaded with load_config_file using the same format.
Output pipes resemble UNIX shell pipes and are useful to filter or modify CLI output. Example:
cli> print all | grep parameter
<parameter>5</parameter>
<parameter>x</parameter>
cli> show config | showas json
{
"table": {
"parameter": [
...
cli>
Output pipe functions are declared using a special variant of a CLI tree with a name starting with a vertical bar. Example:
CLICON_MODE="|mypipe";
\| {
grep <arg:rest>, pipe_grep_fn("-e", "arg");
showas json, pipe_json_fn();
}
where pipe_grep_fn and pipe_json_fn are special callbacks that use stdio to modify output.
Such a pipe tree can be referenced with either an explicit reference, or an implicit rule.
Only a single level of pipes is possibly in this release. For example, a|b|c is not possible.
Note
Only one level of pipes is supported
An explicit reference is for single commands. For example, adding a pipe to the print commands:
print, print_cb("all");{
@|mypipe, print_cb("all");
all @|mypipe, print_cb("all");
detail;
}
where a pipe tree is added as a tree reference, appending pipe functions to the regular print_cb callback.
Note that the following commands are possible in this example:
print print | count print all | count print detail
An implicit rule adds pipes to all commands in a cli mode. An example of an implicit rule is as follows:
CLICON_PIPETREE="|mypipe";
print, print_cb("all");{
all, print_cb("all");
detail, print_cb("detail);
}
where the pipe tree is added implicitly to all commands in that file, and possibly on other files with the same mode.
Pipe trees also work for sub-trees, ie a subtree referenced by the top-level tree may also use output pipes.
It is possible to combine an implicit (default) rule with an explict rule as follows:
CLICON_MODE="|commonpipe";
print, print_cb("all");{
@|mypipe, print_cb("all");
all @|mypipe, print_cb("all");
detail;
}
In this example, print and print all use the ¡mypipe menu, while print detail uses the |common menu
- Sub-trees inherit pipe commands from the top-level according to the following rules:
- Top-level implicit rules are inherited to all sub-trees, unless
- Explicit rules are present at the tree-reference
- No pipe commands are allowed in a pipe-command (only single level allowed)
Rules 1 and 2 are illustrated as follows:
CLICON_MODE="|commonpipe";
aaa {
@datamodel, cli_show();
@|mypipe, cli_show();
}
bbb {
@datamodel, cli_show();
}
Pipe commands in the datamodel tree are |mypipe if preceeded by aaa, but |commonpipe if preceeded by bbb
You can write own scripts and use them as output pipe functions by placing them in CLICON_CLI_PIPE_DIR.
Assume for example that CLICON_CLI_PIPE_DIR=/usr/local/lib/controller/pipes, and that a script first.sh is placed in that dir, with the following content:
#!/usr/bin/env bash head -1
Then add a pipe command as follows:
generic("Generic callbacks") <callback:string expand_dir("/usr/local/lib/controller/pipes", "\.sh$")>("Callback"), pipe_generic("callback");
Then, start the CLI:
cli> show config | generic ? first.sh cli> show config | generic first.sh <config> cli>
Clixon provides a simple API for creating CLI aliases.
Three clispec utility functions exists that create and show aliases as follows:
- cli_alias_add : Add a constant alias command by specifying name and command
- cli_aliasref_add : Add an alias command by using completion
- cli_alias_show : Show aliases
An example clispec using these three functions:
alias("Define alias function") <name:string>("Name of alias") <command:rest>("Alias commands"), cli_alias_add("name", "command");
aliasref("Define alias function using completion") <name:string>("Name of alias") @example, cli_aliasref_add("name");
show("Show a particular state of the system") alias, cli_alias_show();
Example of setting a CLI alias, invoking it, and showing its definition:
cli> alias cmd show config
cli> cmd
<table>
<parameter/>
</table>
cli> show alias
cmd: show config
cli>
The alias API is limited and an application developer may need to modify or extend these functions when integrating aliases into a system. These cases include:
- Saving aliases persistently, eg an src file.
- Adding help-texts
- Mode/tree support
This section describes some advanced options in the Clixon CLI not described elsewhere.
By default, the CLI uses a UNIX socket as an IPC to communicate with the backend. It is possible to use an IP socket but with a restricted functionality, see :ref:`backend section<clixon_backend>`.
The session creation is "lazy" in the sense that a NETCONF session is only established when needed. After the session has been established, it is maintained (cached) by the CLI client to keep track of candidate edits and locks, as described in 7.5 of RFC 6241.
If there is no backend running at the time of session establishment, a warning is printed:
cli /> show config Mar 18 11:53:43: clicon_rpc_connect_unix: 541: Protocol error: /usr/local/var/example/example.sock: config daemon not running?: No such file or directory Protocol error: /usr/local/var/example/example.sock: config daemon not running?: No such file or directory cli />
If at a later time, the backend is started, the session is established normally
After a session is established and the backend exits, crashes or restarts, any state associated with the session will be lost, including:
- explicit locks
- edits in candidate-db
If the backend exits during an existing session, it will close with the same error message as above:
cli /> show config Mar 18 11:53:43: clicon_rpc_connect_unix: 541: Protocol error: /usr/local/var/example/example.sock: config daemon not running?: No such file or directory Protocol error: /usr/local/var/example/example.sock: config daemon not running?: No such file or directory cli />
If the backend restarts, a new session is created with a warning:
cli /> show configuration Mar 18 11:57:55: The backend was probably restarted and the client has reconnected to the backend. Any locks or candidate edits are lost. cli />
It is possible to change the default behavior by undefining the compile-option: #undef PROTO_RESTART_RECONNECT. If so, the CLI is exited when the existing session is closed in anyway:
cli /> show configuration Mar 18 12:02:57: clicon_rpc_msg: 210: Protocol error: Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed.: Cannot send after transport endpoint shutdown Protocol error: Unexpected close of CLICON_SOCK. Clixon backend daemon may have crashed.: Cannot send after transport endpoint shutdown bash#
Sub-trees are defined using the tree operator @. Every mode gets assigned a tree which can be referenced as @name. This tree can be either on the top-level or as a sub-tree. For example, create a specific sub-tree that is used as sub-trees in other modes:
CLICON_MODE="subtree";
subcommand{
a, a();
b, b();
}
then access that subtree from other modes:
CLICON_MODE="configure"; main @subtree; other @subtree,c();
The configure mode will now use the same subtree in two different commands. Additionally, in the other command, the callbacks are overwritten by c. That is, if other a, or other b is called, callback function c is invoked.
CLIgen supports wrapper functions that can take the output of a callback and transform it to something else.
The CLI can perform variable translation. This is useful if you want to process the input, such as hashing, encrypting or in other way translate the input.
The following example is based on the main Clixon example and is included in the regression tests. In the following CLI specification, a "translate" command sets a modifed value to the "table/parameter=translate/value":
translate <value:string translate:cli_incstr()>, cli_set("/clixon-example:table/parameter=translate/value");
If you run this example using the cli_incstr() function which increments the characters in the input, you get this result:
user@host> translate HAL
user@host> show configuration
table {
parameter {
name translate;
value IBM;
}
}
The example is very simple and based on strings, but can be used also for other types and more advanced functions.
The autocli trees described in tree expansion are implemented using filtering of CLIgen
labels. While @basemodel includes all labels, the other trees have
removed some labels.
For most uses, the pre-defined trees above are enough, using explicit label filtering is more powerful.
The currently defined labels are:
act-list: Terminal entries of YANG LIST nodes.act-container: Terminal entries of YANG CONTAINER nodes.ac-leaf: Leaf/leaf-list nodesact-prekey: Terminal entries of LIST leaf keys, except the last keys in multi-key cases.act-lastkey: Terminal entries of LIST leaf keys, except the last keys in multi-key cases.act-leafconst: Terminal entries of non-empty non-key YANG LEAF/LEAF_LISTs command nodes.act-leafvar: Terminal entries of non-key YANG LEAF/LEAF_LISTs variable nodes.ac-state: Nodes which have YANGconfig falseas childac-config: Nodes which do not have any state nodes as siblingsac-strict-expand: Only show exactly the expanded options of a variable.
Labels with prefix act_ are terminal labels in the sense that they mark a terminal command, ie the node itself; while labels with ac_ represent the non-terminal, ie the whole sub-tree.
As an example, the @datamodel tree is basemodel with labels removed as follows:
@basemodel, @remove:act-prekey, @remove:act-list, @remove:act-leaf, @remove:ac-state;
which is an alternative way of specifying the datamodel tree.
One example of the strict-expand label is to remove all general
variable options for some trees and keep them for others. For example,
one may need to set a new unknown value to a variable, but when deleting, it may not make sense to delete a non-existing value:
delete @datamodel, @add:ac-strict-expand, cli_auto_del(); set @datamodel, cli_auto_set();
In the cli:
# clixon_cli cli /> set table parameter ? a b <name> cli /> delete table parameter ? a b cli />
Note the absence of <name> in the delete rule.
Clixon adds some features and structure to CLIgen which include:
- A plugin framework for both textual CLI specifications(.cli) and object files (.so)
- Object files contains compiled C functions referenced by callbacks in the CLI specification. For example, in the cli spec command: a,fn(), fn must exist in the object file as a C function.
- The CLIgen treename syntax does not work.
- A CLI specification file is enhanced with the CLIgen variables CLICON_MODE, CLICON_PROMPT, CLICON_PLUGIN and CLICON_PIPETREE.
- Clixon generates a command syntax from the Yang specification that can be referenced as @datamodel. This is useful if you do not want to hand-craft CLI syntax for configuration syntax.
Example of @datamodel syntax:
set @datamodel, cli_set();
merge @datamodel, cli_merge();
create @datamodel, cli_create();
show @datamodel, cli_show_auto("running", "xml");
The commands (eg cli_set) will be called with the first argument an api-path to the referenced object.
The CLI can run scripts using either the -1 option for single commands:
clixon_cli -1 show version 4.8.0.PRE
Or using the -F <file> command-line option to redirect input from file
clixon_cli -F file
Or using "shebang":
#!/usr/local/bin/clixon_cli -F show version quit
- Two caveats regarding "shebang":
- The clixon config file is /usr/local/etc/clixon.xml
- The mode is CLICON_CLI_MODE
You may mod this by using soft links or creating a new executable to use use in the "shebang" with other default values.
CLIgen is designed to handle large specifications in runtime, but it may be difficult to handle large specifications from a design perspective.
Here are some techniques and hints on how to reduce the complexity of large CLI specs:
The CLICON_MODE is used to specify in which modes the syntax in a specific file should be added. For example, if you have major modes configure and operation you can have a file with commands for only that mode, or files with commands in both, (or in all).
First, lets add a basic set in each:
CLICON_MODE="configure"; show configure;
and:
CLICON_MODE="operation"; show configure;
Note that CLI command trees are merged so that show commands in other files are shown together. Thus, for example:
CLICON_MODE="operation:files";
show("Show") files("files");
will result in both commands in the operation mode:
> clixon_cli -m operation user@host> show <TAB> configure files
but:
> clixon_cli -m configure user@host> show <TAB> configure
You can also use sub-trees and the the tree operator @. Every mode gets assigned a tree which can be referenced as @name. This tree can be either on the top-level or as a sub-tree. For example, create a specific sub-tree that is used as sub-trees in other modes:
CLICON_MODE="subtree";
subcommand{
a, a();
b, b();
}
then access that subtree from other modes:
CLICON_MODE="configure"; main @subtree; other @subtree,c();
The configure mode will now use the same subtree in two different commands. Additionally, in the other command, the callbacks will be overwritten by c. That is, if other a, or other b is called, callback function c will be invoked.
You can also add the C preprocessor as a first step. You can then define macros, include files, etc. Here is an example of a Makefile using cpp:
C_CPP = clispec_example1.cpp clispec_example2.cpp
C_CLI = $(C_CPP:.cpp=.cli
CLIS = $(C_CLI)
all: $(CLIS)
%.cli : %.cpp
$(CPP) -P -x assembler-with-cpp $(INCLUDES) -o $@ $<
The Yang bits built-in type as defined in RFC 7950 Sec 9.7 provides a set of bit names. In the CLI, the names should be given in a white-spaced delimited list, such as "fin syn rst".
The RFC defines a "canonical form" where the bits appear ordered by their position in YANG, but Clixon validation accepts them in any order.
Given them in XML and JSON follows thus, eg XML:
<flags>fin rst syn</flags>
Clixon CLI does not treat individual bits as "first-level objects". Instead it only validates the whole string of bit names. Operations (add/remove) are made atomically on the whole string.
The clixon CLI uses an internal meta-format called api_path_fmt which is used to generate api-paths, as described in Section :ref:`XML <clixon_xml>`.
An api-path-fmt extends an api-path with % flag characters (like printf) as follows:
- %s: The value of a cligen-variable
- %k: The key of a YANG list
Example, an explicit clispec expansion variable could be:
<name:string expand_dbvar("candidate","/interface=%s/%k")>
which could expand to /interface=eth0/mykey if "eth0" is given as the "name" variable and "mykey" is the YANG interface list key.