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
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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