* Added yang-binding yb parameter to xmldb_get0() and all xmldb get functions.

This commit is contained in:
Olof hagsand 2020-08-03 13:57:04 +02:00
parent 5911d23efc
commit 65733ffe69
12 changed files with 132 additions and 78 deletions

View file

@ -61,6 +61,7 @@ Expected: July 2020
### C/CLI-API changes on existing features (For developers)
* Added yang-binding yb parameter to xmldb_get0() and all xmldb get functions.
* Changed module-specific upgrade API, not backward compatible. The API has been simplified which means more has to be done by the programmer.
* In summary, a user registers an upgrade callback per module. The callback is called at startup if the module is added, has been removed or if the revision on file is different from the one in the system.
* The register function has removed `from` and `rev` parameters: `upgrade_callback_register(h, cb, namespace, arg)`

View file

@ -416,7 +416,7 @@ client_get_config_only(clicon_handle h,
* so zero-copy cant be used
* Also, must use external namespace context here due to <filter stmt
*/
if (xmldb_get0(h, db, nsc, xpath, 1, &xret, NULL) < 0) {
if (xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, &xret, NULL) < 0) {
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
@ -1049,14 +1049,14 @@ from_client_get(clicon_handle h,
* Also, must use external namespace context here due to <filter> stmt
*/
if (clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){
if (xmldb_get0(h, "running", nsc, NULL, 1, &xret, NULL) < 0) {
if (xmldb_get0(h, "running", YB_MODULE, nsc, NULL, 1, &xret, NULL) < 0) {
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;
}
}
else{
if (xmldb_get0(h, "running", nsc, xpath, 1, &xret, NULL) < 0) {
if (xmldb_get0(h, "running", YB_MODULE, nsc, xpath, 1, &xret, NULL) < 0) {
if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done;
goto ok;

View file

@ -189,8 +189,16 @@ startup_common(clicon_handle h,
if ((msdiff = modstate_diff_new()) == NULL)
goto done;
clicon_debug(1, "Reading startup config from %s", db);
if (xmldb_get0(h, db, NULL, "/", 0, &xt, msdiff) < 0)
/* Get the startup datastore WITHOUT binding to YANG, sorting and default setting.
* It is done below, later in this function
*/
if (xmldb_get0(h, db, YB_NONE, NULL, "/", 0, &xt, msdiff) < 0)
goto done;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, 0, "Yang spec not set");
goto done;
}
clicon_debug(1, "Reading startup config done");
/* Clear flags xpath for get */
xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
@ -206,24 +214,19 @@ startup_common(clicon_handle h,
if (ret == 0)
goto fail;
}
#if 1
if (clicon_quit_upgrade_get(h) == 1){ /* Print upgraded db */
/* Print upgraded db: -q backend switch */
if (clicon_quit_upgrade_get(h) == 1){
if (xmldb_dump(h, stdout, xt) < 0)
goto done;
exit(0); /* This is fairly abrupt , but need to avoid side-effects of rewinding
stack. Alternative is to make a separate function stack for this. */
}
#endif
/* If empty skip. Note upgrading can add children, so it may be empty before that. */
if (xml_child_nr(xt) == 0){
td->td_target = xt;
xt = NULL;
goto ok;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, 0, "Yang spec not set");
goto done;
}
/* After upgrading, XML tree needs to be sorted and yang spec populated */
if ((ret = xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 0)
goto done;
@ -434,7 +437,7 @@ from_validate_common(clicon_handle h,
goto done;
}
/* This is the state we are going to */
if (xmldb_get0(h, candidate, NULL, "/", 0, &td->td_target, NULL) < 0)
if (xmldb_get0(h, candidate, YB_MODULE, NULL, "/", 0, &td->td_target, NULL) < 0)
goto done;
/* Clear flags xpath for get */
@ -452,7 +455,7 @@ from_validate_common(clicon_handle h,
/* 2. Parse xml trees
* This is the state we are going from */
if (xmldb_get0(h, "running", NULL, "/", 0, &td->td_src, NULL) < 0)
if (xmldb_get0(h, "running", YB_MODULE, NULL, "/", 0, &td->td_src, NULL) < 0)
goto done;
/* Clear flags xpath for get */
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
@ -861,7 +864,7 @@ from_client_restart_one(clicon_handle h,
if ((td = transaction_new()) == NULL)
goto done;
/* This is the state we are going to */
if (xmldb_get0(h, "running", NULL, "/", 0, &td->td_target, NULL) < 0)
if (xmldb_get0(h, "running", YB_MODULE, NULL, "/", 0, &td->td_target, NULL) < 0)
goto done;
if ((ret = xml_yang_validate_all_top(h, td->td_target, &xerr)) < 0)
goto done;
@ -871,7 +874,7 @@ from_client_restart_one(clicon_handle h,
goto fail;
}
/* This is the state we are going from */
if (xmldb_get0(h, db, NULL, "/", 0, &td->td_src, NULL) < 0)
if (xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, &td->td_src, NULL) < 0)
goto done;
/* 3. Compute differences */

View file

@ -86,7 +86,7 @@ db_merge(clicon_handle h,
cxobj *xt = NULL;
/* Get data as xml from db1 */
if (xmldb_get0(h, (char*)db1, NULL, NULL, 0, &xt, NULL) < 0)
if (xmldb_get0(h, (char*)db1, YB_MODULE, NULL, NULL, 0, &xt, NULL) < 0)
goto done;
/* Merge xml into db2. Without commit */
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, clicon_username_get(h), cbret);

View file

@ -407,7 +407,7 @@ example_statedata(clicon_handle h,
* Get config according to xpath */
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done;
if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
if (xmldb_get0(h, "running", YB_MODULE, nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
goto done;
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done;

View file

@ -51,7 +51,7 @@ int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
/* in clixon_datastore_read.[ch] */
int xmldb_get(clicon_handle h, const char *db, cvec *nsc, char *xpath, cxobj **xtop);
int xmldb_get0(clicon_handle h, const char *db,
int xmldb_get0(clicon_handle h, const char *db, yang_bind yb,
cvec *nsc, const char *xpath,
int copy, cxobj **xtop, modstate_diff_t *msd);
int xmldb_get0_clear(clicon_handle h, cxobj *x);

View file

@ -213,10 +213,11 @@ xml_copy_from_bottom(cxobj *x0t,
x0p = xml_parent(x0);
if (xml_copy_bottom_recurse(x0t, x0p, x1t, &x1p) < 0)
return -1;
y = xml_spec(x0);
if ((y = xml_spec(x0)) != NULL){
/* Look if it exists */
if (match_base_child(x1p, x0, y, &x1) < 0)
goto done;
}
if (x1 == NULL){ /* If not, create it and copy complete tree */
if ((x1 = xml_new(xml_name(x0), x1p, CX_ELMNT)) == NULL)
goto done;
@ -445,13 +446,20 @@ text_read_modstate(clicon_handle h,
/*! Common read function that reads an XML tree from file
* @param[in] th Datastore text handle
* @param[in] db Symbolic database name, eg "candidate", "running"
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Top-level yang spec
* @param[out] xp XML tree read from file
* @param[out] msdiff If set, return modules-state differences
* @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
* @note retval 0 is NYI becaues of functions calling this function
*/
#undef XMLDB_READFILE_FAIL
int
xmldb_readfile(clicon_handle h,
const char *db,
yang_bind yb,
yang_stmt *yspec,
cxobj **xp,
modstate_diff_t *msdiff)
@ -479,12 +487,15 @@ xmldb_readfile(clicon_handle h,
goto done;
}
if (strcmp(format, "json")==0){
if ((ret = clixon_json_parse_file(fd, YB_MODULE, yspec, &x0, NULL)) < 0) /* XXX: ret == 0*/
if ((ret = clixon_json_parse_file(fd, yb, yspec, &x0, NULL)) < 0) /* XXX: ret == 0*/
goto done;
}
else if ((clixon_xml_parse_file(fd, YB_MODULE, yspec, "</config>", &x0, NULL)) < 0)
else if ((ret = clixon_xml_parse_file(fd, yb, yspec, "</config>", &x0, NULL)) < 0)
goto done;
#ifdef XMLDB_READFILE_FAIL /* The functions calling this function cannot handle a failed parse yet */
if (ret == 0)
goto fail;
#endif
/* Always assert a top-level called "config".
To ensure that, deal with two cases:
1. File is empty <top/> -> rename top-level to "config" */
@ -507,7 +518,7 @@ xmldb_readfile(clicon_handle h,
*xp = x0;
x0 = NULL;
}
retval = 0;
retval = 1;
done:
if (fd != -1)
close(fd);
@ -516,6 +527,11 @@ xmldb_readfile(clicon_handle h,
if (x0)
xml_free(x0);
return retval;
#ifdef XMLDB_READFILE_FAIL /* The functions calling this function cannot handle a failed parse yet */
fail:
retval = 0;
goto done;
#endif
}
/*! Get content of database using xpath. return a set of matching sub-trees
@ -524,17 +540,20 @@ xmldb_readfile(clicon_handle h,
* 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] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
* @see xmldb_get the generic API function
*/
static int
xmldb_get_nocache(clicon_handle h,
const char *db,
yang_bind yb,
cvec *nsc,
const char *xpath,
cxobj **xtop,
@ -549,13 +568,17 @@ xmldb_get_nocache(clicon_handle h,
cxobj **xvec = NULL;
size_t xlen;
int i;
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (xmldb_readfile(h, db, yspec, &xt, msdiff) < 0)
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &xt, msdiff)) < 0)
goto done;
if (ret == 0)
goto fail;
/* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
@ -589,7 +612,7 @@ xmldb_get_nocache(clicon_handle h,
clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt;
xt = NULL;
retval = 0;
retval = 1;
done:
if (xt)
xml_free(xt);
@ -600,6 +623,9 @@ xmldb_get_nocache(clicon_handle h,
if (fd != -1)
close(fd);
return retval;
fail:
retval = 0;
goto done;
}
/*! Get content of database using xpath. return a set of matching sub-trees
@ -608,17 +634,20 @@ xmldb_get_nocache(clicon_handle h,
* 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] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
* @see xmldb_get the generic API function
*/
static int
xmldb_get_cache(clicon_handle h,
const char *db,
yang_bind yb,
cvec *nsc,
const char *xpath,
cxobj **xtop,
@ -634,6 +663,7 @@ xmldb_get_cache(clicon_handle h,
db_elmnt *de = NULL;
cxobj *x1t = NULL;
db_elmnt de0 = {0,};
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -642,11 +672,12 @@ xmldb_get_cache(clicon_handle h,
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, msdiff) < 0)
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 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?
if (ret == 0)
goto fail;
/* Should we validate file if read from disk?
* No, 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);
@ -675,6 +706,7 @@ xmldb_get_cache(clicon_handle h,
if (xlen < 1000){
/* This is optimized for the case when the tree is large and xlen is small
* If the tree is large and xlen too, then the other is better.
* This only works if yang bind
*/
for (i=0; i<xlen; i++){
x0 = xvec[i];
@ -709,12 +741,15 @@ xmldb_get_cache(clicon_handle h,
if (clicon_debug_get()>1)
clicon_xml2file(stderr, x1t, 0, 1);
*xtop = x1t;
retval = 0;
retval = 1;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec)
free(xvec);
return retval;
fail:
retval = 0;
goto done;
}
/*! Get the raw cache of whole tree
@ -722,17 +757,20 @@ xmldb_get_cache(clicon_handle h,
* 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] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @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] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
*/
static int
xmldb_get_zerocopy(clicon_handle h,
const char *db,
yang_bind yb,
cvec *nsc,
const char *xpath,
cxobj **xtop,
@ -747,6 +785,7 @@ xmldb_get_zerocopy(clicon_handle h,
cxobj *x0;
db_elmnt *de = NULL;
db_elmnt de0 = {0,};
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -755,11 +794,12 @@ xmldb_get_zerocopy(clicon_handle h,
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, msdiff) < 0)
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 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?
if (ret == 0)
goto fail;
/* Should we validate file if read from disk?
* No, 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);
@ -783,12 +823,15 @@ xmldb_get_zerocopy(clicon_handle h,
if (clicon_debug_get()>1)
clicon_xml2file(stderr, x0t, 0, 1);
*xtop = x0t;
retval = 0;
retval = 1;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec)
free(xvec);
return retval;
fail:
retval = 0;
goto done;
}
/*! Get content of datastore and return a copy of the XML tree
@ -813,7 +856,7 @@ xmldb_get(clicon_handle h,
char *xpath,
cxobj **xret)
{
return xmldb_get0(h, db, nsc, xpath, 1, xret, NULL);
return xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, xret, NULL);
}
/*! Zero-copy variant of get content of database
@ -826,6 +869,7 @@ xmldb_get(clicon_handle h,
* freeing tree must be made after use.
* @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running"
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
@ -835,7 +879,7 @@ xmldb_get(clicon_handle h,
* @retval -1 Error
* @code
* cxobj *xt;
* if (xmldb_get0(xh, "running", nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0)
* if (xmldb_get0(xh, "running", YB_MODULE, nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0)
* err;
* ...
* xmldb_get0_clear(h, xt); # Clear tree from default values and flags
@ -847,6 +891,7 @@ xmldb_get(clicon_handle h,
int
xmldb_get0(clicon_handle h,
const char *db,
yang_bind yb,
cvec *nsc,
const char *xpath,
int copy,
@ -861,7 +906,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_nocache(h, db, nsc, xpath, xret, msdiff);
retval = xmldb_get_nocache(h, db, yb, nsc, xpath, xret, msdiff);
break;
case DATASTORE_CACHE_ZEROCOPY:
/* Get cache (file if empty) mark xpath match in original tree
@ -869,7 +914,7 @@ xmldb_get0(clicon_handle h,
* Default values and markings removed in xmldb_clear
*/
if (!copy){
retval = xmldb_get_zerocopy(h, db, nsc, xpath, xret, msdiff);
retval = xmldb_get_zerocopy(h, db, yb, nsc, xpath, xret, msdiff);
break;
}
/* fall through */
@ -878,7 +923,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy, return copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_cache(h, db, nsc, xpath, xret, msdiff);
retval = xmldb_get_cache(h, db, yb, nsc, xpath, xret, msdiff);
break;
}
return retval;

View file

@ -39,6 +39,6 @@
/*
* Prototypes
*/
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_bind yb, yang_stmt *yspec, cxobj **xp, modstate_diff_t *msd);
#endif /* _CLIXON_DATASTORE_READ_H */

View file

@ -906,6 +906,7 @@ xmldb_put(clicon_handle h,
char *format;
cvec *nsc = NULL; /* nacm namespace context */
int firsttime = 0;
int pretty;
if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL");
@ -927,8 +928,10 @@ xmldb_put(clicon_handle h,
/* If there is no xml x0 tree (in cache), then read it from file */
if (x0 == NULL){
firsttime++; /* to avoid leakage on error, see fail from text_modify */
if (xmldb_readfile(h, db, yspec, &x0, NULL) < 0)
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &x0, NULL)) < 0)
goto done;
if (ret == 0)
goto fail;
}
if (strcmp(xml_name(x0), "config")!=0){
clicon_err(OE_XML, 0, "Top-level symbol is %s, expected \"config\"",
@ -1012,11 +1015,12 @@ xmldb_put(clicon_handle h,
clicon_err(OE_CFG, errno, "Creating file %s", dbfile);
goto done;
}
pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY");
if (strcmp(format,"json")==0){
if (xml2json(f, x0, clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
if (xml2json(f, x0, pretty) < 0)
goto done;
}
else if (clicon_xml2file(f, x0, 0, clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
else if (clicon_xml2file(f, x0, 0, pretty) < 0)
goto done;
/* Remove modules state after writing to file
*/

View file

@ -1166,7 +1166,7 @@ nacm_access_pre(clicon_handle h,
goto done;
}
else if (strcmp(mode, "internal")==0){
if (xmldb_get0(h, "running", nsc, "nacm", 1, &xnacm0, NULL) < 0)
if (xmldb_get0(h, "running", YB_MODULE, nsc, "nacm", 1, &xnacm0, NULL) < 0)
goto done;
}
else{

View file

@ -7,6 +7,7 @@
# A preliminary change list is in Appendix A of
# draft-wang-netmod-module-revision-management-01
# The example here is simplified and also extended.
# For exampe admin and stats field are non-config in the original, not here
# It has also been broken up into two parts to test a series of upgrades.
# These are the operations (authentic move/delete are from ietf-interfaces):
# Move /if:interfaces-state/if:interface/if:admin-status to (2016)

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# A very simple case - Error in this detected by msmith@netgate
# A very simple case - Error in this detected by mgsmith@netgate
# Enable modstate and save running on a simple system without upgrade callback
# Upgrade yang revision, but no other (upgrade) changes
# Then start from running with modstate enabled and the new revision