Non-key list now not accepted in edit-config (before only on validation)
This commit is contained in:
parent
4e23864acd
commit
06e6ef80d1
25 changed files with 620 additions and 71 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue