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

@ -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;
@ -638,7 +659,7 @@ get(char *dbname,
// clicon_debug(1, "%s xkey:%s val:%s", __FUNCTION__, xk, val);
x = xt;
if (xk == NULL || *xk!='/'){
if (xk == NULL || *xk!='/'){
clicon_err(OE_DB, 0, "Invalid key: %s", xk);
goto done;
}
@ -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);
}