* 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) ### 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. * 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. * 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)` * 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 * so zero-copy cant be used
* Also, must use external namespace context here due to <filter stmt * 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) if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done; goto done;
goto ok; goto ok;
@ -1049,14 +1049,14 @@ from_client_get(clicon_handle h,
* Also, must use external namespace context here due to <filter> stmt * Also, must use external namespace context here due to <filter> stmt
*/ */
if (clicon_option_bool(h, "CLICON_VALIDATE_STATE_XML")){ 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) if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done; goto done;
goto ok; goto ok;
} }
} }
else{ 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) if (netconf_operation_failed(cbret, "application", "read registry")< 0)
goto done; goto done;
goto ok; goto ok;

View file

@ -189,8 +189,16 @@ startup_common(clicon_handle h,
if ((msdiff = modstate_diff_new()) == NULL) if ((msdiff = modstate_diff_new()) == NULL)
goto done; goto done;
clicon_debug(1, "Reading startup config from %s", db); 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; 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"); clicon_debug(1, "Reading startup config done");
/* Clear flags xpath for get */ /* Clear flags xpath for get */
xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
@ -206,24 +214,19 @@ startup_common(clicon_handle h,
if (ret == 0) if (ret == 0)
goto fail; goto fail;
} }
#if 1 /* Print upgraded db: -q backend switch */
if (clicon_quit_upgrade_get(h) == 1){ /* Print upgraded db */ if (clicon_quit_upgrade_get(h) == 1){
if (xmldb_dump(h, stdout, xt) < 0) if (xmldb_dump(h, stdout, xt) < 0)
goto done; goto done;
exit(0); /* This is fairly abrupt , but need to avoid side-effects of rewinding 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. */ 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 empty skip. Note upgrading can add children, so it may be empty before that. */
if (xml_child_nr(xt) == 0){ if (xml_child_nr(xt) == 0){
td->td_target = xt; td->td_target = xt;
xt = NULL; xt = NULL;
goto ok; 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 */ /* After upgrading, XML tree needs to be sorted and yang spec populated */
if ((ret = xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 0) if ((ret = xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 0)
goto done; goto done;
@ -434,7 +437,7 @@ from_validate_common(clicon_handle h,
goto done; goto done;
} }
/* This is the state we are going to */ /* 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; goto done;
/* Clear flags xpath for get */ /* Clear flags xpath for get */
@ -452,7 +455,7 @@ from_validate_common(clicon_handle h,
/* 2. Parse xml trees /* 2. Parse xml trees
* This is the state we are going from */ * 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; goto done;
/* Clear flags xpath for get */ /* Clear flags xpath for get */
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, 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) if ((td = transaction_new()) == NULL)
goto done; goto done;
/* This is the state we are going to */ /* 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; goto done;
if ((ret = xml_yang_validate_all_top(h, td->td_target, &xerr)) < 0) if ((ret = xml_yang_validate_all_top(h, td->td_target, &xerr)) < 0)
goto done; goto done;
@ -871,7 +874,7 @@ from_client_restart_one(clicon_handle h,
goto fail; goto fail;
} }
/* This is the state we are going from */ /* 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; goto done;
/* 3. Compute differences */ /* 3. Compute differences */

View file

@ -86,7 +86,7 @@ db_merge(clicon_handle h,
cxobj *xt = NULL; cxobj *xt = NULL;
/* Get data as xml from db1 */ /* 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; goto done;
/* Merge xml into db2. Without commit */ /* Merge xml into db2. Without commit */
retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, clicon_username_get(h), cbret); 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 */ * Get config according to xpath */
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL) if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done; 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; goto done;
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0) if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done; goto done;

View file

@ -51,7 +51,7 @@ int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h); int xmldb_disconnect(clicon_handle h);
/* in clixon_datastore_read.[ch] */ /* in clixon_datastore_read.[ch] */
int xmldb_get(clicon_handle h, const char *db, cvec *nsc, char *xpath, cxobj **xtop); 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, cvec *nsc, const char *xpath,
int copy, cxobj **xtop, modstate_diff_t *msd); int copy, cxobj **xtop, modstate_diff_t *msd);
int xmldb_get0_clear(clicon_handle h, cxobj *x); 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); x0p = xml_parent(x0);
if (xml_copy_bottom_recurse(x0t, x0p, x1t, &x1p) < 0) if (xml_copy_bottom_recurse(x0t, x0p, x1t, &x1p) < 0)
return -1; return -1;
y = xml_spec(x0); if ((y = xml_spec(x0)) != NULL){
/* Look if it exists */ /* Look if it exists */
if (match_base_child(x1p, x0, y, &x1) < 0) if (match_base_child(x1p, x0, y, &x1) < 0)
goto done; goto done;
}
if (x1 == NULL){ /* If not, create it and copy complete tree */ if (x1 == NULL){ /* If not, create it and copy complete tree */
if ((x1 = xml_new(xml_name(x0), x1p, CX_ELMNT)) == NULL) if ((x1 = xml_new(xml_name(x0), x1p, CX_ELMNT)) == NULL)
goto done; goto done;
@ -443,18 +444,25 @@ text_read_modstate(clicon_handle h,
} }
/*! Common read function that reads an XML tree from file /*! Common read function that reads an XML tree from file
* @param[in] th Datastore text handle * @param[in] th Datastore text handle
* @param[in] db Symbolic database name, eg "candidate", "running" * @param[in] db Symbolic database name, eg "candidate", "running"
* @param[in] yspec Top-level yang spec * @param[in] yb How to bind yang to XML top-level when parsing
* @param[out] xp XML tree read from file * @param[in] yspec Top-level yang spec
* @param[out] msdiff If set, return modules-state differences * @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 int
xmldb_readfile(clicon_handle h, xmldb_readfile(clicon_handle h,
const char *db, const char *db,
yang_stmt *yspec, yang_bind yb,
cxobj **xp, yang_stmt *yspec,
modstate_diff_t *msdiff) cxobj **xp,
modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
cxobj *x0 = NULL; cxobj *x0 = NULL;
@ -479,12 +487,15 @@ xmldb_readfile(clicon_handle h,
goto done; goto done;
} }
if (strcmp(format, "json")==0){ 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; 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; 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". /* Always assert a top-level called "config".
To ensure that, deal with two cases: To ensure that, deal with two cases:
1. File is empty <top/> -> rename top-level to "config" */ 1. File is empty <top/> -> rename top-level to "config" */
@ -507,7 +518,7 @@ xmldb_readfile(clicon_handle h,
*xp = x0; *xp = x0;
x0 = NULL; x0 = NULL;
} }
retval = 0; retval = 1;
done: done:
if (fd != -1) if (fd != -1)
close(fd); close(fd);
@ -516,6 +527,11 @@ xmldb_readfile(clicon_handle h,
if (x0) if (x0)
xml_free(x0); xml_free(x0);
return retval; 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 /*! Get content of database using xpath. return a set of matching sub-trees
@ -524,38 +540,45 @@ xmldb_readfile(clicon_handle h,
* This is a clixon datastore plugin of the the xmldb api * This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path * @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] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free() * @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error * @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 * @see xmldb_get the generic API function
*/ */
static int static int
xmldb_get_nocache(clicon_handle h, xmldb_get_nocache(clicon_handle h,
const char *db, const char *db,
cvec *nsc, yang_bind yb,
const char *xpath, cvec *nsc,
cxobj **xtop, const char *xpath,
modstate_diff_t *msdiff) cxobj **xtop,
modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
char *dbfile = NULL; char *dbfile = NULL;
yang_stmt *yspec; yang_stmt *yspec;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj *x; cxobj *x;
int fd = -1; int fd = -1;
cxobj **xvec = NULL; cxobj **xvec = NULL;
size_t xlen; size_t xlen;
int i; int i;
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done; 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; goto done;
if (ret == 0)
goto fail;
/* Here xt looks like: <config>...</config> */ /* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */ /* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) 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); clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt; *xtop = xt;
xt = NULL; xt = NULL;
retval = 0; retval = 1;
done: done:
if (xt) if (xt)
xml_free(xt); xml_free(xt);
@ -600,6 +623,9 @@ xmldb_get_nocache(clicon_handle h,
if (fd != -1) if (fd != -1)
close(fd); close(fd);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Get content of database using xpath. return a set of matching sub-trees /*! 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 * This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path * @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] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free() * @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error * @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 * @see xmldb_get the generic API function
*/ */
static int static int
xmldb_get_cache(clicon_handle h, xmldb_get_cache(clicon_handle h,
const char *db, const char *db,
yang_bind yb,
cvec *nsc, cvec *nsc,
const char *xpath, const char *xpath,
cxobj **xtop, cxobj **xtop,
@ -634,6 +663,7 @@ xmldb_get_cache(clicon_handle h,
db_elmnt *de = NULL; db_elmnt *de = NULL;
cxobj *x1t = NULL; cxobj *x1t = NULL;
db_elmnt de0 = {0,}; db_elmnt de0 = {0,};
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); 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); de = clicon_db_elmnt_get(h, db);
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */ 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 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; goto done;
/* XXX: should we validate file if read from disk? if (ret == 0)
* Argument against: we may want to have a semantically wrong file and wish goto fail;
* to edit? /* 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; de0.de_xml = x0t;
clicon_db_elmnt_set(h, db, &de0); clicon_db_elmnt_set(h, db, &de0);
@ -675,6 +706,7 @@ xmldb_get_cache(clicon_handle h,
if (xlen < 1000){ if (xlen < 1000){
/* This is optimized for the case when the tree is large and xlen is small /* 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. * 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++){ for (i=0; i<xlen; i++){
x0 = xvec[i]; x0 = xvec[i];
@ -709,12 +741,15 @@ xmldb_get_cache(clicon_handle h,
if (clicon_debug_get()>1) if (clicon_debug_get()>1)
clicon_xml2file(stderr, x1t, 0, 1); clicon_xml2file(stderr, x1t, 0, 1);
*xtop = x1t; *xtop = x1t;
retval = 0; retval = 1;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec) if (xvec)
free(xvec); free(xvec);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Get the raw cache of whole tree /*! 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 * This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path * @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] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state * @param[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free() * @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
*/ */
static int static int
xmldb_get_zerocopy(clicon_handle h, xmldb_get_zerocopy(clicon_handle h,
const char *db, const char *db,
yang_bind yb,
cvec *nsc, cvec *nsc,
const char *xpath, const char *xpath,
cxobj **xtop, cxobj **xtop,
@ -747,6 +785,7 @@ xmldb_get_zerocopy(clicon_handle h,
cxobj *x0; cxobj *x0;
db_elmnt *de = NULL; db_elmnt *de = NULL;
db_elmnt de0 = {0,}; db_elmnt de0 = {0,};
int ret;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); 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); de = clicon_db_elmnt_get(h, db);
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */ 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 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; goto done;
/* XXX: should we validate file if read from disk? if (ret == 0)
* Argument against: we may want to have a semantically wrong file and wish goto fail;
* to edit? /* 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; de0.de_xml = x0t;
clicon_db_elmnt_set(h, db, &de0); clicon_db_elmnt_set(h, db, &de0);
@ -783,12 +823,15 @@ xmldb_get_zerocopy(clicon_handle h,
if (clicon_debug_get()>1) if (clicon_debug_get()>1)
clicon_xml2file(stderr, x0t, 0, 1); clicon_xml2file(stderr, x0t, 0, 1);
*xtop = x0t; *xtop = x0t;
retval = 0; retval = 1;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec) if (xvec)
free(xvec); free(xvec);
return retval; return retval;
fail:
retval = 0;
goto done;
} }
/*! Get content of datastore and return a copy of the XML tree /*! Get content of datastore and return a copy of the XML tree
@ -813,7 +856,7 @@ xmldb_get(clicon_handle h,
char *xpath, char *xpath,
cxobj **xret) 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 /*! Zero-copy variant of get content of database
@ -826,6 +869,7 @@ xmldb_get(clicon_handle h,
* freeing tree must be made after use. * freeing tree must be made after use.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running" * @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] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache * @param[in] copy Force copy. Overrides cache_zerocopy -> cache
@ -835,7 +879,7 @@ xmldb_get(clicon_handle h,
* @retval -1 Error * @retval -1 Error
* @code * @code
* cxobj *xt; * 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; * err;
* ... * ...
* xmldb_get0_clear(h, xt); # Clear tree from default values and flags * xmldb_get0_clear(h, xt); # Clear tree from default values and flags
@ -847,6 +891,7 @@ xmldb_get(clicon_handle h,
int int
xmldb_get0(clicon_handle h, xmldb_get0(clicon_handle h,
const char *db, const char *db,
yang_bind yb,
cvec *nsc, cvec *nsc,
const char *xpath, const char *xpath,
int copy, int copy,
@ -861,7 +906,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy * Add default values in copy
* Copy deleted by xmldb_free * 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; break;
case DATASTORE_CACHE_ZEROCOPY: case DATASTORE_CACHE_ZEROCOPY:
/* Get cache (file if empty) mark xpath match in original tree /* 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 * Default values and markings removed in xmldb_clear
*/ */
if (!copy){ if (!copy){
retval = xmldb_get_zerocopy(h, db, nsc, xpath, xret, msdiff); retval = xmldb_get_zerocopy(h, db, yb, nsc, xpath, xret, msdiff);
break; break;
} }
/* fall through */ /* fall through */
@ -878,7 +923,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy, return copy * Add default values in copy, return copy
* Copy deleted by xmldb_free * 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; break;
} }
return retval; return retval;

View file

@ -39,6 +39,6 @@
/* /*
* Prototypes * 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 */ #endif /* _CLIXON_DATASTORE_READ_H */

View file

@ -906,6 +906,7 @@ xmldb_put(clicon_handle h,
char *format; char *format;
cvec *nsc = NULL; /* nacm namespace context */ cvec *nsc = NULL; /* nacm namespace context */
int firsttime = 0; int firsttime = 0;
int pretty;
if (cbret == NULL){ if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is 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 there is no xml x0 tree (in cache), then read it from file */
if (x0 == NULL){ if (x0 == NULL){
firsttime++; /* to avoid leakage on error, see fail from text_modify */ 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; goto done;
if (ret == 0)
goto fail;
} }
if (strcmp(xml_name(x0), "config")!=0){ if (strcmp(xml_name(x0), "config")!=0){
clicon_err(OE_XML, 0, "Top-level symbol is %s, expected \"config\"", 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); clicon_err(OE_CFG, errno, "Creating file %s", dbfile);
goto done; goto done;
} }
pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY");
if (strcmp(format,"json")==0){ 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; 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; goto done;
/* Remove modules state after writing to file /* Remove modules state after writing to file
*/ */

View file

@ -1166,7 +1166,7 @@ nacm_access_pre(clicon_handle h,
goto done; goto done;
} }
else if (strcmp(mode, "internal")==0){ 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; goto done;
} }
else{ else{

View file

@ -7,6 +7,7 @@
# A preliminary change list is in Appendix A of # A preliminary change list is in Appendix A of
# draft-wang-netmod-module-revision-management-01 # draft-wang-netmod-module-revision-management-01
# The example here is simplified and also extended. # 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. # 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): # These are the operations (authentic move/delete are from ietf-interfaces):
# Move /if:interfaces-state/if:interface/if:admin-status to (2016) # Move /if:interfaces-state/if:interface/if:admin-status to (2016)

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/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 # Enable modstate and save running on a simple system without upgrade callback
# Upgrade yang revision, but no other (upgrade) changes # Upgrade yang revision, but no other (upgrade) changes
# Then start from running with modstate enabled and the new revision # Then start from running with modstate enabled and the new revision