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