Non-key list now not accepted in edit-config (before only on validation)

This commit is contained in:
Olof hagsand 2019-05-08 12:51:10 +02:00
parent 4e23864acd
commit 06e6ef80d1
25 changed files with 620 additions and 71 deletions

View file

@ -35,6 +35,10 @@
* Saves Clixon data as clear-text XML (or JSON)
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -193,7 +197,7 @@ xmldb_copy(clicon_handle h,
db_elmnt de0 = {0,};
cxobj *x1 = NULL; /* from */
cxobj *x2 = NULL; /* to */
/* XXX lock */
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
/* Copy in-memory cache */

View file

@ -82,6 +82,16 @@ struct indexhead{
};
typedef struct indexhead indexhead;
static int
indexhead_free(indexhead *ih)
{
indexlist *il;
while ((il = ih->ih_list) != NULL)
DELQ(il, ih->ih_list, indexlist*);
return 0;
}
int
index_get(indexhead *ih,
size_t len, /* in bytes */
@ -178,8 +188,8 @@ index_dump(FILE *f,
* @retval 1 OK xp set (or NULL if empty)
*/
static int
buf2xml(FILE *f,
uint32_t index,
buf2xml(FILE *f,
uint32_t index,
cxobj **xp)
{
int retval = -1;
@ -190,7 +200,7 @@ buf2xml(FILE *f,
cxobj *x = NULL;
char *name;
char *prefix = NULL;
char hdr[8];
char hdr[8] = {0, };
char *buf = NULL;
int vlen;
int i;
@ -362,7 +372,8 @@ xml2buf(FILE *f,
else{
uint32_t index;
for (i=0; i< xml_child_nr(x); i++){
if (xml2buf(f, xml_child_i(x, i), ih, &index) < 0)
if (xml2buf(f, xml_child_i(x, i), ih,
&index) < 0)
goto done;
index = htonl(index);
memcpy(&buf[ptr], &index, sizeof(uint32_t));
@ -376,10 +387,6 @@ xml2buf(FILE *f,
goto done;
}
memcpy(buf0, hdr, sizeof(hdr));
if (0 && fwrite(hdr, sizeof(char), sizeof(hdr), f) < 0){
clicon_err(OE_UNIX, errno, "fwrite");
goto done;
}
if (fwrite(buf0, sizeof(char), len, f) < 0){
clicon_err(OE_XML, errno, "fwrite");
goto done;
@ -401,7 +408,6 @@ datastore_tree_write(clicon_handle h,
int retval = -1;
FILE *f = NULL;
indexhead ih = {0,};
indexlist *il;
if ((f = fopen(filename, "w+b")) == NULL){
clicon_err(OE_XML, errno, "Opening file %s", filename);
@ -413,8 +419,7 @@ datastore_tree_write(clicon_handle h,
index_dump(stderr, &ih);
retval = 0;
done:
while ((il = ih.ih_list) != NULL)
DELQ(il, ih.ih_list, indexlist*);
indexhead_free(&ih);
if (f != NULL)
fclose(f);
return retval;
@ -439,7 +444,8 @@ datastore_tree_read(clicon_handle h,
}
if ((retval = buf2xml(f, 0, xt)) < 0)
goto done;
if (retval == 0){ /* fail */
if (retval == 0 || /* fail */
*xt == NULL){ /* empty */
if ((*xt = xml_new("config", NULL, NULL)) == NULL)
goto done;
xml_type_set(*xt, CX_ELMNT);

View file

@ -633,6 +633,44 @@ check_mandatory(cxobj *xt,
goto done;
}
static int
check_list_key(cxobj *xt,
yang_stmt *yt,
cbuf *cbret)
{
int retval = -1;
int i;
yang_stmt *yc;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
for (i=0; i<yt->ys_len; i++){
yc = yt->ys_stmt[i];
/* Check if a list does not have mandatory key leafs */
if (yt->ys_keyword == Y_LIST &&
yc->ys_keyword == Y_KEY &&
yang_config(yt)){
cvk = yt->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
if (netconf_missing_element(cbret, "application", keyname, "Mandatory key") < 0)
goto done;
goto fail;
}
}
}
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Validate a single XML node with yang specification for added entry
* 1. Check if mandatory leafs present as subs.
* 2. Check leaf values, eg int ranges and string regexps.
@ -737,6 +775,41 @@ xml_yang_validate_add(cxobj *xt,
goto done;
}
/*! Some checks done only at edit_config, eg keys in lists
*/
int
xml_yang_validate_list_key_only(cxobj *xt,
cbuf *cbret)
{
int retval = -1;
yang_stmt *yt; /* yang spec of xt going in */
int ret;
cxobj *x;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
if ((ret = check_list_key(xt, yt, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if ((ret = xml_yang_validate_list_key_only(x, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Validate a single XML node with yang specification for all (not only added) entries
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
* @param[in] xt XML node to be validated
@ -1126,11 +1199,13 @@ xml_diff1(yang_stmt *ys,
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
goto done;
x0c = xml_child_each(x0, x0c, CX_ELMNT);
continue;
}
else if (eq > 0){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
x1c = xml_child_each(x1, x1c, CX_ELMNT);
continue;
}
else{ /* equal */
if ((yc = xml_spec(x0c)) == NULL){
@ -1257,8 +1332,21 @@ yang2api_path_fmt_1(yang_stmt *ys,
yp->ys_keyword != Y_SUBMODULE){
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
goto done;
if (yp->ys_keyword != Y_CHOICE && yp->ys_keyword != Y_CASE)
cprintf(cb, "/");
if (yp->ys_keyword != Y_CHOICE && yp->ys_keyword != Y_CASE){
#if 0
/* In some cases, such as cli_show_auto, a trailing '/' should
* NOT be present if ys is a key in a list.
* But in other cases (I think most), the / should be there,
* so a patch is added in cli_show_auto instead.
*/
if (ys->ys_keyword == Y_LEAF && yp &&
yp->ys_keyword == Y_LIST &&
yang_key_match(yp, ys->ys_argument) == 1)
;
else
#endif
cprintf(cb, "/");
}
}
else /* top symbol - mark with name prefix */
cprintf(cb, "/%s:", yp->ys_argument);
@ -2226,6 +2314,112 @@ api_path2xml(char *api_path,
goto done;
}
/*! Given an XML node, build an xpath to root, internal function
* @retval 0 OK
* @retval -1 Error. eg XML malformed
*/
static int
xml2xpath1(cxobj *x,
cbuf *cb)
{
int retval = -1;
cxobj *xp;
yang_stmt *y = NULL;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
cxobj *xkey;
cxobj *xb;
char *b;
enum rfc_6020 keyword;
if ((xp = xml_parent(x)) != NULL &&
xml_spec(xp) != NULL)
xml2xpath1(xp, cb);
/* XXX: sometimes there should be a /, sometimes not */
cprintf(cb, "/%s", xml_name(x));
if ((y = xml_spec(x)) != NULL){
keyword = yang_keyword_get(y);
if (keyword == Y_LEAF_LIST){
if ((b = xml_body(x)) != NULL)
cprintf(cb, "[.=\"%s\"]", b);
else
cprintf(cb, "[.=\"\"]");
} else if (keyword == Y_LIST){
cvk = yang_cvec_get(y);
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((xkey = xml_find(x, keyname)) == NULL)
goto done; /* No key in xml */
if ((xb = xml_find(x, keyname)) == NULL)
goto done;
b = xml_body(xb);
cprintf(cb, "[%s=\"%s\"]", keyname, b?b:"");
}
}
}
retval = 0;
done:
return retval;
}
/*! Given an XML node, build an xpath to root
* Builds only unqualified xpaths, ie no predicates []
* @param[in] x XML object
* @param[out] xpath Malloced xpath string. Need to free() after use
* @retval 0 OK
* @retval -1 Error. (eg XML malformed)
*/
int
xml2xpath(cxobj *x,
char **xpathp)
{
int retval = -1;
cbuf *cb;
char *xpath = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xml2xpath1(x, cb) < 0)
goto done;
/* XXX: see xpath in test statement,.. */
xpath = cbuf_get(cb);
#if 0 /* debug test */
{
cxobj *xt = x;
cxobj *xcp;
cxobj *x2;
while (xml_parent(xt) != NULL &&
xml_spec(xt) != NULL)
xt = xml_parent(xt);
xcp = xml_parent(xt);
xml_parent_set(xt, NULL);
x2 = xpath_first(xt, "%s", xpath); /* +1: skip first / */
xml_parent_set(xt, xcp);
assert(x2 && x==x2);
if (x==x2)
clicon_debug(1, "%s %s match", __FUNCTION__, xpath);
else
clicon_debug(1, "%s %s no match", __FUNCTION__, xpath);
}
#endif
if (xpathp){
if ((*xpathp = strdup(xpath)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
xpath = NULL;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Check if the module tree x is in is assigned right XML namespace, assign if not
* @param[in] x XML node
*(0. You should probably find the XML root and apply this function to that.)

View file

@ -715,18 +715,16 @@ xp_relop(xp_ctx *xc1,
s1 = xml_body(x);
switch(op){
case XO_EQ:
if (s1==NULL || s2==NULL){
clicon_err(OE_XML, EINVAL, "Malformed xpath: empty string");
goto done;
}
xr->xc_bool = (strcmp(s1, s2)==0);
if (s1 == NULL || s2 == NULL)
xr->xc_bool = (s1==NULL && s2 == NULL);
else
xr->xc_bool = (strcmp(s1, s2)==0);
break;
case XO_NE:
if (s1==NULL || s2==NULL){
clicon_err(OE_XML, EINVAL, "Malformed xpath: empty string");
goto done;
}
xr->xc_bool = (strcmp(s1, s2));
if (s1 == NULL || s2 == NULL)
xr->xc_bool = !(s1==NULL && s2 == NULL);
else
xr->xc_bool = (strcmp(s1, s2));
break;
default:
clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));