* 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:
Olof hagsand 2019-03-21 17:42:53 +01:00
parent 434f0b930e
commit 3f68cca06c
37 changed files with 1475 additions and 351 deletions

View file

@ -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 \

View file

@ -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"

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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();

View file

@ -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)

View file

@ -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; }

View file

@ -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);

View file

@ -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;

View 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;
}

View file

@ -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