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:
parent
c9f1ece53e
commit
7f0b9909b3
30 changed files with 1444 additions and 1054 deletions
|
|
@ -29,13 +29,17 @@
|
|||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ config_snapshot(clicon_handle h,
|
|||
clicon_err(OE_CFG, errno, "Creating file %s", filename0);
|
||||
return -1;
|
||||
}
|
||||
if (xmldb_get(h, db, "/", 0, &xn, NULL, NULL) < 0)
|
||||
if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_print(f, xn) < 0)
|
||||
goto done;
|
||||
|
|
@ -439,7 +439,7 @@ from_client_save(clicon_handle h,
|
|||
clicon_err(OE_CFG, errno, "Creating file %s", filename);
|
||||
return -1;
|
||||
}
|
||||
if (xmldb_get(h, db, "/", 0, &xn, NULL, NULL) < 0)
|
||||
if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_print(f, xn) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -143,9 +143,9 @@ validate_common(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
/* 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;
|
||||
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;
|
||||
|
||||
/* 3. Compute differences */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,6 +37,24 @@
|
|||
#ifndef _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_unblock(clicon_handle h);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@
|
|||
|
||||
*
|
||||
* Translation between database specs
|
||||
* dbspec_key yang_spec CLIgen parse_tree
|
||||
* +-------------+ key2yang +-------------+ yang2cli +-------------+
|
||||
* | keyspec | -------------> | | ------------> | cli |
|
||||
* | A[].B !$a | yang2key | list{key A;}| | syntax |
|
||||
* +-------------+ <------------ +-------------+ +-------------+
|
||||
* yang_spec CLIgen parse_tree
|
||||
* +-------------+ yang2cli +-------------+
|
||||
* | | ------------> | cli |
|
||||
* | list{key A;}| | syntax |
|
||||
* +-------------+ +-------------+
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
|
|||
|
|
@ -660,10 +660,6 @@ int
|
|||
clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr)
|
||||
{
|
||||
cli_output_reset();
|
||||
#ifdef notyet
|
||||
if (isrecording())
|
||||
record_command(cmd);
|
||||
#endif
|
||||
if (!cli_exiting(h)) {
|
||||
clicon_err_reset();
|
||||
if (cligen_eval(cli_cligen(h), match_obj, vr) < 0) {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
/* Exported functions in this file are in 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_raw(FILE *f, cxobj *x);
|
||||
|
|
@ -140,7 +141,7 @@ expandv_dbvar(void *h,
|
|||
*/
|
||||
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
|
||||
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;
|
||||
/* One round to detect duplicates
|
||||
* 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
|
||||
*/
|
||||
|
|
@ -420,6 +311,557 @@ 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,
|
||||
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.
|
||||
* Utility function used by cligen spec file
|
||||
* @param[in] h CLICON handle
|
||||
|
|
@ -462,7 +904,7 @@ show_conf_as(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
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;
|
||||
}
|
||||
/* Dont get attr here, take it from arg instead */
|
||||
|
|
@ -498,7 +940,7 @@ show_conf_as(clicon_handle h,
|
|||
}
|
||||
else
|
||||
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;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -601,54 +1043,6 @@ show_conf_as_json(clicon_handle h,
|
|||
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
|
||||
* 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? */
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
/*! 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,41 +70,62 @@ cligen_handle cli_cligen(clicon_handle h);
|
|||
/* cli_common.c */
|
||||
int init_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
|
||||
/* cli_common.c: CLIgen new vector callbacks */
|
||||
int cli_setv(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_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_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_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_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_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 */
|
||||
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,
|
||||
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_netconf(clicon_handle h, cvec *vars, cg_var *arg);
|
||||
int show_conf_as_json(clicon_handle h, cvec *vars, cg_var *arg);
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ netconf_filter_xmldb(clicon_handle h,
|
|||
switch (foption){
|
||||
case FILTER_SUBTREE:
|
||||
/* 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,
|
||||
"operation-failed",
|
||||
"application",
|
||||
|
|
@ -174,7 +174,7 @@ netconf_filter_xmldb(clicon_handle h,
|
|||
"<bad-attribute>select</bad-attribute>");
|
||||
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,
|
||||
"operation-failed",
|
||||
"application",
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ api_data_get(clicon_handle h,
|
|||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
|
|
|
|||
|
|
@ -117,7 +117,6 @@ xmldb_from_get(clicon_handle h,
|
|||
cxobj *x;
|
||||
cbuf *cb = NULL; /* Outgoing return message */
|
||||
char *db;
|
||||
int vector = 0;
|
||||
char *xpath = "/";
|
||||
cxobj *xt = NULL; /* Top of return tree */
|
||||
cxobj *xc; /* Child */
|
||||
|
|
@ -135,17 +134,15 @@ xmldb_from_get(clicon_handle h,
|
|||
}
|
||||
if ((x = xpath_first(xr, "xpath")) != NULL)
|
||||
xpath = xml_body(x);
|
||||
if (xpath_first(xr, "vector") != NULL)
|
||||
vector++;
|
||||
/* 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;
|
||||
xml_name_set(xt, "config");
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (vector){
|
||||
if (xvec){
|
||||
for (i=0; i<xlen; i++){
|
||||
xc = xvec[i];
|
||||
if (clicon_xml2cbuf(cb, xc, 0, 0) < 0)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ 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");
|
||||
# But there are still many pre-defined in callbacks, eg in cli_common.c that are not made
|
||||
# for this.
|
||||
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
|
||||
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1
|
||||
|
||||
|
|
|
|||
7
configure
vendored
7
configure
vendored
|
|
@ -2328,8 +2328,6 @@ test -n "$target_alias" &&
|
|||
|
||||
|
||||
|
||||
# Some stuff installed in /usr/local/. Such as qdbm
|
||||
|
||||
#
|
||||
ac_ext=c
|
||||
ac_cpp='$CPP $CPPFLAGS'
|
||||
|
|
@ -3529,9 +3527,6 @@ AR_SUFFIX=".a"
|
|||
SH_SUFFIX=".so"
|
||||
AR="ar"
|
||||
|
||||
CPPFLAGS="-I${prefix}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${prefix}/lib ${LDFLAGS}"
|
||||
|
||||
# This is for cligen
|
||||
|
||||
# Check whether --with-cligen was given.
|
||||
|
|
@ -3884,8 +3879,6 @@ if test "${with_qdbm}"; then
|
|||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
fi
|
||||
|
||||
LIBS="${LIBS} ${LDFLAGS}"
|
||||
|
||||
# Problem: depot.h may be in qdbm/depot.h.
|
||||
for ac_header in depot.h
|
||||
do :
|
||||
|
|
|
|||
|
|
@ -78,8 +78,6 @@ AC_SUBST(EXE_SUFFIX)
|
|||
AC_SUBST(AR)
|
||||
AC_SUBST(RANLIB)
|
||||
|
||||
# Some stuff installed in /usr/local/. Such as qdbm
|
||||
|
||||
#
|
||||
AC_PROG_CC()
|
||||
AC_PROG_CPP
|
||||
|
|
@ -113,9 +111,6 @@ AR_SUFFIX=".a"
|
|||
SH_SUFFIX=".so"
|
||||
AR="ar"
|
||||
|
||||
CPPFLAGS="-I${prefix}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${prefix}/lib ${LDFLAGS}"
|
||||
|
||||
# This is for cligen
|
||||
AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] )
|
||||
if test "${with_cligen}"; then
|
||||
|
|
@ -137,8 +132,6 @@ if test "${with_qdbm}"; then
|
|||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
fi
|
||||
|
||||
LIBS="${LIBS} ${LDFLAGS}"
|
||||
|
||||
# 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_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required))
|
||||
|
|
|
|||
BIN
doc/clixon_example_sdk.odg
Normal file
BIN
doc/clixon_example_sdk.odg
Normal file
Binary file not shown.
|
|
@ -5,7 +5,12 @@ Clixon yang routing example
|
|||
------------------
|
||||
cd example
|
||||
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
|
||||
# Send netconf command
|
||||
clixon_netconf -f /usr/local/etc/routing.conf
|
||||
|
||||
1. Setting data example using netconf
|
||||
-------------------------------------
|
||||
|
|
|
|||
|
|
@ -29,3 +29,8 @@ CLICON_XMLDB_ADDR 127.0.0.1
|
|||
# xmldb tcp port (if CLICON_XMLDB_RPC)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ plugin_init(clicon_handle h)
|
|||
|
||||
/*! Example cli function */
|
||||
int
|
||||
mycallback(clicon_handle h, cvec *cvv, cg_var *arg)
|
||||
mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xt = NULL;
|
||||
|
|
@ -77,12 +77,11 @@ mycallback(clicon_handle h, cvec *cvv, cg_var *arg)
|
|||
/* Access cligen callback variables */
|
||||
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, "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 */
|
||||
if (xmldb_get(h, "candidate",
|
||||
"/interfaces/interface[name=eth0]",
|
||||
0,
|
||||
&xt, NULL, NULL) < 0)
|
||||
goto done;
|
||||
xml_print(stdout, xt);
|
||||
|
|
|
|||
|
|
@ -4,43 +4,41 @@ CLICON_PROMPT="%U@%H> ";
|
|||
CLICON_PLUGIN="routing_cli";
|
||||
|
||||
# Note, when switching to PT, change datamodel to only @datamodel
|
||||
#set @datamodel:ietf-routing, cli_merge();
|
||||
#set @datamodel:ietf-ipv4-unicast-routing, cli_merge();
|
||||
set @datamodel:ietf-ip, cli_merge();
|
||||
set @datamodel:ietf-ip, cli_mergev();
|
||||
|
||||
#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();
|
||||
commit("Commit the changes"), cli_commit((int)0); # snapshot
|
||||
quit("Quit Hello"), cli_quit();
|
||||
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("candidate");
|
||||
validate("Validate changes"), cli_validatev();
|
||||
commit("Commit the changes"), cli_commitv((int32)0); # snapshot
|
||||
quit("Quit Hello"), cli_quitv();
|
||||
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);
|
||||
debug("Debugging parts of the system"), cli_debug((int)1);{
|
||||
level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug();
|
||||
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_cliv();
|
||||
}
|
||||
|
||||
discard("Discard edits (rollback 0)"), discard_changes();
|
||||
discard("Discard edits (rollback 0)"), discard_changesv();
|
||||
|
||||
show("Show a particular state of the system"){
|
||||
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);
|
||||
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);
|
||||
}
|
||||
configuration("Show configuration"), show_conf_as_text("candidate /");{
|
||||
xml("Show configuration as XML"), show_conf_as_xml("candidate /");
|
||||
netconf("Show configuration as netconf edit-config operation"), show_conf_as_netconf("candidate /");
|
||||
text("Show configuration as text"), show_conf_as_text("candidate /");
|
||||
cli("Show configuration as cli commands"), show_conf_as_cli("candidate /");
|
||||
json("Show configuration as cli commands"), show_conf_as_json("candidate /");
|
||||
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","/");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
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");
|
||||
}
|
||||
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
|
||||
|
|
@ -37,6 +37,9 @@
|
|||
/* Define to 1 if you have the `crypt' library (-lcrypt). */
|
||||
#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). */
|
||||
#undef HAVE_LIBDL
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ cxobj *xml_dup(cxobj *x0);
|
|||
int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1);
|
||||
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_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_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
||||
int xmlkeyfmt2key(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);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -209,7 +209,6 @@ int yang_print(FILE *f, yang_node *yn, int marginal);
|
|||
int yang_parse(clicon_handle h, const char *yang_dir,
|
||||
const char *module, const char *revision, yang_spec *ysp);
|
||||
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(yang_node *yn, char *xpath);
|
||||
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
|
||||
|
|
|
|||
|
|
@ -494,7 +494,12 @@ xml_new(char *name,
|
|||
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 *
|
||||
xml_new_spec(char *name,
|
||||
|
|
@ -1229,6 +1234,7 @@ cxvec_append(cxobj *x,
|
|||
* @endcode
|
||||
* @note do not delete or move around any children during this function
|
||||
* @note It does not apply fn to the root node,..
|
||||
* @see xml_apply0 including top object
|
||||
*/
|
||||
int
|
||||
xml_apply(cxobj *xn,
|
||||
|
|
@ -1250,6 +1256,25 @@ xml_apply(cxobj *xn,
|
|||
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
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -30,10 +30,59 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** 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
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
|
@ -75,50 +124,13 @@
|
|||
#include "clixon_xml_db_rpc.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
|
||||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* yang: container a -> list b -> key c -> leaf d
|
||||
* xpath: /a/b/%s/d
|
||||
* @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
|
||||
*/
|
||||
static int
|
||||
|
|
@ -126,17 +138,17 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
|
|||
int inclkey,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_node *yn;
|
||||
yang_node *yp; /* parent */
|
||||
yang_stmt *ykey;
|
||||
int i;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
int retval = -1;
|
||||
|
||||
yn = ys->ys_parent;
|
||||
if (yn != NULL &&
|
||||
yn->yn_keyword != Y_MODULE &&
|
||||
yn->yn_keyword != Y_SUBMODULE){
|
||||
if (yang2xmlkeyfmt_1((yang_stmt *)yn, 1, cb) < 0)
|
||||
yp = ys->ys_parent;
|
||||
if (yp != NULL &&
|
||||
yp->yn_keyword != Y_MODULE &&
|
||||
yp->yn_keyword != Y_SUBMODULE){
|
||||
if (yang2xmlkeyfmt_1((yang_stmt *)yp, 1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (inclkey){
|
||||
|
|
@ -144,8 +156,8 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
|
|||
cprintf(cb, "/%s", ys->ys_argument);
|
||||
}
|
||||
else{
|
||||
if (ys->ys_keyword == Y_LEAF && yn && yn->yn_keyword == Y_LIST){
|
||||
if (yang_key_match(yn, ys->ys_argument) == 0)
|
||||
if (ys->ys_keyword == Y_LEAF && yp && yp->yn_keyword == Y_LIST){
|
||||
if (yang_key_match(yp, ys->ys_argument) == 0)
|
||||
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
|
||||
}
|
||||
else
|
||||
|
|
@ -163,12 +175,17 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
|
|||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
if (cvec_len(cvk))
|
||||
cprintf(cb, "=");
|
||||
/* Iterate over individual keys */
|
||||
for (i=0; i<cvec_len(cvk); i++)
|
||||
cprintf(cb, "/%%s");
|
||||
for (i=0; i<cvec_len(cvk); i++){
|
||||
if (i)
|
||||
cprintf(cb, ",");
|
||||
cprintf(cb, "%%s");
|
||||
}
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
cprintf(cb, "/%%s");
|
||||
cprintf(cb, "=%%s");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -184,7 +201,7 @@ yang2xmlkeyfmt_1(yang_stmt *ys,
|
|||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* 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] inclkey If !inclkey then dont include key leaf
|
||||
* @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
|
||||
* Add .* in last %s position.
|
||||
* Example:
|
||||
* xmlkeyfmt: /interface/%s/address/%s
|
||||
* xmlkeyfmt: /interface/%s/address/%s OLDXXX
|
||||
* xmlkeyfmt: /interface=%s/address=%s
|
||||
* cvv: name=eth0
|
||||
* xmlkey: /interface/[name=eth0]/address
|
||||
* Example2:
|
||||
|
|
@ -352,20 +370,11 @@ xmlkeyfmt2xpath(char *xkfmt,
|
|||
esc = 0;
|
||||
if (c!='s')
|
||||
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++);
|
||||
if ((str = cv2str_dup(cv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||
|
|
@ -382,7 +391,10 @@ xmlkeyfmt2xpath(char *xkfmt,
|
|||
if (skip)
|
||||
skip=0;
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
if ((c == '=' || c == ',') && xkfmt[i+1]=='%')
|
||||
; /* skip */
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
}
|
||||
if ((*xk = strdup4(cbuf_get(cb))) == NULL){
|
||||
|
|
@ -504,6 +516,7 @@ append_listkeys(cbuf *ckey,
|
|||
cvec *cvk = NULL; /* vector of index keys */
|
||||
char *keyname;
|
||||
char *bodyenc;
|
||||
int i=0;
|
||||
|
||||
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){
|
||||
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");
|
||||
goto done;
|
||||
}
|
||||
cprintf(ckey, "/%s", bodyenc);
|
||||
if (i++)
|
||||
cprintf(ckey, ",");
|
||||
else
|
||||
cprintf(ckey, "=");
|
||||
cprintf(ckey, "%s", bodyenc);
|
||||
free(bodyenc); bodyenc = NULL;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -622,8 +639,12 @@ get(char *dbname,
|
|||
int retval = -1;
|
||||
char **vec;
|
||||
int nvec;
|
||||
char **valvec;
|
||||
int nvalvec;
|
||||
int i;
|
||||
int j;
|
||||
char *name;
|
||||
char *restval;
|
||||
yang_stmt *y;
|
||||
cxobj *x;
|
||||
cxobj *xc;
|
||||
|
|
@ -654,7 +675,11 @@ get(char *dbname,
|
|||
}
|
||||
i = 1;
|
||||
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 ((y = yang_find_topnode(ys, name)) == NULL){
|
||||
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
|
||||
* be a value
|
||||
*/
|
||||
i++;
|
||||
if (i>=nvec){
|
||||
clicon_err(OE_XML, errno, "Leaf-list %s without argument", name);
|
||||
if ((argdec = curl_easy_unescape(NULL, restval, 0, NULL)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "curl_easy_escape");
|
||||
goto done;
|
||||
}
|
||||
arg = vec[i];
|
||||
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)
|
||||
goto done;
|
||||
/* Assume body is created at end of function */
|
||||
|
|
@ -706,17 +729,17 @@ get(char *dbname,
|
|||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
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;
|
||||
goto done;
|
||||
}
|
||||
j = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
i++;
|
||||
if (i>=nvec){
|
||||
clicon_err(OE_XML, errno, "List %s without argument", name);
|
||||
goto done;
|
||||
}
|
||||
arg = vec[i];
|
||||
if (j>=nvalvec)
|
||||
break;
|
||||
arg = valvec[j++];
|
||||
if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "curl_easy_escape");
|
||||
goto done;
|
||||
|
|
@ -729,12 +752,14 @@ get(char *dbname,
|
|||
if ((xc = xml_new_spec(name, x, y)) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
i -= cvec_len(cvk);
|
||||
// i -= cvec_len(cvk);
|
||||
/* Iterate over individual yang keys */
|
||||
j=0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if (j>=nvalvec)
|
||||
break;
|
||||
arg = valvec[j++];
|
||||
keyname = cv_string_get(cvi);
|
||||
i++;
|
||||
arg = vec[i];
|
||||
if ((argdec = curl_easy_unescape(NULL, arg, 0, NULL)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "curl_easy_escape");
|
||||
goto done;
|
||||
|
|
@ -908,96 +933,14 @@ xml_order(cxobj *x,
|
|||
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
|
||||
* 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
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] yspec Yang specification
|
||||
* @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.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -1016,73 +959,28 @@ xmldb_get_tree(char *dbname,
|
|||
* xml_free(xt);
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
||||
* @see xpath_vec
|
||||
* @see xmldb_get
|
||||
*/
|
||||
static int
|
||||
xmldb_get_vec(char *dbname,
|
||||
char *xpath,
|
||||
yang_spec *yspec,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
xmldb_get_local(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec0,
|
||||
size_t *xlen0)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
char *dbname = NULL;
|
||||
cxobj **xvec;
|
||||
size_t xlen;
|
||||
int i;
|
||||
int npairs;
|
||||
struct db_pair *pairs;
|
||||
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__);
|
||||
if (db2file(h, db, &dbname) < 0)
|
||||
goto done;
|
||||
|
|
@ -1094,17 +992,54 @@ xmldb_get_local(clicon_handle h,
|
|||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if (vector){
|
||||
if (xmldb_get_vec(dbname, xpath, yspec, xtop, xvec, xlen) < 0)
|
||||
/* Read in complete database (this can be optimized) */
|
||||
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;
|
||||
}
|
||||
else
|
||||
if (xmldb_get_tree(dbname, xpath, yspec, xtop) < 0)
|
||||
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 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;
|
||||
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;
|
||||
done:
|
||||
if (dbname)
|
||||
free(dbname);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
|
@ -1128,7 +1063,7 @@ xmldb_get_local(clicon_handle h,
|
|||
* size_t xlen;
|
||||
*
|
||||
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
|
||||
* 1, &xt, &xvec, &xlen) < 0)
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
|
|
@ -1143,7 +1078,6 @@ int
|
|||
xmldb_get(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int vector,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
|
|
@ -1152,9 +1086,9 @@ xmldb_get(clicon_handle h,
|
|||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
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
|
||||
retval = xmldb_get_local(h, db, xpath, vector, xtop, xvec, xlen);
|
||||
retval = xmldb_get_local(h, db, xpath, xtop, xvec, xlen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1215,6 +1149,7 @@ put(char *dbname,
|
|||
char *body;
|
||||
yang_stmt *y;
|
||||
int exists;
|
||||
char *bodyenc=NULL;
|
||||
|
||||
clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
|
||||
if (debug){
|
||||
|
|
@ -1235,7 +1170,11 @@ put(char *dbname,
|
|||
goto done;
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1283,6 +1222,8 @@ put(char *dbname,
|
|||
done:
|
||||
if (cbxk)
|
||||
cbuf_free(cbxk);
|
||||
if (bodyenc)
|
||||
free(bodyenc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1580,7 +1521,16 @@ xmldb_put_tree(clicon_handle h,
|
|||
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
|
||||
xmldb_put_xkey_local(clicon_handle h,
|
||||
char *db,
|
||||
|
|
@ -1594,8 +1544,12 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
yang_stmt *ykey;
|
||||
char **vec;
|
||||
int nvec;
|
||||
char **valvec;
|
||||
int nvalvec;
|
||||
int i;
|
||||
int j;
|
||||
char *name;
|
||||
char *restval;
|
||||
cg_var *cvi;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
char *val2 = NULL;
|
||||
|
|
@ -1632,11 +1586,15 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
}
|
||||
i = 1;
|
||||
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 (!strlen(name) && (op==OP_DELETE || op == OP_REMOVE)){
|
||||
if (strlen(name)==0 && (op==OP_DELETE || op == OP_REMOVE)){
|
||||
/* Special handling of "/" */
|
||||
cprintf(ckey, "/%s", name);
|
||||
cprintf(ckey, "/");
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
|
@ -1660,13 +1618,7 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
i++;
|
||||
switch (y->ys_keyword){
|
||||
case Y_LEAF_LIST:
|
||||
val2 = vec[i];
|
||||
if (i>=nvec){
|
||||
clicon_err(OE_XML, errno, "Leaf-list %s without argument", name);
|
||||
goto done;
|
||||
}
|
||||
i++;
|
||||
cprintf(ckey, "/%s", val2);
|
||||
cprintf(ckey, "=%s", restval);
|
||||
break;
|
||||
case Y_LIST:
|
||||
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>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
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;
|
||||
/* Iterate over individual yang keys */
|
||||
j = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
val2 = vec[i++];
|
||||
if (i>nvec){ /* XXX >= ? */
|
||||
clicon_err(OE_XML, errno, "List %s without argument", name);
|
||||
goto done;
|
||||
}
|
||||
cprintf(ckey, "/%s", val2);
|
||||
if (j)
|
||||
cprintf(ckey, ",");
|
||||
else
|
||||
cprintf(ckey, "=");
|
||||
val2 = valvec[j++];
|
||||
|
||||
cprintf(ckey, "%s", val2);
|
||||
cbuf_reset(csubkey);
|
||||
cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname);
|
||||
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
|
||||
|
|
@ -2124,7 +2085,7 @@ main(int argc, char **argv)
|
|||
if (argc < 5)
|
||||
usage(argv[0]);
|
||||
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;
|
||||
clicon_xml2file(stdout, xt, 0, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,7 +243,6 @@ int
|
|||
xmldb_get_rpc(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int vector,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
|
|
@ -263,8 +262,6 @@ xmldb_get_rpc(clicon_handle h,
|
|||
cprintf(cb, "<source><%s/></source>", db);
|
||||
if (xpath)
|
||||
cprintf(cb, "<xpath>%s</xpath>", xpath);
|
||||
if (vector)
|
||||
cprintf(cb, "<vector/>");
|
||||
cprintf(cb, "</get></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
|
|
@ -273,7 +270,7 @@ xmldb_get_rpc(clicon_handle h,
|
|||
goto done;
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (vector){
|
||||
if (xvec){
|
||||
i=0;
|
||||
if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
* Prototypes
|
||||
*/
|
||||
int xmldb_get_rpc(clicon_handle h, char *db,
|
||||
char *xpath, int vector,
|
||||
char *xpath,
|
||||
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_xkey_rpc(clicon_handle h, char *db, char *xk, char *val,
|
||||
|
|
|
|||
|
|
@ -938,7 +938,7 @@ xpath_each(cxobj *cxtop,
|
|||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec(cxtop, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* got err;
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
* ...
|
||||
|
|
|
|||
|
|
@ -419,10 +419,8 @@ yang_find(yang_node *yn,
|
|||
* the following keyword: container, leaf, list, leaf-list
|
||||
* That is, basic syntax nodes.
|
||||
* @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_find_syntax(yang_node *yn,
|
||||
char *argument)
|
||||
|
|
@ -1638,66 +1636,6 @@ yang_apply(yang_node *yn,
|
|||
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.
|
||||
Ignore prefixes, see _abs */
|
||||
static yang_node *
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue