Moved with-defaults processing from get(-config) to xmldb_get when reading db

With-defaults: fixed cornercase in xpath used in list pagination
C-API: Added `with-defaults` parameter (default 0) to `xmldb_get0()`
This commit is contained in:
Olof hagsand 2022-12-16 16:42:05 +01:00
parent e885d63f68
commit 0d41d49fa9
19 changed files with 420 additions and 196 deletions

View file

@ -75,6 +75,7 @@
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_netconf_lib.h"
#include "clixon_datastore.h"
#include "clixon_datastore_write.h"
#include "clixon_datastore_read.h"

View file

@ -662,6 +662,7 @@ xmldb_readfile(clicon_handle h,
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] wdef With-defaults parameter, see RFC 6243
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences
* @param[out] xerr XML error if retval is 0
@ -677,6 +678,7 @@ xmldb_get_nocache(clicon_handle h,
yang_bind yb,
cvec *nsc,
const char *xpath,
withdefaults_type wdef,
cxobj **xtop,
modstate_diff_t *msdiff,
cxobj **xerr)
@ -734,6 +736,54 @@ xmldb_get_nocache(clicon_handle h,
if (xml_default_recurse(xt, 0) < 0)
goto done;
}
#if 1
/* Sub-optimal: first add defaults, then remove them in some cases
* Reason is code is primarily test for _cache case and I dont have time to "revert" it here
*/
switch (wdef){
case WITHDEFAULTS_REPORT_ALL:
break;
case WITHDEFAULTS_TRIM:
/* Mark and remove nodes having schema default values */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
if (xml_tree_prune_flags(xt, XML_FLAG_MARK, XML_FLAG_MARK)
< 0)
goto done;
if (xml_defaults_nopresence(xt, 1) < 0)
goto done;
break;
case WITHDEFAULTS_EXPLICIT:
if (xml_defaults_nopresence(xt, 2) < 0)
goto done;
break;
case WITHDEFAULTS_REPORT_ALL_TAGGED:{
cxobj *x;
char *ns;
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
ns = NULL;
if (xml2ns(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, &ns) < 0)
goto done;
if (ns == NULL){
if (xmlns_set(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) < 0)
goto done;
}
else if (strcmp(ns, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) != 0){
/* XXX: Assume if namespace is set that it is withdefaults otherwise just ignore? */
continue;
}
}
/* Mark nodes having default schema values */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
/* Add tag attributes to default nodes */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*) xml_add_default_tag, (void*) (XML_FLAG_DEFAULT | XML_FLAG_MARK)) < 0)
goto done;
break;
}
} /* switch wdef */
#endif
/* If empty NACM config, then disable NACM if loaded
*/
if (clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
@ -774,6 +824,7 @@ xmldb_get_nocache(clicon_handle h,
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] wdef With-defaults parameter, see RFC 6243
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences
* @param[out] xerr XML error if retval is 0
@ -789,6 +840,7 @@ xmldb_get_cache(clicon_handle h,
yang_bind yb,
cvec *nsc,
const char *xpath,
withdefaults_type wdef,
cxobj **xtop,
modstate_diff_t *msdiff,
cxobj **xerr)
@ -889,17 +941,52 @@ xmldb_get_cache(clicon_handle h,
if (xml_apply(x1t, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
}
/* Remove global defaults and empty non-presence containers */
/* Original tree: Remove global defaults and empty non-presence containers */
if (xml_defaults_nopresence(x0t, 2) < 0)
goto done;
if (yb != YB_NONE){
/* Add default global values */
if (xml_global_defaults(h, x1t, nsc, xpath, yspec, 0) < 0)
switch (wdef){
case WITHDEFAULTS_REPORT_ALL:
break;
case WITHDEFAULTS_TRIM:
/* Mark and remove nodes having schema default values */
if (xml_apply(x1t, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
/* Add default recursive values */
if (xml_default_recurse(x1t, 0) < 0)
if (xml_tree_prune_flags(x1t, XML_FLAG_MARK, XML_FLAG_MARK)
< 0)
goto done;
if (xml_defaults_nopresence(x1t, 1) < 0)
goto done;
break;
case WITHDEFAULTS_EXPLICIT:
if (xml_defaults_nopresence(x1t, 2) < 0)
goto done;
break;
case WITHDEFAULTS_REPORT_ALL_TAGGED:{
cxobj *x;
char *ns;
x = NULL;
while ((x = xml_child_each(x1t, x, CX_ELMNT)) != NULL){
ns = NULL;
if (xml2ns(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, &ns) < 0)
goto done;
if (ns == NULL){
if (xmlns_set(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) < 0)
goto done;
}
else if (strcmp(ns, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) != 0){
/* XXX: Assume if namespace is set that it is withdefaults otherwise just ignore? */
continue;
}
}
/* Mark nodes having default schema values */
if (xml_apply(x1t, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
/* Add tag attributes to default nodes */
if (xml_apply(x1t, CX_ELMNT, (xml_applyfn_t*) xml_add_default_tag, (void*) (XML_FLAG_DEFAULT | XML_FLAG_MARK)) < 0)
goto done;
break;
}
} /* switch wdef */
/* If empty NACM config, then disable NACM if loaded
*/
if (clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
@ -932,7 +1019,7 @@ xmldb_get_cache(clicon_handle h,
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[in] wdef With-defaults parameter, see RFC 6243
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences
* @param[out] xerr XML error if retval is 0
@ -947,6 +1034,7 @@ xmldb_get_zerocopy(clicon_handle h,
yang_bind yb,
cvec *nsc,
const char *xpath,
withdefaults_type wdef,
cxobj **xtop,
modstate_diff_t *msdiff,
cxobj **xerr)
@ -1005,6 +1093,54 @@ xmldb_get_zerocopy(clicon_handle h,
if (xml_default_recurse(x0t, 0) < 0)
goto done;
}
#if 1
/* Sub-optimal: first add defaults, then remove them in some cases
* Reason is code is primarily test for _cache case and I dont have time to "revert" it here
*/
switch (wdef){
case WITHDEFAULTS_REPORT_ALL:
break;
case WITHDEFAULTS_TRIM:
/* Mark and remove nodes having schema default values */
if (xml_apply(x0t, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
if (xml_tree_prune_flags(x0t, XML_FLAG_MARK, XML_FLAG_MARK)
< 0)
goto done;
if (xml_defaults_nopresence(x0t, 1) < 0)
goto done;
break;
case WITHDEFAULTS_EXPLICIT:
if (xml_defaults_nopresence(x0t, 2) < 0)
goto done;
break;
case WITHDEFAULTS_REPORT_ALL_TAGGED:{
cxobj *x;
char *ns;
x = NULL;
while ((x = xml_child_each(x0t, x, CX_ELMNT)) != NULL){
ns = NULL;
if (xml2ns(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, &ns) < 0)
goto done;
if (ns == NULL){
if (xmlns_set(x, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) < 0)
goto done;
}
else if (strcmp(ns, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) != 0){
/* XXX: Assume if namespace is set that it is withdefaults otherwise just ignore? */
continue;
}
}
/* Mark nodes having default schema values */
if (xml_apply(x0t, CX_ELMNT, (xml_applyfn_t*) xml_flag_default_value, (void*) XML_FLAG_MARK) < 0)
goto done;
/* Add tag attributes to default nodes */
if (xml_apply(x0t, CX_ELMNT, (xml_applyfn_t*) xml_add_default_tag, (void*) (XML_FLAG_DEFAULT | XML_FLAG_MARK)) < 0)
goto done;
break;
}
} /* switch wdef */
#endif
/* If empty NACM config, then disable NACM if loaded
*/
if (clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
@ -1050,7 +1186,7 @@ xmldb_get(clicon_handle h,
char *xpath,
cxobj **xret)
{
return xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, xret, NULL, NULL);
return xmldb_get0(h, db, YB_MODULE, nsc, xpath, 1, 0, xret, NULL, NULL);
}
/*! Zero-copy variant of get content of database
@ -1067,6 +1203,7 @@ xmldb_get(clicon_handle h,
* @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
* @param[in] wdef With-defaults parameter, see RFC 6243
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msdiff If set, return modules-state differences (upgrade code)
* @param[out] xerr XML error if retval is 0
@ -1077,7 +1214,7 @@ xmldb_get(clicon_handle h,
* @code
* cxobj *xt;
* cxobj *xerr = NULL;
* if (xmldb_get0(h, "running", YB_MODULE, nsc, "/interface[name="eth"]", 0, &xt, NULL, &xerr) < 0)
* if (xmldb_get0(h, "running", YB_MODULE, nsc, "/interface[name="eth"]", 0, 0, &xt, NULL, &xerr) < 0)
* err;
* if (ret == 0){ # Not if YB_NONE
* # Error handling
@ -1098,18 +1235,19 @@ xmldb_get(clicon_handle h,
* And a db content:
* <c><x>1</x></c>
* With the following call:
* xmldb_get0(h, "running", NULL, NULL, "/c[x=0]", 1, &xt, NULL, NULL)
* xmldb_get0(h, "running", NULL, NULL, "/c[x=0]", 1, 0, &xt, NULL, NULL)
* which result in a miss (there is no c with x=0), but when the returned xt is printed
* (the existing tree is discarded), the default (empty) xml tree is:
* <c><x>0</x></c>
*/
int
xmldb_get0(clicon_handle h,
xmldb_get0(clicon_handle h,
const char *db,
yang_bind yb,
cvec *nsc,
const char *xpath,
int copy,
withdefaults_type wdef,
cxobj **xret,
modstate_diff_t *msdiff,
cxobj **xerr)
@ -1122,7 +1260,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_nocache(h, db, yb, nsc, xpath, xret, msdiff, xerr);
retval = xmldb_get_nocache(h, db, yb, nsc, xpath, wdef, xret, msdiff, xerr);
break;
case DATASTORE_CACHE_ZEROCOPY:
/* Get cache (file if empty) mark xpath match in original tree
@ -1130,7 +1268,7 @@ xmldb_get0(clicon_handle h,
* Default values and markings removed in xmldb_clear
*/
if (!copy){
retval = xmldb_get_zerocopy(h, db, yb, nsc, xpath, xret, msdiff, xerr);
retval = xmldb_get_zerocopy(h, db, yb, nsc, xpath, wdef, xret, msdiff, xerr);
break;
}
/* fall through */
@ -1139,7 +1277,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy, return copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_cache(h, db, yb, nsc, xpath, xret, msdiff, xerr);
retval = xmldb_get_cache(h, db, yb, nsc, xpath, wdef, xret, msdiff, xerr);
break;
}
return retval;

View file

@ -1216,7 +1216,7 @@ nacm_access_pre(clicon_handle h,
goto done;
}
else if (strcmp(mode, "internal")==0){
if (xmldb_get0(h, "running", YB_MODULE, nsc, "nacm", 1, &xnacm0, NULL, NULL) < 0)
if (xmldb_get0(h, "running", YB_MODULE, nsc, "nacm", 1, 0, &xnacm0, NULL, NULL) < 0)
goto done;
}
else{

View file

@ -77,6 +77,35 @@
#include "clixon_netconf_lib.h"
/* Mapping between RFC6243 withdefaults strings <--> ints
*/
static const map_str2int wdmap[] = {
{"report-all", WITHDEFAULTS_REPORT_ALL},
{"trim", WITHDEFAULTS_TRIM},
{"explicit", WITHDEFAULTS_EXPLICIT},
{"report-all-tagged", WITHDEFAULTS_REPORT_ALL_TAGGED}
};
/*! Map from with-defaults ints to strings
* @param[in] int Integer representation of withdefaults values
* @retval str String representation of withdefaults values
*/
char *
withdefaults_int2str(int keyword)
{
return (char*)clicon_int2str(wdmap, keyword);
}
/*! Map from with-defaults strings to ints
* @param[in] str String representation of withdefaults values
* @retval int Integer representation of withdefaults values
*/
int
withdefaults_str2int(char *str)
{
return clicon_str2int(wdmap, str);
}
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
*
* The request requires a resource that already is in use.

View file

@ -1725,7 +1725,7 @@ xml_find_type_value(cxobj *xt,
* @code
* cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
* @endcode
* @see xml_find which finds any child given name
* @see xml_find which finds any child given name (and no prefix)
* @see xml_find_value where a body can be found as well
*/
cxobj *

View file

@ -781,12 +781,14 @@ xml_default_create(yang_stmt *y,
/*! Try to see if intermediate nodes are necessary for default values, create if so
*
* @param[in] yt Yang container (no-presence)
* @param[in] state Set if global state, otherwise config
* @param[out] createp Need to create XML container
* @retval 0 OK
* @retval -1 Error
*/
static int
xml_nopresence_try(yang_stmt *yt,
int state,
int *createp)
{
int retval = -1;
@ -801,9 +803,14 @@ xml_nopresence_try(yang_stmt *yt,
while ((y = yn_each(yt, y)) != NULL) {
switch (yang_keyword_get(y)){
case Y_LEAF:
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
/* Need to create container */
*createp = 1;
/* Default value exists */
if (!cv_flag(yang_cv_get(y), V_UNSET)){
/* Want to add state defaults, but this is config */
if (state && yang_config_ancestor(y))
;
else
/* Need to create container */
*createp = 1;
goto ok;
}
break;
@ -812,7 +819,7 @@ xml_nopresence_try(yang_stmt *yt,
/* If this is non-presence, (and it does not exist in xt) call recursively
* and create nodes if any default value exist first. Then continue and populate?
*/
if (xml_nopresence_try(y, createp) < 0)
if (xml_nopresence_try(y, state, createp) < 0)
goto done;
if (*createp)
goto ok;
@ -837,6 +844,8 @@ xml_nopresence_try(yang_stmt *yt,
* @param[in] state Set if global state, otherwise config
* @retval 0 OK
* @retval -1 Error
* XXX If state, should not add config defaults
* if (state && yang_config(yc))
*/
static int
xml_default1(yang_stmt *yt,
@ -867,10 +876,14 @@ xml_default1(yang_stmt *yt,
case Y_OUTPUT:
yc = NULL;
while ((yc = yn_each(yt, yc)) != NULL) {
/* If config parameter and local is config false */
if (!state && !yang_config(yc))
continue;
switch (yang_keyword_get(yc)){
case Y_LEAF:
/* Want to add state defaults, but this is config */
if (state && yang_config_ancestor(yc))
break;
if ((cv = yang_cv_get(yc)) == NULL){
clicon_err(OE_YANG,0, "Internal error: yang leaf %s not populated with cv as it should",
yang_argument_get(yc));
@ -903,7 +916,7 @@ xml_default1(yang_stmt *yt,
*/
if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
/* No such container exist, recursively try if needed */
if (xml_nopresence_try(yc, &create) < 0)
if (xml_nopresence_try(yc, state, &create) < 0)
goto done;
if (create){
/* Retval shows there is a default value need to create the
@ -2342,3 +2355,108 @@ purge_tagged_nodes(cxobj *xn,
done:
return retval;
}
/*! Add default attribute to node with default value.
*
* Used in with-default code for report-all-tagged
* @param[in] x XML node
* @param[in] flags Flags indicatiing default nodes
* @retval 0 OK
* @retval -1 Error
*/
int
xml_add_default_tag(cxobj *x,
uint16_t flags)
{
int retval = -1;
cxobj *xattr;
if (xml_flag(x, flags)) {
/* set default attribute */
if ((xattr = xml_new("default", x, CX_ATTR)) == NULL)
goto done;
if (xml_value_set(xattr, "true") < 0)
goto done;
if (xml_prefix_set(xattr, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Set flag on node having schema default value. (non-config)
*
* Used in with-default code for trim/report-all-tagged
* @param[in] x XML node
* @param[in] flag Flag to be used
* @retval 0 OK
* @see xml_flag_default_value for config default value
*/
int
xml_flag_state_default_value(cxobj *x,
uint16_t flag)
{
yang_stmt *y;
cg_var *cv;
char *yv;
char *xv;
xml_flag_reset(x, flag); /* Assume not default value */
if ((xv = xml_body(x)) == NULL)
goto done;
if ((y = xml_spec(x)) == NULL)
goto done;
if (yang_config_ancestor(y) == 1)
goto done;
if ((cv = yang_cv_get(y)) == NULL)
goto done;
if ((cv = yang_cv_get(y)) == NULL)
goto done;
if (cv_name_get(cv) == NULL)
goto done;
if ((yv = cv2str_dup(cv)) == NULL)
goto done;
if (strcmp(xv, yv) == 0)
xml_flag_set(x, flag); /* Actual value same as default value */
free(yv);
done:
return 0;
}
/*! Set flag on node having schema default value. (config)
*
* Used in with-default code for trim and report-all-tagged
* @param[in] x XML node
* @param[in] flag Flag to be used
* @retval 0 OK
* @see xml_flag_state_default_value for non-config default value
*/
int
xml_flag_default_value(cxobj *x,
uint16_t flag)
{
yang_stmt *y;
cg_var *cv;
char *yv;
char *xv;
xml_flag_reset(x, flag); /* Assume not default value */
if ((xv = xml_body(x)) == NULL)
goto done;
if ((y = xml_spec(x)) == NULL)
goto done;
if ((cv = yang_cv_get(y)) == NULL)
goto done;
if ((cv = yang_cv_get(y)) == NULL)
goto done;
if (cv_name_get(cv) == NULL)
goto done;
if ((yv = cv2str_dup(cv)) == NULL)
goto done;
if (strcmp(xv, yv) == 0)
xml_flag_set(x, flag); /* Actual value same as default value */
free(yv);
done:
return 0;
}

View file

@ -1501,7 +1501,7 @@ xml_find_index_yang(cxobj *xp,
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, xv) < 0)
* err;
* for (i=0; i<clixon_xvec_len(xv); i++){
* x = clixon_xpath_i(xv, i);
* x = clixon_xvec_i(xv, i);
* ...
* }
* clixon_xvec_free(xvec);

View file

@ -1602,12 +1602,20 @@ yang_order(yang_stmt *y)
return retval;
}
/*! Map from YANG keywords ints to strings
* @param[in] int Integer representation of YANG keywords
* @retval str String representation of YANG keywords
*/
char *
yang_key2str(int keyword)
{
return (char*)clicon_int2str(ykmap, keyword);
}
/*! Map from yang keyword strings to ints
* @param[in] str String representation of YANG keywords
* @retval int Integer representation of YANG keywords
*/
int
yang_str2key(char *str)
{