removed cli single callback arg code

This commit is contained in:
Olof hagsand 2017-04-06 10:26:10 +02:00
parent 1e92304a52
commit 31c45e5c62
15 changed files with 368 additions and 1120 deletions

View file

@ -29,7 +29,13 @@
#
# ***** END LICENSE BLOCK *****
- show_configuration and cli_copy_object added as generic cli commands
- cli_copy_config added as generic cli command
- cli_show_config added as generic cli command
Replace all show_confv*() and show_conf*() with cli_show_config()
Example: replace:
show_confv_as_json("candidate","/sender");
with:
cli_show_config("candidate","json","/sender");
- Alternative yang spec option -y added to all applications
- Many clicon special string functions have been removed
- The netconf support has been extended with lock/unlock
@ -69,18 +75,40 @@
- Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to
enable. Eg, if backend_main is started with -CIr startup will be copied to
running.
- Added ".." as valid step in xpath
- Use restconf format for internal xmldb keys. Eg /a/b=3,4
- List keys with special characters are RFC 3986 encoded.
- Changed example to use multiple cli callbacks
- Added cli multiple callback and expand support. Use options
CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG
to control these.
The multiple support for expand callbacks is enabled but not for callbacks
since this causes problems for legacy applications.
If you change to multiple argument callbacks change all cli callback functions.
Library functions in clixon_cli_api.h (e.g cli_commit) is rewritten in new
for (eg cli_commitv). See clixon_cli_api.h for new names.
- List keys with special characters RFC 3986 encoded.
- Replaced cli expand functions with single to multiple args
This change is _not_ backward compatible
This effects all calls to expand_dbvar() or user-defined
expand callbacks
- Replaced cli callback functions with single arg to multiple args
This change is _not_ backward compatible.
You are affected if you
(1) use system callbacks (i.e. in clixon_cli_api.h)
(2) write your own cli callbacks
If you use cli callbacks, you need to rewrite cli callbacks from eg:
load("Comment") <filename:string>,load_config_file("filename replace");
to:
load("Comment") <filename:string>,load_config_file("filename", "replace");
If you write your own, you need to change the callback signature from;
int cli_callback(clicon_handle h, cvec *vars, cg_var *arg)
to:
int cli_callback(clicon_handle h, cvec *vars, cvec *argv)
and rewrite the code to handle argv instead of arg.
These are the system functions affected:
cli_set, cli_merge, cli_del, cli_debug_backend, cli_set_mode,
cli_start_shell, cli_quit, cli_commit, cli_validate, compare_dbs,
load_config_file, save_config_file, delete_all, discard_changes, cli_notify,
show_yang, show_conf_xpath
- Added --with-cligen and --with-qdbm configure options
- Added union type check for non-cli (eg xml) input
- Empty yang type. Relaxed yang types for unions, eg two strings with different length.

View file

@ -197,7 +197,7 @@ cli_signal_flush(clicon_handle h)
* @see cli_callback_generate where arg is generated
*/
static int
cli_dbxmlv(clicon_handle h,
cli_dbxml(clicon_handle h,
cvec *cvv,
cvec *argv,
enum operation_type op)
@ -253,40 +253,54 @@ cli_dbxmlv(clicon_handle h,
}
int
cli_setv(clicon_handle h, cvec *cvv, cvec *argv)
cli_set(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = 1;
if (cli_dbxmlv(h, cvv, argv, OP_REPLACE) < 0)
if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0)
goto done;
retval = 0;
done:
return retval;
}
int cli_setv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set(h, vars, argv);
}
int
cli_mergev(clicon_handle h, cvec *cvv, cvec *argv)
cli_merge(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = -1;
if (cli_dbxmlv(h, cvv, argv, OP_MERGE) < 0)
if (cli_dbxml(h, cvv, argv, OP_MERGE) < 0)
goto done;
retval = 0;
done:
return retval;
}
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_merge(h, vars, argv);
}
int
cli_delv(clicon_handle h, cvec *cvv, cvec *argv)
cli_del(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = -1;
if (cli_dbxmlv(h, cvv, argv, OP_REMOVE) < 0)
if (cli_dbxml(h, cvv, argv, OP_REMOVE) < 0)
goto done;
retval = 0;
done:
return retval;
}
int cli_delv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_del(h, vars, argv);
}
/*! Set debug level on CLI client (not backend daemon)
* @param[in] h Clicon handle
@ -296,7 +310,7 @@ cli_delv(clicon_handle h, cvec *cvv, cvec *argv)
* _or_ if a 'level' variable is present in vars use that value instead.
*/
int
cli_debug_cliv(clicon_handle h,
cli_debug_cli(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -318,6 +332,10 @@ cli_debug_cliv(clicon_handle h,
done:
return retval;
}
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_debug_cli(h, vars, argv);
}
/*! Set debug level on backend daemon (not CLI)
* @param[in] h Clicon handle
@ -327,7 +345,7 @@ cli_debug_cliv(clicon_handle h,
* _or_ if a 'level' variable is present in vars use that value instead.
*/
int
cli_debug_backendv(clicon_handle h,
cli_debug_backend(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -348,11 +366,15 @@ cli_debug_backendv(clicon_handle h,
done:
return retval;
}
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_debug_backend(h, vars, argv);
}
/*! Set syntax mode
*/
int
cli_set_modev(clicon_handle h,
cli_set_mode(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -369,12 +391,16 @@ cli_set_modev(clicon_handle h,
done:
return retval;
}
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set_mode(h, vars, argv);
}
/*! Start bash from cli callback
* XXX Application specific??
*/
int
cli_start_shellv(clicon_handle h,
cli_start_shell(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -426,23 +452,31 @@ cli_start_shellv(clicon_handle h,
return 0;
}
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_start_shell(h, vars, argv);
}
/*! Generic quit callback
*/
int
cli_quitv(clicon_handle h,
cli_quit(clicon_handle h,
cvec *vars,
cvec *argv)
{
cli_set_exiting(h, 1);
return 0;
}
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_quit(h, vars, argv);
}
/*! Generic commit callback
* @param[in] argv No arguments expected
*/
int
cli_commitv(clicon_handle h,
cli_commit(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -456,11 +490,15 @@ cli_commitv(clicon_handle h,
done:
return retval;
}
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_commit(h, vars, argv);
}
/*! Generic validate callback
*/
int
cli_validatev(clicon_handle h,
cli_validate(clicon_handle h,
cvec *vars,
cvec *argv)
{
@ -472,7 +510,10 @@ cli_validatev(clicon_handle h,
done:
return retval;
}
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_validate(h, vars, argv);
}
/*! Compare two dbs using XML. Write to file and run diff
*/
@ -541,7 +582,7 @@ compare_xmls(cxobj *xc1,
* @param[in] arg arg: 0 as xml, 1: as text
*/
int
compare_dbsv(clicon_handle h,
compare_dbs(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -573,6 +614,10 @@ compare_dbsv(clicon_handle h,
return retval;
}
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv)
{
return compare_dbs(h, vars, argv);
}
/*! Load a configuration file to candidate database
* Utility function used by cligen spec file
@ -589,7 +634,7 @@ compare_dbsv(clicon_handle h,
* @see save_config_file
*/
int
load_config_filev(clicon_handle h,
load_config_file(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -669,6 +714,10 @@ load_config_filev(clicon_handle h,
close(fd);
return ret;
}
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return load_config_file(h, vars, argv);
}
/*! Copy database to local file
* Utility function used by cligen spec file
@ -686,7 +735,7 @@ load_config_filev(clicon_handle h,
* @see load_config_file
*/
int
save_config_filev(clicon_handle h,
save_config_file(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -736,12 +785,16 @@ save_config_filev(clicon_handle h,
fclose(f);
return retval;
}
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return save_config_file(h, vars, argv);
}
/*! Delete all elements in a database
* Utility function used by cligen spec file
*/
int
delete_allv(clicon_handle h,
delete_all(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -765,16 +818,24 @@ delete_allv(clicon_handle h,
done:
return retval;
}
int delete_allv(clicon_handle h, cvec *vars, cvec *argv)
{
return delete_all(h, vars, argv);
}
/*! Discard all changes in candidate and replace with running
*/
int
discard_changesv(clicon_handle h,
discard_changes(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return clicon_rpc_discard_changes(h);
}
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv)
{
return discard_changes(h, vars, argv);
}
/*! Copy from one database to another, eg running->startup
* @param[in] argv a string: "<db1> <db2>" Copy from db1 to db2
@ -864,7 +925,7 @@ cli_notification_cb(int s,
* XXX: format is a memory leak
*/
int
cli_notifyv(clicon_handle h,
cli_notify(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -897,6 +958,10 @@ cli_notifyv(clicon_handle h,
done:
return retval;
}
int cli_notifyv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_notify(h, vars, argv);
}
/*! Lock database
*
@ -976,13 +1041,13 @@ cli_unlock(clicon_handle h,
* tovar: Name of variable containing name of object to copy to.
* @code
* cli spec:
* copy snd <n1:string> to <n2:string>, copy_object("candidate", "/sender[%s=%s]", "from", "n1", "n2");
* copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s=%s]", "from", "n1", "n2");
* cli command:
* copy snd from to to
* @endcode
*/
int
cli_copy_object(clicon_handle h,
cli_copy_config(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -1080,176 +1145,6 @@ cli_copy_object(clicon_handle h,
return retval;
}
/* Here are backward compatible cligen callback functions used when
* the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set.
*/
cb_single_arg(cli_set)
cb_single_arg(cli_merge)
cb_single_arg(cli_del)
cb_single_arg(cli_debug_cli)
cb_single_arg(cli_debug_backend)
cb_single_arg(cli_set_mode)
cb_single_arg(cli_start_shell)
cb_single_arg(cli_quit)
//cb_single_arg(cli_commit)
int cli_commit(clicon_handle h, cvec *cvv, cg_var *arg)
{
int retval=-1;
cvec *argv = NULL;
if (arg){
if (cv_type_get(arg) > CGV_EMPTY){
cligen_output(stderr, "%s: Illegal cvtype. This is most probably a single-argument cligen callback being used in a multi-argument setting. This can happen if option CLICON_CLIGEN_CALLBACK_SINGLE_ARG is 0 but you call a single argument callback (eg %s) from a .cli file. Please change to a multi-argument callback\n", __FUNCTION__, __FUNCTION__);
goto done;
}
if ((argv = cvec_from_var(arg)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_from_var");
goto done;
}
}
retval = cli_commitv(h, cvv, argv);
done:
if (argv) cvec_free(argv);
return retval;
}
cb_single_arg(cli_validate)
cb_single_arg(compare_dbs)
cb_single_arg(delete_all)
cb_single_arg(discard_changes)
/* Follows some functions not covered by translation macro */
int
load_config_file(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
int retval=-1;
cvec *argv;
cg_var *cv;
char *str;
char **vec = NULL;
int nvec;
/* Split string into two parts and build a cvec of it and supply that to
the multi-arg callback */
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
goto done;
if (nvec != 2){
clicon_err(OE_PLUGIN, 0, "Arg syntax is <varname> <replace|merge>");
goto done;
}
if ((argv = cvec_new(nvec)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_from_var");
goto done;
}
cv = cvec_i(argv, 0);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[0]);
cv = cvec_i(argv, 1);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[1]);
retval = load_config_filev(h, cvv, argv);
done:
if (vec)
free(vec);
return retval;
}
int
save_config_file(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
int retval=-1;
cvec *argv;
cg_var *cv;
char *str;
char **vec = NULL;
int nvec;
/* Split string into two parts and build a cvec of it and supply that to
the multi-arg callback */
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
goto done;
if (nvec != 2){
clicon_err(OE_PLUGIN, 0, "Arg syntax is <dbname> <varname>");
goto done;
}
if ((argv = cvec_new(nvec)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_from_var");
goto done;
}
cv = cvec_i(argv, 0);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[0]);
cv = cvec_i(argv, 1);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[1]);
retval = save_config_filev(h, cvv, argv);
done:
if (vec)
free(vec);
return retval;
}
int
cli_notify(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
int retval=-1;
cvec *argv;
cg_var *cv;
char *str;
char **vec = NULL;
int nvec;
/* Split string into two parts and build a cvec of it and supply that to
the multi-arg callback */
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
goto done;
if (nvec != 2 && nvec != 3){
clicon_err(OE_PLUGIN, 0, "Arg syntax is <logstream> <status> [<format>]");
goto done;
}
if ((argv = cvec_new(nvec)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_from_var");
goto done;
}
cv = cvec_i(argv, 0);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[0]);
cv = cvec_i(argv, 1);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[1]);
if (nvec > 2){
cv = cvec_i(argv, 2);
cv_type_set(cv, CGV_STRING);
cv_string_set(cv, vec[2]);
}
retval = cli_notifyv(h, cvv, argv);
done:
if (vec)
free(vec);
return retval;
}
/*! set debug level on stderr (not syslog).
* The level is either what is specified in arg as int argument.

View file

@ -65,10 +65,9 @@
/* This is the default callback function. But this is typically overwritten */
#define GENERATE_CALLBACK "cli_set"
#define GENERATE_CALLBACKV "cli_setv"
/* variable expand function */
#define GENERATE_EXPAND_XMLDB "expandv_dbvar"
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
/*=====================================================================
* YANG generate CLI
@ -154,10 +153,7 @@ cli_callback_generate(clicon_handle h,
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
goto done;
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1)
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
else
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACKV, xkfmt);
retval = 0;
done:
if (xkfmt)

View file

@ -372,24 +372,11 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
}
/* Resolve callback names to function pointers. */
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1){
if (cligen_callback_str2fn(pt, (cg_str2fn_t*)clixon_str2fn, handle) < 0){
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
filename, plgnam, plgnam);
goto done;
}
}
else
if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
filename, plgnam, plgnam);
goto done;
}
if (clicon_option_int(h, "CLICON_CLIGEN_EXPAND_SINGLE_ARG")==1){
if (cligen_expand_str2fn(pt, (expand_str2fn_t*)clixon_str2fn, handle) < 0)
goto done;
}
else
if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0)
goto done;

View file

@ -74,9 +74,6 @@
#include "clixon_cli_api.h"
#include "cli_common.h" /* internal functions */
static int xml2csv(FILE *f, cxobj *x, cvec *cvv);
//static int xml2csv_raw(FILE *f, cxobj *x);
/*! Completion callback intended for automatically generated data model
*
* Returns an expand-type list of commands as used by cligen 'expand'
@ -94,7 +91,7 @@ static int xml2csv(FILE *f, cxobj *x, cvec *cvv);
* XXX: helptexts?
*/
int
expandv_dbvar(void *h,
expand_dbvar(void *h,
char *name,
cvec *cvv,
cvec *argv,
@ -197,13 +194,24 @@ expandv_dbvar(void *h,
free(xkpath);
return retval;
}
int
expandv_dbvar(void *h,
char *name,
cvec *cvv,
cvec *argv,
cvec *commands,
cvec *helptexts)
{
return expand_dbvar(h, name, cvv, argv, commands, helptexts);
}
/*! List files in a directory
*/
int
expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail)
expand_dir(char *dir,
int *nr,
char ***commands,
mode_t flags,
int detail)
{
DIR *dirp;
struct dirent *dp;
@ -313,11 +321,9 @@ expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail)
return retval;
}
/*! CLI callback show yang spec. If arg given matches yang argument string */
int
show_yangv(clicon_handle h,
show_yang(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -335,362 +341,9 @@ show_yangv(clicon_handle h,
yang_print(stdout, yn, 0);
return 0;
}
#ifdef notused
/*! XML to CSV raw variant
* @see xml2csv
*/
static int
xml2csv_raw(FILE *f, cxobj *x)
int show_yangv(clicon_handle h, cvec *vars, cvec *argv)
{
cxobj *xc;
cxobj *xb;
int retval = -1;
int i = 0;
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
if (xml_child_nr(xc)){
xb = xml_child_i(xc, 0);
if (xml_type(xb) == CX_BODY){
if (i++)
fprintf(f, ";");
fprintf(f, "%s", xml_value(xb));
}
}
}
fprintf(f, "\n");
retval = 0;
return retval;
}
#endif
/*! Translate XML -> CSV commands
* Can only be made in a 'flat tree', ie on the form:
* <X><A>B</A></X> -->
* Type, A
* X, B
* @param[in] f Output file
* @param[in] x XML tree
* @param[in] cvv A vector of field names present in XML
* This means that only fields in x that are listed in cvv will be printed.
*/
static int
xml2csv(FILE *f, cxobj *x, cvec *cvv)
{
cxobj *xe, *xb;
int retval = -1;
cg_var *vs;
fprintf(f, "%s", xml_name(x));
xe = NULL;
vs = NULL;
while ((vs = cvec_each(cvv, vs))) {
if ((xe = xml_find(x, cv_name_get(vs))) == NULL){
fprintf(f, ";");
continue;
}
if (xml_child_nr(xe)){
xb = xml_child_i(xe, 0);
fprintf(f, ";%s", xml_value(xb));
}
}
fprintf(f, "\n");
retval = 0;
return retval;
}
/*! Generic function for showing configurations.
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] argv A string: <dbname> <xpath> [<varname>]
* @param[out] xt Configuration as xml tree.
* Format of argv:
* <dbname> "running", "candidate", "startup"
* <xpath> xpath expression
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
* @code
* show config id <n:string>, show_conf_as("running interfaces/interface[name=%s] n");
* @endcode
*/
static int
show_confv_as(clicon_handle h,
cvec *cvv,
cvec *argv,
cxobj **xt) /* top xml */
{
int retval = -1;
char *db;
char *xpath;
char *attr = NULL;
cbuf *cbx = NULL;
int i;
int j;
cg_var *cvattr;
char *val = NULL;
if (cvec_len(argv) != 2 && cvec_len(argv) != 3){
if (cvec_len(argv)==1)
clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \"<dbname>,<xpath>[,<attr>]\"", cv_string_get(cvec_i(argv,0)));
else
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<xpath>[,<attr>]", cvec_len(argv));
goto done;
}
/* Dont get attr here, take it from arg instead */
db = cv_string_get(cvec_i(argv, 0));
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0) {
clicon_err(OE_PLUGIN, 0, "No such db name: %s", db);
goto done;
}
xpath = cv_string_get(cvec_i(argv, 1));
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (cvec_len(argv) == 3){
attr = cv_string_get(cvec_i(argv, 2));
j = 0;
for (i=0; i<strlen(xpath); i++)
if (xpath[i] == '%')
j++;
if (j != 1){
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'");
goto done;
}
if ((cvattr = cvec_find_var(cvv, attr)) == NULL){
clicon_err(OE_PLUGIN, 0, "attr '%s' not found in cligen var list", attr);
goto done;
}
if ((val = cv2str_dup(cvattr)) == NULL){
clicon_err(OE_PLUGIN, errno, "cv2str_dup");
goto done;
}
cprintf(cbx, xpath, val);
}
else
cprintf(cbx, "%s", xpath);
if (clicon_rpc_get_config(h, db, cbuf_get(cbx), xt) < 0)
goto done;
retval = 0;
done:
if (val)
free(val);
if (cbx)
cbuf_free(cbx);
return retval;
}
/*! Show a configuration database on stdout using XML format
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @param[in] netconf If set print as netconf edit-config, otherwise just xml
* @see show_conf_as the main function
*/
static int
show_confv_as_xml1(clicon_handle h,
cvec *cvv,
cvec *argv,
int netconf)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
if (show_confv_as(h, cvv, argv, &xt) < 0)
goto done;
if (netconf) /* netconf prefix */
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
clicon_xml2file(stdout, xc, netconf?2:0, 1);
if (netconf) /* netconf postfix */
fprintf(stdout, "</config></edit-config></rpc>]]>]]>\n");
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Show configuration as prettyprinted xml
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_confv_as_xml(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return show_confv_as_xml1(h, cvv, argv, 0);
}
/*! Show configuration as prettyprinted xml with netconf hdr/tail
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_confv_as_netconf(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return show_confv_as_xml1(h, cvv, argv, 1);
}
/*! Show configuration as JSON
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_confv_as_json(clicon_handle h,
cvec *cvv,
cvec *argv)
{
cxobj *xt = NULL;
int retval = -1;
if (show_confv_as(h, cvv, argv, &xt) < 0)
goto done;
xml2json(stdout, xt, 1);
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Show configuration as text
* Utility function used by cligen spec file
*/
static int
show_confv_as_text1(clicon_handle h,
cvec *cvv,
cvec *argv)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
if (show_confv_as(h, cvv, argv, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
xml2txt(stdout, xc, 0); /* tree-formed text */
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/* Show configuration as commands, ie not tree format but as one-line commands
*/
static int
show_confv_as_command(clicon_handle h,
cvec *cvv,
cvec *argv,
char *prepend)
{
cxobj *xt = NULL;
cxobj *xc;
enum genmodel_type gt;
int retval = -1;
if (show_confv_as(h, cvv, argv, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
xml2cli(stdout, xc, prepend, gt); /* cli syntax */
}
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
int
show_confv_as_text(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return show_confv_as_text1(h, cvv, argv);
}
int
show_confv_as_cli(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return show_confv_as_command(h, cvv, argv, NULL); /* XXX: how to set prepend? */
}
static int
show_confv_as_csv1(clicon_handle h,
cvec *cvv0,
cvec *argv)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
cvec *cvv=NULL;
char *str;
if (show_confv_as(h, cvv0, argv, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL)
goto done;
#ifdef NOTYET /* yang-spec? */
if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){
cg_var *vs;
fprintf(stdout, "Type");
cvv = db_spec2cvec(ds);
vs = NULL;
while ((vs = cvec_each(cvv, vs)))
fprintf(stdout, ";%s", cv_name_get(vs));
fprintf(stdout, "\n");
} /* Now values just need to follow,... */
#endif /* yang-spec? */
if (cvv== NULL)
goto done;
xml2csv(stdout, xc, cvv); /* csv syntax */
}
retval = 0;
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
int
show_confv_as_csv(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return show_confv_as_csv1(h, cvv, argv);
return show_yang(h, vars, argv);
}
/*! Generic show configuration CLIGEN callback
@ -704,11 +357,11 @@ show_confv_as_csv(clicon_handle h,
* <xpath> xpath expression, that may contain one %, eg "/sender[name=%s]"
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
* @code
* show config id <n:string>, show_conf_as("running","xml","iface[name=%s]","n");
* show config id <n:string>, cli_show_config("running","xml","iface[name=%s]","n");
* @endcode
*/
int
show_configuration(clicon_handle h,
cli_show_config(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -824,7 +477,7 @@ done:
* @note Hardcoded that a variable in cvv is named "xpath"
*/
int
show_confv_xpath(clicon_handle h,
show_conf_xpath(clicon_handle h,
cvec *cvv,
cvec *argv)
{
@ -866,417 +519,7 @@ done:
xml_free(xt);
return retval;
}
/*=================================================================
* Here are backward compatible cligen callback functions used when
* the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set.
*/
cb_single_arg(show_yang)
/*! This is obsolete version of expandv_dbvar
* If CLICON_CLIGEN_EXPAND_SINGLE_ARG is set
*/
int
expand_dbvar(void *h,
char *name,
cvec *cvv,
cg_var *arg,
int *nr,
char ***commands,
char ***helptexts)
int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv)
{
int nvec;
char **vec = NULL;
int retval = -1;
char *xkfmt;
char *str;
char *dbstr;
cxobj *xt = NULL;
char *xkpath = NULL;
cxobj **xvec = NULL;
size_t xlen = 0;
cxobj *x;
char *bodystr;
int i;
int j;
int k;
int i0;
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
return show_conf_xpath(h, vars, argv);
}
/* In the example, str = "candidate /x/m1/%s/b" */
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
goto done;
dbstr = vec[0];
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 0 &&
strcmp(dbstr, "startup") != 0){
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done;
}
xkfmt = vec[1];
/* xkfmt = /interface=%s/address=%s
--> /interface=eth0/address=1.2.3.4
*/
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
goto done;
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
goto done;
if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0)
goto done;
/* One round to detect duplicates
* XXX The code below would benefit from some cleanup
*/
j = 0;
for (i = 0; i < xlen; i++) {
char *str;
x = xvec[i];
if (xml_type(x) == CX_BODY)
bodystr = xml_value(x);
else
bodystr = xml_body(x);
if (bodystr == NULL){
clicon_err(OE_CFG, 0, "No xml body");
goto done;
}
/* detect duplicates */
for (k=0; k<j; k++){
if (xml_type(xvec[k]) == CX_BODY)
str = xml_value(xvec[k]);
else
str = xml_body(xvec[k]);
if (strcmp(str, bodystr)==0)
break;
}
if (k==j) /* not duplicate */
xvec[j++] = x;
}
xlen = j;
i0 = *nr;
*nr += xlen;
if ((*commands = realloc(*commands, sizeof(char *) * (*nr))) == NULL) {
clicon_err(OE_UNIX, errno, "realloc: %s", strerror (errno));
goto done;
}
for (i = 0; i < xlen; i++) {
x = xvec[i];
if (xml_type(x) == CX_BODY)
bodystr = xml_value(x);
else
bodystr = xml_body(x);
if (bodystr == NULL){
clicon_err(OE_CFG, 0, "No xml body");
goto done;
}
(*commands)[i0+i] = strdup(bodystr);
}
retval = 0;
done:
if (vec)
free(vec);
if (xvec)
free(xvec);
if (xt)
xml_free(xt);
if (xkpath)
free(xkpath);
return retval;
}
/*! Generic function for showing configurations.
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @param[out] xt Configuration as xml tree.
* Format of arg:
* <dbname> "running", "candidate", "startup"
* <xpath> xpath expression
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
* @code
* show config id <n:string>, show_conf_as("running interfaces/interface[name=%s] n");
* @endcode
*/
static int
show_conf_as(clicon_handle h,
cvec *cvv,
cg_var *arg,
cxobj **xt) /* top xml */
{
int retval = -1;
char *db;
char **vec = NULL;
int nvec;
char *str;
char *xpath;
char *attr = NULL;
cbuf *cbx = NULL;
int i;
int j;
cg_var *cvattr;
char *val = NULL;
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
goto done;
if (nvec != 2 && nvec != 3){
clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected <dbname> <xpath> [<attr>] got %d arg", str, nvec);
goto done;
}
/* Dont get attr here, take it from arg instead */
db = vec[0];
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0) {
clicon_err(OE_PLUGIN, 0, "No such db name: %s", db);
goto done;
}
xpath = vec[1];
if ((cbx = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (nvec == 3){
attr = vec[2];
j = 0;
for (i=0; i<strlen(xpath); i++)
if (xpath[i] == '%')
j++;
if (j != 1){
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'");
goto done;
}
if ((cvattr = cvec_find_var(cvv, attr)) == NULL){
clicon_err(OE_PLUGIN, 0, "attr '%s' not found in cligen var list", attr);
goto done;
}
if ((val = cv2str_dup(cvattr)) == NULL){
clicon_err(OE_PLUGIN, errno, "cv2str_dup");
goto done;
}
cprintf(cbx, xpath, val);
}
else
cprintf(cbx, "%s", xpath);
if (clicon_rpc_get_config(h, db, cbuf_get(cbx), xt) < 0)
goto done;
retval = 0;
done:
if (val)
free(val);
if (cbx)
cbuf_free(cbx);
if (vec)
free(vec);
return retval;
}
/*! Show a configuration database on stdout using XML format
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @param[in] netconf If set print as netconf edit-config, otherwise just xml
* @see show_conf_as the main function
*/
static int
show_conf_as_xml1(clicon_handle h,
cvec *cvv,
cg_var *arg,
int netconf)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
if (show_conf_as(h, cvv, arg, &xt) < 0)
goto done;
if (netconf) /* netconf prefix */
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
clicon_xml2file(stdout, xc, netconf?2:0, 1);
if (netconf) /* netconf postfix */
fprintf(stdout, "</config></edit-config></rpc>]]>]]>\n");
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Show configuration as prettyprinted xml
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_conf_as_xml(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
return show_conf_as_xml1(h, cvv, arg, 0);
}
/*! Show configuration as prettyprinted xml with netconf hdr/tail
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_conf_as_netconf(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
return show_conf_as_xml1(h, cvv, arg, 1);
}
/*! Show configuration as JSON
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
* @param[in] arg A string: <dbname> <xpath> [<varname>]
* @see show_conf_as the main function
*/
int
show_conf_as_json(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
cxobj *xt = NULL;
int retval = -1;
if (show_conf_as(h, cvv, arg, &xt) < 0)
goto done;
xml2json(stdout, xt, 1);
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Show configuration as text
* Utility function used by cligen spec file
*/
static int
show_conf_as_text1(clicon_handle h, cvec *cvv, cg_var *arg)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
if (show_conf_as(h, cvv, arg, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
xml2txt(stdout, xc, 0); /* tree-formed text */
retval = 0;
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
/* Show configuration as commands, ie not tree format but as one-line commands
*/
static int
show_conf_as_command(clicon_handle h, cvec *cvv, cg_var *arg, char *prepend)
{
cxobj *xt = NULL;
cxobj *xc;
enum genmodel_type gt;
int retval = -1;
if ((xt = xml_new("tmp", NULL)) == NULL)
goto done;
if (show_conf_as(h, cvv, arg, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
xml2cli(stdout, xc, prepend, gt); /* cli syntax */
}
retval = 0;
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
int
show_conf_as_text(clicon_handle h, cvec *cvv, cg_var *arg)
{
return show_conf_as_text1(h, cvv, arg);
}
int
show_conf_as_cli(clicon_handle h, cvec *cvv, cg_var *arg)
{
return show_conf_as_command(h, cvv, arg, NULL); /* XXX: how to set prepend? */
}
static int
show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg)
{
cxobj *xt = NULL;
cxobj *xc;
int retval = -1;
cvec *cvv=NULL;
char *str;
if (show_conf_as(h, cvv0, arg, &xt) < 0)
goto done;
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL)
goto done;
#ifdef NOTYET /* yang-spec? */
if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){
cg_var *vs;
fprintf(stdout, "Type");
cvv = db_spec2cvec(ds);
vs = NULL;
while ((vs = cvec_each(cvv, vs)))
fprintf(stdout, ";%s", cv_name_get(vs));
fprintf(stdout, "\n");
} /* Now values just need to follow,... */
#endif /* yang-spec? */
if (cvv== NULL)
goto done;
xml2csv(stdout, xc, cvv); /* csv syntax */
}
retval = 0;
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
int
show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg)
{
return show_conf_as_csv1(h, cvv, arg);
}

View file

@ -74,67 +74,76 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo
#define cli_output cligen_output
/* cli_common.c: CLIgen new vector callbacks */
int cli_set(clicon_handle h, cvec *vars, cvec *argv);
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
int cli_merge(clicon_handle h, cvec *vars, cvec *argv);
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
int cli_del(clicon_handle h, cvec *vars, cvec *argv);
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
int cli_quit(clicon_handle h, cvec *vars, cvec *argv);
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_commit(clicon_handle h, cvec *vars, cvec *argv);
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_validate(clicon_handle h, cvec *vars, cvec *argv);
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbs(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
int load_config_file(clicon_handle h, cvec *vars, cvec *argv);
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int save_config_file(clicon_handle h, cvec *vars, cvec *argv);
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int delete_all(clicon_handle h, cvec *vars, cvec *argv);
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
int discard_changes(clicon_handle h, cvec *vars, cvec *argv);
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
int cli_notify(clicon_handle h, cvec *cvv, cvec *argv);
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
int db_copy(clicon_handle h, cvec *cvv, cvec *argv);
int cli_lock(clicon_handle h, cvec *cvv, cvec *argv);
int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv);
int cli_copy_object(clicon_handle h, cvec *cvv, cvec *argv);
/* cli_common.c: CLIgen old single arg callbacks */
int cli_set(clicon_handle h, cvec *vars, cg_var *arg);
int cli_merge(clicon_handle h, cvec *vars, cg_var *arg);
int cli_del(clicon_handle h, cvec *vars, cg_var *arg);
int cli_debug_cli(clicon_handle h, cvec *vars, cg_var *arg);
int cli_debug_backend(clicon_handle h, cvec *vars, cg_var *argv);
int cli_set_mode(clicon_handle h, cvec *vars, cg_var *argv);
int cli_start_shell(clicon_handle h, cvec *vars, cg_var *argv);
int cli_quit(clicon_handle h, cvec *vars, cg_var *arg);
int cli_commit(clicon_handle h, cvec *vars, cg_var *arg);
int cli_validate(clicon_handle h, cvec *vars, cg_var *arg);
int compare_dbs(clicon_handle h, cvec *vars, cg_var *arg);
int load_config_file(clicon_handle h, cvec *vars, cg_var *arg);
int save_config_file(clicon_handle h, cvec *vars, cg_var *arg);
int delete_all(clicon_handle h, cvec *vars, cg_var *arg);
int discard_changes(clicon_handle h, cvec *vars, cg_var *arg);
int cli_notify(clicon_handle h, cvec *cvv, cg_var *arg);
int cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv);
/* In cli_show.c */
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
cvec *commands, cvec *helptexts);
int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
cvec *commands, cvec *helptexts);
/* cli_show.c: CLIgen new vector arg callbacks */
int show_confv_as_xml(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_as_netconf(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_as_json(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_as_text(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_as_cli(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_as_csv(clicon_handle h, cvec *vars, cvec *argv);
int show_yang(clicon_handle h, cvec *vars, cvec *argv);
int show_yangv(clicon_handle h, cvec *vars, cvec *argv);
int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv);
int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv);
int show_configuration(clicon_handle h, cvec *cvv, cvec *argv);
/* cli_show.c: CLIgen old single arg callbacks */
int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg);
int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg);
int show_conf_as_json(clicon_handle h, cvec *vars, cg_var *arg);
int show_conf_as_text(clicon_handle h, cvec *vars, cg_var *arg);
int show_conf_as_cli(clicon_handle h, cvec *vars, cg_var *arg);
int show_conf_as_csv(clicon_handle h, cvec *vars, cg_var *arg);
int show_yang(clicon_handle h, cvec *vars, cg_var *arg);
int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv);
#endif /* _CLIXON_CLI_API_H_ */

View file

@ -112,6 +112,8 @@ api_data(clicon_handle h,
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data);
else if (strcmp(request_method, "PUT")==0)
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data);
else if (strcmp(request_method, "PATCH")==0)
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data);
else if (strcmp(request_method, "DELETE")==0)
retval = api_data_delete(h, r, api_path, pi);
else
@ -180,7 +182,7 @@ request_process(clicon_handle h,
else
retval = notfound(r);
done:
clicon_debug(1, "%s retval:%d K", __FUNCTION__, retval);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pvec)
free(pvec);
if (dvec)

View file

@ -347,14 +347,14 @@ api_data_edit(clicon_handle h,
cbuf *cbx = NULL;
cxobj *x;
clicon_debug(1, "%s api_path:%s json:%s",
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
api_path, data);
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Parse input data as json into xml */
if (json_parse_str(data, &xdata) < 0){
clicon_debug(1, "%s json fail", __FUNCTION__);
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
goto done;
}
if ((cbx = cbuf_new()) == NULL)

View file

@ -128,15 +128,3 @@ CLICON_XMLDB_DIR localstatedir/APPNAME
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
# Set if you want to use old obsolete cligen expand variable syntax
# Migration: Set to 0 and change all user-defined cli completion callbacks
# E.g. expand_dbvar("db fmt") ->expandv_dbvar("db","fmt") in all your cli spec files
CLICON_CLIGEN_EXPAND_SINGLE_ARG 0
# Set if you want to use old obsolete cligen callback variable syntax
# Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files
# E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1

View file

@ -4,48 +4,53 @@ CLICON_PROMPT="%U@%H> ";
CLICON_PLUGIN="routing_cli";
# Note, when switching to PT, change datamodel to only @datamodel
set @datamodel:ietf-ip, cli_mergev();
set @datamodel:ietf-ip, cli_merge();
#delete("Delete a configuration item") @datamodel:ietf-ipv4-unicast-routing, cli_del();
delete("Delete a configuration item") @datamodel:ietf-ip, cli_delv();
delete("Delete a configuration item") @datamodel:ietf-ip, cli_del();
validate("Validate changes"), cli_validatev();
commit("Commit the changes"), cli_commitv();
quit("Quit Hello"), cli_quitv();
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_allv("candidate");
validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit();
quit("Quit Hello"), cli_quit();
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("candidate");
startup("Store running as startup config"), db_copy("running", "startup");
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cliv((int32)0);
debug("Debugging parts of the system"), cli_debug_cliv((int32)1);{
level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug_backendv();
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cli((int32)0);
debug("Debugging parts of the system"), cli_debug_cli((int32)1);{
level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug_backend();
}
discard("Discard edits (rollback 0)"), discard_changesv();
compare("Compare running and candidate"), compare_dbsv((int32)1);
copy("Copy and create a new object") {
interface("Copy interface"){
<name:string expand_dbvar("candidate","/interfaces/interface/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s=%s]","name","name","toname");
}
}
discard("Discard edits (rollback 0)"), discard_changes();
compare("Compare running and candidate"), compare_dbs((int32)1);
compare("Compare running and candidate"), compare_dbs((int32)1);
show("Show a particular state of the system"){
xpath("Show configuration") <xpath:string>("XPATH expression"), show_confv_xpath("candidate");
compare("Compare candidate and running databases"), compare_dbsv((int32)0);{
xml("Show comparison in xml"), compare_dbsv((int32)0);
text("Show comparison in text"), compare_dbsv((int32)1);
xpath("Show configuration") <xpath:string>("XPATH expression"), show_conf_xpath("candidate");
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
xml("Show comparison in xml"), compare_dbs((int32)0);
text("Show comparison in text"), compare_dbs((int32)1);
}
configuration("Show configuration"), show_confv_as_text("candidate", "/");{
xml("Show configuration as XML"), show_confv_as_xml("candidate", "/");
netconf("Show configuration as netconf edit-config operation"), show_confv_as_netconf("candidate", "/");
text("Show configuration as text"), show_confv_as_text("candidate","/");
cli("Show configuration as cli commands"), show_confv_as_cli("candidate", "/");
json("Show configuration as cli commands"), show_confv_as_json("candidate", "/");
configuration("Show configuration"), cli_show_config("candidate", "text", "/");{
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");
text("Show configuration as text"), cli_show_config("candidate","text","/");
cli("Show configuration as cli commands"), cli_show_config("candidate", "cli", "/");
json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/");
}
}
save("Save candidate configuration to XML file") <filename:string>("Filename (local filename)"), save_config_filev("candidate","filename");
load("Load configuration from XML file") <filename:string>("Filename (local filename)"),load_config_filev("filename", "replace");{
replace("Replace candidate with file contents"), load_config_filev("filename", "replace");
merge("Merge file with existent candidate"), load_config_filev("filename", "merge");
save("Save candidate configuration to XML file") <filename:string>("Filename (local filename)"), save_config_file("candidate","filename");
load("Load configuration from XML file") <filename:string>("Filename (local filename)"),load_config_file("filename", "replace");{
replace("Replace candidate with file contents"), load_config_file("filename", "replace");
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
}
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
downcall("This is a downcall") <str:rest>, downcall();
notify("Get notifications from backend"), cli_notifyv("ROUTING", "1", "text");
no("Negate") notify("Get notifications from backend"), cli_notifyv("ROUTING", "0", "xml");
notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text");
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
lock,cli_lock("candidate");
unlock,cli_unlock("candidate");

View file

@ -220,6 +220,8 @@ clicon_rpc_generate_error(cxobj *xerr)
clicon_err_fn("Clixon", 0, OE_XML, 0, "%s", cbuf_get(cb));
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}

View file

@ -1479,6 +1479,8 @@ xml_operation(char *opstr,
*op = OP_DELETE;
else if (strcmp("remove", opstr) == 0)
*op = OP_REMOVE;
else if (strcmp("none", opstr) == 0)
*op = OP_NONE;
else{
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
return -1;

View file

@ -47,6 +47,27 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/><
new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config replace"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get replaced config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf edit config create"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config create 2nd"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
new "netconf edit config delete"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get delete config"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock/unlock"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"

View file

@ -6,7 +6,7 @@
# kill old backend (if any)
new "kill old backend"
#sudo clixon_backend -zf $clixon_cf
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err
fi
@ -40,17 +40,22 @@ $'
new "restconf head"
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
new "restconf patch config"
new "restconf POST config"
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
# XXX POST/PUT/PATCH
new "restconf delete config"
new "restconf DELETE config"
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
new "restconf get config"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
$'
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 "Kill restconf daemon"
#sudo pkill -u www-data clixon_restconf

65
test/test4.sh Executable file
View file

@ -0,0 +1,65 @@
#!/bin/bash
# Test2: backend and netconf basic functionality
# include err() and new() functions
. ./lib.sh
# For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf
cat <<EOF > /tmp/test.yang
module ietf-ip{
container x {
list y {
key "a b";
leaf a {
type string;
}
leaf b {
type string;
}
leaf c {
type string;
}
}
leaf d {
type empty;
}
}
}
EOF
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf -y /tmp/test
if [ $? -ne 0 ]; then
err
fi
new "start backend"
# start new backend
sudo clixon_backend -If $clixon_cf -y /tmp/test
if [ $? -ne 0 ]; then
err
fi
new "netconf edit config"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get config xpath"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y></x></config></data></rpc-reply>]]>]]>$"
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err "kill backend"
fi