New: [CLI simple alias](https://github.com/clicon/cligen/issues/112)
This commit is contained in:
parent
9ee55441ac
commit
aa4feee03e
4 changed files with 345 additions and 0 deletions
|
|
@ -16,6 +16,8 @@ Expected: October 2024
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
* New: [CLI simple alias](https://github.com/clicon/cligen/issues/112)
|
||||||
|
* See: https://clixon-docs.readthedocs.io/en/latest/cli.html#cli-aliases
|
||||||
* List pagination: Added where, sort-by and direction parameter for configured data
|
* List pagination: Added where, sort-by and direction parameter for configured data
|
||||||
* New `clixon-lib@2024-04-01.yang` revision
|
* New `clixon-lib@2024-04-01.yang` revision
|
||||||
- Added: list-pagination-partial-state extension
|
- Added: list-pagination-partial-state extension
|
||||||
|
|
|
||||||
|
|
@ -2086,3 +2086,207 @@ cli_process_control(clixon_handle h,
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Alias function
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] cvv Vector of variables: function parameters
|
||||||
|
* @param[in] argv Arguments given at the callback: this is the command to be executed
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see cliread_eval original fn
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
cli_alias_call(cligen_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv)
|
||||||
|
{
|
||||||
|
return cligen_alias_call(cli_cligen(h), cvv, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Define an alias CLI command, insert in top-level of parse-tree
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] cvv Vector of variables: function parameters
|
||||||
|
* @param[in] argv Arguments given at the callback: <name> <command> [<treename>]
|
||||||
|
* <name> Name of variable containing alias name
|
||||||
|
* <command> Name of variable containing alias command
|
||||||
|
* <treename> Optional name of treename (mode), default is active pt
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @code
|
||||||
|
* alias <name:string> <command:rest>, alias_cb("name", "command");
|
||||||
|
* @endcode
|
||||||
|
* @see cli_aliasref_add add using a tree reference
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
cli_alias_add(clixon_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *cvname;
|
||||||
|
char *cvcommand;
|
||||||
|
cg_var *cv;
|
||||||
|
char *name;
|
||||||
|
char *command;
|
||||||
|
char *treename = NULL;
|
||||||
|
int argi = 0;
|
||||||
|
|
||||||
|
if (argv == NULL || cvec_len(argv) < 2 || cvec_len(argv) > 3){
|
||||||
|
clixon_err(OE_PLUGIN, EINVAL, "Expected arguments: <name> <command> [<ptname>]");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* Indirectopn of name argument */
|
||||||
|
if ((cvname = cvec_i_str(argv, argi++)) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "No <name> argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cv = cvec_find_var(cvv, cvname)) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "Expected name argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = cv_string_get(cv);
|
||||||
|
/* Indirection of command argument */
|
||||||
|
if ((cvcommand = cvec_i_str(argv, argi++)) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "No <command> argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cv = cvec_find_var(cvv, cvcommand)) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "Expected command argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
command = cv_string_get(cv);
|
||||||
|
/* Optional parse-tree name argument */
|
||||||
|
if (cvec_len(argv) > argi)
|
||||||
|
treename = cvec_i_str(argv, argi++);
|
||||||
|
if (cligen_alias_add(cli_cligen(h), treename, name, NULL, command, cli_alias_call) < 0){
|
||||||
|
clixon_err(OE_PLUGIN, errno, "Error adding alias %s", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Define an alias CLI command via a tree refernce, insert in top-level of parse-tree
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] cvv Vector of variables: function parameters
|
||||||
|
* @param[in] argv Arguments given at the callback: <name>
|
||||||
|
* <name> Name of variable containing alias name
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @code
|
||||||
|
* aliasref <name:string> , aliasref_cb("name");
|
||||||
|
* @endcode
|
||||||
|
* @see cli_alias_add add a constant string command
|
||||||
|
* @note Not generic implementation, assumes cmd string: <cmd> <name> <rest>
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
cli_aliasref_add(clixon_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cg_var *cv;
|
||||||
|
char *name;
|
||||||
|
char *command =NULL;
|
||||||
|
char *treename = NULL;
|
||||||
|
|
||||||
|
/* argv handling is ad-hoc, unkown argv list */
|
||||||
|
if ((cv = cvec_find_var(cvv, "name")) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "Expected name argument");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = cv_string_get(cv);
|
||||||
|
if ((command = cvec_i_str(cvv, 0)) == NULL){
|
||||||
|
clixon_err(OE_PLUGIN, 0, "Expected cvv[0]");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* skip fisrt two elements */
|
||||||
|
if ((command = index(command, ' ')) == NULL) {
|
||||||
|
clixon_err(OE_PLUGIN, 0, "No command string");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
command++;
|
||||||
|
if ((command = index(command, ' ')) == NULL) {
|
||||||
|
clixon_err(OE_PLUGIN, 0, "No command string");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
command++;
|
||||||
|
if (cligen_alias_add(cli_cligen(h), treename, name, NULL, command, cli_alias_call) < 0){
|
||||||
|
clixon_err(OE_PLUGIN, errno, "Error adding alias %s", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_alias_show(clixon_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
pt_head *ph;
|
||||||
|
char *phname = NULL;
|
||||||
|
cligen_handle ch = cli_cligen(h);
|
||||||
|
int i;
|
||||||
|
cg_obj *co;
|
||||||
|
parse_tree *pt;
|
||||||
|
cg_callback *cc;
|
||||||
|
cg_var *cv;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (phname == NULL) {
|
||||||
|
if ((ph = cligen_ph_active_get(ch)) == NULL){
|
||||||
|
errno = ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((ph = cligen_ph_find(ch, phname)) == NULL) {
|
||||||
|
errno = ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((pt = cligen_ph_parsetree_get(ph)) == NULL){
|
||||||
|
errno = ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i=0; i<pt_len_get(pt); i++){
|
||||||
|
if ((co = pt_vec_i_get(pt, i)) == NULL)
|
||||||
|
continue;
|
||||||
|
if (co->co_type == CO_EMPTY)
|
||||||
|
continue;
|
||||||
|
if (co_flags_get(co, CO_FLAGS_ALIAS) == 0x0)
|
||||||
|
continue;
|
||||||
|
cligen_output(stdout, "%s:", co->co_command);
|
||||||
|
if ((cc = co->co_callbacks) != NULL && cc->cc_cvec){
|
||||||
|
cbuf_reset(cb);
|
||||||
|
cv = NULL;
|
||||||
|
while ((cv = cvec_each(cc->cc_cvec, cv)) != NULL) {
|
||||||
|
cprintf(cb, " ");
|
||||||
|
if (cv2cbuf(cv, cb) < 0){
|
||||||
|
clixon_err(OE_UNIX, errno, "cv2cbuf");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cligen_output(stdout, "%s\n", cbuf_get(cb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ int cli_help(clixon_handle h, cvec *vars, cvec *argv);
|
||||||
cvec *cvec_append(cvec *cvv0, cvec *cvv1);
|
cvec *cvec_append(cvec *cvv0, cvec *cvv1);
|
||||||
int cvec_concat_cb(cvec *cvv, cbuf *cb);
|
int cvec_concat_cb(cvec *cvv, cbuf *cb);
|
||||||
int cli_process_control(clixon_handle h, cvec *vars, cvec *argv);
|
int cli_process_control(clixon_handle h, cvec *vars, cvec *argv);
|
||||||
|
int cli_alias_cb(clixon_handle h, cvec *cvv, cvec *argv);
|
||||||
|
|
||||||
/* In cli_show.c */
|
/* In cli_show.c */
|
||||||
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
||||||
|
|
|
||||||
138
test/test_cli_alias.sh
Executable file
138
test/test_cli_alias.sh
Executable file
|
|
@ -0,0 +1,138 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tests for CLI simple alias
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
cfg=$dir/conf_yang.xml
|
||||||
|
fyang=$dir/example.yang
|
||||||
|
clidir=$dir/cli
|
||||||
|
fin=$dir/alias.cli
|
||||||
|
|
||||||
|
if [ -d $clidir ]; then
|
||||||
|
rm -rf $clidir/*
|
||||||
|
else
|
||||||
|
mkdir $clidir
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate autocli for these modules
|
||||||
|
AUTOCLI=$(autocli_config ${APPNAME}\* kw-nokey false)
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||||
|
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
|
<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_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/run/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
|
||||||
|
${AUTOCLI}
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $clidir/ex.cli
|
||||||
|
CLICON_MODE="example";
|
||||||
|
CLICON_PROMPT="%U@%H %W> ";
|
||||||
|
|
||||||
|
set @datamodel, cli_auto_set();
|
||||||
|
delete("Delete a configuration item") {
|
||||||
|
@datamodel, cli_auto_del();
|
||||||
|
all("Delete whole candidate configuration"), delete_all("candidate");
|
||||||
|
}
|
||||||
|
show("Show a particular state of the system") {
|
||||||
|
configuration("Show configuration"), cli_show_auto_mode("candidate", "xml", false, false);{
|
||||||
|
json, cli_show_auto_mode("candidate", "json", false, false);
|
||||||
|
}
|
||||||
|
alias, cli_alias_show();
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module example {
|
||||||
|
namespace "urn:example:clixon";
|
||||||
|
prefix ex;
|
||||||
|
container table{
|
||||||
|
list parameter{
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg"
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -z -f $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s init -f $cfg"
|
||||||
|
start_backend -s init -f $cfg
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Set alias"
|
||||||
|
expectpart "$($clixon_cli -1f $cfg alias newcmd show config)" 0 "^$"
|
||||||
|
|
||||||
|
cat <<EOF > $fin
|
||||||
|
set table parameter x
|
||||||
|
alias newcmd show config
|
||||||
|
newcmd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "Load and check alias"
|
||||||
|
expectpart "$(cat $fin | $clixon_cli -f $cfg)" 0 "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name></parameter></table>"
|
||||||
|
|
||||||
|
cat <<EOF > $fin
|
||||||
|
alias newcmd show config
|
||||||
|
alias newcmd show config json
|
||||||
|
newcmd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "Replace alias"
|
||||||
|
expectpart "$(cat $fin | $clixon_cli -f $cfg)" 0 '{"example:table":{"parameter":\[{"name":"x"}\]}}' --not-- "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name></parameter></table>"
|
||||||
|
|
||||||
|
cat <<EOF > $fin
|
||||||
|
alias cmd1 show config
|
||||||
|
alias cmd2 show config json
|
||||||
|
show alias
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "show alias"
|
||||||
|
expectpart "$(cat $fin | $clixon_cli -f $cfg)" 0 "cmd1: show config" "cmd2: show config json"
|
||||||
|
|
||||||
|
cat <<EOF > $fin
|
||||||
|
aliasref newcmd show config
|
||||||
|
newcmd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "Load and check alias reference"
|
||||||
|
expectpart "$(cat $fin | $clixon_cli -f $cfg)" 0 "<table xmlns=\"urn:example:clixon\"><parameter><name>x</name></parameter></table>"
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=$(pgrep -u root -f clixon_backend)
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
stop_backend -f $cfg
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
||||||
|
new "endtest"
|
||||||
|
endtest
|
||||||
Loading…
Add table
Add a link
Reference in a new issue