* 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;
|
||||
argv += optind;
|
||||
|
||||
clicon_argv_set(h, argv0, argc, argv);
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
|
||||
|
||||
/* Defer: Wait to the last minute to print help message */
|
||||
|
|
@ -705,6 +706,8 @@ main(int argc,
|
|||
}
|
||||
|
||||
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){
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,15 +304,16 @@ startup_failsafe(clicon_handle h)
|
|||
if ((ret = xmldb_exists(h, db)) < 0)
|
||||
goto done;
|
||||
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;
|
||||
}
|
||||
if ((ret = candidate_commit(h, db, cbret)) < 0) /* diff */
|
||||
goto done;
|
||||
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;
|
||||
}
|
||||
clicon_log(LOG_NOTICE, "Startup failed, Failsafe database loaded ");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbret)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
|
@ -64,6 +66,12 @@ static int _reset = 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 */
|
||||
static int example_stream_timer_setup(clicon_handle h);
|
||||
|
||||
|
|
@ -246,7 +254,7 @@ example_statedata(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Registered Upgrade callback function
|
||||
/*! Testcase 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)
|
||||
|
|
@ -258,9 +266,17 @@ example_statedata(clicon_handle h,
|
|||
* @retval 0 Invalid
|
||||
* @retval -1 Error
|
||||
* @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
|
||||
upgrade_all(clicon_handle h,
|
||||
upgrade_interfaces_2014(clicon_handle h,
|
||||
cxobj *xt,
|
||||
char *ns,
|
||||
uint32_t from,
|
||||
|
|
@ -273,6 +289,108 @@ upgrade_all(clicon_handle h,
|
|||
yang_stmt *ym;
|
||||
cxobj **vec = NULL;
|
||||
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;
|
||||
int i;
|
||||
|
||||
|
|
@ -286,7 +404,35 @@ upgrade_all(clicon_handle h,
|
|||
goto done;
|
||||
for (i=0; i<vlen; 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:
|
||||
retval = 1;
|
||||
|
|
@ -355,7 +501,7 @@ example_reset(clicon_handle h,
|
|||
*
|
||||
* plugin_start is called once everything has been initialized, right before
|
||||
* 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
|
||||
* options specific to the application. These options are passed to the
|
||||
* plugin_start function via the argc and argv arguments which
|
||||
|
|
@ -366,19 +512,6 @@ example_start(clicon_handle h,
|
|||
int argc,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -409,13 +542,36 @@ static clixon_plugin_api api = {
|
|||
* @param[in] h Clixon handle
|
||||
* @retval NULL Error with clicon_err set
|
||||
* @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_init(clicon_handle h)
|
||||
{
|
||||
struct timeval retention = {0,0};
|
||||
int argc; /* command-line options (after --) */
|
||||
char **argv;
|
||||
char c;
|
||||
|
||||
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:
|
||||
* 1) Register EXAMPLE stream
|
||||
* 2) setup timer for notifications, so something happens on stream
|
||||
|
|
@ -464,7 +620,16 @@ clixon_plugin_init(clicon_handle h)
|
|||
"copy-config"
|
||||
) < 0)
|
||||
goto done;
|
||||
/* General purpose upgrade callback */
|
||||
/* Upgrade callback: if you start the backend with -- -u you will get the
|
||||
* test interface example. Otherwise the auto-upgrade feature is enabled.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -214,4 +214,8 @@ int clicon_module_state_set(clicon_handle h, cxobj *xms);
|
|||
cxobj *clicon_xml_changelog_get(clicon_handle h);
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -127,7 +127,9 @@ int xml_cv_set(cxobj *x, cg_var *cv);
|
|||
cxobj *xml_find(cxobj *xn_parent, char *name);
|
||||
|
||||
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_child_rm(cxobj *xp, int i);
|
||||
int xml_rm(cxobj *xc);
|
||||
|
|
|
|||
|
|
@ -1046,3 +1046,60 @@ clicon_xml_changelog_set(clicon_handle h,
|
|||
return -1;
|
||||
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
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see xml_insert
|
||||
* @see xml_wrap
|
||||
*/
|
||||
int
|
||||
xml_addsub(cxobj *xp,
|
||||
|
|
@ -836,30 +836,55 @@ xml_addsub(cxobj *xp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Insert a new element (xc) under an xml node (xp), move all children to xc.
|
||||
* Before: xp --> xt
|
||||
* After: xp --> xc --> xt
|
||||
/*! Wrap a new node between a parent xml node (xp) and all its children
|
||||
* Before: xp --> xc*
|
||||
* 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] tag Name of new xml child
|
||||
* @retval xc Return the new child (xc)
|
||||
* @see xml_addsub
|
||||
* The name of the function is somewhat misleading, should be called "wrap"
|
||||
* @see xml_addsub (give the parent)
|
||||
* @see xml_wrap_all (wrap all children of a node, not just one)
|
||||
*/
|
||||
cxobj *
|
||||
xml_insert(cxobj *xp,
|
||||
xml_wrap(cxobj *xc,
|
||||
char *tag)
|
||||
{
|
||||
cxobj *xc; /* new child */
|
||||
cxobj *xw; /* new wrap node */
|
||||
cxobj *xp; /* parent */
|
||||
|
||||
if ((xc = xml_new(tag, NULL, NULL)) == NULL)
|
||||
goto catch;
|
||||
while (xp->x_childvec_len)
|
||||
if (xml_addsub(xc, xml_child_i(xp, 0)) < 0)
|
||||
goto catch;
|
||||
if (xml_addsub(xp, xc) < 0)
|
||||
goto catch;
|
||||
catch:
|
||||
return xc;
|
||||
xp = xml_parent(xc);
|
||||
if ((xw = xml_new(tag, xp, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_addsub(xw, xc) < 0)
|
||||
goto done;
|
||||
done:
|
||||
return xw;
|
||||
}
|
||||
|
||||
/*! 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
|
||||
* 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..]
|
||||
* After: xc
|
||||
* @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
|
||||
* 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..]
|
||||
* After: xc
|
||||
* @param[in] xp xml parent node. Must be root. Will be deleted
|
||||
|
|
@ -1045,8 +1070,7 @@ xml_rootchild_node(cxobj *xp,
|
|||
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.
|
||||
* When sorting by "ordered by user", the order should remain in its present
|
||||
* state.
|
||||
|
|
|
|||
|
|
@ -266,7 +266,6 @@ start_restconf -f $cfg
|
|||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
|
||||
new "Check failsafe (work in progress)"
|
||||
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>]]>]]>$"
|
||||
|
||||
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