* New yang changelog experimental feature for automatic upgrade
* Added modules-state diff parameter to xmldb_get datastore function for startup scenarios. * Allowed Yang extended Xpath functions (syntax only): * re-match, deref, derived-from, derived-from-or-self, enum-value, bit-is-set * XSD regular expression handling of dash(`-`) *: Translate XDS `[xxx\-yyy]` to POSIX `[xxxyyy-]`. * YANG Anydata treated same as Anyxml
This commit is contained in:
parent
434f0b930e
commit
3f68cca06c
37 changed files with 1475 additions and 351 deletions
|
|
@ -70,11 +70,11 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
|||
clixon_string.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
|
||||
clixon_yang_cardinality.c \
|
||||
clixon_yang_cardinality.c clixon_yang_changelog.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
|
||||
clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
|
||||
clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_db.h"
|
||||
#include "clixon_nacm.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1042,6 +1042,10 @@ netconf_module_load(clicon_handle h)
|
|||
goto done;
|
||||
if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
|
||||
goto done;
|
||||
/* YANG module revision change management */
|
||||
if (clicon_option_bool(h, "CLICON_YANG_CHANGELOG"))
|
||||
if (yang_spec_parse_module(h, "clixon-yang-changelog", NULL, yspec)< 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -989,7 +989,6 @@ cxobj *
|
|||
clicon_module_state_get(clicon_handle h)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
void *p;
|
||||
|
||||
if ((p = hash_value(cdat, "module_state_cache", NULL)) != NULL)
|
||||
|
|
@ -999,7 +998,7 @@ clicon_module_state_get(clicon_handle h)
|
|||
|
||||
/*! Set module state cache
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] s Open socket (or -1 to close)
|
||||
* @param[in] xms Module state cache XML tree
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
|
|
@ -1013,3 +1012,37 @@ clicon_module_state_set(clicon_handle h,
|
|||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get yang module changelog
|
||||
* @param[in] h Clicon handle
|
||||
* @retval xch Module revision changelog XML tree
|
||||
* @see draft-wang-netmod-module-revision-management-01
|
||||
*/
|
||||
cxobj *
|
||||
clicon_yang_changelog_get(clicon_handle h)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
void *p;
|
||||
|
||||
if ((p = hash_value(cdat, "yang-changelog", NULL)) != NULL)
|
||||
return *(cxobj **)p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Set yang module changelog
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] s Module revision changelog XML tree
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see draft-wang-netmod-module-revision-management-01
|
||||
*/
|
||||
int
|
||||
clicon_yang_changelog_set(clicon_handle h,
|
||||
cxobj *xchlog)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
if (hash_add(cdat, "yang_changelog", &xchlog, sizeof(xchlog))==NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -407,23 +407,25 @@ clixon_plugin_auth(clicon_handle h,
|
|||
* When namespace and name match, the callback is made
|
||||
*/
|
||||
typedef struct {
|
||||
qelem_t rc_qelem; /* List header */
|
||||
qelem_t rc_qelem; /* List header */
|
||||
clicon_rpc_cb rc_callback; /* RPC Callback */
|
||||
void *rc_arg; /* Application specific argument to cb */
|
||||
char *rc_namespace;/* Namespace to combine with name tag */
|
||||
char *rc_name; /* Xml/json tag/name */
|
||||
void *rc_arg; /* Application specific argument to cb */
|
||||
char *rc_namespace;/* Namespace to combine with name tag */
|
||||
char *rc_name; /* Xml/json tag/name */
|
||||
} rpc_callback_t;
|
||||
|
||||
/* List of rpc callback entries */
|
||||
/* List of rpc callback entries XXX hang on handle */
|
||||
static rpc_callback_t *rpc_cb_list = NULL;
|
||||
|
||||
/*! Register a RPC callback by appending a new RPC to the list
|
||||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] cb, Callback called
|
||||
* @param[in] arg, Domain-specific argument to send to callback
|
||||
* @param[in] cb Callback called
|
||||
* @param[in] arg Domain-specific argument to send to callback
|
||||
* @param[in] namespace namespace of rpc
|
||||
* @param[in] name RPC name
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see rpc_callback_call which makes the actual callback
|
||||
*/
|
||||
int
|
||||
|
|
@ -530,3 +532,151 @@ rpc_callback_call(clicon_handle h,
|
|||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* Upgrade callbacks for backend upgrade of datastore
|
||||
* Register upgrade callbacks in plugin_init() with a module and a "from" and "to"
|
||||
* revision.
|
||||
*/
|
||||
typedef struct {
|
||||
qelem_t uc_qelem; /* List header */
|
||||
clicon_upgrade_cb uc_callback; /* RPC Callback */
|
||||
void *uc_arg; /* Application specific argument to cb */
|
||||
char *uc_name; /* Module name */
|
||||
char *uc_namespace; /* Module namespace ??? */
|
||||
uint32_t uc_from; /* Module revision (from) or 0 in YYYYMMDD format */
|
||||
uint32_t uc_to; /* Module revision (to) in YYYYMMDD format */
|
||||
} upgrade_callback_t;
|
||||
|
||||
/* List of rpc callback entries XXX hang on handle */
|
||||
static upgrade_callback_t *upgrade_cb_list = NULL;
|
||||
|
||||
/*! Register an upgrade callback by appending the new callback to the list
|
||||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] cb Callback called
|
||||
* @param[in] arg Domain-specific argument to send to callback
|
||||
* @param[in] name Module name (if NULL all modules)
|
||||
* @param[in] namespace Module namespace (NOTE not relevant)
|
||||
* @param[in] from From module revision (0 from any revision)
|
||||
* @param[in] to To module revision (0 means module obsoleted)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see upgrade_callback_call which makes the actual callback
|
||||
*/
|
||||
int
|
||||
upgrade_callback_register(clicon_handle h,
|
||||
clicon_upgrade_cb cb,
|
||||
void *arg,
|
||||
char *name,
|
||||
char *namespace,
|
||||
uint32_t from,
|
||||
uint32_t to)
|
||||
{
|
||||
upgrade_callback_t *uc;
|
||||
|
||||
if ((uc = malloc(sizeof(upgrade_callback_t))) == NULL) {
|
||||
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
uc->uc_callback = cb;
|
||||
uc->uc_arg = arg;
|
||||
if (name)
|
||||
uc->uc_name = strdup(name);
|
||||
if (namespace)
|
||||
uc->uc_namespace = strdup(namespace);
|
||||
uc->uc_from = from;
|
||||
uc->uc_to = to;
|
||||
ADDQ(uc, upgrade_cb_list);
|
||||
return 0;
|
||||
done:
|
||||
if (uc){
|
||||
if (uc->uc_name)
|
||||
free(uc->uc_name);
|
||||
if (uc->uc_namespace)
|
||||
free(uc->uc_namespace);
|
||||
free(uc);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Delete all Upgrade callbacks
|
||||
*/
|
||||
int
|
||||
upgrade_callback_delete_all(void)
|
||||
{
|
||||
upgrade_callback_t *uc;
|
||||
|
||||
while((uc = upgrade_cb_list) != NULL) {
|
||||
DELQ(uc, upgrade_cb_list, upgrade_callback_t *);
|
||||
if (uc->uc_name)
|
||||
free(uc->uc_name);
|
||||
if (uc->uc_namespace)
|
||||
free(uc->uc_namespace);
|
||||
free(uc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Search Upgrade callbacks and invoke if module match
|
||||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] xt XML tree to be updated
|
||||
* @param[in] modname Name of module
|
||||
* @param[in] modns 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[out] cbret Return XML (as string in CLIgen buffer), on invalid
|
||||
* @retval -1 Error
|
||||
* @retval 0 Invalid - cbret contains reason as netconf
|
||||
* @retval 1 OK
|
||||
* @see upgrade_callback_register which registers the callbacks
|
||||
*/
|
||||
int
|
||||
upgrade_callback_call(clicon_handle h,
|
||||
cxobj *xt,
|
||||
char *modname,
|
||||
char *modns,
|
||||
uint32_t from,
|
||||
uint32_t to,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
upgrade_callback_t *uc;
|
||||
int nr = 0; /* How many callbacks */
|
||||
int ret;
|
||||
|
||||
if (upgrade_cb_list == NULL)
|
||||
return 0;
|
||||
uc = upgrade_cb_list;
|
||||
do {
|
||||
/* For matching an upgrade callback:
|
||||
* - No module name registered (matches all modules) OR
|
||||
* - Names match
|
||||
* AND
|
||||
* - No registered from revision (matches all revisions) OR
|
||||
* - Registered from revision >= from AND
|
||||
* - Registered to revision <= to (which includes case both 0)
|
||||
*/
|
||||
if (uc->uc_name == NULL || strcmp(uc->uc_name, modname)==0)
|
||||
if ((uc->uc_from == 0) ||
|
||||
(uc->uc_from >= from && uc->uc_to <= to)){
|
||||
if ((ret = uc->uc_callback(h, xt, modname, modns, from, to, uc->uc_arg, cbret)) < 0){
|
||||
clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_name);
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
nr++;
|
||||
}
|
||||
uc = NEXTQ(upgrade_callback_t *, uc);
|
||||
} while (uc != upgrade_cb_list);
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
return retval;
|
||||
fail:
|
||||
retval =0;
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,17 +636,20 @@ clixon_trim(char *str)
|
|||
}
|
||||
|
||||
/*! Transform from XSD regex to posix ERE
|
||||
* The usecase is that Yang (RFC7950) supports XSD regexpressions but CLIgen supports
|
||||
* Current translations:
|
||||
* \d --> [0-9]
|
||||
* The usecase is that Yang (RFC7950) supports XSD regular expressions but
|
||||
* CLIgen supports POSIX ERE
|
||||
* POSIX ERE regexps according to man regex(3).
|
||||
* @param[in] xsd Input regex string according XSD
|
||||
* @param[out] posix Output (malloced) string according to POSIX ERE
|
||||
* @see https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs
|
||||
* @see https://www.regular-expressions.info/posixbrackets.html#class translation
|
||||
* @see https://www.regular-expressions.info/xml.html
|
||||
* Translation is not complete but covers some character sequences:
|
||||
* \d decimal digit
|
||||
* \w alphanum + underscore
|
||||
* \w all characters except the set of "punctuation", "separator" and
|
||||
* "other" characters: #x0000-#x10FFFF]-[\p{P}\p{Z}\p{C}]
|
||||
* \i letters + underscore and colon
|
||||
* \c XML Namechar, see: https://www.w3.org/TR/2008/REC-xml-20081126/#NT-NameChar
|
||||
*/
|
||||
int
|
||||
regexp_xsd2posix(char *xsd,
|
||||
|
|
@ -657,6 +660,7 @@ regexp_xsd2posix(char *xsd,
|
|||
char x;
|
||||
int i;
|
||||
int esc;
|
||||
int minus = 0;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
@ -668,17 +672,24 @@ regexp_xsd2posix(char *xsd,
|
|||
if (esc){
|
||||
esc = 0;
|
||||
switch (x){
|
||||
case '-': /* \- is translated to -], ie must be last in bracket */
|
||||
minus++;
|
||||
break;
|
||||
case 'c': /* xml namechar */
|
||||
cprintf(cb, "[0-9a-zA-Z\\\\.\\\\-_:]");
|
||||
cprintf(cb, "[0-9a-zA-Z._:-]"); /* also interpunct */
|
||||
break;
|
||||
case 'd':
|
||||
cprintf(cb, "[0-9]");
|
||||
break;
|
||||
case 'w':
|
||||
cprintf(cb, "[0-9a-zA-Z_\\\\-]");
|
||||
case 'i': /* initial */
|
||||
cprintf(cb, "[a-zA-Z_:]");
|
||||
break;
|
||||
case 'W':
|
||||
cprintf(cb, "[^0-9a-zA-Z_\\\\-]");
|
||||
case 'w': /* word */
|
||||
//cprintf(cb, "[0-9a-zA-Z_\\\\-]")
|
||||
cprintf(cb, "[^[:punct:][:space:][:cntrl:]]");
|
||||
break;
|
||||
case 'W': /* inverse of \w */
|
||||
cprintf(cb, "[[:punct:][:space:][:cntrl:]]");
|
||||
break;
|
||||
case 's':
|
||||
cprintf(cb, "[ \t\r\n]");
|
||||
|
|
@ -693,6 +704,10 @@ regexp_xsd2posix(char *xsd,
|
|||
}
|
||||
else if (x == '\\')
|
||||
esc++;
|
||||
else if (x == ']' && minus){
|
||||
cprintf(cb, "-]");
|
||||
minus = 0;
|
||||
}
|
||||
else
|
||||
cprintf(cb, "%c", x);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_db.h"
|
||||
|
||||
/* Set to log get and put requests */
|
||||
|
|
@ -325,7 +326,7 @@ xmldb_setopt(clicon_handle h,
|
|||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xret Single return XML tree. Free with xml_free()
|
||||
* @param[out] xms If set, return modules-state differences
|
||||
* @param[out] msd If set, return modules-state differences
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
|
|
@ -338,12 +339,12 @@ xmldb_setopt(clicon_handle h,
|
|||
* @see xpath_vec
|
||||
*/
|
||||
int
|
||||
xmldb_get(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xret,
|
||||
cxobj **xms)
|
||||
xmldb_get(clicon_handle h,
|
||||
const char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xret,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
int retval = -1;
|
||||
xmldb_handle xh;
|
||||
|
|
@ -361,7 +362,7 @@ xmldb_get(clicon_handle h,
|
|||
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
||||
goto done;
|
||||
}
|
||||
retval = xa->xa_get_fn(xh, db, xpath, config, xret, xms);
|
||||
retval = xa->xa_get_fn(xh, db, xpath, config, xret, msd);
|
||||
#if DEBUG
|
||||
if (retval == 0) {
|
||||
cbuf *cb = cbuf_new();
|
||||
|
|
|
|||
|
|
@ -973,7 +973,7 @@ xp_eval(xp_ctx *xc,
|
|||
break;
|
||||
}
|
||||
/* Eval second child c0
|
||||
* Note, some operators 8like locationpath, need transitive context (use_xr0)
|
||||
* Note, some operators like locationpath, need transitive context (use_xr0)
|
||||
*/
|
||||
if (xs->xs_c1)
|
||||
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, &xr1) < 0)
|
||||
|
|
@ -1060,6 +1060,7 @@ xpath_vec_ctx(cxobj *xcur,
|
|||
goto done;
|
||||
if (xpath_parse_init(&xy) < 0)
|
||||
goto done;
|
||||
clicon_debug(2,"%s",__FUNCTION__);
|
||||
if (clixon_xpath_parseparse(&xy) != 0) { /* yacc returns 1 on error */
|
||||
clicon_log(LOG_NOTICE, "XPATH error: on line %d", xy.xy_linenum);
|
||||
if (clicon_errno == 0)
|
||||
|
|
|
|||
|
|
@ -109,6 +109,12 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
|
|||
<TOKEN>last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>re-match { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>deref { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>derived-from { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>derived-from-or-self { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>enum-value { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>bit-is-set { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
|
||||
<TOKEN>@ { return *yytext; }
|
||||
<TOKEN>ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; }
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
%type <intval> axisspec
|
||||
|
||||
%type <string> string
|
||||
%type <stack> args
|
||||
%type <stack> expr
|
||||
%type <stack> andexpr
|
||||
%type <stack> relexpr
|
||||
|
|
@ -164,8 +165,8 @@ xp_new(enum xp_type type,
|
|||
double d0,
|
||||
char *s0,
|
||||
char *s1,
|
||||
xpath_tree *c0,
|
||||
xpath_tree *c1)
|
||||
xpath_tree *c0,
|
||||
xpath_tree *c1)
|
||||
{
|
||||
xpath_tree *xs = NULL;
|
||||
|
||||
|
|
@ -251,7 +252,7 @@ nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL,
|
|||
| NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(2,"nodetest-> name(%s)",$1); }
|
||||
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : name(%s)", $1, $3); }
|
||||
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : *", $1); }
|
||||
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(2,"nodetest-> nodetype()"); }
|
||||
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype():%s", $1); }
|
||||
;
|
||||
|
||||
/* evaluates to boolean */
|
||||
|
|
@ -266,13 +267,15 @@ primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2
|
|||
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' string '"); }
|
||||
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' '"); }
|
||||
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
|
||||
| FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, $3, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
|
||||
;
|
||||
|
||||
/* XXX Adding this between FUNCTIONNAME() breaks parser,..
|
||||
arguments : arguments expr { clicon_debug(2,"arguments-> arguments expr"); }
|
||||
| { clicon_debug(2,"arguments-> "); }
|
||||
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, $3);
|
||||
clicon_debug(2,"args -> args expr");}
|
||||
| expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);
|
||||
clicon_debug(2,"args -> expr "); }
|
||||
;
|
||||
*/
|
||||
|
||||
string : string CHAR {
|
||||
int len = strlen($1);
|
||||
$$ = realloc($1, len+strlen($2) + 1);
|
||||
|
|
|
|||
|
|
@ -173,9 +173,6 @@ static const map_str2int ykmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* forward declaration */
|
||||
static int ys_parse_date_arg(char *str, uint32_t *date);
|
||||
|
||||
/*! Create new yang specification
|
||||
* @retval yspec Free with yspec_free()
|
||||
* @retval NULL Error
|
||||
|
|
@ -2920,37 +2917,40 @@ yang_desc_schema_nodeid(yang_node *yn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! parse yang date-arg string
|
||||
/*! parse yang date-arg string and return a uint32 useful for arithmetics
|
||||
* @param[in] datearg yang revision string as "YYYY-MM-DD"
|
||||
* @param[out] dateint Integer version as YYYYMMDD
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error, eg str is not on the format "YYYY-MM-DD"
|
||||
*/
|
||||
static int
|
||||
ys_parse_date_arg(char *str,
|
||||
uint32_t *date)
|
||||
int
|
||||
ys_parse_date_arg(char *datearg,
|
||||
uint32_t *dateint)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
uint32_t d = 0;
|
||||
|
||||
if (strlen(str) != 10 || str[4] != '-' || str[7] != '-'){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", str);
|
||||
if (strlen(datearg) != 10 || datearg[4] != '-' || datearg[7] != '-'){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
|
||||
goto done;
|
||||
}
|
||||
if ((i = cligen_tonum(4, str)) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", str);
|
||||
if ((i = cligen_tonum(4, datearg)) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
|
||||
goto done;
|
||||
}
|
||||
d = i*10000; /* year */
|
||||
if ((i = cligen_tonum(2, &str[5])) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", str);
|
||||
if ((i = cligen_tonum(2, &datearg[5])) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
|
||||
goto done;
|
||||
}
|
||||
d += i*100; /* month */
|
||||
if ((i = cligen_tonum(2, &str[8])) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", str);
|
||||
if ((i = cligen_tonum(2, &datearg[8])) < 0){
|
||||
clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
|
||||
goto done;
|
||||
}
|
||||
d += i; /* day */
|
||||
*date = d;
|
||||
*dateint = d;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
|
|||
249
lib/src/clixon_yang_changelog.c
Normal file
249
lib/src/clixon_yang_changelog.c
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* YANG module revision change management.
|
||||
* See draft-wang-netmod-module-revision-management-01
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clixon */
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_changelog.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
|
||||
#if 0
|
||||
/*! Make a specific change
|
||||
<index>0001</index>
|
||||
<change-operation>create</change-operation>
|
||||
<data-definition>
|
||||
<target-node>
|
||||
/a:system/a:y;
|
||||
</target-node>
|
||||
</data-definition>
|
||||
*/
|
||||
static int
|
||||
upgrade_op(cxobj *x)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
xml_print(stderr, x);
|
||||
retval = 0;
|
||||
// done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
upgrade_deleted(clicon_handle h,
|
||||
char *name,
|
||||
cxobj *xs)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
fprintf(stderr, "%s \"%s\" belongs to a removed module\n", __FUNCTION__, name);
|
||||
retval = 0;
|
||||
// done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] xs Module state
|
||||
*/
|
||||
static int
|
||||
upgrade_modified(clicon_handle h,
|
||||
char *name,
|
||||
char *namespace,
|
||||
cxobj *xs,
|
||||
cxobj *xch)
|
||||
{
|
||||
int retval = -1;
|
||||
char *mname;
|
||||
yang_spec *yspec = NULL;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *yrev;
|
||||
char *mrev;
|
||||
cxobj **vec = NULL;
|
||||
size_t veclen;
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "%s: \"%s\" belongs to an upgraded module\n", __FUNCTION__, name);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
|
||||
/* We need module-name of XML since changelog uses that (change in changelog?)*/
|
||||
mname = xml_find_body(xs, "name");
|
||||
|
||||
/* Look up system module (alt send it via argument) */
|
||||
if ((ymod = yang_find_module_by_name(yspec, mname)) == NULL)
|
||||
goto done;
|
||||
if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) == NULL)
|
||||
goto done;
|
||||
mrev = yrev->ys_argument;
|
||||
/* Look up in changelog */
|
||||
|
||||
if (xpath_vec(xch, "module[name=\"%s\" and revision=\"%s\"]/revision-change-log",
|
||||
&vec, &veclen, mname, mrev) < 0)
|
||||
goto done;
|
||||
/* Iterate through changelog */
|
||||
for (i=0; i<veclen; i++)
|
||||
if (upgrade_op(vec[i]) < 0)
|
||||
goto done;
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Automatic upgrade using changelog
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xn XML tree to be updated
|
||||
* @param[in] modname Name of module
|
||||
* @param[in] modns 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
|
||||
*/
|
||||
int
|
||||
yang_changelog_upgrade(clicon_handle h,
|
||||
cxobj *xn,
|
||||
char *modname,
|
||||
char *modns,
|
||||
uint32_t from,
|
||||
uint32_t to,
|
||||
void *arg,
|
||||
cbuf *cbret)
|
||||
{
|
||||
// cxobj *xchlog; /* changelog */
|
||||
// cxobj **vec = NULL;
|
||||
// size_t veclen;
|
||||
|
||||
if (!clicon_option_bool(h, "CLICON_YANG_CHANGELOG"))
|
||||
goto ok;
|
||||
/* Get changelog */
|
||||
// xchlog = clicon_yang_changelog_get(h);
|
||||
/* Get changelog entries for module between from and to
|
||||
* (if to=0 we may not know name, need to use namespace)
|
||||
*/
|
||||
#if 0
|
||||
if (xpath_vec(xchlog, "module[name=\"%s\" and revision=\"%s\"]/revision-change-log",
|
||||
&vec, &veclen, modname, mrev) < 0)
|
||||
goto done;
|
||||
#endif
|
||||
ok:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! Initialize module revision. read changelog, etc
|
||||
*/
|
||||
int
|
||||
clixon_yang_changelog_init(clicon_handle h)
|
||||
{
|
||||
int retval = -1;
|
||||
char *filename;
|
||||
int fd = -1;
|
||||
cxobj *xt = NULL;
|
||||
yang_spec *yspec;
|
||||
cbuf *cbret = NULL;
|
||||
int ret;
|
||||
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((filename = clicon_option_str(h, "CLICON_YANG_CHANGELOG_FILE")) != NULL){
|
||||
if ((fd = open(filename, O_RDONLY)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "open(%s)", filename);
|
||||
goto done;
|
||||
}
|
||||
if (xml_parse_file(fd, NULL, yspec, &xt) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if ((cbret = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = xml_yang_validate_all(xt, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret==1 && (ret = xml_yang_validate_add(xt, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
clicon_err(OE_YANG, 0, "validation failed: %s", cbuf_get(cbret));
|
||||
goto done;
|
||||
}
|
||||
if (clicon_yang_changelog_set(h, xt) < 0)
|
||||
goto done;
|
||||
xt = NULL;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -72,9 +72,35 @@
|
|||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_yang_module.h"
|
||||
|
||||
modstate_diff_t *
|
||||
modstate_diff_new(void)
|
||||
{
|
||||
modstate_diff_t *md;
|
||||
if ((md = malloc(sizeof(modstate_diff_t))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(md, 0, sizeof(modstate_diff_t));
|
||||
return md;
|
||||
}
|
||||
|
||||
int
|
||||
modstate_diff_free(modstate_diff_t *md)
|
||||
{
|
||||
if (md == NULL)
|
||||
return 0;
|
||||
if (md->md_del)
|
||||
free(md->md_del);
|
||||
if (md->md_mod)
|
||||
free(md->md_mod);
|
||||
free(md);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Init the Yang module library
|
||||
*
|
||||
* Load RFC7895 yang spec, module-set-id, etc.
|
||||
|
|
@ -167,7 +193,7 @@ int
|
|||
modules_state_cache_set(clicon_handle h,
|
||||
cxobj *msx)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
cxobj *x; /* module state cache XML */
|
||||
|
||||
if ((x = clicon_module_state_get(h)) != NULL)
|
||||
|
|
@ -185,35 +211,6 @@ modules_state_cache_set(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get modules state according to RFC 7895
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] xpath XML Xpath
|
||||
* @param[in] brief Just name,revision and uri (no cache)
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 OK
|
||||
* @retval 1 Statedata callback failed
|
||||
* @notes NYI: schema, deviation
|
||||
x +--ro modules-state
|
||||
x +--ro module-set-id string
|
||||
x +--ro module* [name revision]
|
||||
x +--ro name yang:yang-identifier
|
||||
x +--ro revision union
|
||||
+--ro schema? inet:uri
|
||||
x +--ro namespace inet:uri
|
||||
+--ro feature* yang:yang-identifier
|
||||
+--ro deviation* [name revision]
|
||||
| +--ro name yang:yang-identifier
|
||||
| +--ro revision union
|
||||
+--ro conformance-type enumeration
|
||||
+--ro submodule* [name revision]
|
||||
+--ro name yang:yang-identifier
|
||||
+--ro revision union
|
||||
+--ro schema? inet:uri
|
||||
* @see netconf_create_hello
|
||||
*/
|
||||
#if 1
|
||||
/*! Actually build the yang modules state XML tree
|
||||
*/
|
||||
static int
|
||||
|
|
@ -298,7 +295,34 @@ yms_build(clicon_handle h,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get modules state according to RFC 7895
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] xpath XML Xpath
|
||||
* @param[in] brief Just name,revision and uri (no cache)
|
||||
* @param[in,out] xret Existing XML tree, merge x into this
|
||||
* @retval -1 Error (fatal)
|
||||
* @retval 0 OK
|
||||
* @retval 1 Statedata callback failed
|
||||
* @notes NYI: schema, deviation
|
||||
x +--ro modules-state
|
||||
x +--ro module-set-id string
|
||||
x +--ro module* [name revision]
|
||||
x +--ro name yang:yang-identifier
|
||||
x +--ro revision union
|
||||
+--ro schema? inet:uri
|
||||
x +--ro namespace inet:uri
|
||||
+--ro feature* yang:yang-identifier
|
||||
+--ro deviation* [name revision]
|
||||
| +--ro name yang:yang-identifier
|
||||
| +--ro revision union
|
||||
+--ro conformance-type enumeration
|
||||
+--ro submodule* [name revision]
|
||||
+--ro name yang:yang-identifier
|
||||
+--ro revision union
|
||||
+--ro schema? inet:uri
|
||||
* @see netconf_create_hello
|
||||
*/
|
||||
int
|
||||
yang_modules_state_get(clicon_handle h,
|
||||
yang_spec *yspec,
|
||||
|
|
@ -350,162 +374,87 @@ yang_modules_state_get(clicon_handle h,
|
|||
xml_free(x);
|
||||
return retval;
|
||||
}
|
||||
#else
|
||||
int
|
||||
yang_modules_state_get(clicon_handle h,
|
||||
yang_spec *yspec,
|
||||
char *xpath,
|
||||
int brief,
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = NULL;
|
||||
cbuf *cb = NULL;
|
||||
yang_stmt *ylib = NULL; /* ietf-yang-library */
|
||||
yang_stmt *yns = NULL; /* namespace */
|
||||
yang_stmt *ymod; /* generic module */
|
||||
yang_stmt *ys;
|
||||
yang_stmt *yc;
|
||||
char *msid; /* modules-set-id */
|
||||
char *module = "ietf-yang-library";
|
||||
cxobj *x1;
|
||||
|
||||
msid = clicon_option_str(h, "CLICON_MODULE_SET_ID");
|
||||
if (modules_state_cache_get(h, msid, &x) < 0)
|
||||
goto done;
|
||||
if (x != NULL){ /* Yes a cache (but no duplicate) */
|
||||
if (xpath_first(x, "%s", xpath)){
|
||||
if ((x1 = xml_dup(x)) == NULL)
|
||||
goto done;
|
||||
x = x1;
|
||||
}
|
||||
else
|
||||
x = NULL;
|
||||
}
|
||||
else { /* No cache -> build the tree */
|
||||
if ((ylib = yang_find((yang_node*)yspec, Y_MODULE, module)) == NULL &&
|
||||
(ylib = yang_find((yang_node*)yspec, Y_SUBMODULE, module)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "%s not found", module);
|
||||
goto done;
|
||||
}
|
||||
if ((yns = yang_find((yang_node*)ylib, Y_NAMESPACE, NULL)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "%s yang namespace not found", module);
|
||||
goto done;
|
||||
}
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, 0, "clicon buffer");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
|
||||
cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
|
||||
|
||||
<<<<<<< HEAD
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
|
||||
if (ymod->ys_keyword != Y_MODULE &&
|
||||
ymod->ys_keyword != Y_SUBMODULE)
|
||||
continue;
|
||||
cprintf(cb,"<module>");
|
||||
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
|
||||
if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
else
|
||||
cprintf(cb,"<revision></revision>");
|
||||
=======
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
|
||||
if (ymod->ys_keyword != Y_MODULE &&
|
||||
ymod->ys_keyword != Y_SUBMODULE)
|
||||
continue;
|
||||
cprintf(cb,"<module>");
|
||||
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
|
||||
if ((ys = yang_find((yang_node*)ymod, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
else
|
||||
cprintf(cb,"<revision></revision>");
|
||||
if (!brief){
|
||||
>>>>>>> modules-state
|
||||
if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
|
||||
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
|
||||
else
|
||||
cprintf(cb,"<namespace></namespace>");
|
||||
<<<<<<< HEAD
|
||||
/* This follows order in rfc 7895: feature, conformance-type, submodules */
|
||||
yc = NULL;
|
||||
=======
|
||||
}
|
||||
/* This follows order in rfc 7895: feature, conformance-type, submodules */
|
||||
yc = NULL;
|
||||
if (!brief)
|
||||
>>>>>>> modules-state
|
||||
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
||||
switch(yc->ys_keyword){
|
||||
case Y_FEATURE:
|
||||
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
|
||||
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
cprintf(cb, "<conformance-type>implement</conformance-type>");
|
||||
yc = NULL;
|
||||
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
||||
switch(yc->ys_keyword){
|
||||
case Y_SUBMODULE:
|
||||
cprintf(cb,"<submodule>");
|
||||
cprintf(cb,"<name>%s</name>", yc->ys_argument);
|
||||
if ((ys = yang_find((yang_node*)yc, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
else
|
||||
cprintf(cb,"<revision></revision>");
|
||||
cprintf(cb,"</submodule>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
=======
|
||||
if (!brief)
|
||||
cprintf(cb, "<conformance-type>implement</conformance-type>");
|
||||
yc = NULL;
|
||||
if (!brief)
|
||||
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
|
||||
switch(yc->ys_keyword){
|
||||
case Y_SUBMODULE:
|
||||
cprintf(cb,"<submodule>");
|
||||
cprintf(cb,"<name>%s</name>", yc->ys_argument);
|
||||
if ((ys = yang_find((yang_node*)yc, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
else
|
||||
cprintf(cb,"<revision></revision>");
|
||||
cprintf(cb,"</submodule>");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
>>>>>>> modules-state
|
||||
}
|
||||
cprintf(cb,"</module>");
|
||||
}
|
||||
cprintf(cb,"</modules-state>");
|
||||
|
||||
if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
|
||||
if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
goto done;
|
||||
/*! Upgrade XML
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xt XML tree (to upgrade)
|
||||
* @param[in] msd Modules-state differences of xt
|
||||
* @retval 1 OK
|
||||
* @retval 0 Validation failed
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clixon_module_upgrade(clicon_handle h,
|
||||
cxobj *xt,
|
||||
modstate_diff_t *msd,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc; /* XML child of data */
|
||||
char *namespace;
|
||||
cxobj *xs; /* XML module state */
|
||||
char *xname; /* XML top-level symbol name */
|
||||
int state; /* 0: no changes, 1: deleted, 2: modified */
|
||||
char *modname;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *yrev;
|
||||
char *rev;
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
int ret;
|
||||
|
||||
/* Iterate through db XML top-level - get namespace info */
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
xname = xml_name(xc); /* xml top-symbol name */
|
||||
if (xml2ns(xc, NULL, &namespace) < 0) /* Get namespace of XML */
|
||||
goto done;
|
||||
if (namespace == NULL){
|
||||
clicon_log(LOG_DEBUG, "XML %s lacks namespace", xname);
|
||||
goto fail;
|
||||
}
|
||||
if (modules_state_cache_set(h, x) < 0)
|
||||
/* Look up module-state via namespace of XML */
|
||||
state = 0; /* XML matches system modules */
|
||||
if (msd){
|
||||
if ((xs = xpath_first(msd->md_del, "module[namespace=\"%s\"]", namespace)) != NULL)
|
||||
state = 1; /* XML belongs to a removed module */
|
||||
else if ((xs = xpath_first(msd->md_mod, "module[namespace=\"%s\"]", namespace)) != NULL)
|
||||
state = 2; /* XML belongs to an outdated module */
|
||||
}
|
||||
/* Pick up more data from data store module-state */
|
||||
from = to = 0;
|
||||
modname = NULL;
|
||||
if (state && xs && msd){ /* sanity: XXX what about no msd?? */
|
||||
modname = xml_find_body(xs, "name"); /* Module name */
|
||||
if ((rev = xml_find_body(xs, "revision")) != NULL) /* Module revision */
|
||||
if (ys_parse_date_arg(rev, &from) < 0)
|
||||
goto done;
|
||||
if (state > 1){
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
/* Look up system module (alt send it via argument) */
|
||||
if ((ymod = yang_find_module_by_name(yspec, modname)) == NULL)
|
||||
goto fail;
|
||||
if ((yrev = yang_find((yang_node*)ymod, Y_REVISION, NULL)) == NULL)
|
||||
goto fail;
|
||||
if (ys_parse_date_arg(yrev->ys_argument, &to) < 0)
|
||||
goto done;
|
||||
|
||||
}
|
||||
}
|
||||
/* Make upgrade callback for this XML, specifying the module name,
|
||||
* namespace, from and to revision.
|
||||
* XXX: namespace may be known but not module!!
|
||||
*/
|
||||
if ((ret = upgrade_callback_call(h, xc, modname, namespace, from, to, NULL)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
if (x && netconf_trymerge(x, yspec, xret) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
retval = 1;
|
||||
done:
|
||||
if (x)
|
||||
xml_free(x);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue