diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9e4cf5..8f19ac6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,18 @@ ## 6.1.0 Expected: beginning of 2023 +### API changes on existing protocol/config features + +Users may have to change how they access the system + +* With-defaults default retrieval mode has changed from `REPORT-ALL` to `EXPLICIT` + * This means that all get operations without `with-defaults` parameter do no longer + return implicit default values, only explicitly set values. + * Applies to NETCONF ``, `` and RESTCONF `GET` + * To keep backward-compatible behavior, define option `NETCONF_DEFAULT_RETRIEVAL_REPORT_ALL` in + include/clixon_custom.h + * Alternatively, change all get operation to include with-defaults parameter `report-all` + ### C/CLI-API changes on existing features Developers may need to change their code @@ -61,7 +73,8 @@ Developers may need to change their code ### Corrected Bugs -* Fixed [Netconf monitoring](https://github.com/clicon/clixon/issues/370) +* Fixed: [Defaults in choice does not work properly](https://github.com/clicon/clixon/issues/390) +* Fixed: [Netconf monitoring](https://github.com/clicon/clixon/issues/370) - Announce module capability - Return origin Yang file in get-schema diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index eeb6e307..31379dd3 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -94,6 +94,7 @@ extern "C" { #include #include #include +#include #include #include #include diff --git a/lib/clixon/clixon_xml_default.h b/lib/clixon/clixon_xml_default.h new file mode 100644 index 00000000..eed447c9 --- /dev/null +++ b/lib/clixon/clixon_xml_default.h @@ -0,0 +1,59 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate) + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * + * XML default values + */ + +#ifndef _CLIXON_XML_DEFAULT_H_ +#define _CLIXON_XML_DEFAULT_H_ + +/* + * Types + */ +/* Declared in clixon_yang_internal */ +typedef enum yang_class yang_class; + +/* + * Prototypes + */ +int xml_default_recurse(cxobj *xn, int state); +int xml_global_defaults(clicon_handle h, cxobj *xn, cvec *nsc, const char *xpath, yang_stmt *yspec, int state); +int xml_defaults_nopresence(cxobj *xn, int purge); +int xml_add_default_tag(cxobj *x, uint16_t flags); +int xml_flag_state_default_value(cxobj *x, uint16_t flag); +int xml_flag_default_value(cxobj *x, uint16_t flag); + +#endif /* _CLIXON_XML_DEFAULT_H_ */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 32f5f76a..95a0b22f 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -61,9 +61,6 @@ int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark); int xml_tree_prune_flagged(cxobj *xt, int flag, int test); int xml_tree_prune_flags(cxobj *xt, int flags, int mask); int xml_namespace_change(cxobj *x, char *ns, char *prefix); -int xml_default_recurse(cxobj *xn, int state); -int xml_global_defaults(clicon_handle h, cxobj *xn, cvec *nsc, const char *xpath, yang_stmt *yspec, int state); -int xml_defaults_nopresence(cxobj *xn, int purge); int xml_sanity(cxobj *x, void *arg); int xml_non_config_data(cxobj *xt, cxobj **xerr); int xml2xpath(cxobj *x, cvec *nsc, char **xpath); @@ -79,8 +76,5 @@ int yang_xml_mandatory(cxobj *xt, yang_stmt *ys); int xml_rpc_isaction(cxobj *xn); int xml_find_action(cxobj *xn, int top, cxobj **xap); int purge_tagged_nodes(cxobj *xn, char *ns, char *name, char *value, int keepnode); -int xml_add_default_tag(cxobj *x, uint16_t flags); -int xml_flag_state_default_value(cxobj *x, uint16_t flag); -int xml_flag_default_value(cxobj *x, uint16_t flag); #endif /* _CLIXON_XML_MAP_H_ */ diff --git a/lib/clixon/clixon_xml_nsctx.h b/lib/clixon/clixon_xml_nsctx.h index 81da6176..84bb94ab 100644 --- a/lib/clixon/clixon_xml_nsctx.h +++ b/lib/clixon/clixon_xml_nsctx.h @@ -66,5 +66,6 @@ int xml2ns_recurse(cxobj *x); int xmlns_set(cxobj *x, char *prefix, char *ns); int xmlns_set_all(cxobj *x, cvec *nsc); int xml2prefix(cxobj *xn, char *ns, char **prefixp); +int xml_add_namespace(cxobj *x, cxobj *xp, char *prefix, char *ns); #endif /* _CLIXON_XML_NSCTX_H */ diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index aa2b970a..0e3e0342 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -257,6 +257,7 @@ char *yang_find_mynamespace(yang_stmt *ys); int yang_find_prefix_by_namespace(yang_stmt *ys, char *ns, char **prefix); int yang_find_namespace_by_prefix(yang_stmt *ys, char *prefix, char **ns); yang_stmt *yang_myroot(yang_stmt *ys); +int choice_case_get(yang_stmt *yc, yang_stmt **ycase, yang_stmt **ychoice); yang_stmt *yang_choice(yang_stmt *y); int yang_order(yang_stmt *y); int yang_print_cb(FILE *f, yang_stmt *yn, clicon_output_cb *fn); diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 9959028f..1d7103aa 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -81,7 +81,7 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \ clixon_string.c clixon_regex.c clixon_handle.c clixon_file.c \ clixon_xml.c clixon_xml_io.c clixon_xml_sort.c clixon_xml_map.c clixon_xml_vec.c \ - clixon_xml_bind.c clixon_json.c clixon_proc.c \ + clixon_xml_default.c clixon_xml_bind.c clixon_json.c clixon_proc.c \ clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_netconf_monitoring.c \ clixon_yang_parse_lib.c clixon_yang_sub_parse.c \ clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \ diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 3a8b6978..bfb21094 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -78,6 +78,7 @@ #include "clixon_yang_module.h" #include "clixon_yang_parse_lib.h" #include "clixon_xml_map.h" +#include "clixon_xml_default.h" #include "clixon_xml_io.h" #include "clixon_xml_nsctx.h" #include "clixon_datastore.h" diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index e97d4ac5..b54e2cb1 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -77,6 +77,7 @@ #include "clixon_yang_module.h" #include "clixon_xml_nsctx.h" #include "clixon_xml_io.h" +#include "clixon_xml_default.h" #include "clixon_xml_map.h" #include "clixon_datastore.h" #include "clixon_datastore_write.h" @@ -336,30 +337,6 @@ check_when_condition(cxobj *x0p, goto done; } -/*! Get cloxest yang case and choice, if any - */ -static int -choice_case_get(yang_stmt *yc, - yang_stmt **ycase, - yang_stmt **ychoice) -{ - yang_stmt *yp; - - if ((yp = yang_parent_get(yc)) == NULL) - return 0; - if (yang_keyword_get(yp) == Y_CASE){ - *ycase = yp; - *ychoice = yang_parent_get(yp); - return 1; - } - else if (yang_keyword_get(yp) == Y_CHOICE){ - *ycase = NULL; - *ychoice = yp; - return 1; - } - return 0; -} - /*! Check if x0/y0 is part of other choice/case than y1 recursively , if so purge * @retval 0 No, y0 it is not in other case than y1 * @retval 1 yes, y0 is in other case than y1 diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 41dbb266..c61c1a3c 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -80,7 +80,7 @@ #include "clixon_xml_nsctx.h" #include "clixon_xml_io.h" #include "clixon_validate.h" -#include "clixon_xml_map.h" +#include "clixon_xml_default.h" /* Mapping between Clicon startup modes string <--> constants, see clixon-config.yang type startup_mode */ diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index 13c9cdd5..e1a942aa 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -76,6 +76,7 @@ #include "clixon_yang_module.h" #include "clixon_yang_type.h" #include "clixon_xml_map.h" +#include "clixon_xml_default.h" #include "clixon_xml_bind.h" #include "clixon_validate_minmax.h" #include "clixon_validate.h" diff --git a/lib/src/clixon_xml_default.c b/lib/src/clixon_xml_default.c new file mode 100644 index 00000000..42b150f6 --- /dev/null +++ b/lib/src/clixon_xml_default.c @@ -0,0 +1,703 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate) + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * + * XML default values + */ +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ + +#include "clixon_queue.h" +#include "clixon_hash.h" +#include "clixon_string.h" +#include "clixon_handle.h" +#include "clixon_log.h" +#include "clixon_err.h" +#include "clixon_yang.h" +#include "clixon_xml.h" +#include "clixon_xpath_ctx.h" +#include "clixon_xpath.h" +#include "clixon_data.h" +#include "clixon_xml_sort.h" +#include "clixon_xml_nsctx.h" +#include "clixon_xml_map.h" +#include "clixon_xml_default.h" + +/* Forward */ +static int xml_default(yang_stmt *yt, cxobj *xt, int state); + +/*! + */ +static int +xml_default_create1(yang_stmt *y, + cxobj *xt, + cxobj **xcp) +{ + int retval = -1; + char *namespace; + char *prefix; + int ret; + cxobj *xc = NULL; + + if ((xc = xml_new(yang_argument_get(y), NULL, CX_ELMNT)) == NULL) + goto done; + xml_spec_set(xc, y); + /* assign right prefix */ + if ((namespace = yang_find_mynamespace(y)) != NULL){ + prefix = NULL; + if ((ret = xml2prefix(xt, namespace, &prefix)) < 0) + goto done; + if (ret){ /* Namespace found, prefix returned in prefix */ + if (xml_prefix_set(xc, prefix) < 0) + goto done; + } + else{ /* Namespace does not exist in target, must add it w xmlns attr. + use source prefix */ + if (xml_add_namespace(xc, xc, prefix, namespace) < 0) + goto done; + /* Add prefix to x, if any */ + if (prefix && xml_prefix_set(xc, prefix) < 0) + goto done; + } + } + if (xml_addsub(xt, xc) < 0) + goto done; + *xcp = xc; + retval = 0; + done: + return retval; +} + +/*! Create leaf from default value + * + * @param[in] y Yang spec + * @param[in] xt XML tree + * @param[in] top Use default namespace (if you create xmlns statement) + * @retval 0 OK + * @retval -1 Error + */ +static int +xml_default_create(yang_stmt *y, + cxobj *xt, + int top) +{ + int retval = -1; + cxobj *xc = NULL; + cxobj *xb; + char *str; + cg_var *cv; + + if (xml_default_create1(y, xt, &xc) < 0) + goto done; + xml_flag_set(xc, XML_FLAG_DEFAULT); + if ((xb = xml_new("body", xc, CX_BODY)) == NULL) + goto done; + if ((cv = yang_cv_get(y)) == NULL){ + clicon_err(OE_UNIX, ENOENT, "No yang cv of %s", yang_argument_get(y)); + goto done; + } + if ((str = cv2str_dup(cv)) == NULL){ + clicon_err(OE_UNIX, errno, "cv2str_dup"); + goto done; + } + if (xml_value_set(xb, str) < 0) + goto done; + free(str); + retval = 0; + done: + return retval; +} + +/*! Traverse a choice + * From RFC7950 Sec 7.9.3 + * 1. Default case, the default if no child nodes from any of the choice's cases exist + * 2. Default for child nodes under a case are only used if one of the nodes under that case + * is present + */ +static int +xml_default_choice(yang_stmt *yc, + cxobj *xt, + int state) +{ + int retval = -1; + cxobj *x = NULL; + cxobj *x0 = NULL; + yang_stmt *y; + yang_stmt *ych = NULL; + yang_stmt *yca = NULL; + yang_stmt *ydef; + + clicon_debug(1, "%s", __FUNCTION__); + /* 1. Is there a default case and no child under this choice? + */ + x = NULL; + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { + if ((y = xml_spec(x)) == NULL) + continue; + /* Check if this child is a child of yc */ + yca = ych = NULL; + if (choice_case_get(y, &yca, &ych) == 1 && + ych == yc){ + x0 = x; + break; + } + } + if (x0 == NULL){ /* case 1: no child nodes of any of the choice's cases */ + if ((ydef = yang_find(yc, Y_DEFAULT, NULL)) != NULL) + yca = yang_find(yc, Y_CASE, yang_argument_get(ydef)); + else + yca = NULL; + } + if (yca) + if (xml_default(yca, xt, state) < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! 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; + yang_stmt *y; + yang_stmt *ydef; + + if (yt == NULL || yang_keyword_get(yt) != Y_CONTAINER){ + clicon_err(OE_XML, EINVAL, "yt argument is not container"); + goto done; + } + *createp = 0; + y = NULL; + while ((y = yn_each(yt, y)) != NULL) { + switch (yang_keyword_get(y)){ + case Y_LEAF: + /* 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; + case Y_CONTAINER: + if (yang_find(y, Y_PRESENCE, NULL) == NULL){ + /* 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, state, createp) < 0) + goto done; + if (*createp) + goto ok; + } + break; + case Y_CHOICE: + if ((ydef = yang_find(y, Y_DEFAULT, NULL)) != NULL && + yang_find(y, Y_CASE, yang_argument_get(ydef))) + *createp = 1; + break; + default: + break; + } /* switch */ + } + ok: + retval = 0; + done: + return retval; +} + +/*! Ensure default values are set on (children of) one single xml node + * + * Not recursive, except in one case with one or several non-presence containers, in which case + * XML containers may be created to host default values. That code may be a little too recursive. + * @param[in] yt Yang spec, usually spec of xt but always (eg Y_CASE) + * @param[in] xt XML tree (with yt as spec of xt, informally) + * @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_default(yang_stmt *yt, + cxobj *xt, + int state) +{ + int retval = -1; + yang_stmt *yc; + cxobj *xc; + int top = 0; /* Top symbol (set default namespace) */ + int create = 0; + char *xpath; + int nr = 0; + int hit = 0; + cg_var *cv; + + if (xt == NULL){ /* No xml */ + clicon_err(OE_XML, EINVAL, "No XML argument"); + goto done; + } + switch (yang_keyword_get(yt)){ + case Y_MODULE: + case Y_SUBMODULE: + top++; + case Y_CONTAINER: /* XXX maybe check for non-presence here as well */ + case Y_LIST: + case Y_INPUT: + case Y_OUTPUT: + case Y_CASE: + 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)); + goto done; + } + if (!cv_flag(cv, V_UNSET)){ /* Default value exists */ + /* Check when condition */ + if (yang_check_when_xpath(NULL, xt, yc, &hit, &nr, &xpath) < 0) + goto done; + if (hit && nr == 0) + break; /* Do not create default if xpath fails */ + if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){ + /* No such child exist, create this leaf */ + if (xml_default_create(yc, xt, top) < 0) + goto done; + xml_sort(xt); + } + } + break; + case Y_CONTAINER: + if (yang_find(yc, Y_PRESENCE, NULL) == NULL){ + /* Check when condition */ + if (yang_check_when_xpath(NULL, xt, yc, &hit, &nr, &xpath) < 0) + goto done; + if (hit && nr == 0) + break; /* Do not create default if xpath fails */ + /* 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_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){ + /* No such container exist, recursively try if needed */ + if (xml_nopresence_try(yc, state, &create) < 0) + goto done; + if (create){ + /* Retval shows there is a default value need to create the + * container */ + if (xml_default_create1(yc, xt, &xc) < 0) + goto done; + xml_sort(xt); + /* Then call it recursively */ + if (xml_default(yc, xc, state) < 0) + goto done; + } + } + } + break; + case Y_CHOICE:{ + if (xml_default_choice(yc, xt, state) < 0) + goto done; + } + default: + break; + } + } + break; + default: + break; + } /* switch */ + retval = 0; + done: + return retval; +} + +/*! Recursively fill in default values in an XML tree + * @param[in] xt XML tree + * @param[in] state If set expand defaults also for state data, otherwise only config + * @retval 0 OK + * @retval -1 Error + * @see xml_global_defaults + */ +int +xml_default_recurse(cxobj *xn, + int state) +{ + int retval = -1; + yang_stmt *yn; + cxobj *x; + yang_stmt *y; + + if ((yn = (yang_stmt*)xml_spec(xn)) != NULL) + if (xml_default(yn, xn, state) < 0) + goto done; + x = NULL; + while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) { + if ((y = (yang_stmt*)xml_spec(x)) != NULL){ + if (!state && !yang_config(y)) + continue; + } + if (xml_default_recurse(x, state) < 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] xt XML tree + * @param[in] yspec Top-level YANG specification tree, all modules + * @param[in] state p Set if global state, otherwise config + * @retval 0 OK + * @retval -1 Error + */ +static int +xml_global_defaults_create(cxobj *xt, + yang_stmt *yspec, + int state) +{ + int retval = -1; + yang_stmt *ymod = NULL; + + if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){ + clicon_err(OE_XML, EINVAL, "yspec argument is not yang spec"); + goto done; + } + while ((ymod = yn_each(yspec, ymod)) != NULL) + if (xml_default(ymod, xt, state) < 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 + * @param[in] state Set if global state, otherwise config + * @retval 0 OK + * @retval -1 Error + * Uses cache? + * @see xml_default_recurse + */ +int +xml_global_defaults(clicon_handle h, + cxobj *xt, + cvec *nsc, + const char *xpath, + yang_stmt *yspec, + int state) +{ + 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; + char *key; + + /* Use different keys for config and state */ + key = state ? "global-defaults-state" : "global-defaults-config"; + /* First get or compute global xml tree cache */ + if ((de = clicon_db_elmnt_get(h, key)) == NULL){ + /* Create it */ + if ((xcache = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) + goto done; + if (xml_global_defaults_create(xcache, yspec, state) < 0) + goto done; + de0.de_xml = xcache; + clicon_db_elmnt_set(h, key, &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; ide_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 $cfg + + $cfg + clixon-restconf:allow-auth-none + $dir + ${YANG_INSTALLDIR} + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + $clidir + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + +EOF + +# See example in RFC 7950 Sec 7.9.3 +cat < $fyang +module transfer{ + yang-version 1.1; + namespace "urn:example:transfer"; + prefix tr; + grouping transfer-container { + description "Example from RFC 7950 Sec 7.9.3"; + container transfer { + choice how { + default interval; + case interval { + leaf interval { + type uint16; + units minutes; + default 30; + } + } + case daily { + leaf daily { + type empty; + } + leaf time-of-day { + type string; + units 24-hour-clock; + default "01.00"; + } + } + case manual { + leaf manual { + type empty; + } + } + } + } + } + uses transfer-container; + /* Same but within list */ + list li{ + key x; + leaf x { + type int32; + } + uses transfer-container; + } +} +EOF + +cat < $clidir/ex.cli +# Clixon example specification +CLICON_MODE="example"; +CLICON_PROMPT="%U@%H %W> "; +CLICON_PLUGIN="example_cli"; + +# Autocli syntax tree operations +set @datamodel, cli_auto_set(); +delete("Delete a configuration item") { + @datamodel, cli_auto_del(); + all("Delete whole candidate configuration"), delete_all("candidate"); +} +validate("Validate changes"), cli_validate(); +commit("Commit the changes"), cli_commit(); +quit("Quit"), cli_quit(); +discard("Discard edits (rollback 0)"), discard_changes(); + +show("Show a particular state of the system"){ + configuration("Show configuration"), cli_show_auto_mode("candidate", "text", true, false);{ + cli("Show configuration as CLI commands"), cli_show_auto_mode("candidate", "cli", true, false, "report-all", "set "); + xml("Show configuration as XML"), cli_show_auto_mode("candidate", "xml", true, false, NULL); + } +} +EOF + +new "test params: -f $cfg" + +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + sudo pkill -f clixon_backend # to be sure + + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg +fi + +new "wait backend" +wait_backend + +new "Default value expected: interval=30" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "report-all" "" "30" + +new "Set transfer/daily" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "Default value expected: time-of-day=01:00" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "report-all" "" "01.00" + +new "Set list element transfer container" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "
  • 42
  • " "" "" + +new "Default value expected: interval=30" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "report-all" "" "
  • 4230
  • " + +new "Set transfer/daily" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "
  • 42
  • " "" "" + +new "Default value expected: time-of-day=01:00" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "report-all" "" "
  • 4201.00
  • " + +if [ $BE -ne 0 ]; then + new "Kill backend" + # Check if premature kill + pid=$(pgrep -u root -f clixon_backend) + if [ -z "$pid" ]; then + err "backend already dead" + fi + # kill backend + stop_backend -f $cfg +fi + +rm -rf $dir + +new "endtest" +endtest