* Added xml_wrap function that adds an XML node above a node as a wrapper
* also renamed `xml_insert` to `xml_wrap_all`. * Added `clicon_argv_get()` function to get the user command-line options, ie the args in `-- <args>`. This is an alternative to using them passed to `plugin_start()`.
This commit is contained in:
parent
8624be0a67
commit
6ff36a2894
9 changed files with 642 additions and 54 deletions
|
|
@ -509,6 +509,7 @@ main(int argc,
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
|
clicon_argv_set(h, argv0, argc, argv);
|
||||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
|
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
|
||||||
|
|
||||||
/* Defer: Wait to the last minute to print help message */
|
/* Defer: Wait to the last minute to print help message */
|
||||||
|
|
@ -705,6 +706,8 @@ main(int argc,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != STARTUP_OK){
|
if (status != STARTUP_OK){
|
||||||
|
if (cbuf_len(cbret))
|
||||||
|
clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret));
|
||||||
if (startup_failsafe(h) < 0){
|
if (startup_failsafe(h) < 0){
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,15 +304,16 @@ startup_failsafe(clicon_handle h)
|
||||||
if ((ret = xmldb_exists(h, db)) < 0)
|
if ((ret = xmldb_exists(h, db)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* No it does not exist, fail */
|
if (ret == 0){ /* No it does not exist, fail */
|
||||||
clicon_err(OE_DB, 0, "No failsafe database");
|
clicon_err(OE_DB, 0, "Startup failed and no Failsafe database found, exiting");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((ret = candidate_commit(h, db, cbret)) < 0) /* diff */
|
if ((ret = candidate_commit(h, db, cbret)) < 0) /* diff */
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
clicon_err(OE_DB, 0, "Failsafe database validation failed %s", cbuf_get(cbret));
|
clicon_err(OE_DB, 0, "Startup failed, Failsafe database validation failed %s", cbuf_get(cbret));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
clicon_log(LOG_NOTICE, "Startup failed, Failsafe database loaded ");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cbret)
|
if (cbret)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
@ -64,6 +66,12 @@ static int _reset = 0;
|
||||||
*/
|
*/
|
||||||
static int _state = 0;
|
static int _state = 0;
|
||||||
|
|
||||||
|
/*! Variable to control upgrade callbacks.
|
||||||
|
* If set, call test-case for upgrading ietf-interfaces, otherwise call
|
||||||
|
* auto-upgrade
|
||||||
|
*/
|
||||||
|
static int _upgrade = 0;
|
||||||
|
|
||||||
/* forward */
|
/* forward */
|
||||||
static int example_stream_timer_setup(clicon_handle h);
|
static int example_stream_timer_setup(clicon_handle h);
|
||||||
|
|
||||||
|
|
@ -246,7 +254,7 @@ example_statedata(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Registered Upgrade callback function
|
/*! Testcase upgrade function moving interfaces-state to interfaces
|
||||||
* @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)
|
||||||
|
|
@ -258,21 +266,131 @@ example_statedata(clicon_handle h,
|
||||||
* @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 upgrade_interfaces_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
|
static int
|
||||||
upgrade_all(clicon_handle h,
|
upgrade_interfaces_2014(clicon_handle h,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
char *ns,
|
char *ns,
|
||||||
uint32_t from,
|
uint32_t from,
|
||||||
uint32_t to,
|
uint32_t to,
|
||||||
void *arg,
|
void *arg,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
yang_stmt *ym;
|
yang_stmt *ym;
|
||||||
cxobj **vec = NULL;
|
cxobj **vec = NULL;
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
cxobj *xi; /* xml /interfaces-states/interface node */
|
||||||
|
cxobj *x;
|
||||||
|
cxobj *xif; /* xml /interfaces/interface node */
|
||||||
|
size_t vlen;
|
||||||
|
int i;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* Get Yang module for this namespace. Note it may not exist (if obsolete) */
|
||||||
|
yspec = clicon_dbspec_yang(h);
|
||||||
|
if ((ym = yang_find_module_by_namespace(yspec, ns)) == NULL)
|
||||||
|
goto ok; /* shouldnt happen */
|
||||||
|
clicon_debug(1, "%s module %s", __FUNCTION__, ym?ym->ys_argument:"none");
|
||||||
|
/* Get all XML nodes with that namespace */
|
||||||
|
if (xml_namespace_vec(h, xt, ns, &vec, &vlen) < 0)
|
||||||
|
goto done;
|
||||||
|
for (i=0; i<vlen; i++){
|
||||||
|
xc = vec[i];
|
||||||
|
/* Iterate through interfaces-state */
|
||||||
|
if (strcmp(xml_name(xc),"interfaces-state") == 0){
|
||||||
|
/* Note you cannot delete or move xml objects directly under xc
|
||||||
|
* in the loop (eg xi objects) but you CAN move children of xi
|
||||||
|
*/
|
||||||
|
xi = NULL;
|
||||||
|
while ((xi = xml_child_each(xc, xi, CX_ELMNT)) != NULL) {
|
||||||
|
if (strcmp(xml_name(xi), "interface"))
|
||||||
|
continue;
|
||||||
|
if ((name = xml_find_body(xi, "name")) == NULL)
|
||||||
|
continue; /* shouldnt happen */
|
||||||
|
/* Get corresponding /interfaces/interface entry */
|
||||||
|
xif = xpath_first(xt, "/interfaces/interface[name=\"%s\"]", name);
|
||||||
|
/* - Move /if:interfaces-state/if:interface/if:admin-status to
|
||||||
|
* /if:interfaces/if:interface/ */
|
||||||
|
if ((x = xml_find(xi, "admin-status")) != NULL && xif){
|
||||||
|
if (xml_addsub(xif, x) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* - Move /if:interfaces-state/if:interface/if:statistics to
|
||||||
|
* /if:interfaces/if:interface/*/
|
||||||
|
if ((x = xml_find(xi, "statistics")) != NULL){
|
||||||
|
if (xml_addsub(xif, x) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(xml_name(xc),"interfaces") == 0){
|
||||||
|
/* Iterate through interfaces */
|
||||||
|
xi = NULL;
|
||||||
|
while ((xi = xml_child_each(xc, xi, CX_ELMNT)) != NULL) {
|
||||||
|
if (strcmp(xml_name(xi), "interface"))
|
||||||
|
continue;
|
||||||
|
/* Rename /interfaces/interface/description to descr */
|
||||||
|
if ((x = xml_find(xi, "description")) != NULL)
|
||||||
|
if (xml_name_set(x, "descr") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok:
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Testcase upgrade function removing interfaces-state
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xn XML tree to be updated
|
||||||
|
* @param[in] ns Namespace of module (for info)
|
||||||
|
* @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..
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Invalid
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see clicon_upgrade_cb
|
||||||
|
* @see test_upgrade_interfaces.sh
|
||||||
|
* @see upgrade_interfaces_2014
|
||||||
|
* The 2016 function does:
|
||||||
|
* - Delete /if:interfaces-state
|
||||||
|
* - Wrap /interfaces/interface/descr to /interfaces/interface/docs/descr
|
||||||
|
* - Change type /interfaces/interface/statistics/in-octets to decimal64 with
|
||||||
|
* fraction-digits 3 and divide all values with 1000
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
upgrade_interfaces_2016(clicon_handle h,
|
||||||
|
cxobj *xt,
|
||||||
|
char *ns,
|
||||||
|
uint32_t from,
|
||||||
|
uint32_t to,
|
||||||
|
void *arg,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
yang_spec *yspec;
|
||||||
|
yang_stmt *ym;
|
||||||
|
cxobj **vec = NULL;
|
||||||
|
cxobj *xc;
|
||||||
|
cxobj *xi;
|
||||||
|
cxobj *x;
|
||||||
|
cxobj *xb;
|
||||||
size_t vlen;
|
size_t vlen;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
@ -286,7 +404,35 @@ upgrade_all(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
for (i=0; i<vlen; i++){
|
for (i=0; i<vlen; i++){
|
||||||
xc = vec[i];
|
xc = vec[i];
|
||||||
clicon_debug(1, "%s update %s", __FUNCTION__, xml_name(xc));
|
/* Delete /if:interfaces-state */
|
||||||
|
if (strcmp(xml_name(xc), "interfaces-state") == 0)
|
||||||
|
xml_purge(xc);
|
||||||
|
/* Iterate through interfaces */
|
||||||
|
else if (strcmp(xml_name(xc),"interfaces") == 0){
|
||||||
|
/* Iterate through interfaces */
|
||||||
|
xi = NULL;
|
||||||
|
while ((xi = xml_child_each(xc, xi, CX_ELMNT)) != NULL) {
|
||||||
|
if (strcmp(xml_name(xi), "interface"))
|
||||||
|
continue;
|
||||||
|
/* Wrap /interfaces/interface/descr to /interfaces/interface/docs/descr */
|
||||||
|
if ((x = xml_find(xi, "descr")) != NULL)
|
||||||
|
if (xml_wrap(x, "docs") < 0)
|
||||||
|
goto done;
|
||||||
|
/* Change type /interfaces/interface/statistics/in-octets to
|
||||||
|
* decimal64 with fraction-digits 3 and divide values with 1000
|
||||||
|
*/
|
||||||
|
if ((x = xpath_first(xi, "statistics/in-octets")) != NULL){
|
||||||
|
if ((xb = xml_body_get(x)) != NULL){
|
||||||
|
uint64_t u64;
|
||||||
|
cbuf *cb = cbuf_new();
|
||||||
|
parse_uint64(xml_value(xb), &u64, NULL);
|
||||||
|
cprintf(cb, "%" PRIu64 ".%03d", u64/1000, (int)(u64%1000));
|
||||||
|
xml_value_set(xb, cbuf_get(cb));
|
||||||
|
cbuf_free(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
retval = 1;
|
retval = 1;
|
||||||
|
|
@ -355,7 +501,7 @@ example_reset(clicon_handle h,
|
||||||
*
|
*
|
||||||
* plugin_start is called once everything has been initialized, right before
|
* plugin_start is called once everything has been initialized, right before
|
||||||
* the main event loop is entered.
|
* the main event loop is entered.
|
||||||
* From the CLI, command line options can be passed to the
|
* From the cli/backend, command line options can be passed to the
|
||||||
* plugins by using "-- <args>" where <args> is any choice of
|
* plugins by using "-- <args>" where <args> is any choice of
|
||||||
* options specific to the application. These options are passed to the
|
* options specific to the application. These options are passed to the
|
||||||
* plugin_start function via the argc and argv arguments which
|
* plugin_start function via the argc and argv arguments which
|
||||||
|
|
@ -363,22 +509,9 @@ example_reset(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
example_start(clicon_handle h,
|
example_start(clicon_handle h,
|
||||||
int argc,
|
int argc,
|
||||||
char **argv)
|
char **argv)
|
||||||
{
|
{
|
||||||
char c;
|
|
||||||
|
|
||||||
opterr = 0;
|
|
||||||
optind = 1;
|
|
||||||
while ((c = getopt(argc, argv, "rs")) != -1)
|
|
||||||
switch (c) {
|
|
||||||
case 'r':
|
|
||||||
_reset = 1;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
_state = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,13 +542,36 @@ static clixon_plugin_api api = {
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @retval NULL Error with clicon_err set
|
* @retval NULL Error with clicon_err set
|
||||||
* @retval api Pointer to API struct
|
* @retval api Pointer to API struct
|
||||||
|
* In this example, you can pass -r, -s, -u to control the behaviour, mainly
|
||||||
|
* for use in the test suites.
|
||||||
*/
|
*/
|
||||||
clixon_plugin_api *
|
clixon_plugin_api *
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
struct timeval retention = {0,0};
|
struct timeval retention = {0,0};
|
||||||
|
int argc; /* command-line options (after --) */
|
||||||
|
char **argv;
|
||||||
|
char c;
|
||||||
|
|
||||||
clicon_debug(1, "%s backend", __FUNCTION__);
|
clicon_debug(1, "%s backend", __FUNCTION__);
|
||||||
|
|
||||||
|
if (clicon_argv_get(h, &argc, &argv) < 0)
|
||||||
|
goto done;
|
||||||
|
opterr = 0;
|
||||||
|
optind = 1;
|
||||||
|
while ((c = getopt(argc, argv, "rsu")) != -1)
|
||||||
|
switch (c) {
|
||||||
|
case 'r':
|
||||||
|
_reset = 1;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
_state = 1;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
_upgrade = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Example stream initialization:
|
/* Example stream initialization:
|
||||||
* 1) Register EXAMPLE stream
|
* 1) Register EXAMPLE stream
|
||||||
* 2) setup timer for notifications, so something happens on stream
|
* 2) setup timer for notifications, so something happens on stream
|
||||||
|
|
@ -464,9 +620,18 @@ clixon_plugin_init(clicon_handle h)
|
||||||
"copy-config"
|
"copy-config"
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* General purpose upgrade callback */
|
/* Upgrade callback: if you start the backend with -- -u you will get the
|
||||||
if (upgrade_callback_register(h, xml_changelog_upgrade, NULL, 0, 0, NULL) < 0)
|
* test interface example. Otherwise the auto-upgrade feature is enabled.
|
||||||
goto done;
|
*/
|
||||||
|
if (_upgrade){
|
||||||
|
if (upgrade_callback_register(h, upgrade_interfaces_2014, "urn:example:interfaces", 0, 0, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
if (upgrade_callback_register(h, upgrade_interfaces_2016, "urn:example:interfaces", 0, 0, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (upgrade_callback_register(h, xml_changelog_upgrade, NULL, 0, 0, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* Return plugin API */
|
/* Return plugin API */
|
||||||
return &api;
|
return &api;
|
||||||
|
|
|
||||||
|
|
@ -214,4 +214,8 @@ int clicon_module_state_set(clicon_handle h, cxobj *xms);
|
||||||
cxobj *clicon_xml_changelog_get(clicon_handle h);
|
cxobj *clicon_xml_changelog_get(clicon_handle h);
|
||||||
int clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog);
|
int clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog);
|
||||||
|
|
||||||
|
/*! Set and get user command-line options (after --) */
|
||||||
|
int clicon_argv_get(clicon_handle h, int *argc, char ***argv);
|
||||||
|
int clicon_argv_set(clicon_handle h, char *argv0, int argc, char **argv);
|
||||||
|
|
||||||
#endif /* _CLIXON_OPTIONS_H_ */
|
#endif /* _CLIXON_OPTIONS_H_ */
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@ int xml_cv_set(cxobj *x, cg_var *cv);
|
||||||
cxobj *xml_find(cxobj *xn_parent, char *name);
|
cxobj *xml_find(cxobj *xn_parent, char *name);
|
||||||
|
|
||||||
int xml_addsub(cxobj *xp, cxobj *xc);
|
int xml_addsub(cxobj *xp, cxobj *xc);
|
||||||
cxobj *xml_insert(cxobj *xt, char *tag);
|
cxobj *xml_wrap_all(cxobj *xp, char *tag);
|
||||||
|
cxobj *xml_wrap(cxobj *xc, char *tag);
|
||||||
|
#define xml_insert(x,t) xml_wrap_all((x),(t))
|
||||||
int xml_purge(cxobj *xc);
|
int xml_purge(cxobj *xc);
|
||||||
int xml_child_rm(cxobj *xp, int i);
|
int xml_child_rm(cxobj *xp, int i);
|
||||||
int xml_rm(cxobj *xc);
|
int xml_rm(cxobj *xc);
|
||||||
|
|
|
||||||
|
|
@ -1046,3 +1046,60 @@ clicon_xml_changelog_set(clicon_handle h,
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get user clicon command-line options argv, argc (after --)
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[out] argc
|
||||||
|
* @param[out] argv
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_argv_get(clicon_handle h,
|
||||||
|
int *argc,
|
||||||
|
char ***argv)
|
||||||
|
|
||||||
|
{
|
||||||
|
clicon_hash_t *cdat = clicon_data(h);
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if ((p = hash_value(cdat, "argc", NULL)) == NULL)
|
||||||
|
return -1;
|
||||||
|
*argc = *(int*)p;
|
||||||
|
if ((p = hash_value(cdat, "argv", NULL)) == NULL)
|
||||||
|
return -1;
|
||||||
|
*argv = *(char***)p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Set clicon user command-line options argv, argc (after --)
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] prog argv[0] - the program name
|
||||||
|
* @param[in] argc Length of argv
|
||||||
|
* @param[in] argv Array of command-line options
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_argv_set(clicon_handle h,
|
||||||
|
char *prgm,
|
||||||
|
int argc,
|
||||||
|
char **argv)
|
||||||
|
{
|
||||||
|
clicon_hash_t *cdat = clicon_data(h);
|
||||||
|
char **argvv = NULL;
|
||||||
|
|
||||||
|
/* add space for null-termination and argv[0] program name */
|
||||||
|
if ((argvv = calloc(argc+2, sizeof(char*))) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "calloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(argvv+1, argv, argc*sizeof(char*));
|
||||||
|
argvv[0] = prgm;
|
||||||
|
if (hash_add(cdat, "argv", &argvv, sizeof(argvv))==NULL)
|
||||||
|
return -1;
|
||||||
|
argc += 1;
|
||||||
|
if (hash_add(cdat, "argc", &argc, sizeof(argc))==NULL)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -808,7 +808,7 @@ xml_find(cxobj *x_up,
|
||||||
* @param[in] xc Child xml node to insert under xp
|
* @param[in] xc Child xml node to insert under xp
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see xml_insert
|
* @see xml_wrap
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_addsub(cxobj *xp,
|
xml_addsub(cxobj *xp,
|
||||||
|
|
@ -836,30 +836,55 @@ xml_addsub(cxobj *xp,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Insert a new element (xc) under an xml node (xp), move all children to xc.
|
/*! Wrap a new node between a parent xml node (xp) and all its children
|
||||||
* Before: xp --> xt
|
* Before: xp --> xc*
|
||||||
* After: xp --> xc --> xt
|
* After: xp --> xw --> xc*
|
||||||
|
* @param[in] xp Parent xml node
|
||||||
|
* @param[in] tag Name of new xml child
|
||||||
|
* @retval xw Return the new child (xw)
|
||||||
|
* @see xml_addsub
|
||||||
|
* @see xml_wrap (wrap s single node)
|
||||||
|
*/
|
||||||
|
cxobj *
|
||||||
|
xml_wrap_all(cxobj *xp,
|
||||||
|
char *tag)
|
||||||
|
{
|
||||||
|
cxobj *xw; /* new wrap node */
|
||||||
|
|
||||||
|
if ((xw = xml_new(tag, NULL, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
while (xp->x_childvec_len)
|
||||||
|
if (xml_addsub(xw, xml_child_i(xp, 0)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_addsub(xp, xw) < 0)
|
||||||
|
goto done;
|
||||||
|
done:
|
||||||
|
return xw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Wrap a new element above a single xml node (xc) with new tag
|
||||||
|
* Before: xp --> xc # specific child
|
||||||
|
* After: xp --> xt(tag) --> xc
|
||||||
* @param[in] xp Parent xml node
|
* @param[in] xp Parent xml node
|
||||||
* @param[in] tag Name of new xml child
|
* @param[in] tag Name of new xml child
|
||||||
* @retval xc Return the new child (xc)
|
* @retval xc Return the new child (xc)
|
||||||
* @see xml_addsub
|
* @see xml_addsub (give the parent)
|
||||||
* The name of the function is somewhat misleading, should be called "wrap"
|
* @see xml_wrap_all (wrap all children of a node, not just one)
|
||||||
*/
|
*/
|
||||||
cxobj *
|
cxobj *
|
||||||
xml_insert(cxobj *xp,
|
xml_wrap(cxobj *xc,
|
||||||
char *tag)
|
char *tag)
|
||||||
{
|
{
|
||||||
cxobj *xc; /* new child */
|
cxobj *xw; /* new wrap node */
|
||||||
|
cxobj *xp; /* parent */
|
||||||
|
|
||||||
if ((xc = xml_new(tag, NULL, NULL)) == NULL)
|
xp = xml_parent(xc);
|
||||||
goto catch;
|
if ((xw = xml_new(tag, xp, NULL)) == NULL)
|
||||||
while (xp->x_childvec_len)
|
goto done;
|
||||||
if (xml_addsub(xc, xml_child_i(xp, 0)) < 0)
|
if (xml_addsub(xw, xc) < 0)
|
||||||
goto catch;
|
goto done;
|
||||||
if (xml_addsub(xp, xc) < 0)
|
done:
|
||||||
goto catch;
|
return xw;
|
||||||
catch:
|
|
||||||
return xc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Remove and free an xml node child from xml parent
|
/*! Remove and free an xml node child from xml parent
|
||||||
|
|
@ -961,7 +986,7 @@ xml_rm(cxobj *xc)
|
||||||
|
|
||||||
/*! Return a child sub-tree, while removing parent and all other children
|
/*! Return a child sub-tree, while removing parent and all other children
|
||||||
* Given a root xml node, and the i:th child, remove the child from its parent
|
* Given a root xml node, and the i:th child, remove the child from its parent
|
||||||
* and return it, remove the parent and all other children.
|
* and return it, remove the parent and all other children. (unwrap)
|
||||||
* Before: xp-->[..xc..]
|
* Before: xp-->[..xc..]
|
||||||
* After: xc
|
* After: xc
|
||||||
* @param[in] xp xml parent node. Will be deleted
|
* @param[in] xp xml parent node. Will be deleted
|
||||||
|
|
@ -1009,7 +1034,7 @@ xml_rootchild(cxobj *xp,
|
||||||
|
|
||||||
/*! Return a child sub-tree, while removing parent and all other children
|
/*! Return a child sub-tree, while removing parent and all other children
|
||||||
* Given a root xml node, remove the child from its parent
|
* Given a root xml node, remove the child from its parent
|
||||||
* , remove the parent and all other children.
|
* , remove the parent and all other children. (unwrap)
|
||||||
* Before: xp-->[..xc..]
|
* Before: xp-->[..xc..]
|
||||||
* After: xc
|
* After: xc
|
||||||
* @param[in] xp xml parent node. Must be root. Will be deleted
|
* @param[in] xp xml parent node. Must be root. Will be deleted
|
||||||
|
|
@ -1045,8 +1070,7 @@ xml_rootchild_node(cxobj *xp,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Help function to sorting: enumerate all children according to present order
|
||||||
/*! help function to sorting: enumerate all children according to present order
|
|
||||||
* This is so that the child itself know its present order in a list.
|
* This is so that the child itself know its present order in a list.
|
||||||
* When sorting by "ordered by user", the order should remain in its present
|
* When sorting by "ordered by user", the order should remain in its present
|
||||||
* state.
|
* state.
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,6 @@ start_restconf -f $cfg
|
||||||
new "waiting"
|
new "waiting"
|
||||||
sleep $RCWAIT
|
sleep $RCWAIT
|
||||||
|
|
||||||
new "Check failsafe (work in progress)"
|
|
||||||
new "Check running db content"
|
new "Check running db content"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply><data>$XML</data></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply><data>$XML</data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
333
test/test_upgrade_interfaces.sh
Executable file
333
test/test_upgrade_interfaces.sh
Executable file
|
|
@ -0,0 +1,333 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Upgrade a module by registering a manually programmed callback
|
||||||
|
# The usecase is insipred by the ietf-interfaces upgrade from
|
||||||
|
# 2014-05-08 to 2018-02-20.
|
||||||
|
# That includes moving parts from interfaces-state to interfaces and then
|
||||||
|
# deprecating the whole /interfaces-state tree.
|
||||||
|
# A preliminary change list is in Appendix A of
|
||||||
|
# draft-wang-netmod-module-revision-management-01
|
||||||
|
# The example here is simplified and also extended.
|
||||||
|
# It has also been broken up into two parts to test a series of upgrades.
|
||||||
|
# These are the operations (authentic):
|
||||||
|
# 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/
|
||||||
|
# Delete /if:interfaces-state
|
||||||
|
# These are extra added for test:
|
||||||
|
# Rename /interfaces/interface/description to /interfaces/interface/descr
|
||||||
|
# Wrap /interfaces/interface/descr to /interfaces/interface/docs/descr
|
||||||
|
# Change type /interfaces/interface/statistics/in-octets to decimal64 and divide all values with 1000
|
||||||
|
#
|
||||||
|
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
cfg=$dir/conf.xml
|
||||||
|
if2014=$dir/interfaces@2014-05-08.yang
|
||||||
|
if2018=$dir/interfaces@2018-02-20.yang
|
||||||
|
|
||||||
|
# Original simplified version - note all is config to allow for storing in
|
||||||
|
# datastore
|
||||||
|
cat <<EOF > $if2014
|
||||||
|
module interfaces{
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:interfaces";
|
||||||
|
prefix "if";
|
||||||
|
|
||||||
|
import ietf-yang-types {
|
||||||
|
prefix yang;
|
||||||
|
}
|
||||||
|
revision 2014-05-08 {
|
||||||
|
description
|
||||||
|
"Initial revision.";
|
||||||
|
reference
|
||||||
|
"RFC 7223: A YANG Data Model for Interface Management";
|
||||||
|
}
|
||||||
|
feature if-mib {
|
||||||
|
description
|
||||||
|
"This feature indicates that the device implements
|
||||||
|
the IF-MIB.";
|
||||||
|
reference
|
||||||
|
"RFC 2863: The Interfaces Group MIB";
|
||||||
|
}
|
||||||
|
container interfaces {
|
||||||
|
description
|
||||||
|
"Interface configuration parameters.";
|
||||||
|
|
||||||
|
list interface {
|
||||||
|
key "name";
|
||||||
|
leaf name {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf description {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf type {
|
||||||
|
type string;
|
||||||
|
mandatory true;
|
||||||
|
}
|
||||||
|
leaf link-up-down-trap-enable {
|
||||||
|
if-feature if-mib;
|
||||||
|
type enumeration {
|
||||||
|
enum enabled;
|
||||||
|
enum disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container interfaces-state {
|
||||||
|
list interface {
|
||||||
|
key "name";
|
||||||
|
leaf name {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf admin-status {
|
||||||
|
if-feature if-mib;
|
||||||
|
type enumeration {
|
||||||
|
enum up;
|
||||||
|
enum down;
|
||||||
|
enum testing;
|
||||||
|
}
|
||||||
|
mandatory true;
|
||||||
|
}
|
||||||
|
container statistics {
|
||||||
|
leaf in-octets {
|
||||||
|
type yang:counter64;
|
||||||
|
}
|
||||||
|
leaf in-unicast-pkts {
|
||||||
|
type yang:counter64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $if2018
|
||||||
|
module interfaces{
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:interfaces";
|
||||||
|
prefix "if";
|
||||||
|
|
||||||
|
import ietf-yang-types {
|
||||||
|
prefix yang;
|
||||||
|
}
|
||||||
|
revision 2018-02-20 {
|
||||||
|
description
|
||||||
|
"Updated to support NMDA.";
|
||||||
|
reference
|
||||||
|
"RFC 8343: A YANG Data Model for Interface Management";
|
||||||
|
}
|
||||||
|
revision 2014-05-08 {
|
||||||
|
description
|
||||||
|
"Initial revision.";
|
||||||
|
reference
|
||||||
|
"RFC 7223: A YANG Data Model for Interface Management";
|
||||||
|
}
|
||||||
|
feature if-mib {
|
||||||
|
description
|
||||||
|
"This feature indicates that the device implements
|
||||||
|
the IF-MIB.";
|
||||||
|
reference
|
||||||
|
"RFC 2863: The Interfaces Group MIB";
|
||||||
|
}
|
||||||
|
container interfaces {
|
||||||
|
description
|
||||||
|
"Interface configuration parameters.";
|
||||||
|
|
||||||
|
list interface {
|
||||||
|
key "name";
|
||||||
|
leaf name {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
container docs{
|
||||||
|
description "Original description is wrapped and renamed";
|
||||||
|
leaf descr {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf type {
|
||||||
|
type string;
|
||||||
|
mandatory true;
|
||||||
|
}
|
||||||
|
leaf link-up-down-trap-enable {
|
||||||
|
if-feature if-mib;
|
||||||
|
type enumeration {
|
||||||
|
enum enabled;
|
||||||
|
enum disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf admin-status {
|
||||||
|
if-feature if-mib;
|
||||||
|
type enumeration {
|
||||||
|
enum up;
|
||||||
|
enum down;
|
||||||
|
enum testing;
|
||||||
|
}
|
||||||
|
mandatory true;
|
||||||
|
}
|
||||||
|
container statistics {
|
||||||
|
leaf in-octets {
|
||||||
|
type decimal64{
|
||||||
|
fraction-digits 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf in-unicast-pkts {
|
||||||
|
type yang:counter64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# Create startup db revision example-a and example-b 2017-12-01
|
||||||
|
# this should be automatically upgraded to 2017-12-20
|
||||||
|
cat <<EOF > $dir/startup_db
|
||||||
|
<config>
|
||||||
|
<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
||||||
|
<module-set-id>42</module-set-id>
|
||||||
|
<module>
|
||||||
|
<name>interfaces</name>
|
||||||
|
<revision>2014-05-08</revision>
|
||||||
|
<namespace>urn:example:interfaces</namespace>
|
||||||
|
</module>
|
||||||
|
</modules-state>
|
||||||
|
<interfaces xmlns="urn:example:interfaces">
|
||||||
|
<interface>
|
||||||
|
<name>e0</name>
|
||||||
|
<type>eth</type>
|
||||||
|
<description>First interface</description>
|
||||||
|
</interface>
|
||||||
|
<interface>
|
||||||
|
<name>e1</name>
|
||||||
|
<type>eth</type>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
<interfaces-state xmlns="urn:example:interfaces">
|
||||||
|
<interface>
|
||||||
|
<name>e0</name>
|
||||||
|
<admin-status>up</admin-status>
|
||||||
|
<statistics>
|
||||||
|
<in-octets>54326432</in-octets>
|
||||||
|
<in-unicast-pkts>8458765</in-unicast-pkts>
|
||||||
|
</statistics>
|
||||||
|
</interface>
|
||||||
|
<interface>
|
||||||
|
<name>e1</name>
|
||||||
|
<admin-status>down</admin-status>
|
||||||
|
</interface>
|
||||||
|
<interface>
|
||||||
|
<name>e2</name>
|
||||||
|
<admin-status>testing</admin-status>
|
||||||
|
</interface>
|
||||||
|
</interfaces-state>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Wanted new XML
|
||||||
|
# Note interface e2 is not moved
|
||||||
|
cat <<EOF > $dir/wanted
|
||||||
|
<config>
|
||||||
|
<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
||||||
|
<module-set-id>42</module-set-id>
|
||||||
|
<module>
|
||||||
|
<name>interfaces</name>
|
||||||
|
<revision>2018-02-20</revision>
|
||||||
|
<namespace>urn:example:interfaces</namespace>
|
||||||
|
</module>
|
||||||
|
</modules-state>
|
||||||
|
<interfaces xmlns="urn:example:interfaces">
|
||||||
|
<interface>
|
||||||
|
<name>e0</name>
|
||||||
|
<type>eth</type>
|
||||||
|
<admin-status>up</admin-status>
|
||||||
|
<docs><descr>First interface</descr></docs>
|
||||||
|
<statistics>
|
||||||
|
<in-octets>54326.432</in-octets>
|
||||||
|
<in-unicast-pkts>8458765</in-unicast-pkts>
|
||||||
|
</statistics>
|
||||||
|
</interface>
|
||||||
|
<interface>
|
||||||
|
<name>e1</name>
|
||||||
|
<type>eth</type>
|
||||||
|
<admin-status>down</admin-status>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
XML='<interfaces xmlns="urn:example:interfaces"><interface><name>e0</name><docs><descr>First interface</descr></docs><type>eth</type><admin-status>up</admin-status><statistics><in-octets>54326.432</in-octets><in-unicast-pkts>8458765</in-unicast-pkts></statistics></interface><interface><name>e1</name><type>eth</type><admin-status>down</admin-status></interface></interfaces>'
|
||||||
|
|
||||||
|
|
||||||
|
# Create configuration
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_FEATURE>*:*</CLICON_FEATURE>
|
||||||
|
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/example/backend</CLICON_BACKEND_DIR>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||||
|
<CLICON_XMLDB_MODSTATE>true</CLICON_XMLDB_MODSTATE>
|
||||||
|
<CLICON_XML_CHANGELOG>false</CLICON_XML_CHANGELOG>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Start new system from old datastore
|
||||||
|
mode=startup
|
||||||
|
|
||||||
|
# -u means trigger example upgrade
|
||||||
|
new "test params: -s $mode -f $cfg -- -u"
|
||||||
|
# Bring your own backend
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
# kill old backend (if any)
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s $mode -f $cfg -- -u"
|
||||||
|
start_backend -s $mode -f $cfg -- -u
|
||||||
|
fi
|
||||||
|
new "waiting"
|
||||||
|
sleep $RCWAIT
|
||||||
|
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "start restconf daemon"
|
||||||
|
start_restconf -f $cfg
|
||||||
|
|
||||||
|
new "waiting"
|
||||||
|
sleep $RCWAIT
|
||||||
|
|
||||||
|
new "Check running db content"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply><data>$XML</data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
stop_restconf
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=`pgrep -u root -f clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
stop_backend -f $cfg
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
fi
|
||||||
Loading…
Add table
Add a link
Reference in a new issue