Fixed: [Defaults in choice does not work properly](https://github.com/clicon/clixon/issues/390)
C: Added new file clixon_xml_default.[ch] and moved all default handling there
This commit is contained in:
parent
7e92f67f4f
commit
ffe918dd0e
16 changed files with 1015 additions and 667 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -41,6 +41,18 @@
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
Expected: beginning of 2023
|
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 `<get>`, `<get-config>` 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
|
### C/CLI-API changes on existing features
|
||||||
Developers may need to change their code
|
Developers may need to change their code
|
||||||
|
|
||||||
|
|
@ -61,7 +73,8 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Corrected Bugs
|
### 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
|
- Announce module capability
|
||||||
- Return origin Yang file in get-schema
|
- Return origin Yang file in get-schema
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ extern "C" {
|
||||||
#include <clixon/clixon_data.h>
|
#include <clixon/clixon_data.h>
|
||||||
#include <clixon/clixon_regex.h>
|
#include <clixon/clixon_regex.h>
|
||||||
#include <clixon/clixon_path.h>
|
#include <clixon/clixon_path.h>
|
||||||
|
#include <clixon/clixon_xml_default.h>
|
||||||
#include <clixon/clixon_xml_map.h>
|
#include <clixon/clixon_xml_map.h>
|
||||||
#include <clixon/clixon_xml_bind.h>
|
#include <clixon/clixon_xml_bind.h>
|
||||||
#include <clixon/clixon_xml_io.h>
|
#include <clixon/clixon_xml_io.h>
|
||||||
|
|
|
||||||
59
lib/clixon/clixon_xml_default.h
Normal file
59
lib/clixon/clixon_xml_default.h
Normal file
|
|
@ -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_ */
|
||||||
|
|
@ -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_flagged(cxobj *xt, int flag, int test);
|
||||||
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);
|
int xml_tree_prune_flags(cxobj *xt, int flags, int mask);
|
||||||
int xml_namespace_change(cxobj *x, char *ns, char *prefix);
|
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_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
int xml_non_config_data(cxobj *xt, cxobj **xerr);
|
||||||
int xml2xpath(cxobj *x, cvec *nsc, char **xpath);
|
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_rpc_isaction(cxobj *xn);
|
||||||
int xml_find_action(cxobj *xn, int top, cxobj **xap);
|
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 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_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -66,5 +66,6 @@ int xml2ns_recurse(cxobj *x);
|
||||||
int xmlns_set(cxobj *x, char *prefix, char *ns);
|
int xmlns_set(cxobj *x, char *prefix, char *ns);
|
||||||
int xmlns_set_all(cxobj *x, cvec *nsc);
|
int xmlns_set_all(cxobj *x, cvec *nsc);
|
||||||
int xml2prefix(cxobj *xn, char *ns, char **prefixp);
|
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 */
|
#endif /* _CLIXON_XML_NSCTX_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_prefix_by_namespace(yang_stmt *ys, char *ns, char **prefix);
|
||||||
int yang_find_namespace_by_prefix(yang_stmt *ys, char *prefix, char **ns);
|
int yang_find_namespace_by_prefix(yang_stmt *ys, char *prefix, char **ns);
|
||||||
yang_stmt *yang_myroot(yang_stmt *ys);
|
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);
|
yang_stmt *yang_choice(yang_stmt *y);
|
||||||
int yang_order(yang_stmt *y);
|
int yang_order(yang_stmt *y);
|
||||||
int yang_print_cb(FILE *f, yang_stmt *yn, clicon_output_cb *fn);
|
int yang_print_cb(FILE *f, yang_stmt *yn, clicon_output_cb *fn);
|
||||||
|
|
|
||||||
|
|
@ -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 \
|
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_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.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.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_parse_lib.c clixon_yang_sub_parse.c \
|
||||||
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
|
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_yang_parse_lib.h"
|
#include "clixon_yang_parse_lib.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
|
#include "clixon_xml_default.h"
|
||||||
#include "clixon_xml_io.h"
|
#include "clixon_xml_io.h"
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
#include "clixon_datastore.h"
|
#include "clixon_datastore.h"
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
#include "clixon_xml_io.h"
|
#include "clixon_xml_io.h"
|
||||||
|
#include "clixon_xml_default.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
#include "clixon_datastore.h"
|
#include "clixon_datastore.h"
|
||||||
#include "clixon_datastore_write.h"
|
#include "clixon_datastore_write.h"
|
||||||
|
|
@ -336,30 +337,6 @@ check_when_condition(cxobj *x0p,
|
||||||
goto done;
|
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
|
/*! 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 0 No, y0 it is not in other case than y1
|
||||||
* @retval 1 yes, y0 is in other case than y1
|
* @retval 1 yes, y0 is in other case than y1
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
#include "clixon_xml_io.h"
|
#include "clixon_xml_io.h"
|
||||||
#include "clixon_validate.h"
|
#include "clixon_validate.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_default.h"
|
||||||
|
|
||||||
/* Mapping between Clicon startup modes string <--> constants,
|
/* Mapping between Clicon startup modes string <--> constants,
|
||||||
see clixon-config.yang type startup_mode */
|
see clixon-config.yang type startup_mode */
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
|
#include "clixon_xml_default.h"
|
||||||
#include "clixon_xml_bind.h"
|
#include "clixon_xml_bind.h"
|
||||||
#include "clixon_validate_minmax.h"
|
#include "clixon_validate_minmax.h"
|
||||||
#include "clixon_validate.h"
|
#include "clixon_validate.h"
|
||||||
|
|
|
||||||
703
lib/src/clixon_xml_default.c
Normal file
703
lib/src/clixon_xml_default.c
Normal file
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
/* cligen */
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* 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; i<xlen; i++){
|
||||||
|
x0 = xvec[i];
|
||||||
|
xml_flag_set(x0, XML_FLAG_MARK);
|
||||||
|
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
|
||||||
|
}
|
||||||
|
/* Create a new tree and copy over the parts from the cache that matches xpath */
|
||||||
|
if ((xpart = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_copy_marked(xcache, xpart) < 0) /* config */
|
||||||
|
goto done;
|
||||||
|
if (xml_apply(xcache, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_apply(xpart, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Merge global pruned tree with xt */
|
||||||
|
if ((ret = xml_merge(xt, xpart, yspec, NULL)) < 1) /* XXX reason */
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xpart)
|
||||||
|
xml_free(xpart);
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Recursively find empty nopresence containers and default leaves, optionally purge
|
||||||
|
*
|
||||||
|
* @param[in] xn XML tree
|
||||||
|
* @param[in] purge 0: Dont remove any nodes
|
||||||
|
* 1: Remove config sub-nodes that are empty non-presence container or default leaf
|
||||||
|
* 2: Remove all sub-nodes that are empty non-presence container or default leaf
|
||||||
|
* @retval 1 Node is an (recursive) empty non-presence container or default leaf
|
||||||
|
* @retval 0 Other node
|
||||||
|
* @retval -1 Error
|
||||||
|
* @note xn is not itself removed if purge
|
||||||
|
* @note for purge=1 are removed only if config or no yang spec(!)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_defaults_nopresence(cxobj *xn,
|
||||||
|
int purge)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *x;
|
||||||
|
cxobj *xprev;
|
||||||
|
yang_stmt *yn;
|
||||||
|
yang_stmt *y;
|
||||||
|
int rmx = 0; /* If set, remove this xn */
|
||||||
|
int ret;
|
||||||
|
enum rfc_6020 keyw;
|
||||||
|
int config = 1;
|
||||||
|
|
||||||
|
if ((yn = xml_spec(xn)) != NULL){
|
||||||
|
keyw = yang_keyword_get(yn);
|
||||||
|
if (keyw == Y_CONTAINER &&
|
||||||
|
yang_find(yn, Y_PRESENCE, NULL) == NULL)
|
||||||
|
rmx = 1;
|
||||||
|
else if (keyw == Y_LEAF &&
|
||||||
|
xml_flag(xn, XML_FLAG_DEFAULT))
|
||||||
|
rmx = 1;
|
||||||
|
config = yang_config_ancestor(yn);
|
||||||
|
}
|
||||||
|
/* Loop thru children */
|
||||||
|
x = NULL;
|
||||||
|
xprev = NULL;
|
||||||
|
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
||||||
|
if ((ret = xml_defaults_nopresence(x, purge)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 1){
|
||||||
|
switch (purge){
|
||||||
|
case 1: /* config nodes only */
|
||||||
|
if (!config)
|
||||||
|
break;
|
||||||
|
if ((y = xml_spec(x)) != NULL &&
|
||||||
|
!yang_config(y))
|
||||||
|
break;
|
||||||
|
/* fall thru */
|
||||||
|
case 2: /* purge all nodes */
|
||||||
|
if (xml_purge(x) < 0)
|
||||||
|
goto done;
|
||||||
|
x = xprev;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rmx)
|
||||||
|
/* May switch an empty non-presence container (rmx=1) to non-empty non-presence container (rmx=0) */
|
||||||
|
rmx = 0;
|
||||||
|
}
|
||||||
|
retval = rmx;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -605,45 +605,6 @@ xml_tree_prune_flags(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Add prefix:namespace pair to xml node, set cache, etc
|
|
||||||
* @param[in] x XML node whose namespace should change
|
|
||||||
* @param[in] xp XML node where namespace attribute should be declared (can be same)
|
|
||||||
* @param[in] prefix1 Use this prefix
|
|
||||||
* @param[in] namespace Use this namespace
|
|
||||||
* @note x and xp must be different if x is an attribute and may be different otherwise
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
add_namespace(cxobj *x,
|
|
||||||
cxobj *xp,
|
|
||||||
char *prefix,
|
|
||||||
char *namespace)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cxobj *xa = NULL;
|
|
||||||
|
|
||||||
/* Add binding to x1p. We add to parent due to heurestics, so we dont
|
|
||||||
* end up in adding it to large number of siblings
|
|
||||||
*/
|
|
||||||
if (nscache_set(x, prefix, namespace) < 0)
|
|
||||||
goto done;
|
|
||||||
/* Create xmlns attribute to x1p/x1 XXX same code v */
|
|
||||||
if (prefix){
|
|
||||||
if ((xa = xml_new(prefix, xp, CX_ATTR)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if (xml_prefix_set(xa, "xmlns") < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if ((xa = xml_new("xmlns", xp, CX_ATTR)) == NULL)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (xml_value_set(xa, namespace) < 0)
|
|
||||||
goto done;
|
|
||||||
xml_sort(xp); /* Ensure attr is first / XXX xml_insert? */
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Change namespace of XML node
|
/*! Change namespace of XML node
|
||||||
*
|
*
|
||||||
|
|
@ -682,7 +643,7 @@ xml_namespace_change(cxobj *x,
|
||||||
xp = x;
|
xp = x;
|
||||||
else
|
else
|
||||||
xp = xml_parent(x);
|
xp = xml_parent(x);
|
||||||
if (add_namespace(x, xp, prefix, ns) < 0)
|
if (xml_add_namespace(x, xp, prefix, ns) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Add prefix to x, if any */
|
/* Add prefix to x, if any */
|
||||||
if (prefix && xml_prefix_set(x, prefix) < 0)
|
if (prefix && xml_prefix_set(x, prefix) < 0)
|
||||||
|
|
@ -695,493 +656,6 @@ xml_namespace_change(cxobj *x,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
*/
|
|
||||||
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 (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
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
|
|
||||||
* @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_default1(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:
|
|
||||||
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_default1(yc, xc, state) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
} /* switch */
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Ensure default values are set on existing leaf children of this node
|
|
||||||
*
|
|
||||||
* Assume yang is bound to the 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
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
xml_default(cxobj *xt,
|
|
||||||
int state)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *ys;
|
|
||||||
|
|
||||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (xml_default1(ys, xt, state) < 0)
|
|
||||||
goto done;
|
|
||||||
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;
|
|
||||||
cxobj *x;
|
|
||||||
yang_stmt *y;
|
|
||||||
|
|
||||||
if (xml_default(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 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_default1(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; i<xlen; i++){
|
|
||||||
x0 = xvec[i];
|
|
||||||
xml_flag_set(x0, XML_FLAG_MARK);
|
|
||||||
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
|
|
||||||
}
|
|
||||||
/* Create a new tree and copy over the parts from the cache that matches xpath */
|
|
||||||
if ((xpart = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if (xml_copy_marked(xcache, xpart) < 0) /* config */
|
|
||||||
goto done;
|
|
||||||
if (xml_apply(xcache, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (xml_apply(xpart, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
|
||||||
goto done;
|
|
||||||
/* Merge global pruned tree with xt */
|
|
||||||
if ((ret = xml_merge(xt, xpart, yspec, NULL)) < 1) /* XXX reason */
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (xpart)
|
|
||||||
xml_free(xpart);
|
|
||||||
if (xvec)
|
|
||||||
free(xvec);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Recursively find empty nopresence containers and default leaves, optionally purge
|
|
||||||
*
|
|
||||||
* @param[in] xn XML tree
|
|
||||||
* @param[in] purge 0: Dont remove any nodes
|
|
||||||
* 1: Remove config sub-nodes that are empty non-presence container or default leaf
|
|
||||||
* 2: Remove all sub-nodes that are empty non-presence container or default leaf
|
|
||||||
* @retval 1 Node is an (recursive) empty non-presence container or default leaf
|
|
||||||
* @retval 0 Other node
|
|
||||||
* @retval -1 Error
|
|
||||||
* @note xn is not itself removed if purge
|
|
||||||
* @note for purge=1 are removed only if config or no yang spec(!)
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_defaults_nopresence(cxobj *xn,
|
|
||||||
int purge)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cxobj *x;
|
|
||||||
cxobj *xprev;
|
|
||||||
yang_stmt *yn;
|
|
||||||
yang_stmt *y;
|
|
||||||
int rmx = 0; /* If set, remove this xn */
|
|
||||||
int ret;
|
|
||||||
enum rfc_6020 keyw;
|
|
||||||
int config = 1;
|
|
||||||
|
|
||||||
if ((yn = xml_spec(xn)) != NULL){
|
|
||||||
keyw = yang_keyword_get(yn);
|
|
||||||
if (keyw == Y_CONTAINER &&
|
|
||||||
yang_find(yn, Y_PRESENCE, NULL) == NULL)
|
|
||||||
rmx = 1;
|
|
||||||
else if (keyw == Y_LEAF &&
|
|
||||||
xml_flag(xn, XML_FLAG_DEFAULT))
|
|
||||||
rmx = 1;
|
|
||||||
config = yang_config_ancestor(yn);
|
|
||||||
}
|
|
||||||
/* Loop thru children */
|
|
||||||
x = NULL;
|
|
||||||
xprev = NULL;
|
|
||||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
|
||||||
if ((ret = xml_defaults_nopresence(x, purge)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 1){
|
|
||||||
switch (purge){
|
|
||||||
case 1: /* config nodes only */
|
|
||||||
if (!config)
|
|
||||||
break;
|
|
||||||
if ((y = xml_spec(x)) != NULL &&
|
|
||||||
!yang_config(y))
|
|
||||||
break;
|
|
||||||
/* fall thru */
|
|
||||||
case 2: /* purge all nodes */
|
|
||||||
if (xml_purge(x) < 0)
|
|
||||||
goto done;
|
|
||||||
x = xprev;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (rmx)
|
|
||||||
/* May switch an empty non-presence container (rmx=1) to non-empty non-presence container (rmx=0) */
|
|
||||||
rmx = 0;
|
|
||||||
}
|
|
||||||
retval = rmx;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
|
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
|
||||||
* @param[in] xt XML top of tree
|
* @param[in] xt XML top of tree
|
||||||
*/
|
*/
|
||||||
|
|
@ -1505,7 +979,7 @@ assign_namespace(cxobj *x1, /* target */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (add_namespace(x1, x1, prefix1, ns) < 0)
|
if (xml_add_namespace(x1, x1, prefix1, ns) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1611,7 +1085,7 @@ assign_namespace_body(cxobj *x0, /* source */
|
||||||
if (namespace1 && strcmp(namespace, namespace1)==0)
|
if (namespace1 && strcmp(namespace, namespace1)==0)
|
||||||
continue;
|
continue;
|
||||||
/* No, add entry */
|
/* No, add entry */
|
||||||
if (add_namespace(x1, x1, prefix1, namespace) < 0)
|
if (xml_add_namespace(x1, x1, prefix1, namespace) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2356,107 +1830,3 @@ purge_tagged_nodes(cxobj *xn,
|
||||||
return retval;
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
|
#include "clixon_xml_sort.h"
|
||||||
#include "clixon_xml_nsctx.h"
|
#include "clixon_xml_nsctx.h"
|
||||||
|
|
||||||
/* Undefine if you want to ensure strict namespace assignment on all netconf
|
/* Undefine if you want to ensure strict namespace assignment on all netconf
|
||||||
|
|
@ -699,3 +700,42 @@ xml2prefix(cxobj *xn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Add prefix:namespace pair to xml node, set cache, etc
|
||||||
|
* @param[in] x XML node whose namespace should change
|
||||||
|
* @param[in] xp XML node where namespace attribute should be declared (can be same)
|
||||||
|
* @param[in] prefix1 Use this prefix
|
||||||
|
* @param[in] namespace Use this namespace
|
||||||
|
* @note x and xp must be different if x is an attribute and may be different otherwise
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_add_namespace(cxobj *x,
|
||||||
|
cxobj *xp,
|
||||||
|
char *prefix,
|
||||||
|
char *namespace)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xa = NULL;
|
||||||
|
|
||||||
|
/* Add binding to x1p. We add to parent due to heurestics, so we dont
|
||||||
|
* end up in adding it to large number of siblings
|
||||||
|
*/
|
||||||
|
if (nscache_set(x, prefix, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Create xmlns attribute to x1p/x1 XXX same code v */
|
||||||
|
if (prefix){
|
||||||
|
if ((xa = xml_new(prefix, xp, CX_ATTR)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_prefix_set(xa, "xmlns") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if ((xa = xml_new("xmlns", xp, CX_ATTR)) == NULL)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xml_value_set(xa, namespace) < 0)
|
||||||
|
goto done;
|
||||||
|
xml_sort(xp); /* Ensure attr is first / XXX xml_insert? */
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -963,7 +963,7 @@ yn_insert1(yang_stmt *ys_parent,
|
||||||
|
|
||||||
/*! Iterate through all yang statements from a yang node
|
/*! Iterate through all yang statements from a yang node
|
||||||
*
|
*
|
||||||
* @param[in] yparent yang statement whose children should be iterated
|
* @param[in] yparent yang statement whose children are iterated
|
||||||
* @param[in] yprev previous child, or NULL on init
|
* @param[in] yprev previous child, or NULL on init
|
||||||
* @code
|
* @code
|
||||||
* yang_stmt *yprev = NULL;
|
* yang_stmt *yprev = NULL;
|
||||||
|
|
@ -1431,6 +1431,32 @@ yang_myroot(yang_stmt *ys)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Get closest yang case and choice, if any
|
||||||
|
*/
|
||||||
|
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){
|
||||||
|
if (ycase)
|
||||||
|
*ycase = yp;
|
||||||
|
*ychoice = yang_parent_get(yp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (yang_keyword_get(yp) == Y_CHOICE){
|
||||||
|
if (ycase)
|
||||||
|
*ycase = NULL;
|
||||||
|
*ychoice = yp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! If a given yang stmt has a choice/case as parent, return the choice statement
|
/*! If a given yang stmt has a choice/case as parent, return the choice statement
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
|
|
|
||||||
161
test/test_default_choice.sh
Executable file
161
test/test_default_choice.sh
Executable file
|
|
@ -0,0 +1,161 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tests of defaults in choices.
|
||||||
|
# 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
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
cfg=$dir/choice.xml
|
||||||
|
clidir=$dir/cli
|
||||||
|
fyang=$dir/transfer.yang
|
||||||
|
|
||||||
|
test -d ${clidir} || rm -rf ${clidir}
|
||||||
|
mkdir $clidir
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
|
||||||
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# See example in RFC 7950 Sec 7.9.3
|
||||||
|
cat <<EOF > $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 <<EOF > $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" "<rpc $DEFAULTNS><get-config><source><candidate/></source><with-defaults xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">report-all</with-defaults></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><transfer xmlns=\"urn:example:transfer\"><interval>30</interval></transfer></data></rpc-reply>"
|
||||||
|
|
||||||
|
new "Set transfer/daily"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><transfer xmlns=\"urn:example:transfer\"><daily/></transfer></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "Default value expected: time-of-day=01:00"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><with-defaults xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">report-all</with-defaults></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><transfer xmlns=\"urn:example:transfer\"><daily/><time-of-day>01.00</time-of-day></transfer></data></rpc-reply>"
|
||||||
|
|
||||||
|
new "Set list element transfer container"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><li xmlns=\"urn:example:transfer\"><x>42</x></li></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "Default value expected: interval=30"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/tr:li\" xmlns:tr=\"urn:example:transfer\"/><with-defaults xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">report-all</with-defaults></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><li xmlns=\"urn:example:transfer\"><x>42</x><transfer><interval>30</interval></transfer></li></data></rpc-reply>"
|
||||||
|
|
||||||
|
new "Set transfer/daily"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><li xmlns=\"urn:example:transfer\"><x>42</x><transfer xmlns=\"urn:example:transfer\"><daily/></transfer></li></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||||
|
|
||||||
|
new "Default value expected: time-of-day=01:00"
|
||||||
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/tr:li\" xmlns:tr=\"urn:example:transfer\"/><with-defaults xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">report-all</with-defaults></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><li xmlns=\"urn:example:transfer\"><x>42</x><transfer><daily/><time-of-day>01.00</time-of-day></transfer></li></data></rpc-reply>"
|
||||||
|
|
||||||
|
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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue