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.
Use restconf format for internal xmldb keys. Eg /a/b=3,4
Changed example to use multiple cli callbacks
This commit is contained in:
Olof hagsand 2017-01-31 22:36:14 +01:00
parent c9f1ece53e
commit 7f0b9909b3
30 changed files with 1444 additions and 1054 deletions

View file

@ -29,13 +29,17 @@
# #
# ***** END LICENSE BLOCK ***** # ***** END LICENSE BLOCK *****
- Use restconf format for internal xmldb keys. Eg /a/b=3,4
- List keys with special characters are RFC 3986 encoded. - 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 - Added cli multiple callback and expand support. Use options
CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG
to control these. to control these.
The multiple support for expand callbacks is enabled but not for callbacks The multiple support for expand callbacks is enabled but not for callbacks
since this causes problems for legacy applications. 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.
- Added --with-cligen and --with-qdbm configure options - Added --with-cligen and --with-qdbm configure options
- Added union type check for non-cli (eg xml) input - Added union type check for non-cli (eg xml) input
- Empty yang type. Relaxed yang types for unions, eg two strings with different length. - Empty yang type. Relaxed yang types for unions, eg two strings with different length.

View file

@ -373,7 +373,7 @@ config_snapshot(clicon_handle h,
clicon_err(OE_CFG, errno, "Creating file %s", filename0); clicon_err(OE_CFG, errno, "Creating file %s", filename0);
return -1; return -1;
} }
if (xmldb_get(h, db, "/", 0, &xn, NULL, NULL) < 0) if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0)
goto done; goto done;
if (xml_print(f, xn) < 0) if (xml_print(f, xn) < 0)
goto done; goto done;
@ -439,7 +439,7 @@ from_client_save(clicon_handle h,
clicon_err(OE_CFG, errno, "Creating file %s", filename); clicon_err(OE_CFG, errno, "Creating file %s", filename);
return -1; return -1;
} }
if (xmldb_get(h, db, "/", 0, &xn, NULL, NULL) < 0) if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0)
goto done; goto done;
if (xml_print(f, xn) < 0) if (xml_print(f, xn) < 0)
goto done; goto done;

View file

@ -143,9 +143,9 @@ validate_common(clicon_handle h,
goto done; goto done;
} }
/* 2. Parse xml trees */ /* 2. Parse xml trees */
if (xmldb_get(h, "running", "/", 0, &td->td_src, NULL, NULL) < 0) if (xmldb_get(h, "running", "/", &td->td_src, NULL, NULL) < 0)
goto done; goto done;
if (xmldb_get(h, candidate, "/", 0, &td->td_target, NULL, NULL) < 0) if (xmldb_get(h, candidate, "/", &td->td_target, NULL, NULL) < 0)
goto done; goto done;
/* 3. Compute differences */ /* 3. Compute differences */

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,24 @@
#ifndef _CLI_COMMON_H_ #ifndef _CLI_COMMON_H_
#define _CLI_COMMON_H_ #define _CLI_COMMON_H_
/*! macro to create a single-argument callback from multiple */
#define cb_single_arg(fn) \
int fn(clicon_handle h, cvec *cvv, cg_var *arg) \
{ \
int retval=-1; \
cvec *argv = NULL; \
\
if (arg && (argv = cvec_from_var(arg)) == NULL){ \
clicon_err(OE_UNIX, errno, "cvec_from_var"); \
goto done; \
} \
retval = fn##v(h, cvv, argv); \
done: \
if (argv) cvec_free(argv); \
return retval; \
}
void cli_signal_block(clicon_handle h); void cli_signal_block(clicon_handle h);
void cli_signal_unblock(clicon_handle h); void cli_signal_unblock(clicon_handle h);

View file

@ -33,11 +33,11 @@
* *
* Translation between database specs * Translation between database specs
* dbspec_key yang_spec CLIgen parse_tree * yang_spec CLIgen parse_tree
* +-------------+ key2yang +-------------+ yang2cli +-------------+ * +-------------+ yang2cli +-------------+
* | keyspec | -------------> | | ------------> | cli | * | | ------------> | cli |
* | A[].B !$a | yang2key | list{key A;}| | syntax | * | list{key A;}| | syntax |
* +-------------+ <------------ +-------------+ +-------------+ * +-------------+ +-------------+
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */ #include "clixon_config.h" /* generated by config & autoconf */

View file

@ -660,10 +660,6 @@ int
clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr) clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr)
{ {
cli_output_reset(); cli_output_reset();
#ifdef notyet
if (isrecording())
record_command(cmd);
#endif
if (!cli_exiting(h)) { if (!cli_exiting(h)) {
clicon_err_reset(); clicon_err_reset();
if (cligen_eval(cli_cligen(h), match_obj, vr) < 0) { if (cligen_eval(cli_cligen(h), match_obj, vr) < 0) {

View file

@ -72,6 +72,7 @@
/* Exported functions in this file are in clixon_cli_api.h */ /* Exported functions in this file are in clixon_cli_api.h */
#include "clixon_cli_api.h" #include "clixon_cli_api.h"
#include "cli_common.h" /* internal functions */
static int xml2csv(FILE *f, cxobj *x, cvec *cvv); static int xml2csv(FILE *f, cxobj *x, cvec *cvv);
//static int xml2csv_raw(FILE *f, cxobj *x); //static int xml2csv_raw(FILE *f, cxobj *x);
@ -140,7 +141,7 @@ expandv_dbvar(void *h,
*/ */
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0) if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
goto done; goto done;
if (xmldb_get(h, dbstr, xkpath, 1, &xt, &xvec, &xlen) < 0) if (xmldb_get(h, dbstr, xkpath, &xt, &xvec, &xlen) < 0)
goto done; goto done;
/* One round to detect duplicates /* One round to detect duplicates
* XXX The code below would benefit from some cleanup * XXX The code below would benefit from some cleanup
@ -196,116 +197,6 @@ expandv_dbvar(void *h,
} }
/*! 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 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;
}
/* In the example, str = "candidate /x/m1/%s/b" */
if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
goto done;
}
dbstr = vec[0];
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 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/.*$
--> /interface/[name=eth0]/address
*/
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
goto done;
if (xmldb_get(h, dbstr, xkpath, 1, &xt, &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:
unchunk_group(__FUNCTION__);
if (xvec)
free(xvec);
if (xt)
xml_free(xt);
if (xkpath)
free(xkpath);
return retval;
}
/*! List files in a directory /*! List files in a directory
*/ */
@ -420,6 +311,557 @@ expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail)
return retval; return retval;
} }
/*! CLI callback show yang spec. If arg given matches yang argument string */
int
show_yangv(clicon_handle h,
cvec *cvv,
cvec *argv)
{
yang_node *yn;
char *str = NULL;
yang_spec *yspec;
yspec = clicon_dbspec_yang(h);
if (cvec_len(argv) > 0){
str = cv_string_get(cvec_i(argv, 0));
yn = (yang_node*)yang_find((yang_node*)yspec, 0, str);
}
else
yn = (yang_node*)yspec;
yang_print(stdout, yn, 0);
return 0;
}
#ifdef notused
/*! XML to CSV raw variant
* @see xml2csv
*/
static int
xml2csv_raw(FILE *f, cxobj *x)
{
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] arg A string: <dbname> <xpath> [<varname>]
* @param[out] xt Configuration as xml tree.
* Format of arg:
* <dbname> "running", "candidate"
* <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) {
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 (xmldb_get(h, db, cbuf_get(cbx), xt, NULL, NULL) < 0)
goto done;
retval = 0;
done:
if (val)
free(val);
if (cbx)
cbuf_free(cbx);
unchunk_group(__FUNCTION__);
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);
unchunk_group(__FUNCTION__);
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 ((xt = xml_new("tmp", NULL)) == NULL)
goto done;
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, __FUNCTION__); /* cli syntax */
}
retval = 0;
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
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);
}
/*! Show configuration as text given an xpath
* 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>
* @note Hardcoded that a variable in cvv is named "xpath"
*/
int
show_confv_xpath(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
char *str;
char *xpath;
cg_var *cv;
cxobj *xt = NULL;
cxobj **xv = NULL;
size_t xlen;
int i;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be <dbname>", __FUNCTION__);
goto done;
}
str = cv_string_get(cvec_i(argv, 0));
/* Dont get attr here, take it from arg instead */
if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){
clicon_err(OE_PLUGIN, 0, "No such db name: %s", str);
goto done;
}
cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv);
if (xmldb_get(h, str, xpath, &xt, &xv, &xlen) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
retval = 0;
done:
if (xv)
free(xv);
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
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 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;
}
/* In the example, str = "candidate /x/m1/%s/b" */
if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
goto done;
}
dbstr = vec[0];
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 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 (xmldb_get(h, dbstr, xkpath, &xt, &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:
unchunk_group(__FUNCTION__);
if (xvec)
free(xvec);
if (xt)
xml_free(xt);
if (xkpath)
free(xkpath);
return retval;
}
/*! Generic function for showing configurations. /*! Generic function for showing configurations.
* Utility function used by cligen spec file * Utility function used by cligen spec file
* @param[in] h CLICON handle * @param[in] h CLICON handle
@ -462,7 +904,7 @@ show_conf_as(clicon_handle h,
goto done; goto done;
} }
if (nvec != 2 && nvec != 3){ if (nvec != 2 && nvec != 3){
clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected <dbname> <xpath> [<attr>]", str); clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected <dbname> <xpath> [<attr>] got %d arg", str, nvec);
goto done; goto done;
} }
/* Dont get attr here, take it from arg instead */ /* Dont get attr here, take it from arg instead */
@ -498,7 +940,7 @@ show_conf_as(clicon_handle h,
} }
else else
cprintf(cbx, "%s", xpath); cprintf(cbx, "%s", xpath);
if (xmldb_get(h, db, cbuf_get(cbx), 0, xt, NULL, NULL) < 0) if (xmldb_get(h, db, cbuf_get(cbx), xt, NULL, NULL) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
@ -601,54 +1043,6 @@ show_conf_as_json(clicon_handle h,
return retval; return retval;
} }
/*! Show configuration as text givebn an xpath
* 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>
* @note Hardcoded that a variable in cvv is named "xpath"
*/
int
show_conf_xpath(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
int retval = -1;
char *str;
char *xpath;
cg_var *cv;
cxobj *xt = NULL;
cxobj **xv = NULL;
size_t xlen;
int i;
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
/* Dont get attr here, take it from arg instead */
if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){
clicon_err(OE_PLUGIN, 0, "No such db name: %s", str);
goto done;
}
cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv);
if (xmldb_get(h, str, xpath, 1, &xt, &xv, &xlen) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
retval = 0;
done:
if (xv)
free(xv);
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
/*! Show configuration as text /*! Show configuration as text
* Utility function used by cligen spec file * Utility function used by cligen spec file
@ -714,90 +1108,6 @@ 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? */ return show_conf_as_command(h, cvv, arg, NULL); /* XXX: how to set prepend? */
} }
int
show_yang(clicon_handle h, cvec *cvv, cg_var *arg)
{
yang_node *yn;
char *str = NULL;
yang_spec *yspec;
yspec = clicon_dbspec_yang(h);
if (arg != NULL){
str = cv_string_get(arg);
yn = (yang_node*)yang_find((yang_node*)yspec, 0, str);
}
else
yn = (yang_node*)yspec;
yang_print(stdout, yn, 0);
return 0;
}
#ifdef notused
/*! XML to CSV raw variant
* @see xml2csv
*/
static int
xml2csv_raw(FILE *f, cxobj *x)
{
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;
}
static int static int
show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg) show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg)
{ {
@ -841,3 +1151,50 @@ show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg)
{ {
return show_conf_as_csv1(h, cvv, arg); return show_conf_as_csv1(h, cvv, arg);
} }
/*! Show configuration as text given an xpath
* 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>
* @note Hardcoded that a variable in cvv is named "xpath"
*/
int
show_conf_xpath(clicon_handle h,
cvec *cvv,
cg_var *arg)
{
int retval = -1;
char *str;
char *xpath;
cg_var *cv;
cxobj *xt = NULL;
cxobj **xv = NULL;
size_t xlen;
int i;
if (arg == NULL || (str = cv_string_get(arg)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__);
goto done;
}
/* Dont get attr here, take it from arg instead */
if (strcmp(str, "running") != 0 && strcmp(str, "candidate") != 0){
clicon_err(OE_PLUGIN, 0, "No such db name: %s", str);
goto done;
}
cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv);
if (xmldb_get(h, str, xpath, &xt, &xv, &xlen) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
retval = 0;
done:
if (xv)
free(xv);
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}

View file

@ -70,41 +70,62 @@ cligen_handle cli_cligen(clicon_handle h);
/* cli_common.c */ /* cli_common.c */
int init_candidate_db(clicon_handle h); int init_candidate_db(clicon_handle h);
int exit_candidate_db(clicon_handle h); int exit_candidate_db(clicon_handle h);
int cli_notification_register(clicon_handle h, char *stream, enum format_enum format,
char *filter, int status,
int (*fn)(int, void*), void *arg);
#define cli_output cligen_output #define cli_output cligen_output
/* cli_common.c: CLIgen new vector callbacks */
int cli_setv(clicon_handle h, cvec *vars, cvec *argv); int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv); int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
int cli_delv(clicon_handle h, cvec *vars, cvec *argv); int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
int cli_notifyv(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_set(clicon_handle h, cvec *vars, cg_var *arg);
int cli_merge(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_del(clicon_handle h, cvec *vars, cg_var *arg);
int cli_debug_cli(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_debug_backend(clicon_handle h, cvec *vars, cg_var *argv);
int cli_record(clicon_handle h, cvec *vars, cg_var *argv);
int isrecording(void);
int record_command(char *str);
int cli_set_mode(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_start_shell(clicon_handle h, cvec *vars, cg_var *argv);
int cli_quit(clicon_handle h, cvec *vars, cg_var *arg); int cli_quit(clicon_handle h, cvec *vars, cg_var *arg);
int cli_commit(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 cli_validate(clicon_handle h, cvec *vars, cg_var *arg);
int compare_dbs(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 load_config_file(clicon_handle h, cvec *vars, cg_var *arg);
int save_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 delete_all(clicon_handle h, cvec *vars, cg_var *arg);
int discard_changes(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_notify(clicon_handle h, cvec *cvv, cg_var *arg);
int cli_notification_register(clicon_handle h, char *stream, enum format_enum format,
char *filter, int status,
int (*fn)(int, void*), void *arg);
/* In cli_show.c */ /* In cli_show.c */
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail); int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv, int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
cvec *commands, cvec *helptexts); 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_yangv(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_xpath(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_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_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_json(clicon_handle h, cvec *vars, cg_var *arg);

View file

@ -127,7 +127,7 @@ netconf_filter_xmldb(clicon_handle h,
switch (foption){ switch (foption){
case FILTER_SUBTREE: case FILTER_SUBTREE:
/* Get the whole database as xml */ /* Get the whole database as xml */
if (xmldb_get(h, source, "/", 0, &xdb, NULL, NULL) < 0){ if (xmldb_get(h, source, "/", &xdb, NULL, NULL) < 0){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,
"operation-failed", "operation-failed",
"application", "application",
@ -174,7 +174,7 @@ netconf_filter_xmldb(clicon_handle h,
"<bad-attribute>select</bad-attribute>"); "<bad-attribute>select</bad-attribute>");
goto done; goto done;
} }
if (xmldb_get(h, source, selector, 0, &xdb, NULL, NULL) < 0){ if (xmldb_get(h, source, selector, &xdb, NULL, NULL) < 0){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,
"operation-failed", "operation-failed",
"application", "application",

View file

@ -217,7 +217,7 @@ api_data_get(clicon_handle h,
} }
} }
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path)); clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
if (xmldb_get(h, "running", cbuf_get(path), 1, &xt, &vec, &veclen) < 0) if (xmldb_get(h, "running", cbuf_get(path), &xt, &vec, &veclen) < 0)
goto done; goto done;
if ((cbx = cbuf_new()) == NULL) if ((cbx = cbuf_new()) == NULL)

View file

@ -117,7 +117,6 @@ xmldb_from_get(clicon_handle h,
cxobj *x; cxobj *x;
cbuf *cb = NULL; /* Outgoing return message */ cbuf *cb = NULL; /* Outgoing return message */
char *db; char *db;
int vector = 0;
char *xpath = "/"; char *xpath = "/";
cxobj *xt = NULL; /* Top of return tree */ cxobj *xt = NULL; /* Top of return tree */
cxobj *xc; /* Child */ cxobj *xc; /* Child */
@ -135,17 +134,15 @@ xmldb_from_get(clicon_handle h,
} }
if ((x = xpath_first(xr, "xpath")) != NULL) if ((x = xpath_first(xr, "xpath")) != NULL)
xpath = xml_body(x); xpath = xml_body(x);
if (xpath_first(xr, "vector") != NULL)
vector++;
/* Actual get call */ /* Actual get call */
if (xmldb_get(h, db, xpath, vector, &xt, &xvec, &xlen) < 0) if (xmldb_get(h, db, xpath, &xt, &xvec, &xlen) < 0)
goto done; goto done;
xml_name_set(xt, "config"); xml_name_set(xt, "config");
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; goto done;
} }
if (vector){ if (xvec){
for (i=0; i<xlen; i++){ for (i=0; i<xlen; i++){
xc = xvec[i]; xc = xvec[i];
if (clicon_xml2cbuf(cb, xc, 0, 0) < 0) if (clicon_xml2cbuf(cb, xc, 0, 0) < 0)

View file

@ -146,7 +146,6 @@ CLICON_CLIGEN_EXPAND_SINGLE_ARG 0
# Set if you want to use old obsolete cligen callback variable syntax # 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 # 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"); # E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
# But there are still many pre-defined in callbacks, eg in cli_common.c that are not made # And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
# for this.
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1 CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1

7
configure vendored
View file

@ -2328,8 +2328,6 @@ test -n "$target_alias" &&
# Some stuff installed in /usr/local/. Such as qdbm
# #
ac_ext=c ac_ext=c
ac_cpp='$CPP $CPPFLAGS' ac_cpp='$CPP $CPPFLAGS'
@ -3529,9 +3527,6 @@ AR_SUFFIX=".a"
SH_SUFFIX=".so" SH_SUFFIX=".so"
AR="ar" AR="ar"
CPPFLAGS="-I${prefix}/include ${CPPFLAGS}"
LDFLAGS="-L${prefix}/lib ${LDFLAGS}"
# This is for cligen # This is for cligen
# Check whether --with-cligen was given. # Check whether --with-cligen was given.
@ -3884,8 +3879,6 @@ if test "${with_qdbm}"; then
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}" LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
fi fi
LIBS="${LIBS} ${LDFLAGS}"
# Problem: depot.h may be in qdbm/depot.h. # Problem: depot.h may be in qdbm/depot.h.
for ac_header in depot.h for ac_header in depot.h
do : do :

View file

@ -78,8 +78,6 @@ AC_SUBST(EXE_SUFFIX)
AC_SUBST(AR) AC_SUBST(AR)
AC_SUBST(RANLIB) AC_SUBST(RANLIB)
# Some stuff installed in /usr/local/. Such as qdbm
# #
AC_PROG_CC() AC_PROG_CC()
AC_PROG_CPP AC_PROG_CPP
@ -113,9 +111,6 @@ AR_SUFFIX=".a"
SH_SUFFIX=".so" SH_SUFFIX=".so"
AR="ar" AR="ar"
CPPFLAGS="-I${prefix}/include ${CPPFLAGS}"
LDFLAGS="-L${prefix}/lib ${LDFLAGS}"
# This is for cligen # This is for cligen
AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] ) AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] )
if test "${with_cligen}"; then if test "${with_cligen}"; then
@ -137,8 +132,6 @@ if test "${with_qdbm}"; then
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}" LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
fi fi
LIBS="${LIBS} ${LDFLAGS}"
# Problem: depot.h may be in qdbm/depot.h. # Problem: depot.h may be in qdbm/depot.h.
AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))]) AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))])
AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required)) AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required))

BIN
doc/clixon_example_sdk.odg Normal file

Binary file not shown.

View file

@ -5,7 +5,12 @@ Clixon yang routing example
------------------ ------------------
cd example cd example
make && sudo make install make && sudo make install
# Start backend
clixon_backend -f /usr/local/etc/routing.conf -I
# Edit cli
clixon_cli -f /usr/local/etc/routing.conf clixon_cli -f /usr/local/etc/routing.conf
# Send netconf command
clixon_netconf -f /usr/local/etc/routing.conf
1. Setting data example using netconf 1. Setting data example using netconf
------------------------------------- -------------------------------------

View file

@ -29,3 +29,8 @@ CLICON_XMLDB_ADDR 127.0.0.1
# xmldb tcp port (if CLICON_XMLDB_RPC) # xmldb tcp port (if CLICON_XMLDB_RPC)
CLICON_XMLDB_PORT 7878 CLICON_XMLDB_PORT 7878
# 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 0

View file

@ -68,7 +68,7 @@ plugin_init(clicon_handle h)
/*! Example cli function */ /*! Example cli function */
int int
mycallback(clicon_handle h, cvec *cvv, cg_var *arg) mycallback(clicon_handle h, cvec *cvv, cvec *argv)
{ {
int retval = -1; int retval = -1;
cxobj *xt = NULL; cxobj *xt = NULL;
@ -77,12 +77,11 @@ mycallback(clicon_handle h, cvec *cvv, cg_var *arg)
/* Access cligen callback variables */ /* Access cligen callback variables */
myvar = cvec_find(cvv, "var"); /* get a cligen variable from vector */ myvar = cvec_find(cvv, "var"); /* get a cligen variable from vector */
cli_output(stderr, "%s: %d\n", __FUNCTION__, cv_int32_get(myvar)); /* get int value */ cli_output(stderr, "%s: %d\n", __FUNCTION__, cv_int32_get(myvar)); /* get int value */
cli_output(stderr, "arg = %s\n", cv_string_get(arg)); /* get string value */ cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */
/* Show eth0 interfaces config using XPATH */ /* Show eth0 interfaces config using XPATH */
if (xmldb_get(h, "candidate", if (xmldb_get(h, "candidate",
"/interfaces/interface[name=eth0]", "/interfaces/interface[name=eth0]",
0,
&xt, NULL, NULL) < 0) &xt, NULL, NULL) < 0)
goto done; goto done;
xml_print(stdout, xt); xml_print(stdout, xt);

View file

@ -4,43 +4,41 @@ CLICON_PROMPT="%U@%H> ";
CLICON_PLUGIN="routing_cli"; CLICON_PLUGIN="routing_cli";
# Note, when switching to PT, change datamodel to only @datamodel # Note, when switching to PT, change datamodel to only @datamodel
#set @datamodel:ietf-routing, cli_merge(); set @datamodel:ietf-ip, cli_mergev();
#set @datamodel:ietf-ipv4-unicast-routing, cli_merge();
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-ipv4-unicast-routing, cli_del();
delete("Delete a configuration item") @datamodel:ietf-ip, cli_del(); delete("Delete a configuration item") @datamodel:ietf-ip, cli_delv();
validate("Validate changes"), cli_validate(); validate("Validate changes"), cli_validatev();
commit("Commit the changes"), cli_commit((int)0); # snapshot commit("Commit the changes"), cli_commitv((int32)0); # snapshot
quit("Quit Hello"), cli_quit(); quit("Quit Hello"), cli_quitv();
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("candidate"); delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_allv("candidate");
no("Negate or remove") debug("Debugging parts of the system"), cli_debug((int)0); no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cliv((int32)0);
debug("Debugging parts of the system"), cli_debug((int)1);{ 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(); level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug_cliv();
} }
discard("Discard edits (rollback 0)"), discard_changes(); discard("Discard edits (rollback 0)"), discard_changesv();
show("Show a particular state of the system"){ show("Show a particular state of the system"){
xpath("Show configuration") <xpath:string>("XPATH expression"), show_conf_xpath("candidate"); xpath("Show configuration") <xpath:string>("XPATH expression"), show_confv_xpath("candidate");
compare("Compare candidate and running databases"), compare_dbs((int32)0);{ compare("Compare candidate and running databases"), compare_dbsv((int32)0);{
xml("Show comparison in xml"), compare_dbs((int32)0); xml("Show comparison in xml"), compare_dbsv((int32)0);
text("Show comparison in text"), compare_dbs((int32)1); text("Show comparison in text"), compare_dbsv((int32)1);
} }
configuration("Show configuration"), show_conf_as_text("candidate /");{ configuration("Show configuration"), show_confv_as_text("candidate","/");{
xml("Show configuration as XML"), show_conf_as_xml("candidate /"); xml("Show configuration as XML"), show_confv_as_xml("candidate","/");
netconf("Show configuration as netconf edit-config operation"), show_conf_as_netconf("candidate /"); netconf("Show configuration as netconf edit-config operation"), show_confv_as_netconf("candidate","/");
text("Show configuration as text"), show_conf_as_text("candidate /"); text("Show configuration as text"), show_confv_as_text("candidate","/");
cli("Show configuration as cli commands"), show_conf_as_cli("candidate /"); cli("Show configuration as cli commands"), show_confv_as_cli("candidate","/");
json("Show configuration as cli commands"), show_conf_as_json("candidate /"); json("Show configuration as cli commands"), show_confv_as_json("candidate","/");
} }
} }
save("Save candidate configuration to XML file") <filename:string>("Filename (local filename)"), save_config_file("candidate filename"); 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_file("filename replace");{ load("Load configuration from XML file") <filename:string>("Filename (local filename)"),load_config_filev("filename","replace");{
replace("Replace candidate with file contents"), load_config_file("filename replace"); replace("Replace candidate with file contents"), load_config_filev("filename","replace");
merge("Merge file with existent candidate"), load_config_file("filename merge"); merge("Merge file with existent candidate"), load_config_filev("filename","merge");
} }
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg"); example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");

View file

@ -37,6 +37,9 @@
/* Define to 1 if you have the `crypt' library (-lcrypt). */ /* Define to 1 if you have the `crypt' library (-lcrypt). */
#undef HAVE_LIBCRYPT #undef HAVE_LIBCRYPT
/* Define to 1 if you have the `curl' library (-lcurl). */
#undef HAVE_LIBCURL
/* Define to 1 if you have the `dl' library (-ldl). */ /* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL #undef HAVE_LIBDL

View file

@ -131,6 +131,7 @@ cxobj *xml_dup(cxobj *x0);
int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1); int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1);
int cxvec_append(cxobj *x, cxobj ***vec, size_t *len); int cxvec_append(cxobj *x, cxobj ***vec, size_t *len);
int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg); int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply0(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg); int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg);
int xml_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp); int xml_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp);

View file

@ -42,7 +42,7 @@
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt); int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
int xmldb_get(clicon_handle h, char *db, char *xpath, int vector, int xmldb_get(clicon_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put(clicon_handle h, char *db, cxobj *xt, enum operation_type op); int xmldb_put(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
int xmldb_put_tree(clicon_handle h, char *db, char *api_path, int xmldb_put_tree(clicon_handle h, char *db, char *api_path,

View file

@ -209,7 +209,6 @@ int yang_print(FILE *f, yang_node *yn, int marginal);
int yang_parse(clicon_handle h, const char *yang_dir, int yang_parse(clicon_handle h, const char *yang_dir,
const char *module, const char *revision, yang_spec *ysp); const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, yang_applyfn_t fn, void *arg); int yang_apply(yang_node *yn, yang_applyfn_t fn, void *arg);
yang_stmt *dbkey2yang(yang_node *yn, char *dbkey);
yang_node *yang_xpath_abs(yang_node *yn, char *xpath); yang_node *yang_xpath_abs(yang_node *yn, char *xpath);
yang_node *yang_xpath(yang_node *yn, char *xpath); yang_node *yang_xpath(yang_node *yn, char *xpath);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype); cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);

View file

@ -494,7 +494,12 @@ xml_new(char *name,
return xn; return xn;
} }
/*! Create new xml node given a name, parent and spec. Free it with xml_free(). /*! Create new xml node given a name, parent and spec.
* @param[in] name Name of new xml node
* @param[in] xp XML parent
* @param[in] spec Yang spec
* @retval NULL Error
* @retval x XML tree. Free with xml_free().
*/ */
cxobj * cxobj *
xml_new_spec(char *name, xml_new_spec(char *name,
@ -1229,6 +1234,7 @@ cxvec_append(cxobj *x,
* @endcode * @endcode
* @note do not delete or move around any children during this function * @note do not delete or move around any children during this function
* @note It does not apply fn to the root node,.. * @note It does not apply fn to the root node,..
* @see xml_apply0 including top object
*/ */
int int
xml_apply(cxobj *xn, xml_apply(cxobj *xn,
@ -1250,6 +1256,25 @@ xml_apply(cxobj *xn,
return retval; return retval;
} }
/*! Apply a function call on top object and all xml node children recursively
* @see xml_apply not including top object
*/
int
xml_apply0(cxobj *xn,
enum cxobj_type type,
xml_applyfn_t fn,
void *arg)
{
int retval = -1;
if (fn(xn, arg) < 0)
goto done;
retval = xml_apply(xn, type, fn, arg);
done:
return retval;
}
/*! Apply a function call recursively on all ancestors /*! Apply a function call recursively on all ancestors
* Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for * Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an * each object found. The function is called with the xml node and an

View file

@ -30,10 +30,59 @@
the terms of any one of the Apache License version 2 or the GPL. the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* XML database
* TODO: xmldb_del: or dbxml_put_xkey delete
*/ */
/*
* An xml database consists of key-value pairs for xml-trees.
* Each node in an xml-tree has a key and an optional value.
* The key (xmlkey) is constructed from the xml node name concatenated
* with its ancestors and any eventual list keys.
* A xmlkeyfmt is a help-structure used when accessing the XML database.
* It consists of an xmlkey but with the key fields replaced with wild-chars(%s)
* Example: /aaa/bbb/%s/%s/ccc
* Such an xmlkeyfmt can be obtained from a yang-statement by following
* its ancestors to the root module. If one of the ancestors is a list,
* a wildchar (%s) is inserted for each key.
* These xmlkeyfmt keys are saved and used in cli callbacks such as when
* modifying syntax (eg cli_merge/cli_delete) or when completing for sub-symbols
* In this case, the variables are set and the wildcards can be instantiated.
* An xml tree can then be formed that can be used to the xmldb_get() or
* xmldb_put() functions.
* The relations between the functions and formats are as follows:
*
* +-----------------+ +-----------------+
* | yang-stmt | yang2xmlkeyfmt | xmlkeyfmt | xmlkeyfmt2xpath
* | list aa,leaf k | ----------------->| /aa=%s |---------------->
* +-----------------+ +-----------------+
* |
* | xmlkeyfmt2key
* | k=17
* v
* +-------------------+ +-----------------+
* | xml-tree/cxobj | xmlkey2xml | xmlkey RFC3986|
* | <aa><k>17</k></aa>| <------------- | /aa=17 |
* +-------------------+ +-----------------+
*
* Alternative for xmlkeyfmt would be eg:
* RESTCONF: /interfaces/interface=%s/ipv4/address/ip=%s (used)
* XPATH: /interfaces/interface[name=%s]/ipv4/address/[ip=%s]
*
* Paths through the code (for coverage)
* cli_callback_generate +----------------+
* cli_expand_var_generate | yang2xmlkeyfmt |
* yang -------------> | |
* +----------------+
* xmldb_get_tree
* - compare_dbs
* - netconf
* - validate
* - from_client_save
*
* xmldb_get_vec
* - restconf
* - expand_dbvar
* - show_conf_xpath
*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */ #include "clixon_config.h" /* generated by config & autoconf */
#endif #endif
@ -75,50 +124,13 @@
#include "clixon_xml_db_rpc.h" #include "clixon_xml_db_rpc.h"
#include "clixon_xml_db.h" #include "clixon_xml_db.h"
/*
* An xml database consists of key-value pairs for xml-trees.
* Each node in an xml-tree has a key and an optional value.
* The key (xmlkey) is constructed from the xml node name concatenated
* with its ancestors and any eventual list keys.
* A xmlkeyfmt is a help-structure used when accessing the XML database.
* It consists of an xmlkey but with the key fields replaced with wild-chars(%s)
* Example: /aaa/bbb/%s/%s/ccc
* Such an xmlkeyfmt can be obtained from a yang-statement by following
* its ancestors to the root module. If one of the ancestors is a list,
* a wildchar (%s) is inserted for each key.
* These xmlkeyfmt keys are saved and used in cli callbacks such as when
* modifying syntax (eg cli_merge/cli_delete) or when completing for sub-symbols
* In this case, the variables are set and the wildcards can be instantiated.
* An xml tree can then be formed that can be used to the xmldb_get() or
* xmldb_put() functions.
* The relations between the functions and formats are as follows:
*
* +-----------------+ +-----------------+
* | yang-stmt | yang2xmlkeyfmt | xmlkeyfmt |
* | list aa,leaf k | ----------------->| /aa/%s |
* | | | XXX: /aa=%s |
* +-----------------+ +-----------------+
* |
* | xmlkeyfmt2key
* | k=17
* v
* +-------------------+ +-----------------+
* | xml-tree/cxobj | xmlkey2xml | xmlkey RFC3986|
* | <aa><k>17</k></aa>| <------------- | /aa/17 |
* | | | XXX: /aa=17 |
* +-------------------+ +-----------------+
*
* Alternative for xmlkeyfmt would be xpath: eg
* instead of /interfaces/interface/%s/ipv4/address/ip/%s
* you can have: /interfaces/interface[name=%s]/ipv4/address/[ip=%s]
*/
/*! Construct an xml key format from yang statement using wildcards for keys /*! Construct an xml key format from yang statement using wildcards for keys
* Recursively construct it to the top. * Recursively construct it to the top.
* Example: * Example:
* yang: container a -> list b -> key c -> leaf d * yang: container a -> list b -> key c -> leaf d
* xpath: /a/b/%s/d * xpath: /a/b/%s/d
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] inclkey If !inclkey then dont include key leaf * @param[in] inclkey If inclkey then include key leaf (eg last leaf d in ex)
* @param[out] cbuf keyfmt * @param[out] cbuf keyfmt
*/ */
static int static int
@ -126,17 +138,17 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
int inclkey, int inclkey,
cbuf *cb) cbuf *cb)
{ {
yang_node *yn; yang_node *yp; /* parent */
yang_stmt *ykey; yang_stmt *ykey;
int i; int i;
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
int retval = -1; int retval = -1;
yn = ys->ys_parent; yp = ys->ys_parent;
if (yn != NULL && if (yp != NULL &&
yn->yn_keyword != Y_MODULE && yp->yn_keyword != Y_MODULE &&
yn->yn_keyword != Y_SUBMODULE){ yp->yn_keyword != Y_SUBMODULE){
if (yang2xmlkeyfmt_1((yang_stmt *)yn, 1, cb) < 0) if (yang2xmlkeyfmt_1((yang_stmt *)yp, 1, cb) < 0)
goto done; goto done;
} }
if (inclkey){ if (inclkey){
@ -144,8 +156,8 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
cprintf(cb, "/%s", ys->ys_argument); cprintf(cb, "/%s", ys->ys_argument);
} }
else{ else{
if (ys->ys_keyword == Y_LEAF && yn && yn->yn_keyword == Y_LIST){ if (ys->ys_keyword == Y_LEAF && yp && yp->yn_keyword == Y_LIST){
if (yang_key_match(yn, ys->ys_argument) == 0) if (yang_key_match(yp, ys->ys_argument) == 0)
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */ cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
} }
else else
@ -163,12 +175,17 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
/* The value is a list of keys: <key>[ <key>]* */ /* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done; goto done;
if (cvec_len(cvk))
cprintf(cb, "=");
/* Iterate over individual keys */ /* Iterate over individual keys */
for (i=0; i<cvec_len(cvk); i++) for (i=0; i<cvec_len(cvk); i++){
cprintf(cb, "/%%s"); if (i)
cprintf(cb, ",");
cprintf(cb, "%%s");
}
break; break;
case Y_LEAF_LIST: case Y_LEAF_LIST:
cprintf(cb, "/%%s"); cprintf(cb, "=%%s");
break; break;
default: default:
break; break;
@ -184,7 +201,7 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
* Recursively construct it to the top. * Recursively construct it to the top.
* Example: * Example:
* yang: container a -> list b -> key c -> leaf d * yang: container a -> list b -> key c -> leaf d
* xpath: /a/b/%s/d * xpath: /a/b=%s/d
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] inclkey If !inclkey then dont include key leaf * @param[in] inclkey If !inclkey then dont include key leaf
* @param[out] xkfmt XML key format. Needs to be freed after use. * @param[out] xkfmt XML key format. Needs to be freed after use.
@ -300,7 +317,8 @@ xmlkeyfmt2key(char *xkfmt,
* Used to input xmldb_get() or xmldb_get_vec * Used to input xmldb_get() or xmldb_get_vec
* Add .* in last %s position. * Add .* in last %s position.
* Example: * Example:
* xmlkeyfmt: /interface/%s/address/%s * xmlkeyfmt: /interface/%s/address/%s OLDXXX
* xmlkeyfmt: /interface=%s/address=%s
* cvv: name=eth0 * cvv: name=eth0
* xmlkey: /interface/[name=eth0]/address * xmlkey: /interface/[name=eth0]/address
* Example2: * Example2:
@ -352,20 +370,11 @@ xmlkeyfmt2xpath(char *xkfmt,
esc = 0; esc = 0;
if (c!='s') if (c!='s')
continue; continue;
if (j == cvec_len(cvv)) /* last element */
skip++;
else{
/* XXX: remove preceding '/' :
a/[x=y] -> a[x=y] */
if ((str = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
str[strlen(str)-1] = '\0';
cbuf_reset(cb);
cprintf(cb, "%s", str);
free(str);
if (j == cvec_len(cvv)) /* last element */
//skip++;
;
else{
cv = cvec_i(cvv, j++); cv = cvec_i(cvv, j++);
if ((str = cv2str_dup(cv)) == NULL){ if ((str = cv2str_dup(cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup"); clicon_err(OE_UNIX, errno, "cv2str_dup");
@ -382,7 +391,10 @@ xmlkeyfmt2xpath(char *xkfmt,
if (skip) if (skip)
skip=0; skip=0;
else else
cprintf(cb, "%c", c); if ((c == '=' || c == ',') && xkfmt[i+1]=='%')
; /* skip */
else
cprintf(cb, "%c", c);
} }
} }
if ((*xk = strdup4(cbuf_get(cb))) == NULL){ if ((*xk = strdup4(cbuf_get(cb))) == NULL){
@ -504,6 +516,7 @@ append_listkeys(cbuf *ckey,
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
char *keyname; char *keyname;
char *bodyenc; char *bodyenc;
int i=0;
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){ if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
@ -526,7 +539,11 @@ append_listkeys(cbuf *ckey,
clicon_err(OE_UNIX, errno, "curl_easy_escape"); clicon_err(OE_UNIX, errno, "curl_easy_escape");
goto done; goto done;
} }
cprintf(ckey, "/%s", bodyenc); if (i++)
cprintf(ckey, ",");
else
cprintf(ckey, "=");
cprintf(ckey, "%s", bodyenc);
free(bodyenc); bodyenc = NULL; free(bodyenc); bodyenc = NULL;
} }
retval = 0; retval = 0;
@ -622,8 +639,12 @@ get(char *dbname,
int retval = -1; int retval = -1;
char **vec; char **vec;
int nvec; int nvec;
char **valvec;
int nvalvec;
int i; int i;
int j;
char *name; char *name;
char *restval;
yang_stmt *y; yang_stmt *y;
cxobj *x; cxobj *x;
cxobj *xc; cxobj *xc;
@ -638,7 +659,7 @@ get(char *dbname,
// clicon_debug(1, "%s xkey:%s val:%s", __FUNCTION__, xk, val); // clicon_debug(1, "%s xkey:%s val:%s", __FUNCTION__, xk, val);
x = xt; x = xt;
if (xk == NULL || *xk!='/'){ if (xk == NULL || *xk!='/'){
clicon_err(OE_DB, 0, "Invalid key: %s", xk); clicon_err(OE_DB, 0, "Invalid key: %s", xk);
goto done; goto done;
} }
@ -654,7 +675,11 @@ get(char *dbname,
} }
i = 1; i = 1;
while (i<nvec){ while (i<nvec){
name = vec[i]; name = vec[i]; /* E.g "x=1,2" -> name:x restval=1,2 */
if ((restval = index(name, '=')) != NULL){
*restval = '\0';
restval++;
}
if (i == 1){ /* spec->module->node */ if (i == 1){ /* spec->module->node */
if ((y = yang_find_topnode(ys, name)) == NULL){ if ((y = yang_find_topnode(ys, name)) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name); clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
@ -672,14 +697,12 @@ get(char *dbname,
* If xml element is a leaf-list, then the next element is expected to * If xml element is a leaf-list, then the next element is expected to
* be a value * be a value
*/ */
i++; if ((argdec = curl_easy_unescape(NULL, restval, 0, NULL)) == NULL){
if (i>=nvec){ clicon_err(OE_UNIX, errno, "curl_easy_escape");
clicon_err(OE_XML, errno, "Leaf-list %s without argument", name);
goto done; goto done;
} }
arg = vec[i];
if ((xc = xml_find(x, name))==NULL || if ((xc = xml_find(x, name))==NULL ||
(xb = xml_find(xc, arg))==NULL){ (xb = xml_find(xc, argdec))==NULL){
if ((xc = xml_new_spec(name, x, y)) == NULL) if ((xc = xml_new_spec(name, x, y)) == NULL)
goto done; goto done;
/* Assume body is created at end of function */ /* Assume body is created at end of function */
@ -706,17 +729,17 @@ get(char *dbname,
cvi = NULL; cvi = NULL;
/* Iterate over individual yang keys */ /* Iterate over individual yang keys */
cprintf(cb, "%s", name); cprintf(cb, "%s", name);
if (cvec_len(cvk) > 1 && i+cvec_len(cvk) >= nvec-1){ if ((valvec = clicon_strsplit(restval, ",", &nvalvec, __FUNCTION__)) == NULL)
goto done;
if (cvec_len(cvk)!=nvalvec){
retval = 0; retval = 0;
goto done; goto done;
} }
j = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL){ while ((cvi = cvec_each(cvk, cvi)) != NULL){
i++; if (j>=nvalvec)
if (i>=nvec){ break;
clicon_err(OE_XML, errno, "List %s without argument", name); arg = valvec[j++];
goto done;
}
arg = vec[i];
if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){ if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){
clicon_err(OE_UNIX, errno, "curl_easy_escape"); clicon_err(OE_UNIX, errno, "curl_easy_escape");
goto done; goto done;
@ -729,12 +752,14 @@ get(char *dbname,
if ((xc = xml_new_spec(name, x, y)) == NULL) if ((xc = xml_new_spec(name, x, y)) == NULL)
goto done; goto done;
cvi = NULL; cvi = NULL;
i -= cvec_len(cvk); // i -= cvec_len(cvk);
/* Iterate over individual yang keys */ /* Iterate over individual yang keys */
j=0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if (j>=nvalvec)
break;
arg = valvec[j++];
keyname = cv_string_get(cvi); keyname = cv_string_get(cvi);
i++;
arg = vec[i];
if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){ if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){
clicon_err(OE_UNIX, errno, "curl_easy_escape"); clicon_err(OE_UNIX, errno, "curl_easy_escape");
goto done; goto done;
@ -908,96 +933,14 @@ xml_order(cxobj *x,
return retval; return retval;
} }
/*! Get content of database using xpath. return a single tree
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax (or NULL for all)
* @param[in] yspec Yang specification
* @param[in] vector If set, return list of results in xvec
* @param[out] xtop XML tree. Freed by xml_free()
* @retval 0 OK
* @retval -1 Error
*/
static int
xmldb_get_tree(char *dbname,
char *xpath,
yang_spec *yspec,
cxobj **xtop)
{
int retval = -1;
int i;
int npairs;
struct db_pair *pairs;
cxobj *xt = NULL;
cxobj **xvec=NULL;
size_t len;
/* Read in complete database (this can be optimized) */
if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0)
goto done;
if ((xt = xml_new("clicon", NULL)) == NULL)
goto done;
for (i = 0; i < npairs; i++)
clicon_debug(2, "%s %s", pairs[i].dp_key, pairs[i].dp_val?pairs[i].dp_val:"");
// clicon_debug(1, "%s npairs:%d", __FUNCTION__, npairs);
for (i = 0; i < npairs; i++) {
if (get(dbname,
yspec,
pairs[i].dp_key, /* xml key */
pairs[i].dp_val, /* may be NULL */
xt) < 0)
goto done;
}
/*
* 1. Read whole tree, call xpath, then retrace 'back'
* 2. only read necessary parts,...?
*/
if (xpath){
if (xpath_vec(xt, xpath, &xvec, &len) < 0)
goto done;
if (xvec != NULL){
/* Prune everything except nodes in xvec and how to get there
* Alt: Create a new subtree from xt with that property,...(no)
*/
for (i=0; i<len; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
if (xml_tree_prune_unmarked(xt, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
}
*xtop = xt;
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (1) /* sanity */
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
if (debug)
clicon_xml2file(stdout, xt, 0, 1);
retval = 0;
done:
if (retval < 0 && xt){
xml_free(xt);
*xtop = NULL;
}
if (xvec)
free(xvec);
unchunk_group(__FUNCTION__);
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees /*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match * The function returns a minimal tree that includes all sub-trees that match
* xpath. * xpath.
* @param[in] dbname Name of database to search in (filename including dir path * @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] yspec Yang specification * @param[in] yspec Yang specification
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free() * @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use * @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector. * @param[out] xlen Length of vector.
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
@ -1016,73 +959,28 @@ xmldb_get_tree(char *dbname,
* xml_free(xt); * xml_free(xt);
* free(xvec); * free(xvec);
* @endcode * @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec * @see xpath_vec
* @see xmldb_get * @see xmldb_get
*/ */
static int static int
xmldb_get_vec(char *dbname, xmldb_get_local(clicon_handle h,
char *xpath, char *db,
yang_spec *yspec, char *xpath,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec, cxobj ***xvec0,
size_t *xlen) size_t *xlen0)
{ {
int retval = -1; int retval = -1;
yang_spec *yspec;
char *dbname = NULL;
cxobj **xvec;
size_t xlen;
int i; int i;
int npairs; int npairs;
struct db_pair *pairs; struct db_pair *pairs;
cxobj *xt = NULL; cxobj *xt = NULL;
/* Read in complete database (this can be optimized) */
if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0)
goto done;
if ((xt = xml_new("clicon", NULL)) == NULL)
goto done;
if (debug) /* debug */
for (i = 0; i < npairs; i++)
fprintf(stderr, "%s %s\n", pairs[i].dp_key, pairs[i].dp_val?pairs[i].dp_val:"");
/* Read in whole tree */
for (i = 0; i < npairs; i++) {
if (get(dbname,
yspec,
pairs[i].dp_key, /* xml key */
pairs[i].dp_val, /* may be NULL */
xt) < 0)
goto done;
}
if (xpath_vec(xt, xpath, xvec, xlen) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (1)
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
if (0)
clicon_xml2file(stdout, xt, 0, 1);
retval = 0;
*xtop = xt;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Local variant of xmldb_get */
static int
xmldb_get_local(clicon_handle h,
char *db,
char *xpath,
int vector,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
yang_spec *yspec;
char *dbname = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (db2file(h, db, &dbname) < 0) if (db2file(h, db, &dbname) < 0)
goto done; goto done;
@ -1094,17 +992,54 @@ xmldb_get_local(clicon_handle h,
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done; goto done;
} }
if (vector){ /* Read in complete database (this can be optimized) */
if (xmldb_get_vec(dbname, xpath, yspec, xtop, xvec, xlen) < 0) if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0)
goto done;
if ((xt = xml_new_spec("clicon", NULL, yspec)) == NULL)
goto done;
/* Translate to complete xml tree */
for (i = 0; i < npairs; i++) {
if (get(dbname,
yspec,
pairs[i].dp_key, /* xml key */
pairs[i].dp_val, /* may be NULL */
xt) < 0)
goto done; goto done;
} }
else if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
if (xmldb_get_tree(dbname, xpath, yspec, xtop) < 0) goto done;
/* If vectors are specified then filter out everything else,
* otherwise return complete tree.
*/
if (xvec0 && xlen0){
if (xvec != NULL)
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
if (xml_tree_prune_unmarked(xt, NULL) < 0)
goto done; goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
*xvec0 = xvec;
xvec = NULL;
*xlen0 = xlen;
xlen = 0;
}
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
if (debug)
clicon_xml2file(stdout, xt, 0, 1);
*xtop = xt;
retval = 0; retval = 0;
done: done:
if (dbname) if (dbname)
free(dbname); free(dbname);
if (xvec)
free(xvec);
return retval; return retval;
} }
@ -1128,7 +1063,7 @@ xmldb_get_local(clicon_handle h,
* size_t xlen; * size_t xlen;
* *
* if (xmldb_get("running", "/interfaces/interface[name="eth"]", * if (xmldb_get("running", "/interfaces/interface[name="eth"]",
* 1, &xt, &xvec, &xlen) < 0) * &xt, &xvec, &xlen) < 0)
* err; * err;
* for (i=0; i<xlen; i++){ * for (i=0; i<xlen; i++){
* xn = xv[i]; * xn = xv[i];
@ -1143,7 +1078,6 @@ int
xmldb_get(clicon_handle h, xmldb_get(clicon_handle h,
char *db, char *db,
char *xpath, char *xpath,
int vector,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec, cxobj ***xvec,
size_t *xlen) size_t *xlen)
@ -1152,9 +1086,9 @@ xmldb_get(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clicon_xmldb_rpc(h)) if (clicon_xmldb_rpc(h))
retval = xmldb_get_rpc(h, db, xpath, vector, xtop, xvec, xlen); retval = xmldb_get_rpc(h, db, xpath, xtop, xvec, xlen);
else else
retval = xmldb_get_local(h, db, xpath, vector, xtop, xvec, xlen); retval = xmldb_get_local(h, db, xpath, xtop, xvec, xlen);
return retval; return retval;
} }
@ -1215,6 +1149,7 @@ put(char *dbname,
char *body; char *body;
yang_stmt *y; yang_stmt *y;
int exists; int exists;
char *bodyenc=NULL;
clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument); clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
if (debug){ if (debug){
@ -1235,7 +1170,11 @@ put(char *dbname,
goto done; goto done;
break; break;
case Y_LEAF_LIST: case Y_LEAF_LIST:
cprintf(cbxk, "/%s", body); if ((bodyenc = curl_easy_escape(NULL, body, 0)) == NULL){
clicon_err(OE_UNIX, errno, "curl_easy_escape");
goto done;
}
cprintf(cbxk, "=%s", bodyenc);
break; break;
default: default:
break; break;
@ -1283,6 +1222,8 @@ put(char *dbname,
done: done:
if (cbxk) if (cbxk)
cbuf_free(cbxk); cbuf_free(cbxk);
if (bodyenc)
free(bodyenc);
return retval; return retval;
} }
@ -1580,7 +1521,16 @@ xmldb_put_tree(clicon_handle h,
return xmldb_put_tree_local(h, db, api_path, xt, op); return xmldb_put_tree_local(h, db, api_path, xt, op);
} }
/*! Local variant of xmldb_put_xkey */ /*! Modify database provided an XML database key and an operation
* @param[in] h CLICON handle
* @param[in] db Database name
* @param[in] xk XML Key, eg /aa/bb/17/name
* @param[in] val Key value, eg "17"
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @retval 0 OK
* @retval -1 Error
* Local variant of xmldb_put_xkey
*/
static int static int
xmldb_put_xkey_local(clicon_handle h, xmldb_put_xkey_local(clicon_handle h,
char *db, char *db,
@ -1594,8 +1544,12 @@ xmldb_put_xkey_local(clicon_handle h,
yang_stmt *ykey; yang_stmt *ykey;
char **vec; char **vec;
int nvec; int nvec;
char **valvec;
int nvalvec;
int i; int i;
int j;
char *name; char *name;
char *restval;
cg_var *cvi; cg_var *cvi;
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
char *val2 = NULL; char *val2 = NULL;
@ -1632,11 +1586,15 @@ xmldb_put_xkey_local(clicon_handle h,
} }
i = 1; i = 1;
while (i<nvec){ while (i<nvec){
name = vec[i]; name = vec[i]; /* E.g "x=1,2" -> name:x restval=1,2 */
if ((restval = index(name, '=')) != NULL){
*restval = '\0';
restval++;
}
if (i==1){ if (i==1){
if (!strlen(name) && (op==OP_DELETE || op == OP_REMOVE)){ if (strlen(name)==0 && (op==OP_DELETE || op == OP_REMOVE)){
/* Special handling of "/" */ /* Special handling of "/" */
cprintf(ckey, "/%s", name); cprintf(ckey, "/");
break; break;
} }
else else
@ -1660,13 +1618,7 @@ xmldb_put_xkey_local(clicon_handle h,
i++; i++;
switch (y->ys_keyword){ switch (y->ys_keyword){
case Y_LEAF_LIST: case Y_LEAF_LIST:
val2 = vec[i]; cprintf(ckey, "=%s", restval);
if (i>=nvec){
clicon_err(OE_XML, errno, "Leaf-list %s without argument", name);
goto done;
}
i++;
cprintf(ckey, "/%s", val2);
break; break;
case Y_LIST: case Y_LIST:
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){ if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
@ -1677,16 +1629,25 @@ xmldb_put_xkey_local(clicon_handle h,
/* The value is a list of keys: <key>[ <key>]* */ /* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done; goto done;
if ((valvec = clicon_strsplit(restval, ",", &nvalvec, __FUNCTION__)) == NULL)
goto done;
if (cvec_len(cvk) != nvalvec){
clicon_err(OE_XML, errno, "List %s key length mismatch", name);
goto done;
}
cvi = NULL; cvi = NULL;
/* Iterate over individual yang keys */ /* Iterate over individual yang keys */
j = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); keyname = cv_string_get(cvi);
val2 = vec[i++]; if (j)
if (i>nvec){ /* XXX >= ? */ cprintf(ckey, ",");
clicon_err(OE_XML, errno, "List %s without argument", name); else
goto done; cprintf(ckey, "=");
} val2 = valvec[j++];
cprintf(ckey, "/%s", val2);
cprintf(ckey, "%s", val2);
cbuf_reset(csubkey); cbuf_reset(csubkey);
cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname); cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname);
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
@ -2124,7 +2085,7 @@ main(int argc, char **argv)
if (argc < 5) if (argc < 5)
usage(argv[0]); usage(argv[0]);
xpath = argc>5?argv[5]:NULL; xpath = argc>5?argv[5]:NULL;
if (xmldb_get(h, db, xpath, 0, &xt, NULL, NULL) < 0) if (xmldb_get(h, db, xpath, &xt, NULL, NULL) < 0)
goto done; goto done;
clicon_xml2file(stdout, xt, 0, 1); clicon_xml2file(stdout, xt, 0, 1);
} }

View file

@ -243,7 +243,6 @@ int
xmldb_get_rpc(clicon_handle h, xmldb_get_rpc(clicon_handle h,
char *db, char *db,
char *xpath, char *xpath,
int vector,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec, cxobj ***xvec,
size_t *xlen) size_t *xlen)
@ -263,8 +262,6 @@ xmldb_get_rpc(clicon_handle h,
cprintf(cb, "<source><%s/></source>", db); cprintf(cb, "<source><%s/></source>", db);
if (xpath) if (xpath)
cprintf(cb, "<xpath>%s</xpath>", xpath); cprintf(cb, "<xpath>%s</xpath>", xpath);
if (vector)
cprintf(cb, "<vector/>");
cprintf(cb, "</get></rpc>]]>]]>"); cprintf(cb, "</get></rpc>]]>]]>");
if (xmldb_rpc(h, if (xmldb_rpc(h,
cbuf_get(cb), cbuf_get(cb),
@ -273,7 +270,7 @@ xmldb_get_rpc(clicon_handle h,
goto done; goto done;
if (clicon_xml_parse_str(rb, &xt) < 0) if (clicon_xml_parse_str(rb, &xt) < 0)
goto done; goto done;
if (vector){ if (xvec){
i=0; i=0;
if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){ if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){
clicon_err(OE_UNIX, errno, "calloc"); clicon_err(OE_UNIX, errno, "calloc");

View file

@ -39,7 +39,7 @@
* Prototypes * Prototypes
*/ */
int xmldb_get_rpc(clicon_handle h, char *db, int xmldb_get_rpc(clicon_handle h, char *db,
char *xpath, int vector, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op); int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val, int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val,

View file

@ -938,7 +938,7 @@ xpath_each(cxobj *cxtop,
* cxobj **vec; * cxobj **vec;
* size_t veclen; * size_t veclen;
* if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0) * if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0)
* got err; * goto err;
* for (i=0; i<veclen; i++){ * for (i=0; i<veclen; i++){
* xn = vec[i]; * xn = vec[i];
* ... * ...

View file

@ -419,10 +419,8 @@ yang_find(yang_node *yn,
* the following keyword: container, leaf, list, leaf-list * the following keyword: container, leaf, list, leaf-list
* That is, basic syntax nodes. * That is, basic syntax nodes.
* @note check if argument==NULL really required? * @note check if argument==NULL really required?
* Is this yang-stmt a container, list, leaf or leaf-list?
*/ */
/*! Is this yang-stmt a container, list, leaf or leaf-list? */
yang_stmt * yang_stmt *
yang_find_syntax(yang_node *yn, yang_find_syntax(yang_node *yn,
char *argument) char *argument)
@ -1638,66 +1636,6 @@ yang_apply(yang_node *yn,
return retval; return retval;
} }
static yang_stmt *
yang_dbkey_vec(yang_node *yn,
char **vec,
int nvec)
{
char *key;
yang_stmt *ys;
int64_t i;
int ret;
if (nvec <= 0)
return NULL;
key = vec[0];
if (yn->yn_keyword == Y_LIST){
ret = parse_int64(key, &i, NULL);
if (ret != 1){
clicon_err(OE_YANG, errno, "strtol");
goto done;
}
if (nvec == 1)
return (yang_stmt*)yn;
vec++;
nvec--;
key = vec[0];
}
if ((ys = yang_find_syntax(yn, key)) == NULL)
goto done;
if (nvec == 1)
return ys;
return yang_dbkey_vec((yang_node*)ys, vec+1, nvec-1);
done:
return NULL;
}
/*! Given a dbkey (eg a.b.0) recursively find matching yang specification
*
* e.g. a.0 matches the db_spec corresponding to a[].
* Input args:
* @param[in] yn top-of yang tree where to start finding
* @param[in] dbkey database key to match in yang spec tree
* @see yang_dbkey_get
*/
yang_stmt *
dbkey2yang(yang_node *yn,
char *dbkey)
{
char **vec;
int nvec;
yang_stmt *ys;
/* Split key into parts, eg "a.0.b" -> "a" "0" "b" */
if ((vec = clicon_strsplit(dbkey, ".", &nvec, __FUNCTION__)) == NULL){
clicon_err(OE_YANG, errno, "%s: strsplit", __FUNCTION__);
return NULL;
}
ys = yang_dbkey_vec(yn, vec, nvec);
unchunk_group(__FUNCTION__);
return ys;
}
/*! All the work for yang_xpath. /*! All the work for yang_xpath.
Ignore prefixes, see _abs */ Ignore prefixes, see _abs */
static yang_node * static yang_node *