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)`
  * The callback function has a new `op` parameter with possible values: `XML_FLAG_ADD`, `XML_FLAG_CHANGE` or `XML_FLAG_CHANGE`: `clicon_upgrade_cb(h, xn, ns, op, from, to, arg, cbret)`
This commit is contained in:
Olof hagsand 2020-06-26 14:39:04 +02:00
parent e2b3cdb3f6
commit 8f1de15ad3
10 changed files with 257 additions and 185 deletions

View file

@ -51,6 +51,11 @@ Expected: July 2020
### C/CLI-API changes on existing features (For developers) ### C/CLI-API changes on existing features (For developers)
* 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)`
* The callback function has a new `op` parameter with possible values: `XML_FLAG_ADD`, `XML_FLAG_CHANGE` or `XML_FLAG_CHANGE`: `clicon_upgrade_cb(h, xn, ns, op, from, to, arg, cbret)`
* Added new cli show functions to work with cligen_output for cligen pageing to work. To achieve this, replace function calls as follows: * Added new cli show functions to work with cligen_output for cligen pageing to work. To achieve this, replace function calls as follows:
* xml2txt(...) --> xml2txt_cb(..., cligen_output) * xml2txt(...) --> xml2txt_cb(..., cligen_output)
* xml2cli(...) --> xml2cli_cb(..., cligen_output) * xml2cli(...) --> xml2cli_cb(..., cligen_output)

View file

