* Top-level default leafs assigned.

* Enforcing RFC 7950 Sec 7.6.1 means unassigned top-level leafs (or leafs under non-presence containers) are assigned default values.
* NACM default behaviour is read-only (empty configs are dead-lockedd)
  * This applies if NACM is loaded and `CLICON_NACM_MODE` is `internal`
* Fixed: [default values don't show up in datastores #111](https://github.com/clicon/clixon/issues/111)
This commit is contained in:
Olof hagsand 2020-08-06 15:19:38 +02:00
parent 65733ffe69
commit 794d51a365
30 changed files with 593 additions and 167 deletions

View file

@ -72,6 +72,7 @@
#include "clixon_xpath.h"
#include "clixon_json.h"
#include "clixon_nacm.h"
#include "clixon_path.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_xml_map.h"
@ -104,7 +105,7 @@ singleconfigroot(cxobj *xt,
}
}
if (i != 1){
clicon_err(OE_DB, ENOENT, "Top-element is not unique, expecting single config");
clicon_err(OE_DB, ENOENT, "Top-element is not unique, expecting single config");
goto done;
}
x = NULL;
@ -443,33 +444,63 @@ text_read_modstate(clicon_handle h,
return retval;
}
static int
disable_nacm_on_empty(cxobj *x0,
yang_stmt *yspec)
{
int retval = -1;
yang_stmt *ymod;
cxobj **vec = NULL;
int len = 0;
cxobj *xb;
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-netconf-acm")) == NULL)
goto ok;
if (clixon_xml_find_instance_id(x0, yspec, &vec, &len, "/nacm:nacm/nacm:enable-nacm") < 1)
goto done;
if (len){
if ((xb = xml_body_get(vec[0])) == NULL)
goto done;
if (xml_value_set(xb, "false") < 0)
goto done;
}
if (vec)
free(vec);
ok:
retval = 0;
done:
return retval;
}
/*! Common read function that reads an XML tree from file
* @param[in] th Datastore text handle
* @param[in] db Symbolic database name, eg "candidate", "running"
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Top-level yang spec
* @param[out] xp XML tree read from file
* @param[out] de If set, return db-element status (eg empty flag)
* @param[out] msdiff If set, return modules-state differences
* @retval -1 Error
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
* @retval 1 OK
* @note retval 0 is NYI becaues of functions calling this function
* @note retval 0 is NYI because of functions calling this function cannot handle it yet
*/
#undef XMLDB_READFILE_FAIL
#undef XMLDB_READFILE_FAIL /* See comment on retval = 0 above */
int
xmldb_readfile(clicon_handle h,
const char *db,
yang_bind yb,
yang_stmt *yspec,
cxobj **xp,
db_elmnt *de,
modstate_diff_t *msdiff)
{
int retval = -1;
cxobj *x0 = NULL;
char *dbfile = NULL;
int fd = -1;
char *format;
int ret;
int retval = -1;
cxobj *x0 = NULL;
char *dbfile = NULL;
int fd = -1;
char *format;
int ret;
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
@ -497,8 +528,9 @@ xmldb_readfile(clicon_handle h,
goto fail;
#endif
/* Always assert a top-level called "config".
To ensure that, deal with two cases:
1. File is empty <top/> -> rename top-level to "config" */
* To ensure that, deal with two cases:
* 1. File is empty <top/> -> rename top-level to "config"
*/
if (xml_child_nr(x0) == 0){
if (xml_name_set(x0, "config") < 0)
goto done;
@ -509,7 +541,10 @@ xmldb_readfile(clicon_handle h,
if (singleconfigroot(x0, &x0) < 0)
goto done;
}
/* From Clixon 3.10,datastore files may contain module-state defining
if (xml_child_nr(x0) == 0 && de)
de->de_empty = 1;
/* Datastore files may contain module-state defining
* which modules are used in the file.
*/
if (text_read_modstate(h, yspec, x0, msdiff) < 0)
@ -569,16 +604,36 @@ xmldb_get_nocache(clicon_handle h,
size_t xlen;
int i;
int ret;
db_elmnt de0 = {0,};
int empty = 0;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &xt, msdiff)) < 0)
if ((ret = xmldb_readfile(h, db, YB_MODULE, yspec, &xt, &de0, msdiff)) < 0)
goto done;
if (ret == 0)
goto fail;
clicon_db_elmnt_set(h, db, &de0); /* Content is copied */
/* Check if empty */
if (xml_child_nr(xt) == 0)
empty = 1;
/* Add global defaults.
* Must do it before xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, xt) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(xt, yspec) < 0)
goto done;
}
/* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
@ -664,6 +719,7 @@ xmldb_get_cache(clicon_handle h,
cxobj *x1t = NULL;
db_elmnt de0 = {0,};
int ret;
int empty = 0;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -672,7 +728,7 @@ xmldb_get_cache(clicon_handle h,
de = clicon_db_elmnt_get(h, db);
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
/* If there is no xml x0 tree (in cache), then read it from file */
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 0)
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, &de0, msdiff)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -680,10 +736,30 @@ xmldb_get_cache(clicon_handle h,
* No, argument against: we may want to have a semantically wrong file and wish to edit?
*/
de0.de_xml = x0t;
clicon_db_elmnt_set(h, db, &de0);
clicon_db_elmnt_set(h, db, &de0); /* Content is copied */
} /* x0t == NULL */
else
x0t = de->de_xml;
/* Check if empty, must be before add global defaults */
if (xml_child_nr(x0t) == 0)
empty = 1;
/* Add global defaults.
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, x0t) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x0t, yspec) < 0)
goto done;
}
/* Here x0t looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec
* Can we do everything in one go?
@ -693,8 +769,6 @@ xmldb_get_cache(clicon_handle h,
* a) for every node that is found, copy to new tree
* b) if config dont dont state data
*/
/* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
@ -703,6 +777,7 @@ xmldb_get_cache(clicon_handle h,
goto done;
xml_spec_set(x1t, xml_spec(x0t));
if (xlen < 1000){
/* This is optimized for the case when the tree is large and xlen is small
* If the tree is large and xlen too, then the other is better.
@ -786,6 +861,7 @@ xmldb_get_zerocopy(clicon_handle h,
db_elmnt *de = NULL;
db_elmnt de0 = {0,};
int ret;
int empty = 0;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
@ -794,7 +870,7 @@ xmldb_get_zerocopy(clicon_handle h,
de = clicon_db_elmnt_get(h, db);
if (de == NULL || de->de_xml == NULL){ /* Cache miss, read XML from file */
/* If there is no xml x0 tree (in cache), then read it from file */
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, msdiff)) < 0)
if ((ret = xmldb_readfile(h, db, yb, yspec, &x0t, &de0, msdiff)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -806,6 +882,25 @@ xmldb_get_zerocopy(clicon_handle h,
} /* x0t == NULL */
else
x0t = de->de_xml;
/* Check if empty, must be before add global defaults */
if (xml_child_nr(x0t) == 0)
empty = 1;
/* Add global defaults.
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, x0t) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x0t, yspec) < 0)
goto done;
}
/* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
@ -874,7 +969,7 @@ xmldb_get(clicon_handle h,
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences (upgrade code)
* @param[out] msdiff If set, return modules-state differences (upgrade code)
* @retval 0 OK
* @retval -1 Error
* @code
@ -944,12 +1039,16 @@ xmldb_get0_clear(clicon_handle h,
if (x == NULL)
goto ok;
/* clear XML tree of defaults */
/* Mark non-presence containers as XML_FLAG_DEFAULT */
if (xml_apply(x, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
goto done;
/* Clear XML tree of defaults */
if (xml_tree_prune_flagged(x, XML_FLAG_DEFAULT, 1) < 0)
goto done;
/* clear mark and change */
xml_apply0(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(0xff));
(void*)(0xffff));
ok:
retval = 0;
done:
@ -972,3 +1071,4 @@ xmldb_get0_free(clicon_handle h,
*xp = NULL;
return 0;
}