diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 8a19657b..0eb7d18b 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -1650,7 +1650,6 @@ from_client_msg(clicon_handle h,
if ((ret = nacm_access_pre(h, ce->ce_username, username, &xnacm)) < 0)
goto done;
/* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack
- *
*/
if (clicon_nacm_cache_set(h, xnacm) < 0)
goto done;
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index 57514d19..5291bbef 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -198,7 +198,6 @@ startup_common(clicon_handle h,
clicon_err(OE_YANG, 0, "Yang spec not set");
goto done;
}
-
clicon_debug(1, "Reading startup config done");
/* Clear flags xpath for get */
xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
@@ -251,8 +250,16 @@ startup_common(clicon_handle h,
goto done;
goto fail;
}
+ /* Sort xml */
if (xml_sort_recurse(xt) < 0)
goto done;
+ /* Add global defaults. */
+ if (xml_global_defaults(h, xt, NULL, NULL, yspec) < 0)
+ goto done;
+ /* Apply default values (removed in clear function) */
+ if (xml_default_recurse(xt) < 0)
+ goto done;
+
/* Handcraft transition with with only add tree */
td->td_target = xt;
xt = NULL;
diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c
index a3447c07..8839e269 100644
--- a/apps/backend/backend_startup.c
+++ b/apps/backend/backend_startup.c
@@ -238,7 +238,7 @@ startup_extraxml(clicon_handle h,
goto fail;
}
/*
- * Check if tmp db is empty. XXX no this is not possible.
+ * Check if tmp db is empty.
* It should be empty if extra-xml is null and reset plugins did nothing
* then skip validation.
*/
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index 19a936ed..a25639cf 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -86,8 +86,8 @@ struct backend_handle {
int bh_magic; /* magic (HDR)*/
clicon_hash_t *bh_copt; /* clicon option list (HDR) */
clicon_hash_t *bh_data; /* internal clicon data (HDR) */
- clicon_hash_t *ch_db_elmnt; /* xml datastore element cache data */
- event_stream_t *bh_stream; /* notification streams, see clixon_stream.[ch] */
+ clicon_hash_t *ch_db_elmnt; /* xml datastore element cache data */
+ event_stream_t *bh_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */
struct client_entry *bh_ce_list; /* The client list */
diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h
index 8a40173c..79b5131e 100644
--- a/lib/clixon/clixon_xml.h
+++ b/lib/clixon/clixon_xml.h
@@ -141,12 +141,12 @@ typedef struct clixon_xml_vec clixon_xvec; /* struct defined in clicon_xml_vec.c
/*
* xml_flag() flags:
*/
-#define XML_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
-#define XML_FLAG_ADD 0x02 /* Node is added (commits) or parent added rec*/
-#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) or parent deleted rec */
-#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
-#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
-#define XML_FLAG_DEFAULT 0x20 /* Added when a value is set as default @see xml_default */
+#define XML_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
+#define XML_FLAG_ADD 0x02 /* Node is added (commits) or parent added rec*/
+#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) or parent deleted rec */
+#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
+#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
+#define XML_FLAG_DEFAULT 0x20 /* Added when a value is set as default @see xml_default */
/*
* Prototypes
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index 15adc657..55e26faa 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -66,7 +66,7 @@ int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
int xml_namespace_change(cxobj *x, char *ns, char *prefix);
int xml_default(cxobj *x);
int xml_default_recurse(cxobj *xn);
-int xml_default_yspec(yang_stmt *yspec, cxobj *xn);
+int xml_global_defaults(clicon_handle h, cxobj *xn, cvec *nsc, const char *xpath, yang_stmt *yspec);
int xml_nopresence_default(cxobj *xt);
int xml_nopresence_default_mark(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg);
@@ -77,5 +77,6 @@ int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, char *x0bstr, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_enum_int_value(cxobj *node, int32_t *val);
+int xml_copy_marked(cxobj *x0, cxobj *x1);
#endif /* _CLIXON_XML_MAP_H_ */
diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c
index 94b9cb22..9a3fc9b9 100644
--- a/lib/src/clixon_data.c
+++ b/lib/src/clixon_data.c
@@ -660,6 +660,7 @@ clicon_db_elmnt_get(clicon_handle h,
* @param[in] de Database element
* @retval 0 OK
* @retval -1 Error
+ * @see xmldb_disconnect
*/
int
clicon_db_elmnt_set(clicon_handle h,
diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c
index 9c9cad1a..e2e01ea0 100644
--- a/lib/src/clixon_datastore_read.c
+++ b/lib/src/clixon_datastore_read.c
@@ -231,96 +231,6 @@ xml_copy_from_bottom(cxobj *x0t,
return retval;
}
-/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
- * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
- *
- * The algorithm works as following:
- * (1) Copy individual nodes marked with XML_FLAG_CHANGE
- * until nodes marked with XML_FLAG_MARK are reached, where
- * (2) the complete subtree of that node is copied.
- * (3) Special case: key nodes in lists are copied if any node in list is marked
- * @note you may want to check:!yang_config(ys)
- */
-static int
-xml_copy_marked(cxobj *x0,
- cxobj *x1)
-{
- int retval = -1;
- int mark;
- cxobj *x;
- cxobj *xcopy;
- int iskey;
- yang_stmt *yt;
- char *name;
- char *prefix;
-
- assert(x0 && x1);
- yt = xml_spec(x0); /* can be null */
- xml_spec_set(x1, yt);
- /* Copy prefix*/
- if ((prefix = xml_prefix(x0)) != NULL)
- if (xml_prefix_set(x1, prefix) < 0)
- goto done;
-
- /* Copy all attributes */
- x = NULL;
- while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
- name = xml_name(x);
- if ((xcopy = xml_new(name, x1, CX_ATTR)) == NULL)
- goto done;
- if (xml_copy(x, xcopy) < 0)
- goto done;
- }
-
- /* Go through children to detect any marked nodes:
- * (3) Special case: key nodes in lists are copied if any
- * node in list is marked
- */
- mark = 0;
- x = NULL;
- while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
- if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
- mark++;
- break;
- }
- }
- x = NULL;
- while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
- name = xml_name(x);
- if (xml_flag(x, XML_FLAG_MARK)){
- /* (2) the complete subtree of that node is copied. */
- if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
- goto done;
- if (xml_copy(x, xcopy) < 0)
- goto done;
- continue;
- }
- if (xml_flag(x, XML_FLAG_CHANGE)){
- /* Copy individual nodes marked with XML_FLAG_CHANGE */
- if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
- goto done;
- if (xml_copy_marked(x, xcopy) < 0) /* */
- goto done;
- }
- /* (3) Special case: key nodes in lists are copied if any
- * node in list is marked */
- if (mark && yt && yang_keyword_get(yt) == Y_LIST){
- /* XXX: I think yang_key_match is suboptimal here */
- if ((iskey = yang_key_match(yt, name)) < 0)
- goto done;
- if (iskey){
- if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
- goto done;
- if (xml_copy(x, xcopy) < 0)
- goto done;
- }
- }
- }
- retval = 0;
- done:
- return retval;
-}
-
/*! Read module-state in an XML tree
*
* @param[in] th Datastore text handle
@@ -620,20 +530,6 @@ xmldb_get_nocache(clicon_handle h,
/* 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: ... */
/* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
@@ -656,9 +552,23 @@ xmldb_get_nocache(clicon_handle h,
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
- /* Add default values (if not set) */
- if (xml_default_recurse(xt) < 0)
- goto done;
+ if (yb != YB_NONE){
+ /* Add global defaults. */
+ if (xml_global_defaults(h, xt, nsc, xpath, yspec) < 0)
+ goto done;
+ /* Add default values (if not set) */
+ if (xml_default_recurse(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;
+ }
#if 0 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
@@ -740,26 +650,10 @@ xmldb_get_cache(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 x0t looks like: ... */
/* Given the xpath, return a vector of matches in xvec
* Can we do everything in one go?
@@ -812,10 +706,23 @@ xmldb_get_cache(clicon_handle h,
/* Clear XML tree of defaults */
if (xml_tree_prune_flagged(x0t, XML_FLAG_DEFAULT, 1) < 0)
goto done;
- /* x1t is wrong here should be .. but is .. */
- /* XXX where should we apply default values once? */
- if (xml_default_recurse(x1t) < 0)
- goto done;
+ if (yb != YB_NONE){
+ /* Add default global values */
+ if (xml_global_defaults(h, x1t, nsc, xpath, yspec) < 0)
+ goto done;
+ /* Add default recursive values */
+ if (xml_default_recurse(x1t) < 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(x1t, yspec) < 0)
+ goto done;
+ }
/* Copy the matching parts of the (relevant) XML tree.
* If cache was empty, also update to datastore cache
*/
@@ -892,21 +799,6 @@ xmldb_get_zerocopy(clicon_handle h,
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: ... */
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
@@ -918,9 +810,23 @@ xmldb_get_zerocopy(clicon_handle h,
xml_flag_set(x0, XML_FLAG_MARK);
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
- /* Apply default values (removed in clear function) */
- if (xml_default_recurse(x0t) < 0)
- goto done;
+ if (yb != YB_NONE){
+ /* Add global defaults. */
+ if (xml_global_defaults(h, x0t, nsc, xpath, yspec) < 0)
+ goto done;
+ /* Apply default values (removed in clear function) */
+ if (xml_default_recurse(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;
+ }
if (clicon_debug_get()>1)
clicon_xml2file(stderr, x0t, 0, 1);
*xtop = x0t;
@@ -970,7 +876,7 @@ xmldb_get(clicon_handle h,
* freeing tree must be made after use.
* @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running"
- * @param[in] yb How to bind yang to XML top-level when parsing
+ * @param[in] yb How to bind yang to XML top-level when parsing (if YB_NONE, no defaults)
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index f4333a5a..2a6f7c23 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -583,7 +583,7 @@ xml_parent_set(cxobj *xn,
/*! Get xml node flags, used for internal algorithms
* @param[in] xn xml node
- * @retval flag Flag value(s), see XML_FLAG_*
+ * @retval flag Flag value(s), see XML_FLAG_MARK et al
*/
uint16_t
xml_flag(cxobj *xn,
@@ -594,7 +594,7 @@ xml_flag(cxobj *xn,
/*! Set xml node flags, used for internal algorithms
* @param[in] xn xml node
- * @param[in] flag Flag values to set, see XML_FLAG_*
+ * @param[in] flag Flag values to set, see XML_FLAG_MARK et al
*/
int
xml_flag_set(cxobj *xn,
@@ -1926,6 +1926,8 @@ xml_copy(cxobj *x0,
return retval;
}
+
+
/*! Create and return a copy of xml tree.
*
* @code
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 1e976217..ae31a829 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -1239,17 +1239,19 @@ xml_default_recurse(cxobj *xn)
return retval;
}
-/*! Ensure default values are set on top-level
+/*! Expand and set default values of global top-level on XML tree
*
* Not recursive, except in one case with one or several non-presence containers
+ * @param[in] h Clixon handle
+ * @param[in] yspec Top-level YANG specification tree, all modules
* @param[in] xt XML tree
- * Typically called in a recursive apply function:
* @retval 0 OK
* @retval -1 Error
*/
-int
-xml_default_yspec(yang_stmt *yspec,
- cxobj *xt)
+static int
+xml_global_defaults_create(cxobj *xt,
+ yang_stmt *yspec)
+
{
int retval = -1;
yang_stmt *ymod = NULL;
@@ -1261,12 +1263,86 @@ xml_default_yspec(yang_stmt *yspec,
while ((ymod = yn_each(yspec, ymod)) != NULL)
if (xml_default1(ymod, xt) < 0)
goto done;
-
retval = 0;
done:
return retval;
}
+/*! Expand and set default values of global top-level on XML tree
+ *
+ * Not recursive, except in one case with one or several non-presence containers
+ * @param[in] h Clixon handle
+ * @param[in] xt XML tree, assume already filtered with xpath
+ * @param[in] xpath Filter global defaults with this and merge with xt
+ * @param[in] yspec Top-level YANG specification tree, all modules
+ * @retval 0 OK
+ * @retval -1 Error
+ * Uses cache?
+ */
+int
+xml_global_defaults(clicon_handle h,
+ cxobj *xt,
+ cvec *nsc,
+ const char *xpath,
+ yang_stmt *yspec)
+{
+ int retval = -1;
+ db_elmnt de0 = {0,};
+ db_elmnt *de = NULL;
+ cxobj *xcache = NULL;
+ cxobj *xpart = NULL;
+ cxobj **xvec = NULL;
+ size_t xlen;
+ int i;
+ cxobj *x0;
+ int ret;
+
+ /* First get or compute global xml tree cache */
+ if ((de = clicon_db_elmnt_get(h, "global-defaults")) == NULL){
+ /* Create it it */
+ if ((xcache = xml_new("config", NULL, CX_ELMNT)) == NULL)
+ goto done;
+ if (xml_global_defaults_create(xcache, yspec) < 0)
+ goto done;
+ de0.de_xml = xcache;
+ clicon_db_elmnt_set(h, "global-defaults", &de0);
+ }
+ else
+ xcache = de->de_xml;
+
+ /* Here xcache has all global defaults. Now find the matching nodes
+ * XXX: nsc as 2nd argument
+ */
+ if (xpath_vec(xcache, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
+ goto done;
+ /* Iterate through match vector
+ * For every node found in x0, mark the tree up to t1
+ */
+ for (i=0; i