@ -177,19 +177,19 @@ startup_common(clicon_handle h,
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
int ret; int ret;
modstate_diff_t *msd = NULL; modstate_diff_t *msdiff = NULL;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj *x; cxobj *x;
cxobj *xret = NULL; cxobj *xret = NULL;
/* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with /* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with
* potentially non-matching module-state in msd * potentially non-matching module-state in msdiff
*/ */
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
if ((msd = 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, msd) < 0) if (xmldb_get0(h, db, NULL, "/", 0, &xt, msdiff) < 0)
goto done; 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 */
@ -202,13 +202,15 @@ startup_common(clicon_handle h,
} }
/* Here xt is old syntax */ /* Here xt is old syntax */
/* General purpose datastore upgrade */ /* General purpose datastore upgrade */
if (clixon_plugin_datastore_upgrade_all(h, db, xt, msd) < 0) if (clixon_plugin_datastore_upgrade_all(h, db, xt, msdiff) < 0)
goto done; goto done;
/* Module-specific upgrade callbacks */ /* Module-specific upgrade callbacks */
if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0) if (msdiff){
goto done; if ((ret = clixon_module_upgrade(h, xt, msdiff, cbret)) < 0)
if (ret == 0) goto done;
goto fail; if (ret == 0)
goto fail;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, 0, "Yang spec not set"); clicon_err(OE_YANG, 0, "Yang spec not set");
goto done; goto done;
@ -257,8 +259,8 @@ startup_common(clicon_handle h,
xml_free(xret); xml_free(xret);
if (xt) if (xt)
xml_free(xt); xml_free(xt);
if (msd) if (msdiff)
modstate_diff_free(msd); modstate_diff_free(msdiff);
return retval; return retval;
fail: fail:
retval = 0; retval = 0;

View file

@ -535,9 +535,9 @@ static const map_str2str namespace_map[] = {
*/ */
int int
example_upgrade(clicon_handle h, example_upgrade(clicon_handle h,
char *db, char *db,
cxobj *xt, cxobj *xt,
modstate_diff_t *msd) modstate_diff_t *msd)
{ {
int retval = -1; int retval = -1;
cvec *nsc = NULL; /* Canonical namespace */ cvec *nsc = NULL; /* Canonical namespace */
@ -616,16 +616,17 @@ example_upgrade(clicon_handle h,
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xn XML tree to be updated * @param[in] xn XML tree to be updated
* @param[in] ns Namespace of module (for info) * @param[in] ns Namespace of module (for info)
* @param[in] op One of XML_FLAG_ADD, _DEL, _CHANGE
* @param[in] from From revision on the form YYYYMMDD * @param[in] from From revision on the form YYYYMMDD
* @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] to To revision on the form YYYYMMDD (0 not in system)
* @param[in] arg User argument given at rpc_callback_register() * @param[in] arg User argument given at rpc_callback_register()
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. * @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. if retval = 0
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid * @retval 0 Invalid
* @retval -1 Error * @retval -1 Error
* @see clicon_upgrade_cb * @see clicon_upgrade_cb
* @see test_upgrade_interfaces.sh * @see test_upgrade_interfaces.sh
* @see upgrade_2016 * @see upgrade_2014_to_2016
* This example shows a two-step upgrade where the 2014 function does: * This example shows a two-step upgrade where the 2014 function does:
* - Move /if:interfaces-state/if:interface/if:admin-status to * - Move /if:interfaces-state/if:interface/if:admin-status to
* /if:interfaces/if:interface/ * /if:interfaces/if:interface/
@ -634,13 +635,14 @@ example_upgrade(clicon_handle h,
* - Rename /interfaces/interface/description to descr * - Rename /interfaces/interface/description to descr
*/ */
static int static int
upgrade_2016(clicon_handle h, upgrade_2014_to_2016(clicon_handle h,
cxobj *xt, cxobj *xt,
char *ns, char *ns,
uint32_t from, uint16_t op,
uint32_t to, uint32_t from,
void *arg, uint32_t to,
cbuf *cbret) void *arg,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
@ -654,6 +656,7 @@ upgrade_2016(clicon_handle h,
int i; int i;
char *name; char *name;
clicon_debug(1, "%s from:%d to:%d", __FUNCTION__, from, to);
/* Get Yang module for this namespace. Note it may not exist (if obsolete) */ /* Get Yang module for this namespace. Note it may not exist (if obsolete) */
yspec = clicon_dbspec_yang(h); yspec = clicon_dbspec_yang(h);
if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL) if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL)
@ -716,16 +719,17 @@ upgrade_2016(clicon_handle h,
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xn XML tree to be updated * @param[in] xn XML tree to be updated
* @param[in] ns Namespace of module (for info) * @param[in] ns Namespace of module (for info)
* @param[in] op One of XML_FLAG_ADD, _DEL, _CHANGE
* @param[in] from From revision on the form YYYYMMDD * @param[in] from From revision on the form YYYYMMDD
* @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] to To revision on the form YYYYMMDD (0 not in system)
* @param[in] arg User argument given at rpc_callback_register() * @param[in] arg User argument given at rpc_callback_register()
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. * @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. if retval = 0
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid * @retval 0 Invalid
* @retval -1 Error * @retval -1 Error
* @see clicon_upgrade_cb * @see clicon_upgrade_cb
* @see test_upgrade_interfaces.sh * @see test_upgrade_interfaces.sh
* @see upgrade_2016 * @see upgrade_2016_to_2018
* The 2016 function does: * The 2016 function does:
* - Delete /if:interfaces-state * - Delete /if:interfaces-state
* - Wrap /interfaces/interface/descr to /interfaces/interface/docs/descr * - Wrap /interfaces/interface/descr to /interfaces/interface/docs/descr
@ -733,13 +737,14 @@ upgrade_2016(clicon_handle h,
* fraction-digits 3 and divide all values with 1000 * fraction-digits 3 and divide all values with 1000
*/ */
static int static int
upgrade_2018(clicon_handle h, upgrade_2016_to_2018(clicon_handle h,
cxobj *xt, cxobj *xt,
char *ns, char *ns,
uint32_t from, uint16_t op,
uint32_t to, uint32_t from,
void *arg, uint32_t to,
cbuf *cbret) void *arg,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
@ -752,6 +757,7 @@ upgrade_2018(clicon_handle h,
size_t vlen; size_t vlen;
int i; int i;
clicon_debug(1, "%s from:%d to:%d", __FUNCTION__, from, to);
/* Get Yang module for this namespace. Note it may not exist (if obsolete) */ /* Get Yang module for this namespace. Note it may not exist (if obsolete) */
yspec = clicon_dbspec_yang(h); yspec = clicon_dbspec_yang(h);
if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL) if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL)
@ -800,6 +806,58 @@ upgrade_2018(clicon_handle h,
return retval; return retval;
} }
/*! Testcase module-specific upgrade function moving interfaces-state to interfaces
* @param[in] h Clicon handle
* @param[in] xn XML tree to be updated
* @param[in] ns Namespace of module (for info)
* @param[in] op One of XML_FLAG_ADD, _DEL, _CHANGE
* @param[in] from From revision on the form YYYYMMDD
* @param[in] to To revision on the form YYYYMMDD (0 not in system)
* @param[in] arg User argument given at rpc_callback_register()
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. if retval = 0
* @retval 1 OK
* @retval 0 Invalid
* @retval -1 Error
* @see clicon_upgrade_cb
* @see test_upgrade_interfaces.sh
* @see upgrade_2014_to_2016
* This example shows a two-step upgrade where the 2014 function does:
* - Move /if:interfaces-state/if:interface/if:admin-status to
* /if:interfaces/if:interface/
* - Move /if:interfaces-state/if:interface/if:statistics to
* /if:interfaces/if:interface/
* - Rename /interfaces/interface/description to descr
*/
static int
upgrade_interfaces(clicon_handle h,
cxobj *xt,
char *ns,
uint16_t op,
uint32_t from,
uint32_t to,
void *arg,
cbuf *cbret)
{
int retval = -1;
if (from <= 20140508){
if ((retval = upgrade_2014_to_2016(h, xt, ns, op, from, to, arg, cbret)) < 0)
goto done;
if (retval == 0)
goto done;
}
if (from <= 20160101){
if ((retval = upgrade_2016_to_2018(h, xt, ns, op, from, to, arg, cbret)) < 0)
goto done;
if (retval == 0)
goto done;
}
// ok:
retval = 1;
done:
return retval;
}
/*! Plugin state reset. Add xml or set state in backend machine. /*! Plugin state reset. Add xml or set state in backend machine.
* Called in each backend plugin. plugin_reset is called after all plugins * Called in each backend plugin. plugin_reset is called after all plugins
* have been initialized. This give the application a chance to reset * have been initialized. This give the application a chance to reset
@ -1026,13 +1084,18 @@ clixon_plugin_init(clicon_handle h)
* test interface example. Otherwise the auto-upgrade feature is enabled. * test interface example. Otherwise the auto-upgrade feature is enabled.
*/ */
if (_module_upgrade){ if (_module_upgrade){
if (upgrade_callback_register(h, upgrade_2016, "urn:example:interfaces", 20140508, 20160101, NULL) < 0) #if 1
if (upgrade_callback_register(h, upgrade_interfaces, "urn:example:interfaces", NULL) < 0)
goto done; goto done;
if (upgrade_callback_register(h, upgrade_2018, "urn:example:interfaces", 20160101, 20180220, NULL) < 0) #else
if (upgrade_callback_register(h, upgrade_2014_to_2016, "urn:example:interfaces", NULL) < 0)
goto done; goto done;
if (upgrade_callback_register(h, upgrade_2016_to_2018, "urn:example:interfaces", NULL) < 0)
goto done;
#endif
} }
else else
if (upgrade_callback_register(h, xml_changelog_upgrade, NULL, 0, 0, NULL) < 0) if (upgrade_callback_register(h, xml_changelog_upgrade, NULL, NULL) < 0)
goto done; goto done;
/* Return plugin API */ /* Return plugin API */

View file

@ -72,10 +72,11 @@ typedef int (*clicon_rpc_cb)(
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xn XML tree to be updated * @param[in] xn XML tree to be updated
* @param[in] namespace Namespace of module * @param[in] namespace Namespace of module
* @param[in] from From revision on the form YYYYMMDD * @param[in] op One of XML_FLAG_ADD, _DEL, _CHANGE
* @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] from From revision on the form YYYYMMDD (if DEL or CHANGE)
* @param[in] to To revision on the form YYYYMMDD (if ADD or CHANGE)
* @param[in] arg User argument given at rpc_callback_register() * @param[in] arg User argument given at rpc_callback_register()
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. * @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error.. (if retval = 0)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid * @retval 0 Invalid
* @retval -1 Error * @retval -1 Error
@ -84,6 +85,7 @@ typedef int (*clicon_upgrade_cb)(
clicon_handle h, clicon_handle h,
cxobj *xn, cxobj *xn,
char *namespace, char *namespace,
uint16_t op,
uint32_t from, uint32_t from,
uint32_t to, uint32_t to,
void *arg, void *arg,
@ -253,7 +255,7 @@ struct clixon_plugin_api{
/* /*
* Macros * Macros
*/ */
#define upgrade_callback_register(h, cb, namespace, from, rev, arg) upgrade_callback_reg_fn((h), (cb), #cb, (namespace), (from), (rev), (arg)) #define upgrade_callback_register(h, cb, namespace, arg) upgrade_callback_reg_fn((h), (cb), #cb, (namespace), (arg))
typedef struct clixon_plugin_api clixon_plugin_api; typedef struct clixon_plugin_api clixon_plugin_api;
@ -309,8 +311,8 @@ int rpc_callback_delete_all(clicon_handle h);
int rpc_callback_call(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg); int rpc_callback_call(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg);
/* upgrade callback API */ /* upgrade callback API */
int upgrade_callback_reg_fn(clicon_handle h, clicon_upgrade_cb cb, const char *strfn, char *namespace, uint32_t from, uint32_t to, void *arg); int upgrade_callback_reg_fn(clicon_handle h, clicon_upgrade_cb cb, const char *strfn, char *namespace, void *arg);
int upgrade_callback_delete_all(clicon_handle h); int upgrade_callback_delete_all(clicon_handle h);
int upgrade_callback_call(clicon_handle h, cxobj *xt, char *namespace, uint32_t from, uint32_t to, cbuf *cbret); int upgrade_callback_call(clicon_handle h, cxobj *xt, char *namespace, uint16_t op, uint32_t from, uint32_t to, cbuf *cbret);
#endif /* _CLIXON_PLUGIN_H_ */ #endif /* _CLIXON_PLUGIN_H_ */

View file

@ -40,7 +40,7 @@
/* /*
* Prototypes * Prototypes
*/ */
int xml_changelog_upgrade(clicon_handle h, cxobj *xn, char *namespace, uint32_t from, uint32_t to, void *arg, cbuf *cbret); int xml_changelog_upgrade(clicon_handle h, cxobj *xn, char *namespace, uint16_t op, uint32_t from, uint32_t to, void *arg, cbuf *cbret);
int clixon_xml_changelog_init(clicon_handle h); int clixon_xml_changelog_init(clicon_handle h);
int xml_namespace_vec(clicon_handle h, cxobj *xt, char *namespace, cxobj ***vec, size_t *veclen); int xml_namespace_vec(clicon_handle h, cxobj *xt, char *namespace, cxobj ***vec, size_t *veclen);

View file

@ -54,8 +54,7 @@
typedef struct { typedef struct {
int md_status; /* 0 if no module-state in a datastore, 1 if there is */ int md_status; /* 0 if no module-state in a datastore, 1 if there is */
char *md_set_id; /* server-specific identifier */ char *md_set_id; /* server-specific identifier */
cxobj *md_del; /* yang module state deletes */ cxobj *md_diff; /* yang module state containing revisions and XML_FLAG_ADD|DEL|CHANGE */
cxobj *md_mod; /* yang module state modifications */
} modstate_diff_t; } modstate_diff_t;
/* /*

View file

@ -212,92 +212,111 @@ xml_copy_marked(cxobj *x0,
/*! Read module-state in an XML tree /*! Read module-state in an XML tree
* *
* @param[in] th Datastore text handle * @param[in] th Datastore text handle
* @param[in] yspec Top-level yang spec * @param[in] yspec Top-level yang spec
* @param[in] xt XML tree * @param[in] xt XML tree
* @param[out] msd If set, return modules-state differences * @param[out] msdiff Modules-state differences
* *
* The modstate difference contains:
* - if there is a modstate
* - the modstate identifier
* - An entry for each modstate that differs marked with flag: ADD|DEL|CHANGE
*
* Algorithm:
* Read mst (module-state-tree) from xml tree (if any) and compare it with * Read mst (module-state-tree) from xml tree (if any) and compare it with
* the system state mst. * the system state mst.
* This can happen: * This can happen:
* 1) There is no modules-state info in the file * 1) There is no modules-state info in the file
* 2) There is module state info in the file * 2) There is module state info in the file
* 3) For each module state m in the file: * 3) For each module state m in the file:
* 3a) There is no such module in the system * 3a) There is no such module in the system -> add to list mark as DEL
* 3b) File module-state matches system * 3b) File module-state matches system
* 3c) File module-state does not match system * 3c) File module-state does not match system -> add to list mark as CHANGE
* 4) For each module state s in the system
* 4a) If there is no such module in the file -> add to list mark as ADD
*/ */
static int static int
text_read_modstate(clicon_handle h, text_read_modstate(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
cxobj *xt, cxobj *xt,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
cxobj *xmodst; cxobj *xmodfile = NULL; /* modstate of system (loaded yang modules in runtime) */
cxobj *xm = NULL; cxobj *xmodsystem = NULL; /* modstate of file, eg startup */
cxobj *xm2; cxobj *xf = NULL; /* xml modstate in file */
cxobj *xs; cxobj *xf2; /* copy */
char *name; /* module name */ cxobj *xs; /* xml modstate in system */
char *mrev; /* file revision */ cxobj *xs2; /* copy */
char *srev; /* system revision */ char *name; /* module name */
cxobj *xmcache = NULL; char *frev; /* file revision */
char *srev; /* system revision */
xmcache = clicon_modst_cache_get(h, 1); /* Read module-state as computed at startup, see startup_module_state() */
if ((xmodst = xml_find_type(xt, NULL, "modules-state", CX_ELMNT)) == NULL){ xmodsystem = clicon_modst_cache_get(h, 1);
if ((xmodfile = xml_find_type(xt, NULL, "modules-state", CX_ELMNT)) == NULL){
/* 1) There is no modules-state info in the file */ /* 1) There is no modules-state info in the file */
} }
else if (xmcache && msd){ else if (xmodsystem && msdiff){
msd->md_status = 1; /* There is module state in the file */ msdiff->md_status = 1; /* There is module state in the file */
/* Create diff trees */ /* Create modstate tree for this file */
if (clixon_xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", if (clixon_xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>",
YB_MODULE, yspec, &msd->md_del, NULL) < 0) YB_MODULE, yspec, &msdiff->md_diff, NULL) < 0)
goto done; goto done;
if (xml_rootchild(msd->md_del, 0, &msd->md_del) < 0) if (xml_rootchild(msdiff->md_diff, 0, &msdiff->md_diff) < 0)
goto done;
if (clixon_xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>",
YB_MODULE, yspec, &msd->md_mod, NULL) < 0)
goto done;
if (xml_rootchild(msd->md_mod, 0, &msd->md_mod) < 0)
goto done; goto done;
/* 3) For each module state m in the file */ /* 3) For each module state m in the file */
while ((xm = xml_child_each(xmodst, xm, CX_ELMNT)) != NULL) { xf = NULL;
if (strcmp(xml_name(xm), "module-set-id") == 0){ while ((xf = xml_child_each(xmodfile, xf, CX_ELMNT)) != NULL) {
if (xml_body(xm) && (msd->md_set_id = strdup(xml_body(xm))) == NULL){ if (strcmp(xml_name(xf), "module-set-id") == 0){
if (xml_body(xf) && (msdiff->md_set_id = strdup(xml_body(xf))) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
goto done; goto done;
} }
} }
if (strcmp(xml_name(xm), "module")) if (strcmp(xml_name(xf), "module"))
continue; /* ignore other tags, such as module-set-id */ continue; /* ignore other tags, such as module-set-id */
if ((name = xml_find_body(xm, "name")) == NULL) if ((name = xml_find_body(xf, "name")) == NULL)
continue; continue;
/* 3a) There is no such module in the system */ /* 3a) There is no such module in the system */
if ((xs = xpath_first(xmcache, NULL, "module[name=\"%s\"]", name)) == NULL){ if ((xs = xpath_first(xmodsystem, NULL, "module[name=\"%s\"]", name)) == NULL){
// fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name); if ((xf2 = xml_dup(xf)) == NULL) /* Make a copy of this modstate */
if ((xm2 = xml_dup(xm)) == NULL)
goto done; goto done;
if (xml_addsub(msd->md_del, xm2) < 0) if (xml_addsub(msdiff->md_diff, xf2) < 0) /* Add it to modstatediff */
goto done; goto done;
xml_flag_set(xf2, XML_FLAG_DEL);
continue; continue;
} }
/* These two shouldnt happen since revision is key, just ignore */ /* These two shouldnt happen since revision is key, just ignore */
if ((mrev = xml_find_body(xm, "revision")) == NULL) if ((frev = xml_find_body(xf, "revision")) == NULL)
continue; continue;
if ((srev = xml_find_body(xs, "revision")) == NULL) if ((srev = xml_find_body(xs, "revision")) == NULL)
continue; continue;
if (strcmp(mrev, srev)==0){ if (strcmp(frev, srev)!=0){
/* 3b) File module-state matches system */
// fprintf(stderr, "%s: Module %s: file \"%s\" and system revisions match\n", __FUNCTION__, name, mrev);
}
else{
/* 3c) File module-state does not match system */ /* 3c) File module-state does not match system */
// fprintf(stderr, "%s: Module %s: file \"%s\" and system \"%s\" revisions do not match\n", __FUNCTION__, name, mrev, srev); if ((xf2 = xml_dup(xf)) == NULL)
if ((xm2 = xml_dup(xm)) == NULL)
goto done; goto done;
if (xml_addsub(msd->md_mod, xm2) < 0) if (xml_addsub(msdiff->md_diff, xf2) < 0)
goto done; goto done;
xml_flag_set(xf2, XML_FLAG_CHANGE);
}
}
/* 4) For each module state s in the system (xmodsystem) */
xs = NULL;
while ((xs = xml_child_each(xmodsystem, xs, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xs), "module"))
continue; /* ignore other tags, such as module-set-id */
if ((name = xml_find_body(xs, "name")) == NULL)
continue;
/* 4a) If there is no such module in the file -> add to list mark as ADD */
if ((xf = xpath_first(xmodfile, NULL, "module[name=\"%s\"]", name)) == NULL){
if ((xs2 = xml_dup(xs)) == NULL) /* Make a copy of this modstate */
goto done;
if (xml_addsub(msdiff->md_diff, xs2) < 0) /* Add it to modstatediff */
goto done;
xml_flag_set(xs2, XML_FLAG_ADD);
continue;
} }
} }
} }
@ -305,8 +324,8 @@ text_read_modstate(clicon_handle h,
* in all cases, whether CLICON_XMLDB_MODSTATE is on or not. * in all cases, whether CLICON_XMLDB_MODSTATE is on or not.
* Clixon systems with CLICON_XMLDB_MODSTATE disabled ignores it * Clixon systems with CLICON_XMLDB_MODSTATE disabled ignores it
*/ */
if (xmodst){ if (xmodfile){
if (xml_purge(xmodst) < 0) if (xml_purge(xmodfile) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -319,14 +338,14 @@ text_read_modstate(clicon_handle h,
* @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] yspec Top-level yang spec
* @param[out] xp XML tree read from file * @param[out] xp XML tree read from file
* @param[out] msd If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
*/ */
int int
xmldb_readfile(clicon_handle h, xmldb_readfile(clicon_handle h,
const char *db, const char *db,
yang_stmt *yspec, yang_stmt *yspec,
cxobj **xp, cxobj **xp,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
cxobj *x0 = NULL; cxobj *x0 = NULL;
@ -373,7 +392,7 @@ xmldb_readfile(clicon_handle h,
/* From Clixon 3.10,datastore files may contain module-state defining /* From Clixon 3.10,datastore files may contain module-state defining
* which modules are used in the file. * which modules are used in the file.
*/ */
if (text_read_modstate(h, yspec, x0, msd) < 0) if (text_read_modstate(h, yspec, x0, msdiff) < 0)
goto done; goto done;
if (xp){ if (xp){
*xp = x0; *xp = x0;
@ -399,7 +418,7 @@ xmldb_readfile(clicon_handle h,
* @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] msd If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xmldb_get the generic API function * @see xmldb_get the generic API function
@ -410,7 +429,7 @@ xmldb_get_nocache(clicon_handle h,
cvec *nsc, cvec *nsc,
char *xpath, char *xpath,
cxobj **xtop, cxobj **xtop,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
char *dbfile = NULL; char *dbfile = NULL;
@ -426,7 +445,7 @@ xmldb_get_nocache(clicon_handle h,
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, msd) < 0) if (xmldb_readfile(h, db, yspec, &xt, msdiff) < 0)
goto done; goto done;
/* 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 */
@ -483,7 +502,7 @@ xmldb_get_nocache(clicon_handle h,
* @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] msd If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xmldb_get the generic API function * @see xmldb_get the generic API function
@ -494,7 +513,7 @@ xmldb_get_cache(clicon_handle h,
cvec *nsc, cvec *nsc,
char *xpath, char *xpath,
cxobj **xtop, cxobj **xtop,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
@ -514,7 +533,7 @@ 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, msd) < 0) if (xmldb_readfile(h, db, yspec, &x0t, msdiff) < 0)
goto done; goto done;
/* XXX: should we validate file if read from disk? /* XXX: should we validate file if read from disk?
* Argument against: we may want to have a semantically wrong file and wish * Argument against: we may want to have a semantically wrong file and wish
@ -585,7 +604,7 @@ xmldb_get_cache(clicon_handle h,
* @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] msd If set, return modules-state differences * @param[out] msdiff If set, return modules-state differences
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
@ -595,7 +614,7 @@ xmldb_get_zerocopy(clicon_handle h,
cvec *nsc, cvec *nsc,
char *xpath, char *xpath,
cxobj **xtop, cxobj **xtop,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
yang_stmt *yspec; yang_stmt *yspec;
@ -614,7 +633,7 @@ 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, msd) < 0) if (xmldb_readfile(h, db, yspec, &x0t, msdiff) < 0)
goto done; goto done;
/* XXX: should we validate file if read from disk? /* XXX: should we validate file if read from disk?
* Argument against: we may want to have a semantically wrong file and wish * Argument against: we may want to have a semantically wrong file and wish
@ -689,7 +708,7 @@ xmldb_get(clicon_handle h,
* @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
* @param[out] xret Single return XML tree. Free with xml_free() * @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences (upgrade code) * @param[out] msdiff If set, return modules-state differences (upgrade code)
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @code * @code
@ -710,7 +729,7 @@ xmldb_get0(clicon_handle h,
char *xpath, char *xpath,
int copy, int copy,
cxobj **xret, cxobj **xret,
modstate_diff_t *msd) modstate_diff_t *msdiff)
{ {
int retval = -1; int retval = -1;
@ -720,7 +739,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, msd); retval = xmldb_get_nocache(h, db, 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
@ -728,7 +747,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, msd); retval = xmldb_get_zerocopy(h, db, nsc, xpath, xret, msdiff);
break; break;
} }
/* fall through */ /* fall through */
@ -737,7 +756,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, msd); retval = xmldb_get_cache(h, db, nsc, xpath, xret, msdiff);
break; break;
} }
return retval; return retval;

View file

@ -793,9 +793,6 @@ typedef struct {
const char *uc_fnstr; /* Stringified fn name for debug */ const char *uc_fnstr; /* Stringified fn name for debug */
void *uc_arg; /* Application specific argument to cb */ void *uc_arg; /* Application specific argument to cb */
char *uc_namespace; /* Module namespace */ char *uc_namespace; /* Module namespace */
uint32_t uc_rev; /* Module revision (to) in YYYYMMDD format or 0 */
uint32_t uc_from; /* Module revision (from) or 0 in YYYYMMDD format */
} upgrade_callback_t; } upgrade_callback_t;
/* List of rpc callback entries XXX hang on handle */ /* List of rpc callback entries XXX hang on handle */
@ -808,8 +805,6 @@ static upgrade_callback_t *upgrade_cb_list = NULL;
* @param[in] fnstr Stringified function for debug * @param[in] fnstr Stringified function for debug
* @param[in] arg Domain-specific argument to send to callback * @param[in] arg Domain-specific argument to send to callback
* @param[in] namespace Module namespace (if NULL all modules) * @param[in] namespace Module namespace (if NULL all modules)
* @param[in] from From module revision (0 from any revision)
* @param[in] revision To module revision (0 means module obsoleted)
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see upgrade_callback_call which makes the actual callback * @see upgrade_callback_call which makes the actual callback
@ -819,8 +814,6 @@ upgrade_callback_reg_fn(clicon_handle h,
clicon_upgrade_cb cb, clicon_upgrade_cb cb,
const char *fnstr, const char *fnstr,
char *namespace, char *namespace,
uint32_t from,
uint32_t revision,
void *arg) void *arg)
{ {
upgrade_callback_t *uc; upgrade_callback_t *uc;
@ -835,8 +828,6 @@ upgrade_callback_reg_fn(clicon_handle h,
uc->uc_arg = arg; uc->uc_arg = arg;
if (namespace) if (namespace)
uc->uc_namespace = strdup(namespace); uc->uc_namespace = strdup(namespace);
uc->uc_rev = revision;
uc->uc_from = from;
ADDQ(uc, upgrade_cb_list); ADDQ(uc, upgrade_cb_list);
return 0; return 0;
done: done:
@ -864,14 +855,14 @@ upgrade_callback_delete_all(clicon_handle h)
return 0; return 0;
} }
/*! Search Upgrade callbacks and invoke if module match /*! Upgrade specific module identified by namespace, search matching callbacks
* *
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] xt Top-level XML tree to be updated (includes other ns as well) * @param[in] xt Top-level XML tree to be updated (includes other ns as well)
* @param[in] modname Name of module * @param[in] ns Namespace of module
* @param[in] modns Namespace of module (for info) * @param[in] op One of XML_FLAG_ADD, _DEL or _CHANGE
* @param[in] from From revision on the form YYYYMMDD * @param[in] from From revision on the form YYYYMMDD (if DEL or CHANGE)
* @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] to To revision on the form YYYYMMDD (if ADD or CHANGE)
* @param[out] cbret Return XML (as string in CLIgen buffer), on invalid * @param[out] cbret Return XML (as string in CLIgen buffer), on invalid
* @retval -1 Error * @retval -1 Error
* @retval 0 Invalid - cbret contains reason as netconf * @retval 0 Invalid - cbret contains reason as netconf
@ -881,7 +872,8 @@ upgrade_callback_delete_all(clicon_handle h)
int int
upgrade_callback_call(clicon_handle h, upgrade_callback_call(clicon_handle h,
cxobj *xt, cxobj *xt,
char *namespace, char *ns,
uint16_t op,
uint32_t from, uint32_t from,
uint32_t to, uint32_t to,
cbuf *cbret) cbuf *cbret)
@ -903,23 +895,21 @@ upgrade_callback_call(clicon_handle h,
* - Registered from revision >= from AND * - Registered from revision >= from AND
* - Registered to revision <= to (which includes case both 0) * - Registered to revision <= to (which includes case both 0)
*/ */
if (uc->uc_namespace == NULL || strcmp(uc->uc_namespace, namespace)==0) if (uc->uc_namespace == NULL || strcmp(uc->uc_namespace, ns)==0){
if ((uc->uc_from == 0) || if ((ret = uc->uc_callback(h, xt, ns, op, from, to, uc->uc_arg, cbret)) < 0){
(uc->uc_from >= from && uc->uc_rev <= to)){ clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_namespace);
if ((ret = uc->uc_callback(h, xt, namespace, from, to, uc->uc_arg, cbret)) < 0){ goto done;
clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_namespace); }
if (ret == 0){
if (cbuf_len(cbret)==0){
clicon_err(OE_CFG, 0, "Validation fail %s(%s): cbret not set",
uc->uc_fnstr, ns);
goto done; goto done;
} }
if (ret == 0){ goto fail;
if (cbuf_len(cbret)==0){
clicon_err(OE_CFG, 0, "Validation fail %s(%s): cbret not set",
uc->uc_fnstr, namespace);
goto done;
}
goto fail;
}
nr++;
} }
nr++;
}
uc = NEXTQ(upgrade_callback_t *, uc); uc = NEXTQ(upgrade_callback_t *, uc);
} while (uc != upgrade_cb_list); } while (uc != upgrade_cb_list);
retval = 1; retval = 1;

View file

@ -356,6 +356,7 @@ changelog_iterate(clicon_handle h,
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xt Top-level XML tree to be updated (includes other ns as well) * @param[in] xt Top-level XML tree to be updated (includes other ns as well)
* @param[in] namespace Namespace of module (for info) * @param[in] namespace Namespace of module (for info)
* @param[in] op One of XML_FLAG_ADD, _DEL, _CHANGE
* @param[in] from From revision on the form YYYYMMDD * @param[in] from From revision on the form YYYYMMDD
* @param[in] to To revision on the form YYYYMMDD (0 not in system) * @param[in] to To revision on the form YYYYMMDD (0 not in system)
* @param[in] arg User argument given at rpc_callback_register() * @param[in] arg User argument given at rpc_callback_register()
@ -367,12 +368,13 @@ changelog_iterate(clicon_handle h,
*/ */
int int
xml_changelog_upgrade(clicon_handle h, xml_changelog_upgrade(clicon_handle h,
cxobj *xt, cxobj *xt,
char *namespace, char *namespace,
uint32_t from, uint16_t op,
uint32_t to, uint32_t from,
void *arg, uint32_t to,
cbuf *cbret) void *arg,
cbuf *cbret)
{ {
int retval = -1; int retval = -1;
cxobj *xchlog; /* changelog */ cxobj *xchlog; /* changelog */

View file

@ -101,10 +101,8 @@ modstate_diff_free(modstate_diff_t *md)
return 0; return 0;
if (md->md_set_id) if (md->md_set_id)
free(md->md_set_id); free(md->md_set_id);
if (md->md_del) if (md->md_diff)
xml_free(md->md_del); xml_free(md->md_diff);
if (md->md_mod)
xml_free(md->md_mod);
free(md); free(md);
return 0; return 0;
} }
@ -375,13 +373,13 @@ yang_modules_state_get(clicon_handle h,
/*! For single module state with namespace, get revisions and send upgrade callbacks /*! For single module state with namespace, get revisions and send upgrade callbacks
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xt Top-level XML tree to be updated (includes other ns as well) * @param[in] xt Top-level XML tree to be updated (includes other ns as well)
* @param[in] xs XML module state (for one yang module) * @param[in] xd XML module state diff (for one yang module)
* @param[in] xvec Help vector where to store XML child nodes (??) * @param[in] xvec Help vector where to store XML child nodes (??)
* @param[in] xlen Length of xvec * @param[in] xlen Length of xvec
* @param[in] ns0 Namespace of module state we are looking for * @param[in] ns0 Namespace of module state we are looking for
* @param[in] deleted If set, dont look for system yang module and "to" rev * @param[in] op add,del, or mod
* @param[out] cbret Netconf error message if invalid * @param[out] cbret Netconf error message if invalid
* @retval 1 OK * @retval 1 OK
* @retval 0 Validation failed (cbret set) * @retval 0 Validation failed (cbret set)
* @retval -1 Error * @retval -1 Error
@ -389,9 +387,8 @@ yang_modules_state_get(clicon_handle h,
static int static int
mod_ns_upgrade(clicon_handle h, mod_ns_upgrade(clicon_handle h,
cxobj *xt, cxobj *xt,
cxobj *xs, cxobj *xmod,
char *ns, char *ns,
int deleted,
cbuf *cbret) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
@ -403,15 +400,14 @@ mod_ns_upgrade(clicon_handle h,
int ret; int ret;
yang_stmt *yspec; yang_stmt *yspec;
/* Make upgrade callback for this XML, specifying the module /* If modified or removed get from revision from file */
* namespace, from and to revision. if (xml_flag(xmod, (XML_FLAG_CHANGE|XML_FLAG_DEL)) != 0x0){
*/ if ((b = xml_find_body(xmod, "revision")) != NULL) /* Module revision */
if ((b = xml_find_body(xs, "revision")) != NULL) /* Module revision */ if (ys_parse_date_arg(b, &from) < 0)
if (ys_parse_date_arg(b, &from) < 0) goto done;
goto done; }
if (deleted) /* If modified or added get to revision from system */
to = 0; if (xml_flag(xmod, (XML_FLAG_CHANGE|XML_FLAG_ADD)) != 0x0){
else { /* Look up system module (alt send it via argument) */
yspec = clicon_dbspec_yang(h); yspec = clicon_dbspec_yang(h);
if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL) if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL)
goto fail; goto fail;
@ -420,7 +416,9 @@ mod_ns_upgrade(clicon_handle h,
if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0) if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0)
goto done; goto done;
} }
if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0) if ((ret = upgrade_callback_call(h, xt, ns,
xml_flag(xmod, (XML_FLAG_CHANGE|XML_FLAG_ADD|XML_FLAG_CHANGE)),
from, to, cbret)) < 0)
goto done; goto done;
if (ret == 0) /* XXX ignore and continue? */ if (ret == 0) /* XXX ignore and continue? */
goto fail; goto fail;
@ -447,33 +445,25 @@ clixon_module_upgrade(clicon_handle h,
modstate_diff_t *msd, modstate_diff_t *msd,
cbuf *cbret) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
char *ns; /* Namespace */ char *ns; /* Namespace */
cxobj *xs; /* XML module state */ cxobj *xmod; /* XML module state diff */
int ret; int ret;
if (msd == NULL) if (msd == NULL){
clicon_err(OE_CFG, EINVAL, "No modstate");
goto done;
}
if (msd->md_status == 0) /* No modstate in startup */
goto ok; goto ok;
/* Iterate through xml modified module state */ /* Iterate through xml modified module state */
xs = NULL; xmod = NULL;
while ((xs = xml_child_each(msd->md_mod, xs, CX_ELMNT)) != NULL) { while ((xmod = xml_child_each(msd->md_diff, xmod, CX_ELMNT)) != NULL) {
/* Extract namespace */ /* Extract namespace */
if ((ns = xml_find_body(xs, "namespace")) == NULL) if ((ns = xml_find_body(xmod, "namespace")) == NULL)
goto done; goto done;
/* Extract revisions and make callbacks */ /* Extract revisions and make callbacks */
if ((ret = mod_ns_upgrade(h, xt, xs, ns, 0, cbret)) < 0) if ((ret = mod_ns_upgrade(h, xt, xmod, ns, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
/* Iterate through xml deleted module state */
xs = NULL;
while ((xs = xml_child_each(msd->md_del, xs, CX_ELMNT)) != NULL) {
/* Extract namespace */
if ((ns = xml_find_body(xs, "namespace")) == NULL)
continue;
/* Extract revisions and make callbacks (now w deleted=1) */
if ((ret = mod_ns_upgrade(h, xt, xs, ns, 1, cbret)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;