* Optimized validation by making xml_diff work on raw cache tree (not copies)
* xmldb_get() removed unnecessary config option
This commit is contained in:
parent
4fbec973d7
commit
e29cd7cfb9
57 changed files with 1044 additions and 263 deletions
|
|
@ -172,42 +172,6 @@ xmldb_disconnect(clicon_handle h)
|
|||
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] h Clicon handle
|
||||
* @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] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt, NULL) < 0)
|
||||
* err;
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
||||
* @see xpath_vec
|
||||
*/
|
||||
int
|
||||
xmldb_get(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xret,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE"))
|
||||
retval = xmldb_get_cache(h, db, xpath, config, xret, msd);
|
||||
else
|
||||
retval = xmldb_get_nocache(h, db, xpath, config, xret, msd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Copy database from db1 to db2
|
||||
* @param[in] h Clicon handle
|
||||
|
|
@ -264,11 +228,11 @@ xmldb_copy(clicon_handle h,
|
|||
clicon_db_elmnt_set(h, to, &de0);
|
||||
}
|
||||
}
|
||||
/* Copy the files themselves (above only in-memory cache) */
|
||||
if (xmldb_db2file(h, from, &fromfile) < 0)
|
||||
goto done;
|
||||
if (xmldb_db2file(h, to, &tofile) < 0)
|
||||
goto done;
|
||||
/* Copy the files themselves (above only in-memory cache) */
|
||||
if (clicon_file_copy(fromfile, tofile) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -374,18 +374,16 @@ xmldb_readfile(clicon_handle h,
|
|||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see xmldb_get the generic API function
|
||||
*/
|
||||
int
|
||||
static int
|
||||
xmldb_get_nocache(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
|
|
@ -457,15 +455,6 @@ xmldb_get_nocache(clicon_handle h,
|
|||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||
goto done;
|
||||
|
||||
/* filter out state (operations) data if config not set. Mark all nodes
|
||||
that are not config data */
|
||||
if (config){
|
||||
if (xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
|
||||
goto done;
|
||||
/* Remove (prune) nodes that are marked (that does not pass test) */
|
||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Add default values (if not set) */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
|
|
@ -497,20 +486,18 @@ xmldb_get_nocache(clicon_handle h,
|
|||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see xmldb_get the generic API function
|
||||
*/
|
||||
int
|
||||
static int
|
||||
xmldb_get_cache(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -591,3 +578,183 @@ xmldb_get_cache(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get the raw cache of whole tree
|
||||
* Useful for some higer level usecases for optimized access
|
||||
* This is a clixon datastore plugin of the the xmldb api
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xmldb_get1_cache(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
modstate_diff_t *modst)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
cxobj *x0t = NULL; /* (cached) top of tree */
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen;
|
||||
int i;
|
||||
cxobj *x0;
|
||||
db_elmnt *de = NULL;
|
||||
db_elmnt de0 = {0,};
|
||||
|
||||
if (!clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
|
||||
clicon_err(OE_CFG, 0, "CLICON_XMLDB_CACHE must be set");
|
||||
goto done;
|
||||
}
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
de = clicon_db_elmnt_get(h, db);
|
||||
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
|
||||
/* If there is no xml x0 tree (in cache), then read it from file */
|
||||
if (xmldb_readfile(h, db, yspec, &x0t, modst) < 0)
|
||||
goto done;
|
||||
/* XXX: should we validate file if read from disk?
|
||||
* Argument against: we may want to have a semantically wrong file and wish
|
||||
* to edit?
|
||||
*/
|
||||
de0.de_xml = x0t;
|
||||
clicon_db_elmnt_set(h, db, &de0);
|
||||
} /* x0t == NULL */
|
||||
else
|
||||
x0t = de->de_xml;
|
||||
/* Here xt looks like: <config>...</config> */
|
||||
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
|
||||
goto done;
|
||||
/* Iterate through the match vector
|
||||
* For every node found in x0, mark the tree up to t1
|
||||
*/
|
||||
for (i=0; i<xlen; i++){
|
||||
x0 = xvec[i];
|
||||
xml_flag_set(x0, XML_FLAG_MARK);
|
||||
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
|
||||
}
|
||||
/* Apply default values (removed in clear function) */
|
||||
if (xml_apply(x0t, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
if (debug>1)
|
||||
clicon_xml2file(stderr, x0t, 0, 1);
|
||||
*xtop = x0t;
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
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] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt, NULL) < 0)
|
||||
* err;
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
||||
* @see xpath_vec
|
||||
*/
|
||||
int
|
||||
xmldb_get(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xret,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE"))
|
||||
retval = xmldb_get_cache(h, db, xpath, xret, msd);
|
||||
else
|
||||
retval = xmldb_get_nocache(h, db, xpath, xret, msd);
|
||||
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] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[out] xret Single return XML tree. see note
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt, NULL) < 0)
|
||||
* err;
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @note if xvec is given, then purge tree, if not return whole tree.
|
||||
* @see xmldb_get This version uses direct cache access and needs to be
|
||||
* cleanued up after use
|
||||
* @see xmldb_get1_clean Must call after use
|
||||
* @note If !CLICON_XMLDB_CACHE you need to free xret after use
|
||||
* This should probably replace xmldb_get completely
|
||||
*/
|
||||
int
|
||||
xmldb_get1(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
cxobj **xret,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE"))
|
||||
retval = xmldb_get1_cache(h, db, xpath, xret, msd);
|
||||
else
|
||||
retval = xmldb_get_nocache(h, db, xpath, xret, msd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Clear cached tree after accessed by xmldb_get1
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] dbname Name of database to search in (filename including dir path
|
||||
* @see xmldb_get1
|
||||
*/
|
||||
int
|
||||
xmldb_get1_clear(clicon_handle h,
|
||||
const char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
db_elmnt *de = NULL;
|
||||
|
||||
if (!clicon_option_bool(h, "CLICON_XMLDB_CACHE"))
|
||||
goto ok; /* dont bother, tree is a copy */
|
||||
de = clicon_db_elmnt_get(h, db);
|
||||
if (de != NULL && de->de_xml != NULL){
|
||||
/* clear XML tree of defaults */
|
||||
if (xml_tree_prune_flagged(de->de_xml, XML_FLAG_DEFAULT, 1) < 0)
|
||||
goto done;
|
||||
/* clear mark and change */
|
||||
xml_apply0(de->de_xml, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||
(void*)(0xff));
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xmldb_get_cache(clicon_handle h, const char *db, char *xpath, int config, cxobj **xret, modstate_diff_t *msd);
|
||||
int xmldb_get_nocache(clicon_handle h, const char *db, char *xpath, int config, cxobj **xret, modstate_diff_t *msd);
|
||||
int xmldb_readfile(clicon_handle h, const char *db, yang_stmt *yspec, cxobj **xp, modstate_diff_t *msd);
|
||||
int xmldb_readfile(clicon_handle h, const char *db, yang_stmt *yspec, cxobj **xp, modstate_diff_t *msd);
|
||||
|
||||
#endif /* _CLIXON_DATASTORE_READ_H */
|
||||
|
|
|
|||
|
|
@ -30,15 +30,6 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
1000 entries
|
||||
valgrind --tool=callgrind datastore_client -d candidate -b /tmp/text -p ../datastore/text/text.so -y /tmp -m ietf-ip mget 300 /x/y[a=574][b=574] > /dev/null
|
||||
xml_copy_marked 87% 200x
|
||||
yang_key_match 81% 600K
|
||||
yang_arg2cvec 52% 400K
|
||||
cvecfree 23% 400K
|
||||
|
||||
10000 entries
|
||||
valgrind --tool=callgrind datastore_client -d candidate -b /tmp/text -p ../datastore/text/text.so -y /tmp -m ietf-ip mget 10 /x/y[a=574][b=574] > /dev/null
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -136,6 +127,7 @@ text_modify(clicon_handle h,
|
|||
cxobj **x0vec = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
int changed = 0; /* Only if x0p's children have changed-> sort is necessary */
|
||||
|
||||
assert(x1 && xml_type(x1) == CX_ELMNT);
|
||||
assert(y0);
|
||||
|
|
@ -167,6 +159,7 @@ text_modify(clicon_handle h,
|
|||
// int iamkey=0;
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
changed++;
|
||||
|
||||
/* Copy xmlns attributes */
|
||||
x1a = NULL;
|
||||
|
|
@ -291,6 +284,7 @@ text_modify(clicon_handle h,
|
|||
}
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
goto done;
|
||||
changed++;
|
||||
/* Copy xmlns attributes */
|
||||
x1a = NULL;
|
||||
while ((x1a = xml_child_each(x1, x1a, CX_ATTR)) != NULL)
|
||||
|
|
@ -368,13 +362,15 @@ text_modify(clicon_handle h,
|
|||
}
|
||||
if (xml_purge(x0) < 0)
|
||||
goto done;
|
||||
changed++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} /* CONTAINER switch op */
|
||||
} /* else Y_CONTAINER */
|
||||
xml_sort(x0p, NULL);
|
||||
if (changed)
|
||||
xml_sort(x0p, NULL);
|
||||
retval = 1;
|
||||
done:
|
||||
if (x0vec)
|
||||
|
|
@ -671,7 +667,7 @@ xmldb_put(clicon_handle h,
|
|||
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
|
||||
goto done;
|
||||
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||
(void*)XML_FLAG_NONE) < 0)
|
||||
(void*)(XML_FLAG_NONE|XML_FLAG_MARK)) < 0)
|
||||
goto done;
|
||||
/* Mark non-presence containers that do not have children */
|
||||
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_container_presence, NULL) < 0)
|
||||
|
|
|
|||
|
|
@ -890,7 +890,7 @@ nacm_access_pre(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
else if (strcmp(mode, "internal")==0){
|
||||
if (xmldb_get(h, "running", "nacm", 0, &xnacm0, NULL) < 0)
|
||||
if (xmldb_get(h, "running", "nacm", &xnacm0, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -811,6 +811,7 @@ xml_find(cxobj *x_up,
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see xml_wrap
|
||||
* @note xc is not sorted correctly, need to call xml_sort on parent
|
||||
*/
|
||||
int
|
||||
xml_addsub(cxobj *xp,
|
||||
|
|
@ -1094,6 +1095,18 @@ xml_enumerate_children(cxobj *xp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Reset enumeration as done by xml_enumerate_children
|
||||
*/
|
||||
int
|
||||
xml_enumerate_reset(cxobj *xp)
|
||||
{
|
||||
cxobj *x = NULL;
|
||||
|
||||
while ((x = xml_child_each(xp, x, -1)) != NULL)
|
||||
x->_x_i = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get the enumeration of a single child set by enumeration of parent
|
||||
* @see xml_children_enumerate
|
||||
* @note that it has to be called right after xml_children_enumerate. If not,
|
||||
|
|
|
|||
|
|
@ -1051,96 +1051,98 @@ cvec2xml_1(cvec *cvv,
|
|||
}
|
||||
|
||||
/*! Recursive help function to compute differences between two xml trees
|
||||
* @param[in] x1 First XML tree
|
||||
* @param[in] x2 Second XML tree
|
||||
* @param[out] x1vec Pointervector to XML nodes existing in only first tree
|
||||
* @param[out] x1veclen Length of first vector
|
||||
* @param[out] x2vec Pointervector to XML nodes existing in only second tree
|
||||
* @param[out] x2veclen Length of x2vec vector
|
||||
* @param[out] changed_x1 Pointervector to XML nodes changed orig value
|
||||
* @param[out] changed_x2 Pointervector to XML nodes changed wanted value
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @param[out] x0vec Pointervector to XML nodes existing in only first tree
|
||||
* @param[out] x0veclen Length of first vector
|
||||
* @param[out] x1vec Pointervector to XML nodes existing in only second tree
|
||||
* @param[out] x1veclen Length of x1vec vector
|
||||
* @param[out] changed_x0 Pointervector to XML nodes changed orig value
|
||||
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
|
||||
* @param[out] changedlen Length of changed vector
|
||||
*/
|
||||
static int
|
||||
xml_diff1(yang_stmt *ys,
|
||||
cxobj *x1,
|
||||
cxobj *x2,
|
||||
cxobj *x0,
|
||||
cxobj *x1,
|
||||
cxobj ***x0vec,
|
||||
size_t *x0veclen,
|
||||
cxobj ***x1vec,
|
||||
size_t *x1veclen,
|
||||
cxobj ***x2vec,
|
||||
size_t *x2veclen,
|
||||
cxobj ***changed_x0,
|
||||
cxobj ***changed_x1,
|
||||
cxobj ***changed_x2,
|
||||
size_t *changedlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x0c = NULL; /* x0 child */
|
||||
cxobj *x1c = NULL; /* x1 child */
|
||||
cxobj *x2c = NULL; /* x2 child */
|
||||
yang_stmt *yc;
|
||||
char *b1;
|
||||
char *b2;
|
||||
|
||||
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
|
||||
/* Check nodes present in x1 and x2 + nodes only in x1
|
||||
* Loop over x1
|
||||
/* Check nodes present in x0 and x1 + nodes only in x0
|
||||
* Loop over x0
|
||||
* XXX: room for improvement. Compare with match_base_child()
|
||||
*/
|
||||
x0c = NULL;
|
||||
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
|
||||
if ((yc = xml_spec(x0c)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x0c));
|
||||
goto done;
|
||||
}
|
||||
/* Does x1 have a child matching x0c? */
|
||||
if (match_base_child(x1, x0c, yc, &x1c) < 0)
|
||||
goto done;
|
||||
if (x1c == NULL){
|
||||
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (yang_choice(yc)){
|
||||
/* if x0c and x1c are choice/case, then they are changed */
|
||||
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
|
||||
goto done;
|
||||
(*changedlen)--; /* append two vectors */
|
||||
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* if x0c and x1c are leafs w bodies, then they are changed */
|
||||
if (yc->ys_keyword == Y_LEAF){
|
||||
if ((b1 = xml_body(x0c)) == NULL) /* empty type */
|
||||
break;
|
||||
if ((b2 = xml_body(x1c)) == NULL) /* empty type */
|
||||
break;
|
||||
if (strcmp(b1, b2)){
|
||||
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
|
||||
goto done;
|
||||
(*changedlen)--; /* append two vectors */
|
||||
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (xml_diff1(yc, x0c, x1c,
|
||||
x0vec, x0veclen,
|
||||
x1vec, x1veclen,
|
||||
changed_x0, changed_x1, changedlen)< 0)
|
||||
goto done;
|
||||
}
|
||||
} /* while x0 */
|
||||
/* Check nodes present only in x1
|
||||
* Loop over x1
|
||||
*/
|
||||
x1c = NULL;
|
||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
|
||||
if ((yc = xml_spec(x1c)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
|
||||
goto done;
|
||||
}
|
||||
if (match_base_child(x2, x1c, yc, &x2c) < 0)
|
||||
/* Does x0 have a child matching x1c? */
|
||||
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||
goto done;
|
||||
if (x2c == NULL){
|
||||
if (x0c == NULL)
|
||||
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (yang_choice(yc)){
|
||||
/* if x1c and x2c are choice/case, then they are changed */
|
||||
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
(*changedlen)--; /* append two vectors */
|
||||
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* if x1c and x2c are leafs w bodies, then they are changed */
|
||||
if (yc->ys_keyword == Y_LEAF){
|
||||
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
|
||||
break;
|
||||
if ((b2 = xml_body(x2c)) == NULL) /* empty type */
|
||||
break;
|
||||
if (strcmp(b1, b2)){
|
||||
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
(*changedlen)--; /* append two vectors */
|
||||
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (xml_diff1(yc, x1c, x2c,
|
||||
x1vec, x1veclen,
|
||||
x2vec, x2veclen,
|
||||
changed_x1, changed_x2, changedlen)< 0)
|
||||
goto done;
|
||||
}
|
||||
} /* while x1 */
|
||||
/* Check nodes present only in x2
|
||||
* Loop over x2
|
||||
*/
|
||||
x2c = NULL;
|
||||
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
|
||||
if ((yc = xml_spec(x2c)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x2c));
|
||||
goto done;
|
||||
}
|
||||
if (match_base_child(x1, x2c, yc, &x1c) < 0)
|
||||
goto done;
|
||||
if (x1c == NULL)
|
||||
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
|
||||
goto done;
|
||||
} /* while x1 */
|
||||
} /* while x0 */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1148,28 +1150,28 @@ xml_diff1(yang_stmt *ys,
|
|||
|
||||
/*! Compute differences between two xml trees
|
||||
* @param[in] yspec Yang specification
|
||||
* @param[in] x1 First XML tree
|
||||
* @param[in] x2 Second XML tree
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @param[out] first Pointervector to XML nodes existing in only first tree
|
||||
* @param[out] firstlen Length of first vector
|
||||
* @param[out] second Pointervector to XML nodes existing in only second tree
|
||||
* @param[out] secondlen Length of second vector
|
||||
* @param[out] changed1 Pointervector to XML nodes changed orig value
|
||||
* @param[out] changed2 Pointervector to XML nodes changed wanted value
|
||||
* @param[out] changed_x0 Pointervector to XML nodes changed orig value
|
||||
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
|
||||
* @param[out] changedlen Length of changed vector
|
||||
* All xml vectors should be freed after use.
|
||||
* Bot xml trees should be freed with xml_free()
|
||||
*/
|
||||
int
|
||||
xml_diff(yang_stmt *yspec,
|
||||
cxobj *x1,
|
||||
cxobj *x2,
|
||||
cxobj *x0,
|
||||
cxobj *x1,
|
||||
cxobj ***first,
|
||||
size_t *firstlen,
|
||||
cxobj ***second,
|
||||
size_t *secondlen,
|
||||
cxobj ***changed1,
|
||||
cxobj ***changed2,
|
||||
cxobj ***changed_x0,
|
||||
cxobj ***changed_x1,
|
||||
size_t *changedlen)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1177,22 +1179,22 @@ xml_diff(yang_stmt *yspec,
|
|||
*firstlen = 0;
|
||||
*secondlen = 0;
|
||||
*changedlen = 0;
|
||||
if (x1 == NULL && x2 == NULL)
|
||||
if (x0 == NULL && x1 == NULL)
|
||||
return 0;
|
||||
if (x2 == NULL){
|
||||
if (cxvec_append(x1, first, firstlen) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (x1 == NULL){
|
||||
if (cxvec_append(x1, second, secondlen) < 0)
|
||||
if (cxvec_append(x0, first, firstlen) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (xml_diff1((yang_stmt*)yspec, x1, x2,
|
||||
if (x0 == NULL){
|
||||
if (cxvec_append(x0, second, secondlen) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (xml_diff1((yang_stmt*)yspec, x0, x1,
|
||||
first, firstlen,
|
||||
second, secondlen,
|
||||
changed1, changed2, changedlen) < 0)
|
||||
changed_x0, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1565,14 +1567,13 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Prune everything that passes test
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] flag Which flag to test for
|
||||
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||
* The function removes all branches that does not pass test
|
||||
* @code
|
||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
|
|
@ -1638,6 +1639,7 @@ xml_default(cxobj *xt,
|
|||
if (!xml_find(xt, y->ys_argument)){
|
||||
if ((xc = xml_new(y->ys_argument, xt, y)) == NULL)
|
||||
goto done;
|
||||
xml_flag_set(xc, XML_FLAG_DEFAULT);
|
||||
if ((xb = xml_new("body", xc, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xb, CX_BODY);
|
||||
|
|
@ -1652,7 +1654,7 @@ xml_default(cxobj *xt,
|
|||
}
|
||||
}
|
||||
}
|
||||
// xml_sort(xt, NULL);
|
||||
xml_sort(xt, NULL);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@
|
|||
*/
|
||||
static int
|
||||
xml_cv_cache(cxobj *x,
|
||||
char *body,
|
||||
cg_var **cvp)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -88,7 +87,9 @@ xml_cv_cache(cxobj *x,
|
|||
char *reason=NULL;
|
||||
int options = 0;
|
||||
uint8_t fraction = 0;
|
||||
|
||||
char *body;
|
||||
|
||||
body = xml_body(x);
|
||||
if ((cv = xml_cv(x)) != NULL)
|
||||
goto ok;
|
||||
if ((y = xml_spec(x)) == NULL)
|
||||
|
|
@ -186,24 +187,21 @@ xml_child_spec(cxobj *x,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Help function to qsort for sorting entries in xml child vector
|
||||
* @param[in] arg1 - actually cxobj**
|
||||
* @param[in] arg2 - actually cxobj**
|
||||
/*! Help function to qsort for sorting entries in xml child vector same parent
|
||||
* @param[in] xml object 1
|
||||
* @param[in] xml object 2
|
||||
* @retval 0 If equal
|
||||
* @retval <0 if arg1 is less than arg2
|
||||
* @retval >0 if arg1 is greater than arg2
|
||||
* @note args are pointer ot pointers, to fit into qsort cmp function
|
||||
* @retval <0 if x1 is less than x2
|
||||
* @retval >0 if x1 is greater than x2
|
||||
* @see xml_cmp1 Similar, but for one object
|
||||
* @note empty value/NULL is smallest value
|
||||
* @note xml_enumerate_children must have been called prior to this call
|
||||
* @note some error cases return as -1 (qsort cant handle errors)
|
||||
*/
|
||||
static int
|
||||
xml_cmp(const void* arg1,
|
||||
const void* arg2)
|
||||
xml_cmp(cxobj *x1,
|
||||
cxobj *x2)
|
||||
{
|
||||
cxobj *x1 = *(struct xml**)arg1;
|
||||
cxobj *x2 = *(struct xml**)arg2;
|
||||
yang_stmt *y1;
|
||||
yang_stmt *y2;
|
||||
int yi1 = 0;
|
||||
|
|
@ -242,7 +240,6 @@ xml_cmp(const void* arg1,
|
|||
equal = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
e=2;
|
||||
if (y1 != y2){
|
||||
yi1 = yang_order(y1);
|
||||
|
|
@ -268,9 +265,9 @@ xml_cmp(const void* arg1,
|
|||
else if ((b2 = xml_body(x2)) == NULL)
|
||||
equal = 1;
|
||||
else{
|
||||
if (xml_cv_cache(x1, b1, &cv1) < 0) /* error case */
|
||||
if (xml_cv_cache(x1, &cv1) < 0) /* error case */
|
||||
goto done;
|
||||
if (xml_cv_cache(x2, b2, &cv2) < 0) /* error case */
|
||||
if (xml_cv_cache(x2, &cv2) < 0) /* error case */
|
||||
goto done;
|
||||
equal = cv_cmp(cv1, cv2);
|
||||
}
|
||||
|
|
@ -283,15 +280,15 @@ xml_cmp(const void* arg1,
|
|||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi); /* operational data may have NULL keys*/
|
||||
if ((x1b = xml_find(x1, keyname)) == NULL ||
|
||||
(b1 = xml_body(x1b)) == NULL)
|
||||
xml_body(x1b) == NULL)
|
||||
equal = -1;
|
||||
else if ((x2b = xml_find(x2, keyname)) == NULL ||
|
||||
(b2 = xml_body(x2b)) == NULL)
|
||||
xml_body(x2b) == NULL)
|
||||
equal = 1;
|
||||
else{
|
||||
if (xml_cv_cache(x1b, b1, &cv1) < 0) /* error case */
|
||||
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
|
||||
goto done;
|
||||
if (xml_cv_cache(x2b, b2, &cv2) < 0) /* error case */
|
||||
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
|
||||
goto done;
|
||||
if ((equal = cv_cmp(cv1, cv2)) != 0)
|
||||
goto done;
|
||||
|
|
@ -308,6 +305,16 @@ xml_cmp(const void* arg1,
|
|||
return equal;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @note args are pointer ot pointers, to fit into qsort cmp function
|
||||
*/
|
||||
static int
|
||||
xml_cmp_qsort(const void* arg1,
|
||||
const void* arg2)
|
||||
{
|
||||
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2);
|
||||
}
|
||||
|
||||
/*! Compare xml object
|
||||
* @param[in] x XML node to compare with
|
||||
* @param[in] y The yang spec of x
|
||||
|
|
@ -358,7 +365,7 @@ xml_cmp1(cxobj *x,
|
|||
match = 1;
|
||||
else{
|
||||
if (keycvec[0]){
|
||||
if (xml_cv_cache(x, b, &cv) < 0) /* error case */
|
||||
if (xml_cv_cache(x, &cv) < 0) /* error case */
|
||||
goto done;
|
||||
match = cv_cmp(keycvec[0], cv);
|
||||
}
|
||||
|
|
@ -377,7 +384,7 @@ xml_cmp1(cxobj *x,
|
|||
break; /* error case */
|
||||
if ((b = xml_body(xb)) == NULL)
|
||||
break; /* error case */
|
||||
if (xml_cv_cache(xb, b, &cv) < 0) /* error case */
|
||||
if (xml_cv_cache(xb, &cv) < 0) /* error case */
|
||||
goto done;
|
||||
if (keycvec[i]){
|
||||
if ((match = cv_cmp(keycvec[i], cv)) != 0)
|
||||
|
|
@ -395,7 +402,7 @@ xml_cmp1(cxobj *x,
|
|||
return match;
|
||||
}
|
||||
|
||||
/*! Sort children of an XML node
|
||||
/*! Sort children of an XML node
|
||||
* Assume populated by yang spec.
|
||||
* @param[in] x0 XML node
|
||||
* @param[in] arg Dummy so it can be called by xml_apply()
|
||||
|
|
@ -414,7 +421,7 @@ xml_sort(cxobj *x,
|
|||
if ((ys = xml_spec(x)) != 0 && yang_config(ys)==0)
|
||||
return 1;
|
||||
xml_enumerate_children(x);
|
||||
qsort(xml_childvec_get(x), xml_child_nr(x), sizeof(cxobj *), xml_cmp);
|
||||
qsort(xml_childvec_get(x), xml_child_nr(x), sizeof(cxobj *), xml_cmp_qsort);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -489,6 +496,7 @@ xml_search1(cxobj *x0,
|
|||
if ((y = xml_spec(xc)) == NULL)
|
||||
return NULL;
|
||||
cmp = yangi-yang_order(y);
|
||||
/* Here is right yang order == same yang? */
|
||||
if (cmp == 0){
|
||||
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, &userorder);
|
||||
if (userorder && cmp) /* Look inside this yangi order */
|
||||
|
|
@ -507,9 +515,9 @@ xml_search1(cxobj *x0,
|
|||
|
||||
/*! Find XML children using binary search
|
||||
* @param[in] yangi yang child order
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
* @param[in] keynr Length of keyvec/keyval vector when applicable
|
||||
* @param[in] keyvec Array of of yang key identifiers
|
||||
* @param[in] keyval Array of of yang key values
|
||||
*/
|
||||
static cxobj *
|
||||
xml_search(cxobj *x0,
|
||||
|
|
@ -534,7 +542,65 @@ xml_search(cxobj *x0,
|
|||
low, high);
|
||||
}
|
||||
|
||||
|
||||
#ifdef NOTUSED
|
||||
/*! Position where to insert xml object into a list of children nodes
|
||||
* @note EXPERIMENTAL
|
||||
* Insert after position returned
|
||||
* @param[in] x0 XML parent node.
|
||||
* @param[in] low Lower bound
|
||||
* @param[in] upper Upper bound (+1)
|
||||
* @retval position
|
||||
* XXX: Problem with this is that evrything must be known before insertion
|
||||
*/
|
||||
int
|
||||
xml_insert_pos(cxobj *x0,
|
||||
char *name,
|
||||
int yangi,
|
||||
enum rfc_6020 keyword,
|
||||
int keynr,
|
||||
char **keyvec,
|
||||
char **keyval,
|
||||
int low,
|
||||
int upper)
|
||||
{
|
||||
int mid;
|
||||
cxobj *xc;
|
||||
yang_stmt *y;
|
||||
int cmp;
|
||||
int i;
|
||||
int userorder= 0;
|
||||
|
||||
if (upper < low)
|
||||
return low; /* not found */
|
||||
mid = (low + upper) / 2;
|
||||
if (mid >= xml_child_nr(x0))
|
||||
return xml_child_nr(x0); /* upper range */
|
||||
xc = xml_child_i(x0, mid);
|
||||
y = xml_spec(xc);
|
||||
cmp = yangi-yang_order(y);
|
||||
if (cmp == 0){
|
||||
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, &userorder);
|
||||
if (userorder){ /* Look inside this yangi order */
|
||||
/* Special case: append last of equals if ordered by user */
|
||||
for (i=mid+1;i<xml_child_nr(x0);i++){
|
||||
xc = xml_child_i(x0, i);
|
||||
if (strcmp(xml_name(xc), name))
|
||||
break;
|
||||
mid=i; /* still ok */
|
||||
}
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
if (cmp == 0)
|
||||
return mid;
|
||||
else if (cmp < 0)
|
||||
return xml_insert_pos(x0, name, yangi, keyword,
|
||||
keynr, keyvec, keyval, keycvec, low, mid-1);
|
||||
else
|
||||
return xml_insert_pos(x0, name, yangi, keyword,
|
||||
keynr, keyvec, keyval, keycvec, mid+1, upper);
|
||||
}
|
||||
#endif /* NOTUSED */
|
||||
|
||||
/*! Verify all children of XML node are sorted according to xml_sort()
|
||||
* @param[in] x XML node. Check its children
|
||||
|
|
@ -560,7 +626,7 @@ xml_sort_verify(cxobj *x0,
|
|||
xml_enumerate_children(x0);
|
||||
while ((x = xml_child_each(x0, x, -1)) != NULL) {
|
||||
if (xprev != NULL){ /* Check xprev <= x */
|
||||
if (xml_cmp(&xprev, &x) > 0)
|
||||
if (xml_cmp(xprev, x) > 0)
|
||||
goto done;
|
||||
}
|
||||
xprev = x;
|
||||
|
|
@ -632,7 +698,7 @@ match_base_child(cxobj *x0,
|
|||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
if (xml_cv_cache(x1c, keyval[0], &keycvec[0]) < 0) /* error case */
|
||||
if (xml_cv_cache(x1c, &keycvec[0]) < 0) /* error case */
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST: /* Match with key values */
|
||||
|
|
@ -665,7 +731,7 @@ match_base_child(cxobj *x0,
|
|||
if ((b = xml_body(xb)) == NULL)
|
||||
goto ok;
|
||||
keyval[i] = b;
|
||||
if (xml_cv_cache(xb, b, &keycvec[i]) < 0) /* error case */
|
||||
if (xml_cv_cache(xb, &keycvec[i]) < 0) /* error case */
|
||||
goto done;
|
||||
i++;
|
||||
}
|
||||
|
|
@ -673,8 +739,7 @@ match_base_child(cxobj *x0,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
/* Get match.
|
||||
*/
|
||||
/* Get match. */
|
||||
yorder = yang_order(yc);
|
||||
x0c = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval, keycvec);
|
||||
ok:
|
||||
|
|
@ -685,6 +750,8 @@ match_base_child(cxobj *x0,
|
|||
free(keyval);
|
||||
if (keyvec)
|
||||
free(keyvec);
|
||||
if (keycvec)
|
||||
free(keycvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -839,9 +839,9 @@ xp_union(xp_ctx *xc1,
|
|||
* @param[out] xrp Resulting context
|
||||
*/
|
||||
static int
|
||||
xp_eval(xp_ctx *xc,
|
||||
xp_eval(xp_ctx *xc,
|
||||
xpath_tree *xs,
|
||||
xp_ctx **xrp)
|
||||
xp_ctx **xrp)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue